From 91459fb6723e29e923380cec0023af93819ae69d Mon Sep 17 00:00:00 2001 From: bellard <6490144+bellard@users.noreply.github.com> Date: Sun, 6 Sep 2020 18:53:08 +0200 Subject: [PATCH] 2020-01-05 release --- Changelog | 86 + Makefile | 439 + TODO | 78 + VERSION | 1 + cutils.c | 620 + cutils.h | 293 + doc/jsbignum.html | 1045 + doc/jsbignum.pdf | Bin 0 -> 199698 bytes doc/jsbignum.texi | 835 + doc/quickjs.html | 1254 + doc/quickjs.pdf | Bin 0 -> 162866 bytes doc/quickjs.texi | 983 + examples/fib.c | 72 + examples/fib_module.js | 10 + examples/hello.js | 1 + examples/hello_module.js | 6 + examples/pi_bigdecimal.js | 70 + examples/pi_bigfloat.js | 66 + examples/pi_bigint.js | 118 + examples/point.c | 151 + examples/test_fib.js | 6 + examples/test_point.js | 40 + jscompress.c | 918 + libbf.c | 8166 ++++++ libbf.h | 499 + libregexp-opcode.h | 58 + libregexp.c | 2541 ++ libregexp.h | 91 + libunicode-table.h | 4313 +++ libunicode.c | 1538 ++ libunicode.h | 124 + list.h | 100 + qjs.c | 520 + qjsc.c | 707 + qjscalc.js | 2442 ++ quickjs-atom.h | 289 + quickjs-libc.c | 2804 ++ quickjs-libc.h | 49 + quickjs-opcode.h | 371 + quickjs.c | 51583 ++++++++++++++++++++++++++++++++++++ quickjs.h | 976 + readme.txt | 1 + release.sh | 107 + repl.js | 1544 ++ run-test262.c | 2098 ++ test262.conf | 182 + test262_errors.txt | 4 + test262o.conf | 410 + test262o_errors.txt | 0 tests/bjson.c | 88 + tests/microbench.js | 1053 + tests/test262.patch | 71 + tests/test_bignum.js | 241 + tests/test_bjson.js | 125 + tests/test_builtin.js | 647 + tests/test_closure.js | 221 + tests/test_loop.js | 368 + tests/test_op.js | 363 + tests/test_qjscalc.js | 244 + tests/test_std.js | 247 + unicode_download.sh | 19 + unicode_gen.c | 3057 +++ unicode_gen_def.h | 280 + 63 files changed, 95633 insertions(+) create mode 100644 Changelog create mode 100644 Makefile create mode 100644 TODO create mode 100644 VERSION create mode 100644 cutils.c create mode 100644 cutils.h create mode 100644 doc/jsbignum.html create mode 100644 doc/jsbignum.pdf create mode 100644 doc/jsbignum.texi create mode 100644 doc/quickjs.html create mode 100644 doc/quickjs.pdf create mode 100644 doc/quickjs.texi create mode 100644 examples/fib.c create mode 100644 examples/fib_module.js create mode 100644 examples/hello.js create mode 100644 examples/hello_module.js create mode 100644 examples/pi_bigdecimal.js create mode 100644 examples/pi_bigfloat.js create mode 100644 examples/pi_bigint.js create mode 100644 examples/point.c create mode 100644 examples/test_fib.js create mode 100644 examples/test_point.js create mode 100644 jscompress.c create mode 100644 libbf.c create mode 100644 libbf.h create mode 100644 libregexp-opcode.h create mode 100644 libregexp.c create mode 100644 libregexp.h create mode 100644 libunicode-table.h create mode 100644 libunicode.c create mode 100644 libunicode.h create mode 100644 list.h create mode 100644 qjs.c create mode 100644 qjsc.c create mode 100644 qjscalc.js create mode 100644 quickjs-atom.h create mode 100644 quickjs-libc.c create mode 100644 quickjs-libc.h create mode 100644 quickjs-opcode.h create mode 100644 quickjs.c create mode 100644 quickjs.h create mode 100644 readme.txt create mode 100755 release.sh create mode 100644 repl.js create mode 100644 run-test262.c create mode 100644 test262.conf create mode 100644 test262_errors.txt create mode 100644 test262o.conf create mode 100644 test262o_errors.txt create mode 100644 tests/bjson.c create mode 100644 tests/microbench.js create mode 100644 tests/test262.patch create mode 100644 tests/test_bignum.js create mode 100644 tests/test_bjson.js create mode 100644 tests/test_builtin.js create mode 100644 tests/test_closure.js create mode 100644 tests/test_loop.js create mode 100644 tests/test_op.js create mode 100644 tests/test_qjscalc.js create mode 100644 tests/test_std.js create mode 100755 unicode_download.sh create mode 100644 unicode_gen.c create mode 100644 unicode_gen_def.h diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..73b1250 --- /dev/null +++ b/Changelog @@ -0,0 +1,86 @@ +2020-01-05: + +- always compile the bignum code. Added '--bignum' option to qjs. +- added BigDecimal +- added String.prototype.replaceAll +- misc bug fixes + +2019-12-21: + +- added nullish coalescing operator (ES2020) +- added optional chaining (ES2020) +- removed recursions in garbage collector +- test stack overflow in the parser +- improved backtrace logic +- added JS_SetHostPromiseRejectionTracker() +- allow exotic constructors +- improved c++ compatibility +- misc bug fixes + +2019-10-27: + +- added example of C class in a module (examples/test_point.js) +- added JS_GetTypedArrayBuffer() +- misc bug fixes + +2019-09-18: + +- added os.exec and other system calls +- exported JS_ValueToAtom() +- qjsc: added 'qjsc_' prefix to the generated C identifiers +- added cross-compilation support +- misc bug fixes + +2019-09-01: + +- added globalThis +- documented JS_EVAL_FLAG_COMPILE_ONLY +- added import.meta.url and import.meta.main +- added 'debugger' statement +- misc bug fixes + +2019-08-18: + +- added os.realpath, os.getcwd, os.mkdir, os.stat, os.lstat, + os.readlink, os.readdir, os.utimes, std.popen +- module autodetection +- added import.meta +- misc bug fixes + +2019-08-10: + +- added public class fields and private class fields, methods and + accessors (TC39 proposal) +- changed JS_ToCStringLen() prototype +- qjsc: handle '-' in module names and modules with the same filename +- added std.urlGet +- exported JS_GetOwnPropertyNames() and JS_GetOwnProperty() +- exported some bigint C functions +- added support for eshost in run-test262 +- misc bug fixes + +2019-07-28: + +- added dynamic import +- added Promise.allSettled +- added String.prototype.matchAll +- added Object.fromEntries +- reduced number of ticks in await +- added BigInt support in Atomics +- exported JS_NewPromiseCapability() +- misc async function and async generator fixes +- enabled hashbang support by default + +2019-07-21: + +- updated test262 tests +- updated to Unicode version 12.1.0 +- fixed missing Date object in qjsc +- fixed multi-context creation +- misc ES2020 related fixes +- simplified power and division operators in bignum extension +- fixed several crash conditions + +2019-07-09: + +- first public release diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e402d6b --- /dev/null +++ b/Makefile @@ -0,0 +1,439 @@ +# +# QuickJS Javascript Engine +# +# Copyright (c) 2017-2019 Fabrice Bellard +# Copyright (c) 2017-2019 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. + +ifeq ($(shell uname -s),Darwin) +CONFIG_DARWIN=y +endif +# Windows cross compilation from Linux +#CONFIG_WIN32=y +# use link time optimization (smaller and faster executables but slower build) +CONFIG_LTO=y +# consider warnings as errors (for development) +#CONFIG_WERROR=y +# force 32 bit build for some utilities +#CONFIG_M32=y + +ifdef CONFIG_DARWIN +# use clang instead of gcc +CONFIG_CLANG=y +CONFIG_DEFAULT_AR=y +endif + +# installation directory +prefix=/usr/local + +# use the gprof profiler +#CONFIG_PROFILE=y +# use address sanitizer +#CONFIG_ASAN=y + +OBJDIR=.obj + +ifdef CONFIG_WIN32 + CROSS_PREFIX=i686-w64-mingw32- + EXE=.exe +else + CROSS_PREFIX= + EXE= +endif +ifdef CONFIG_CLANG + HOST_CC=clang + CC=$(CROSS_PREFIX)clang + CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d + CFLAGS += -Wextra + CFLAGS += -Wno-sign-compare + CFLAGS += -Wno-missing-field-initializers + CFLAGS += -Wundef -Wuninitialized + CFLAGS += -Wunused -Wno-unused-parameter + CFLAGS += -Wwrite-strings + CFLAGS += -Wchar-subscripts -funsigned-char + CFLAGS += -MMD -MF $(OBJDIR)/$(@F).d + ifdef CONFIG_DEFAULT_AR + AR=$(CROSS_PREFIX)ar + else + ifdef CONFIG_LTO + AR=$(CROSS_PREFIX)llvm-ar + else + AR=$(CROSS_PREFIX)ar + endif + endif +else + HOST_CC=gcc + CC=$(CROSS_PREFIX)gcc + CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d + CFLAGS += -Wno-array-bounds -Wno-format-truncation + ifdef CONFIG_LTO + AR=$(CROSS_PREFIX)gcc-ar + else + AR=$(CROSS_PREFIX)ar + endif +endif +STRIP=$(CROSS_PREFIX)strip +ifdef CONFIG_WERROR +CFLAGS+=-Werror +endif +DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\" +CFLAGS+=$(DEFINES) +CFLAGS_DEBUG=$(CFLAGS) -O0 +CFLAGS_SMALL=$(CFLAGS) -Os +CFLAGS_OPT=$(CFLAGS) -O2 +CFLAGS_NOLTO:=$(CFLAGS_OPT) +LDFLAGS=-g +ifdef CONFIG_LTO +CFLAGS_SMALL+=-flto +CFLAGS_OPT+=-flto +LDFLAGS+=-flto +endif +ifdef CONFIG_PROFILE +CFLAGS+=-p +LDFLAGS+=-p +endif +ifdef CONFIG_ASAN +CFLAGS+=-fsanitize=address +LDFLAGS+=-fsanitize=address +endif +ifdef CONFIG_WIN32 +LDEXPORT= +else +LDEXPORT=-rdynamic +endif + +PROGS=qjs$(EXE) qjsc$(EXE) run-test262 +ifneq ($(CROSS_PREFIX),) +QJSC_CC=gcc +QJSC=./host-qjsc +PROGS+=$(QJSC) +else +QJSC_CC=$(CC) +QJSC=./qjsc$(EXE) +endif +ifndef CONFIG_WIN32 +PROGS+=qjscalc +endif +ifdef CONFIG_M32 +PROGS+=qjs32 qjs32_s +endif +PROGS+=libquickjs.a +ifdef CONFIG_LTO +PROGS+=libquickjs.lto.a +endif + +# examples +ifeq ($(CROSS_PREFIX),) +ifdef CONFIG_ASAN +PROGS+= +else +PROGS+=examples/hello examples/hello_module examples/test_fib +ifndef CONFIG_DARWIN +PROGS+=examples/fib.so examples/point.so +endif +endif +endif + +all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) + +QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/libbf.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o + +QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(OBJDIR)/qjscalc.o $(QJS_LIB_OBJS) + +LIBS=-lm +ifndef CONFIG_WIN32 +LIBS+=-ldl +endif + +$(OBJDIR): + mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests + +qjs$(EXE): $(QJS_OBJS) + $(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) + +qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS)) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +ifneq ($(CROSS_PREFIX),) + +$(QJSC): $(OBJDIR)/qjsc.host.o \ + $(patsubst %.o, %.host.o, $(QJS_LIB_OBJS)) + $(HOST_CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +endif #CROSS_PREFIX + +QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(prefix)\" +ifdef CONFIG_LTO +QJSC_DEFINES+=-DCONFIG_LTO +endif +QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(prefix)\" + +$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES) +$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES) + +qjs32: $(patsubst %.o, %.m32.o, $(QJS_OBJS)) + $(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) + +qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS)) + $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) + @size $@ + +qjscalc: qjs + ln -sf $< $@ + +ifdef CONFIG_LTO +LTOEXT=.lto +else +LTOEXT= +endif + +libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS) + $(AR) rcs $@ $^ + +ifdef CONFIG_LTO +libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS)) + $(AR) rcs $@ $^ +endif # CONFIG_LTO + +repl.c: $(QJSC) repl.js + $(QJSC) -c -o $@ -m repl.js + +qjscalc.c: $(QJSC) qjscalc.js + $(QJSC) -fbignum -c -o $@ qjscalc.js + +ifneq ($(wildcard unicode/UnicodeData.txt),) +$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o \ + $(OBJDIR)/libunicode.nolto.o: libunicode-table.h + +libunicode-table.h: unicode_gen + ./unicode_gen unicode $@ +endif + +run-test262: $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread + +run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread + +run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) + $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread + +# object suffix order: nolto, [m32|m32s] + +$(OBJDIR)/%.o: %.c | $(OBJDIR) + $(CC) $(CFLAGS_OPT) -c -o $@ $< + +$(OBJDIR)/%.host.o: %.c | $(OBJDIR) + $(HOST_CC) $(CFLAGS_OPT) -c -o $@ $< + +$(OBJDIR)/%.pic.o: %.c | $(OBJDIR) + $(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $< + +$(OBJDIR)/%.nolto.o: %.c | $(OBJDIR) + $(CC) $(CFLAGS_NOLTO) -c -o $@ $< + +$(OBJDIR)/%.m32.o: %.c | $(OBJDIR) + $(CC) -m32 $(CFLAGS_OPT) -c -o $@ $< + +$(OBJDIR)/%.m32s.o: %.c | $(OBJDIR) + $(CC) -m32 $(CFLAGS_SMALL) -c -o $@ $< + +$(OBJDIR)/%.debug.o: %.c | $(OBJDIR) + $(CC) $(CFLAGS_DEBUG) -c -o $@ $< + +$(OBJDIR)/%.check.o: %.c | $(OBJDIR) + $(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $< + +regexp_test: libregexp.c libunicode.c cutils.c + $(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS) + +jscompress: jscompress.c + $(CC) $(LDFLAGS) $(CFLAGS) -o $@ jscompress.c + +unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c unicode_gen_def.h + $(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o + +clean: + rm -f repl.c qjscalc.c out.c + rm -f *.a *.o *.d *~ jscompress unicode_gen regexp_test $(PROGS) + rm -f hello.c hello_module.c test_fib.c + rm -f examples/*.so tests/*.so + rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug + rm -rf run-test262-debug run-test262-32 + +install: all + mkdir -p "$(DESTDIR)$(prefix)/bin" + $(STRIP) qjs qjsc + install -m755 qjs qjsc "$(DESTDIR)$(prefix)/bin" + ln -sf qjs "$(DESTDIR)$(prefix)/bin/qjscalc" + mkdir -p "$(DESTDIR)$(prefix)/lib/quickjs" + install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs" +ifdef CONFIG_LTO + install -m644 libquickjs.lto.a "$(DESTDIR)$(prefix)/lib/quickjs" +endif + mkdir -p "$(DESTDIR)$(prefix)/include/quickjs" + install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(prefix)/include/quickjs" + +############################################################################### +# examples + +# example of static JS compilation +HELLO_SRCS=examples/hello.js +HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ + -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ + -fno-date -fno-module-loader -fno-bigint + +hello.c: $(QJSC) $(HELLO_SRCS) + $(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS) + +ifdef CONFIG_M32 +examples/hello: $(OBJDIR)/hello.m32s.o $(patsubst %.o, %.m32s.o, $(QJS_LIB_OBJS)) + $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) +else +examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) +endif + +# example of static JS compilation with modules +HELLO_MODULE_SRCS=examples/hello_module.js +HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ + -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ + -fno-date -m +examples/hello_module: $(QJSC) libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS) + $(QJSC) $(HELLO_MODULE_OPTS) -o $@ $(HELLO_MODULE_SRCS) + +# use of an external C module (static compilation) + +test_fib.c: $(QJSC) examples/test_fib.js + $(QJSC) -e -M examples/fib.so,fib -m -o $@ examples/test_fib.js + +examples/test_fib: $(OBJDIR)/test_fib.o $(OBJDIR)/examples/fib.o libquickjs$(LTOEXT).a + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +examples/fib.so: $(OBJDIR)/examples/fib.pic.o + $(CC) $(LDFLAGS) -shared -o $@ $^ + +examples/point.so: $(OBJDIR)/examples/point.pic.o + $(CC) $(LDFLAGS) -shared -o $@ $^ + +############################################################################### +# documentation + +DOCS=doc/quickjs.pdf doc/quickjs.html doc/jsbignum.pdf doc/jsbignum.html + +build_doc: $(DOCS) + +clean_doc: + rm -f $(DOCS) + +doc/%.pdf: doc/%.texi + texi2pdf --clean -o $@ -q $< + +doc/%.html.pre: doc/%.texi + makeinfo --html --no-headers --no-split --number-sections -o $@ $< + +doc/%.html: doc/%.html.pre + sed -e 's||\n|' < $< > $@ + +############################################################################### +# tests + +ifndef CONFIG_DARWIN +test: tests/bjson.so examples/point.so +endif +ifdef CONFIG_M32 +test: qjs32 +endif + +test: qjs + ./qjs tests/test_closure.js + ./qjs tests/test_op.js + ./qjs tests/test_builtin.js + ./qjs tests/test_loop.js + ./qjs tests/test_std.js +ifndef CONFIG_DARWIN + ./qjs --bignum tests/test_bjson.js + ./qjs examples/test_point.js +endif + ./qjs --bignum tests/test_bignum.js + ./qjs --qjscalc tests/test_qjscalc.js +ifdef CONFIG_M32 + ./qjs32 tests/test_closure.js + ./qjs32 tests/test_op.js + ./qjs32 tests/test_builtin.js + ./qjs32 tests/test_loop.js + ./qjs32 tests/test_std.js + ./qjs32 --bignum tests/test_bignum.js + ./qjs32 --qjscalc tests/test_qjscalc.js +endif + +stats: qjs qjs32 + ./qjs -qd + ./qjs32 -qd + +microbench: qjs + ./qjs tests/microbench.js + +microbench-32: qjs32 + ./qjs32 tests/microbench.js + +# ES5 tests (obsolete) +test2o: run-test262 + time ./run-test262 -m -c test262o.conf + +test2o-32: run-test262-32 + time ./run-test262-32 -m -c test262o.conf + +test2o-update: run-test262 + ./run-test262 -u -c test262o.conf + +# Test262 tests +test2-default: run-test262 + time ./run-test262 -m -c test262.conf + +test2: run-test262 + time ./run-test262 -m -c test262.conf -a + +test2-32: run-test262-32 + time ./run-test262-32 -m -c test262.conf -a + +test2-update: run-test262 + ./run-test262 -u -c test262.conf -a + +test2-check: run-test262 + time ./run-test262 -m -c test262.conf -E -a + +testall: all test microbench test2o test2 + +testall-32: all test-32 microbench-32 test2o-32 test2-32 + +testall-complete: testall testall-32 + +bench-v8: qjs + make -C tests/bench-v8 + ./qjs -d tests/bench-v8/combined.js + +tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o + $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) + +-include $(wildcard $(OBJDIR)/*.d) diff --git a/TODO b/TODO new file mode 100644 index 0000000..dbb4612 --- /dev/null +++ b/TODO @@ -0,0 +1,78 @@ +Misc: +- use custom printf to avoid C library compatibility issues +- rename CONFIG_ALL_UNICODE, CONFIG_BIGNUM, CONFIG_ATOMICS, CONFIG_CHECK_JSVALUE ? +- unify coding style and naming conventions +- use names from the ECMA spec in library implementation +- modules: if no ".", use a well known module loading path ? +- use JSHoistedDef only for global variables (JSHoistedDef.var_name != JS_ATOM_NULL) +- add index in JSVarDef and is_arg flag to merge args and vars in JSFunctionDef +- replace most JSVarDef flags with var_type enumeration +- use byte code emitters with typed arguments (for clarity) +- use 2 bytecode DynBufs in JSFunctionDef, one for reading, one for writing + and use the same wrappers in all phases +- use more generic method for line numbers in resolve_variables and resolve_labels +- use custom timezone support to avoid C library compatibility issues + +Memory: +- test border cases for max number of atoms, object properties, string length +- add emergency malloc mode for out of memory exceptions. +- test all DynBuf memory errors +- test all js_realloc memory errors +- bignum: handle memory errors +- use memory pools for objects, etc? +- improve JS_ComputeMemoryUsage() with more info + +Optimizations: +- 64-bit atoms in 64-bit mode ? +- use auto-init properties for more global objects +- reuse stack slots for disjoint scopes, if strip +- optimize `for of` iterator for built-in array objects +- add heuristic to avoid some cycles in closures +- small String (0-2 charcodes) with immediate storage +- perform static string concatenation at compile time +- optimize string concatenation with ropes or miniropes? +- add implicit numeric strings for Uint32 numbers? +- optimize `s += a + b`, `s += a.b` and similar simple expressions +- ensure string canonical representation and optimise comparisons and hashes? +- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references +- optimize function storage with length and name accessors? +- property access optimization on the global object, functions, + prototypes and special non extensible objects. +- create object literals with the correct length by backpatching length argument +- remove redundant set_loc_uninitialized/check_uninitialized opcodes +- peephole optim: push_atom_value, to_propkey -> push_atom_value +- peephole optim: put_loc x, get_loc_check x -> set_loc x +- comparative performance benchmark +- use variable name when throwing uninitialized exception if available +- convert slow array to fast array when all properties != length are numeric +- optimize destructuring assignments for global and local variables +- implement some form of tail-call-optimization +- optimize OP_apply +- optimize f(...b) + +Extensions: +- support more features in [features] section +- add built-in preprocessor in compiler, get rid of jscompress + handle #if, #ifdef, #line, limited support for #define +- get rid of __loadScript, use more common name +- BSD sockets +- Workers + +REPL: +- debugger +- readline: support MS Windows terminal +- readline: handle dynamic terminal resizing +- multiline editing +- runtime object and function inspectors +- interactive object browser +- use more generic approach to display evaluation results +- improve directive handling: dispatch, colorize, completion... +- save history +- close all predefined methods in repl.js and jscalc.js + +Test262o: 0/11262 errors, 463 excluded +Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) + +Test262: 4/67619 errors, 913 excluded, 1660 skipped +Test262bn: 4/69722 errors, 846 excluded, 672 skipped +test262 commit: 19fd4bea797646ae9bbfc9d325f14052ca370b54 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..242aa92 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2020-01-05 diff --git a/cutils.c b/cutils.c new file mode 100644 index 0000000..64a32d3 --- /dev/null +++ b/cutils.c @@ -0,0 +1,620 @@ +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include + +#include "cutils.h" + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +int has_suffix(const char *str, const char *suffix) +{ + size_t len = strlen(str); + size_t slen = strlen(suffix); + return (len >= slen && !memcmp(str + len - slen, suffix, slen)); +} + +/* Dynamic buffer package */ + +static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func) +{ + memset(s, 0, sizeof(*s)); + if (!realloc_func) + realloc_func = dbuf_default_realloc; + s->opaque = opaque; + s->realloc_func = realloc_func; +} + +void dbuf_init(DynBuf *s) +{ + dbuf_init2(s, NULL, NULL); +} + +/* return < 0 if error */ +int dbuf_realloc(DynBuf *s, size_t new_size) +{ + size_t size; + uint8_t *new_buf; + if (new_size > s->allocated_size) { + if (s->error) + return -1; + size = s->allocated_size * 3 / 2; + if (size > new_size) + new_size = size; + new_buf = s->realloc_func(s->opaque, s->buf, new_size); + if (!new_buf) { + s->error = TRUE; + return -1; + } + s->buf = new_buf; + s->allocated_size = new_size; + } + return 0; +} + +int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len) +{ + size_t end; + end = offset + len; + if (dbuf_realloc(s, end)) + return -1; + memcpy(s->buf + offset, data, len); + if (end > s->size) + s->size = end; + return 0; +} + +int dbuf_put(DynBuf *s, const uint8_t *data, size_t len) +{ + if (unlikely((s->size + len) > s->allocated_size)) { + if (dbuf_realloc(s, s->size + len)) + return -1; + } + memcpy(s->buf + s->size, data, len); + s->size += len; + return 0; +} + +int dbuf_put_self(DynBuf *s, size_t offset, size_t len) +{ + if (unlikely((s->size + len) > s->allocated_size)) { + if (dbuf_realloc(s, s->size + len)) + return -1; + } + memcpy(s->buf + s->size, s->buf + offset, len); + s->size += len; + return 0; +} + +int dbuf_putc(DynBuf *s, uint8_t c) +{ + return dbuf_put(s, &c, 1); +} + +int dbuf_putstr(DynBuf *s, const char *str) +{ + return dbuf_put(s, (const uint8_t *)str, strlen(str)); +} + +int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, + const char *fmt, ...) +{ + va_list ap; + char buf[128]; + int len; + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (len < sizeof(buf)) { + /* fast case */ + return dbuf_put(s, (uint8_t *)buf, len); + } else { + if (dbuf_realloc(s, s->size + len + 1)) + return -1; + va_start(ap, fmt); + vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size, + fmt, ap); + va_end(ap); + s->size += len; + } + return 0; +} + +void dbuf_free(DynBuf *s) +{ + /* we test s->buf as a fail safe to avoid crashing if dbuf_free() + is called twice */ + if (s->buf) { + s->realloc_func(s->opaque, s->buf, 0); + } + memset(s, 0, sizeof(*s)); +} + +/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes + are output. */ +int unicode_to_utf8(uint8_t *buf, unsigned int c) +{ + uint8_t *q = buf; + + if (c < 0x80) { + *q++ = c; + } else { + if (c < 0x800) { + *q++ = (c >> 6) | 0xc0; + } else { + if (c < 0x10000) { + *q++ = (c >> 12) | 0xe0; + } else { + if (c < 0x00200000) { + *q++ = (c >> 18) | 0xf0; + } else { + if (c < 0x04000000) { + *q++ = (c >> 24) | 0xf8; + } else if (c < 0x80000000) { + *q++ = (c >> 30) | 0xfc; + *q++ = ((c >> 24) & 0x3f) | 0x80; + } else { + return 0; + } + *q++ = ((c >> 18) & 0x3f) | 0x80; + } + *q++ = ((c >> 12) & 0x3f) | 0x80; + } + *q++ = ((c >> 6) & 0x3f) | 0x80; + } + *q++ = (c & 0x3f) | 0x80; + } + return q - buf; +} + +static const unsigned int utf8_min_code[5] = { + 0x80, 0x800, 0x10000, 0x00200000, 0x04000000, +}; + +static const unsigned char utf8_first_code_mask[5] = { + 0x1f, 0xf, 0x7, 0x3, 0x1, +}; + +/* return -1 if error. *pp is not updated in this case. max_len must + be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */ +int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp) +{ + int l, c, b, i; + + c = *p++; + if (c < 0x80) { + *pp = p; + return c; + } + switch(c) { + case 0xc0 ... 0xdf: + l = 1; + break; + case 0xe0 ... 0xef: + l = 2; + break; + case 0xf0 ... 0xf7: + l = 3; + break; + case 0xf8 ... 0xfb: + l = 4; + break; + case 0xfc ... 0xfd: + l = 5; + break; + default: + return -1; + } + /* check that we have enough characters */ + if (l > (max_len - 1)) + return -1; + c &= utf8_first_code_mask[l - 1]; + for(i = 0; i < l; i++) { + b = *p++; + if (b < 0x80 || b >= 0xc0) + return -1; + c = (c << 6) | (b & 0x3f); + } + if (c < utf8_min_code[l - 1]) + return -1; + *pp = p; + return c; +} + +#if 0 + +#if defined(EMSCRIPTEN) || defined(__ANDROID__) + +static void *rqsort_arg; +static int (*rqsort_cmp)(const void *, const void *, void *); + +static int rqsort_cmp2(const void *p1, const void *p2) +{ + return rqsort_cmp(p1, p2, rqsort_arg); +} + +/* not reentrant, but not needed with emscripten */ +void rqsort(void *base, size_t nmemb, size_t size, + int (*cmp)(const void *, const void *, void *), + void *arg) +{ + rqsort_arg = arg; + rqsort_cmp = cmp; + qsort(base, nmemb, size, rqsort_cmp2); +} + +#endif + +#else + +typedef void (*exchange_f)(void *a, void *b, size_t size); +typedef int (*cmp_f)(const void *, const void *, void *opaque); + +static void exchange_bytes(void *a, void *b, size_t size) { + uint8_t *ap = (uint8_t *)a; + uint8_t *bp = (uint8_t *)b; + + while (size-- != 0) { + uint8_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_byte(void *a, void *b, size_t size) { + uint8_t *ap = (uint8_t *)a; + uint8_t *bp = (uint8_t *)b; + uint8_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int16s(void *a, void *b, size_t size) { + uint16_t *ap = (uint16_t *)a; + uint16_t *bp = (uint16_t *)b; + + for (size /= sizeof(uint16_t); size-- != 0;) { + uint16_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int16(void *a, void *b, size_t size) { + uint16_t *ap = (uint16_t *)a; + uint16_t *bp = (uint16_t *)b; + uint16_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int32s(void *a, void *b, size_t size) { + uint32_t *ap = (uint32_t *)a; + uint32_t *bp = (uint32_t *)b; + + for (size /= sizeof(uint32_t); size-- != 0;) { + uint32_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int32(void *a, void *b, size_t size) { + uint32_t *ap = (uint32_t *)a; + uint32_t *bp = (uint32_t *)b; + uint32_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int64s(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + + for (size /= sizeof(uint64_t); size-- != 0;) { + uint64_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int64(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + uint64_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int128s(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + + for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) { + uint64_t t = ap[0]; + uint64_t u = ap[1]; + ap[0] = bp[0]; + ap[1] = bp[1]; + bp[0] = t; + bp[1] = u; + } +} + +static void exchange_one_int128(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + uint64_t t = ap[0]; + uint64_t u = ap[1]; + ap[0] = bp[0]; + ap[1] = bp[1]; + bp[0] = t; + bp[1] = u; +} + +static inline exchange_f exchange_func(const void *base, size_t size) { + switch (((uintptr_t)base | (uintptr_t)size) & 15) { + case 0: + if (size == sizeof(uint64_t) * 2) + return exchange_one_int128; + else + return exchange_int128s; + case 8: + if (size == sizeof(uint64_t)) + return exchange_one_int64; + else + return exchange_int64s; + case 4: + case 12: + if (size == sizeof(uint32_t)) + return exchange_one_int32; + else + return exchange_int32s; + case 2: + case 6: + case 10: + case 14: + if (size == sizeof(uint16_t)) + return exchange_one_int16; + else + return exchange_int16s; + default: + if (size == 1) + return exchange_one_byte; + else + return exchange_bytes; + } +} + +static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) +{ + uint8_t *basep = (uint8_t *)base; + size_t i, n, c, r; + exchange_f swap = exchange_func(base, size); + + if (nmemb > 1) { + i = (nmemb / 2) * size; + n = nmemb * size; + + while (i > 0) { + i -= size; + for (r = i; (c = r * 2 + size) < n; r = c) { + if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0) + c += size; + if (cmp(basep + r, basep + c, opaque) > 0) + break; + swap(basep + r, basep + c, size); + } + } + for (i = n - size; i > 0; i -= size) { + swap(basep, basep + i, size); + + for (r = 0; (c = r * 2 + size) < i; r = c) { + if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0) + c += size; + if (cmp(basep + r, basep + c, opaque) > 0) + break; + swap(basep + r, basep + c, size); + } + } + } +} + +static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque) +{ + return cmp(a, b, opaque) < 0 ? + (cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) : + (cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c )); +} + +/* pointer based version with local stack and insertion sort threshhold */ +void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) +{ + struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack; + uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m; + size_t m4, i, lt, gt, span, span2; + int c, depth; + exchange_f swap = exchange_func(base, size); + exchange_f swap_block = exchange_func(base, size | 128); + + if (nmemb < 2 || size <= 0) + return; + + sp->base = (uint8_t *)base; + sp->count = nmemb; + sp->depth = 0; + sp++; + + while (sp > stack) { + sp--; + ptr = sp->base; + nmemb = sp->count; + depth = sp->depth; + + while (nmemb > 6) { + if (++depth > 50) { + /* depth check to ensure worst case logarithmic time */ + heapsortx(ptr, nmemb, size, cmp, opaque); + nmemb = 0; + break; + } + /* select median of 3 from 1/4, 1/2, 3/4 positions */ + /* should use median of 5 or 9? */ + m4 = (nmemb >> 2) * size; + m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque); + swap(ptr, m, size); /* move the pivot to the start or the array */ + i = lt = 1; + pi = plt = ptr + size; + gt = nmemb; + pj = pgt = top = ptr + nmemb * size; + for (;;) { + while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) { + if (c == 0) { + swap(plt, pi, size); + lt++; + plt += size; + } + i++; + pi += size; + } + while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) { + if (c == 0) { + gt--; + pgt -= size; + swap(pgt, pj, size); + } + } + if (pi >= pj) + break; + swap(pi, pj, size); + i++; + pi += size; + } + /* array has 4 parts: + * from 0 to lt excluded: elements identical to pivot + * from lt to pi excluded: elements smaller than pivot + * from pi to gt excluded: elements greater than pivot + * from gt to n excluded: elements identical to pivot + */ + /* move elements identical to pivot in the middle of the array: */ + /* swap values in ranges [0..lt[ and [i-lt..i[ + swapping the smallest span between lt and i-lt is sufficient + */ + span = plt - ptr; + span2 = pi - plt; + lt = i - lt; + if (span > span2) + span = span2; + swap_block(ptr, pi - span, span); + /* swap values in ranges [gt..top[ and [i..top-(top-gt)[ + swapping the smallest span between top-gt and gt-i is sufficient + */ + span = top - pgt; + span2 = pgt - pi; + pgt = top - span2; + gt = nmemb - (gt - i); + if (span > span2) + span = span2; + swap_block(pi, top - span, span); + + /* now array has 3 parts: + * from 0 to lt excluded: elements smaller than pivot + * from lt to gt excluded: elements identical to pivot + * from gt to n excluded: elements greater than pivot + */ + /* stack the larger segment and keep processing the smaller one + to minimize stack use for pathological distributions */ + if (lt > nmemb - gt) { + sp->base = ptr; + sp->count = lt; + sp->depth = depth; + sp++; + ptr = pgt; + nmemb -= gt; + } else { + sp->base = pgt; + sp->count = nmemb - gt; + sp->depth = depth; + sp++; + nmemb = lt; + } + } + /* Use insertion sort for small fragments */ + for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) { + for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size) + swap(pj, pj - size, size); + } + } +} + +#endif diff --git a/cutils.h b/cutils.h new file mode 100644 index 0000000..26c68ee --- /dev/null +++ b/cutils.h @@ -0,0 +1,293 @@ +/* + * 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 +#include + +/* set if CPU is big endian */ +#undef WORDS_BIGENDIAN + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#define force_inline inline __attribute__((always_inline)) +#define no_inline __attribute__((noinline)) +#define __maybe_unused __attribute__((unused)) + +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#endif + +typedef int BOOL; + +#ifndef FALSE +enum { + FALSE = 0, + TRUE = 1, +}; +#endif + +void pstrcpy(char *buf, int buf_size, const char *str); +char *pstrcat(char *buf, int buf_size, const char *s); +int strstart(const char *str, const char *val, const char **ptr); +int has_suffix(const char *str, const char *suffix); + +static inline int max_int(int a, int b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int min_int(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +static inline uint32_t max_uint32(uint32_t a, uint32_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline uint32_t min_uint32(uint32_t a, uint32_t b) +{ + if (a < b) + return a; + else + return b; +} + +static inline int64_t max_int64(int64_t a, int64_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int64_t min_int64(int64_t a, int64_t b) +{ + if (a < b) + return a; + else + return b; +} + +/* WARNING: undefined if a = 0 */ +static inline int clz32(unsigned int a) +{ + return __builtin_clz(a); +} + +/* WARNING: undefined if a = 0 */ +static inline int clz64(uint64_t a) +{ + return __builtin_clzll(a); +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz32(unsigned int a) +{ + return __builtin_ctz(a); +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz64(uint64_t a) +{ + return __builtin_ctzll(a); +} + +struct __attribute__((packed)) packed_u64 { + uint64_t v; +}; + +struct __attribute__((packed)) packed_u32 { + uint32_t v; +}; + +struct __attribute__((packed)) packed_u16 { + uint16_t v; +}; + +static inline uint64_t get_u64(const uint8_t *tab) +{ + return ((const struct packed_u64 *)tab)->v; +} + +static inline int64_t get_i64(const uint8_t *tab) +{ + return (int64_t)((const struct packed_u64 *)tab)->v; +} + +static inline void put_u64(uint8_t *tab, uint64_t val) +{ + ((struct packed_u64 *)tab)->v = val; +} + +static inline uint32_t get_u32(const uint8_t *tab) +{ + return ((const struct packed_u32 *)tab)->v; +} + +static inline int32_t get_i32(const uint8_t *tab) +{ + return (int32_t)((const struct packed_u32 *)tab)->v; +} + +static inline void put_u32(uint8_t *tab, uint32_t val) +{ + ((struct packed_u32 *)tab)->v = val; +} + +static inline uint32_t get_u16(const uint8_t *tab) +{ + return ((const struct packed_u16 *)tab)->v; +} + +static inline int32_t get_i16(const uint8_t *tab) +{ + return (int16_t)((const struct packed_u16 *)tab)->v; +} + +static inline void put_u16(uint8_t *tab, uint16_t val) +{ + ((struct packed_u16 *)tab)->v = val; +} + +static inline uint32_t get_u8(const uint8_t *tab) +{ + return *tab; +} + +static inline int32_t get_i8(const uint8_t *tab) +{ + return (int8_t)*tab; +} + +static inline void put_u8(uint8_t *tab, uint8_t val) +{ + *tab = val; +} + +static inline uint16_t bswap16(uint16_t x) +{ + return (x >> 8) | (x << 8); +} + +static inline uint32_t bswap32(uint32_t v) +{ + return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | + ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); +} + +static inline uint64_t bswap64(uint64_t v) +{ + return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | + ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | + ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | + ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | + ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | + ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | + ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | + ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); +} + +/* 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; +} + +#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 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/doc/jsbignum.html b/doc/jsbignum.html new file mode 100644 index 0000000..c60dc45 --- /dev/null +++ b/doc/jsbignum.html @@ -0,0 +1,1045 @@ + + + + +Javascript Bignum Extensions + + + + + + + + + + + + + + + +

Javascript Bignum Extensions

+ + +

Table of Contents

+ + + + + +

1 Introduction

+ +

The Bignum extensions add the following features to the Javascript +language while being 100% backward compatible: +

+
    +
  • Overloading of the standard operators +to support new types such as complex numbers, fractions or matrices. + +
  • Bigint mode where arbitrarily large integers are available by default (no n suffix is necessary as in the TC39 BigInt proposal1). + +
  • Arbitrarily large floating point numbers (BigFloat) in base 2 using the IEEE 754 semantics. + +
  • Optional math mode which modifies the semantics of the division, modulo and power operator. The division and power operator return a fraction with integer operands and the modulo operator is defined as the Euclidian remainder. + +
+ +

The extensions are independent from each other except the math +mode which relies on the bigint mode and the operator overloading. +

+ +

2 Operator overloading

+ + +

2.1 Introduction

+ +

If the operands of an operator have at least one object type, a custom +operator method is searched before doing the legacy Javascript +ToNumber conversion. +

+

For unary operators, the custom function is looked up in the object +and has the following name: +

+
+
unary +
+

Symbol.operatorPlus +

+
+
unary -
+

Symbol.operatorNeg +

+
+
++
+

Symbol.operatorInc +

+
+
--
+

Symbol.operatorDec +

+
+
~
+

Symbol.operatorNot +

+
+
+ +

For binary operators: +

+
    +
  • If both operands have the same constructor function, then the operator +is looked up in the constructor. + +
  • Otherwise, the property Symbol.operatorOrder is looked up in both +constructors and converted to Int32. The operator is then +looked in the constructor with the larger Symbol.operatorOrder +value. A TypeError is raised if both constructors have the same +Symbol.operatorOrder value. + +
+ +

The operator is looked up with the following name: +

+
+
+
+

Symbol.operatorAdd +

+
+
-
+

Symbol.operatorSub +

+
+
*
+

Symbol.operatorMul +

+
+
/
+

Symbol.operatorDiv +

+
+
%
+

Symbol.operatorMod +

+
+
% (math mode)
+

Symbol.operatorMathMod +

+
+
**
+

Symbol.operatorPow +

+
+
|
+

Symbol.operatorOr +

+
+
^
+

Symbol.operatorXor +

+
+
&
+

Symbol.operatorAnd +

+
+
<<
+

Symbol.operatorShl +

+
+
>>
+

Symbol.operatorShr +

+
+
<
+

Symbol.operatorCmpLT +

+
+
>
+

Symbol.operatorCmpLT, operands swapped +

+
+
<=
+

Symbol.operatorCmpLE +

+
+
>=
+

Symbol.operatorCmpLE, operands swapped +

+
+
==, !=
+

Symbol.operatorCmpEQ +

+
+
+ +

The return value of Symbol.operatorCmpLT, Symbol.operatorCmpLE and +Symbol.operatorCmpEQ is converted to Boolean. +

+ +

2.2 Builtin Object changes

+ + +

2.2.1 Symbol constructor

+ +

The following global symbols are added for the operator overloading: +

+
operatorOrder
+
operatorAdd
+
operatorSub
+
operatorMul
+
operatorDiv
+
operatorMod
+
operatorPow
+
operatorShl
+
operatorShr
+
operatorAnd
+
operatorOr
+
operatorXor
+
operatorCmpLT
+
operatorCmpLE
+
operatorCmpEQ
+
operatorPlus
+
operatorNeg
+
operatorNot
+
operatorInc
+
operatorDec
+
+ + + +

3 The BigInt Mode

+ + +

3.1 Introduction

+ +

The bigint mode is enabled with the "use bigint" directive. It +propagates the same way as the strict mode. In bigint mode, all +integers are considered as bigint (arbitrarily large integer, +similar to the TC39 BigInt +proposal2) +instead of number (floating point number). In order to be able +to exchange data between standard and bigint modes, numbers are +internally represented as 3 different types: +

+
    +
  • Small integer (SmallInt): 32 bit integer3. + +
  • Big integer (BigInt): arbitrarily large integer. + +
  • Floating point number (Float). + +
+ +

In standard mode, the semantics of each operation is modified so that +when it returns a number, it is either of SmallInt or +Float. But the difference between SmallInt and Float is not observable +in standard mode. +

+

In bigint mode, each operation behaves differently whether its +operands are integer or float. The difference between SmallInt and +BigInt is not observable (i.e. they are both integers). +

+

The following table summarizes the observable types: +

+ + + + + +
Internal typeObservable type
(standard mode)
Observable type
(bigint mode)
SmallIntnumberbigint
BigIntbigintbigint
Floatnumbernumber
+ + +

3.2 Changes that introduce incompatibilities with Javascript

+ + +

3.2.1 Standard mode

+ +

There is no incompatibility with Javascript. +

+ +

3.2.2 Bigint mode

+ +

The following changes are visible: +

+
    +
  • Integer and Float are different types. Constants are typed. For example: typeof 1.0 === "number" and typeof 1 === "bigint". Another consequence is that 1.0 === 1 is false. + +
  • The range of integers is unlimited. In standard mode: 2**53 + 1 === 2**53. This is no longer true with the bignum extensions. + +
  • Binary bitwise operators do not truncate to 32 bits i.e. 0x800000000 | 1 === 0x800000001 while it gives 1 in standard mode. + +
  • Bitwise shift operators do not truncate to 32 bits and do not mask the shift count with 0x1f i.e. 1 << 32 === 4294967296 while it gives 1 in standard mode. However, the >>> operator (unsigned right shift) which is useless with bignums keeps its standard mode behavior4. + +
  • Operators with integer operands never return the minus zero floating point value as result. Hence Object.is(0, -0) === true. Use -0.0 to create a minus zero floating point value. + +
  • The ToPrimitive abstract operation is called with the "integer" preferred type when an integer is required (e.g. for bitwise binary or shift operations). + +
  • The prototype of integers is no longer Number.prototype. Instead
    Object.getPrototypeOf(1) === BigInt.prototype. The prototype of floats remains Number.prototype. + +
  • If the TC39 BigInt proposal is supported, there is no observable difference between integers and bigints. + +
+ + +

3.3 Operators

+ + +

3.3.1 Arithmetic operators

+ +

The operands are converted to number values as in normal +Javascript. Then the general case is that an Integer is returned if +both operands are Integer. Otherwise, a float is returned. +

+

The + operator also accepts strings as input and behaves like +standard Javascript in this case. +

+

The binary operator % returns the truncated remainder of the +division. When the result is an Integer type, a dividend of zero yields a +RangeError exception. +

+

The binary operator % in math mode returns the Euclidian +remainder of the division i.e. it is always positive. +

+

The binary operator / returns a float. +

+

The binary operator / in math mode returns a float if one of +the operands is float. Otherwise, BigInt[Symbol.operatorDiv] is +invoked. +

+

The returned type of a ** b is Float if a or b +are Float. If a and b are integers: +

    +
  • b < 0 returns a Float in bigint mode. In math mode, BigInt[Symbol.operatorPow] is invoked. + +
  • b >= 0 returns an integer. +
+ +

The unary - and unary + return the same type as their +operand. They performs no floating point rounding when the result is a +float. +

+

The unary operators ++ and -- return the same type as +their operand. +

+

In standard mode: +

+

If the operator returns an Integer and that the result fits a +SmallInt, it is converted to SmallInt. Otherwise, the Integer is +converted to a Float. +

+

In bigint mode: +

+

If the operator returns an Integer and that the result fits a +SmallInt, it is converted to SmallInt. Otherwise it is a BigInt. +

+ +

3.3.2 Logical operators

+ +

In standard mode: +

+

The operands have their standard behavior. If the result fits a +SmallInt it is converted to a SmallInt. Otherwise it is a Float. +

+

In bigint mode: +

+

The operands are converted to integer values. The floating point +values are converted to integer by rounding them to zero. +

+

The logical operators are defined assuming the integers are +represented in two complement notation. +

+

For << and <<, the shift can be positive or negative. So +a << b is defined as \lfloor a/2^{-b} \rfloor and +a >> b is defined as \lfloor a/2^{b} \rfloor. +

+

The operator >>> is supported for backward compatibility and +behaves the same way as Javascript i.e. implicit conversion to Uint32. +

+

If the result fits a SmallInt it is converted to a SmallInt. Otherwise +it is a BigInt. +

+ +

3.3.3 Relational operators

+ +

The relational operators <, <=, >, >=, ==, != work as expected with +integers and floating point numbers (e.g. 1.0 == 1 is true). +

+

The strict equality operators === and !== have the usual Javascript +semantics. In particular, different types never equal, so 1.0 +=== 1 is false. +

+ +

3.4 Number literals

+ +

Number literals in bigint mode have a slightly different behavior than +in standard Javascript: +

+
    +
  1. A number literal without a decimal point or an exponent is considered +as an Integer. Otherwise it is a Float. + +
  2. Hexadecimal, octal or binary floating point literals are accepted with +a decimal point or an exponent. The exponent is specified with the +p letter assuming a base 2. The same convention is used by +C99. Example: 0x1p3 is the same as 8.0. + +
+ + +

3.5 Builtin Object changes

+ + +

3.5.1 BigInt function

+ +

The BigInt function cannot be invoked as a constructor. When +invoked as a function, it converts its first parameter to an +integer. When a floating point number is given as parameter, it is +truncated to an integer with infinite precision. +

+

BigInt properties: +

+
+
asIntN(bits, a)
+

Set b=a \pmod{2^{bits}}. Return b if b < 2^{bits-1} +otherwise b-2^{bits}. +

+
+
asUintN(bits, a)
+

Return a \pmod{2^{bits}}. +

+
+
tdiv(a, b)
+

Return trunc(a/b). b = 0 raises a RangeError +exception. +

+
+
fdiv(a, b)
+

Return \lfloor a/b \rfloor. b = 0 raises a RangeError +exception. +

+
+
cdiv(a, b)
+

Return \lceil a/b \rceil. b = 0 raises a RangeError +exception. +

+
+
ediv(a, b)
+

Return sgn(b) \lfloor a/{|b|} \rfloor (Euclidian +division). b = 0 raises a RangeError exception. +

+
+
tdivrem(a, b)
+
fdivrem(a, b)
+
cdivrem(a, b)
+
edivrem(a, b)
+

Return an array of two elements. The first element is the quotient, +the second is the remainder. The same rounding is done as the +corresponding division operation. +

+
+
sqrt(a)
+

Return \lfloor \sqrt(a) \rfloor. A RangeError exception is +raised if a < 0. +

+
+
sqrtrem(a)
+

Return an array of two elements. The first element is \lfloor +\sqrt{a} \rfloor. The second element is a-\lfloor \sqrt{a} +\rfloor^2. A RangeError exception is raised if a < 0. +

+
+
floorLog2(a)
+

Return -1 if a \leq 0 otherwise return \lfloor \log2(a) \rfloor. +

+
+
ctz(a)
+

Return the number of trailing zeros in the two’s complement binary representation of a. Return -1 if a=0. +

+
+
+ + +

3.5.2 BigInt.prototype

+ +

It is a normal object. +

+ +

3.5.3 Number constructor

+ +

The number constructor returns its argument rounded to a Float using +the global floating point environment. In bigint mode, the Number +constructor returns a Float. In standard mode, it returns a SmallInt +if the value fits it, otherwise a Float. +

+ +

3.5.4 Number.prototype

+ +

The following properties are modified: +

+
+
toString(radix)
+
+

In bigint mode, integers are converted to the specified radix with +infinite precision. +

+
+
toPrecision(p)
+
toFixed(p)
+
toExponential(p)
+
+

In bigint mode, integers are accepted and converted to string with +infinite precision. +

+
+
parseInt(string, radix)
+
+

In bigint mode, an integer is returned and the conversion is done with +infinite precision. +

+
+
+ + +

3.5.5 Math object

+ +

The following properties are modified: +

+
+
abs(x)
+

Absolute value. Return an integer if x is an Integer. Otherwise +return a Float. No rounding is performed. +

+
+
min(a, b)
+
max(a, b)
+

No rounding is performed. The returned type is the same one as the +minimum (resp. maximum) value. +

+
+
+ + +

4 Arbitrarily large floating point numbers

+ + +

4.1 Introduction

+ +

This extension adds the BigFloat primitive type. The +BigFloat type represents floating point numbers are in base 2 +with the IEEE 754 semantics. A floating +point number is represented as a sign, mantissa and exponent. The +special values NaN, +/-Infinity, +0 and -0 +are supported. The mantissa and exponent can have any bit length with +an implementation specific minimum and maximum. +

+ +

4.2 Floating point rounding

+ +

Each floating point operation operates with infinite precision and +then rounds the result according to the specified floating point +environment (BigFloatEnv object). The status flags of the +environment are also set according to the result of the operation. +

+

If no floating point environment is provided, the global floating +point environment is used. +

+

The rounding mode of the global floating point environment is always +RNDN (“round to nearest with ties to even”)5. The status flags of the global environment cannot be +read6. The precision of the global environment is +BigFloatEnv.prec. The number of exponent bits of the global +environment is BigFloatEnv.expBits. If BigFloatEnv.expBits is +strictly smaller than the maximum allowed number of exponent bits +(BigFloatEnv.expBitsMax), then the global environment subnormal +flag is set to true. Otherwise it is set to false; +

+

For example, prec = 53 and expBits = 11 give exactly +the same precision as the IEEE 754 64 bit floating point. The +default precision is prec = 113 and expBits = 15 (IEEE +754 128 bit floating point). +

+

The global floating point environment can only be modified temporarily +when calling a function (see BigFloatEnv.setPrec). Hence a +function can change the global floating point environment for its +callees but not for its caller. +

+ +

4.3 Operators

+ +

The builtin operators are extended so that a BigFloat is returned if +at least one operand is a BigFloat. The computations are always done +with infinite precision and rounded according to the global floating +point environment. +

+

typeof applied on a BigFloat returns bigfloat. +

+

BigFloat can be compared with all the other numeric types and the +result follows the expected mathematical relations. +

+

However, since BigFloat and Number are different types they are never +equal when using the strict comparison operators (e.g. 0.0 === +0.0l is false). +

+ +

4.4 BigFloat literals

+ +

BigFloat literals are floating point numbers with a trailing l +suffix. BigFloat literals have an infinite precision. They are rounded +according to the global floating point environment when they are +evaluated.7 +

+ +

4.5 Builtin Object changes

+ + +

4.5.1 BigFloat function

+ +

The BigFloat function cannot be invoked as a constructor. When +invoked as a function: the parameter is converted to a primitive +type. If the result is a numeric type, it is converted to BigFloat +without rounding. If the result is a string, it is converted to +BigFloat using the precision of the global floating point environment. +

+

BigFloat properties: +

+
+
LN2
+
PI
+

Getter. Return the value of the corresponding mathematical constant +rounded to nearest, ties to even with the current global +precision. The constant values are cached for small precisions. +

+
+
MIN_VALUE
+
MAX_VALUE
+
EPSILON
+

Getter. Return the minimum, maximum and epsilon BigFloat values +(same definition as the corresponding Number constants). +

+
+
fpRound(a[, e])
+

Round the floating point number a according to the floating +point environment e or the global environment if e is +undefined. +

+
+
parseFloat(a[, radix[, e]])
+

Parse the string a as a floating point number in radix +radix. The radix is 0 (default) or from 2 to 36. The radix 0 +means radix 10 unless there is a hexadecimal or binary prefix. The +result is rounded according to the floating point environment e +or the global environment if e is undefined. +

+
+
isFinite(a)
+

Return true if a is a finite bigfloat. +

+
+
isNaN(a)
+

Return true if a is a NaN bigfloat. +

+
+
add(a, b[, e])
+
sub(a, b[, e])
+
mul(a, b[, e])
+
div(a, b[, e])
+

Perform the specified floating point operation and round the floating +point number a according to the floating point environment +e or the global environment if e is undefined. If +e is specified, the floating point status flags are updated. +

+
+
floor(x)
+
ceil(x)
+
round(x)
+
trunc(x)
+

Round to an integer. No additional rounding is performed. +

+
+
fmod(x, y[, e])
+
remainder(x, y[, e])
+

Floating point remainder. The quotient is truncated to zero (fmod) or +to the nearest integer with ties to even (remainder). e is an +optional floating point environment. +

+
+
sqrt(x[, e])
+

Square root. Return a rounded floating point number. e is an +optional floating point environment. +

+
+
sin(x[, e])
+
cos(x[, e])
+
tan(x[, e])
+
asin(x[, e])
+
acos(x[, e])
+
atan(x[, e])
+
atan2(x, y[, e])
+
exp(x[, e])
+
log(x[, e])
+
pow(x, y[, e])
+

Transcendental operations. Return a rounded floating point +number. e is an optional floating point environment. +

+
+
+ + +

4.5.2 BigFloat.prototype

+ +

The following properties are modified: +

+
+
toString(radix)
+
+

For floating point numbers: +

+
    +
  • If the radix is a power of two, the conversion is done with infinite +precision. +
  • Otherwise, the number is rounded to nearest with ties to even using +the global precision. It is then converted to string using the minimum +number of digits so that its conversion back to a floating point using +the global precision and round to nearest gives the same number. + +
+ +
+
toPrecision(p[, rnd_mode])
+
toFixed(p[, rnd_mode])
+
toExponential(p[, rnd_mode])
+

Same semantics as the corresponding Number functions with +BigFloats. There is no limit on the accepted precision p. The +rounding mode can be optionally specified. It is set by default to +BigFloatEnv.RNDNA. +

+
+
+ + +

4.5.3 BigFloatEnv constructor

+ +

The BigFloatEnv([p, [,rndMode]] constructor cannot be invoked as a +function. The floating point environment contains: +

+
    +
  • the mantissa precision in bits + +
  • the exponent size in bits assuming an IEEE 754 representation; + +
  • the subnormal flag (if true, subnormal floating point numbers can +be generated by the floating point operations). + +
  • the rounding mode + +
  • the floating point status. The status flags can only be set by the floating point operations. They can be reset with BigFloatEnv.prototype.clearStatus() or with the various status flag setters. + +
+ +

new BigFloatEnv([p, [,rndMode]] creates a new floating point +environment. The status flags are reset. If no parameter is given the +precision, exponent bits and subnormal flags are copied from the +global floating point environment. Otherwise, the precision is set to +p, the number of exponent bits is set to expBitsMax and the +subnormal flags is set to false. If rndMode is +undefined, the rounding mode is set to RNDN. +

+

BigFloatEnv properties: +

+
+
prec
+

Getter. Return the precision in bits of the global floating point +environment. The initial value is 53. +

+
+
expBits
+

Getter. Return the exponent size in bits of the global floating point +environment assuming an IEEE 754 representation. If expBits < +expBitsMax, then subnormal numbers are supported. The initial value +is 11. +

+
+
setPrec(f, p[, e])
+

Set the precision of the global floating point environment to p +and the exponent size to e then call the function +f. Then the Float precision and exponent size are reset to +their precious value and the return value of f is returned (or +an exception is raised if f raised an exception). If e +is undefined it is set to BigFloatEnv.expBitsMax. p +must be >= 53 and e must be >= 11 so that the global precision +is at least equivalent to the IEEE 754 64 bit doubles. +

+
+
precMin
+

Read-only integer. Return the minimum allowed precision. Must be at least 2. +

+
+
precMax
+

Read-only integer. Return the maximum allowed precision. Must be at least 53. +

+
+
expBitsMin
+

Read-only integer. Return the minimum allowed exponent size in +bits. Must be at least 3. +

+
+
expBitsMax
+

Read-only integer. Return the maximum allowed exponent size in +bits. Must be at least 11. +

+
+
RNDN
+

Read-only integer. Round to nearest, with ties to even rounding mode. +

+
+
RNDZ
+

Read-only integer. Round to zero rounding mode. +

+
+
RNDD
+

Read-only integer. Round to -Infinity rounding mode. +

+
+
RNDU
+

Read-only integer. Round to +Infinity rounding mode. +

+
+
RNDNA
+

Read-only integer. Round to nearest, with ties away from zero rounding mode. +

+
+
RNDNU
+

Read-only integer. Round to nearest, with ties to +Infinity rounding mode. +

+
+
RNDF8
+

Read-only integer. Faithful rounding mode. The result is +non-deterministically rounded to -Infinity or +Infinity. This rounding +mode usually gives a faster and deterministic running time for the +floating point operations. +

+
+
+ +

BigFloatEnv.prototype properties: +

+
+
prec
+

Getter and setter (Integer). Return or set the precision in bits. +

+
+
expBits
+

Getter and setter (Integer). Return or set the exponent size in bits +assuming an IEEE 754 representation. +

+
+
rndMode
+

Getter and setter (Integer). Return or set the rounding mode. +

+
+
subnormal
+

Getter and setter (Boolean). subnormal flag. It is false when +expBits = expBitsMax. +

+
+
clearStatus()
+

Clear the status flags. +

+
+
invalidOperation
+
divideByZero
+
overflow
+
underflow
+
inexact
+

Getter and setter (Boolean). Status flags. +

+
+
+ + +

4.5.4 Math object

+ +

The following properties are modified: +

+
+
abs(x)
+

Absolute value. If x is a BigFloat, its absolute value is +returned as a BigFloat. No rounding is performed. +

+
+
min(a, b)
+
max(a, b)
+

The returned type is the same one as the minimum (resp. maximum) +value, so BigFloat values are accepted. When a BigFloat +is returned, no rounding is performed. +

+
+
+ + +

5 Math mode

+ + +

5.1 Introduction

+ +

A new math mode is enabled with the "use math" +directive. "use bigint" is implied in math mode. With this +mode, writing mathematical expressions is more intuitive, exact +results (e.g. fractions) can be computed for all operators and floating +point literals have the BigFloat type by default. +

+

It propagates the same way as the strict mode. In +this mode: +

+
    +
  • The ^ operator is a similar to the power operator (**). + +
  • The power operator (both ^ and **) grammar is modified so that -2^2 is allowed and yields -4. + +
  • The logical xor operator is still available with the ^^ operator. + +
  • The division operator invokes BigInt[Symbol.operatorDiv] in case both operands are integers. + +
  • The power operator invokes BigInt[Symbol.operatorPow] in case both operands are integers and the exponent is strictly negative. + +
  • The modulo operator returns the Euclidian remainder (always positive) instead of the truncated remainder. + +
  • Floating point literals are BigFloat by default (i.e. a l suffix is implied). + +
+ + +

5.2 Builtin Object changes

+ + +

5.2.1 Symbol constructor

+ +

The following global symbol is added for the operator overloading: +

+
operatorMathMod
+
+ + +

5.3 Remaining issues

+ +
    +
  1. A new floating point literal suffix could be added for Number literals. + +
+ +
+
+

Footnotes

+ +

(1)

+

https://tc39.github.io/proposal-bigint/

+

(2)

+

https://tc39.github.io/proposal-bigint/

+

(3)

+

Could be extended to 53 bits without changing the principle.

+

(4)

+

The unsigned right right operator could be removed in bigint mode.

+

(5)

+

The +rationale is that the rounding mode changes must always be +explicit.

+

(6)

+

The rationale is to avoid side effects for the built-in +operators.

+

(7)

+

Base 10 floating point literals cannot usually be +exactly represented as base 2 floating point number. In order to +ensure that the literal is represented accurately with the current +precision, it must be evaluated at runtime.

+

(8)

+

Could be removed in case a deterministic behavior for floating point operations is required.

+
+
+ + + + + diff --git a/doc/jsbignum.pdf b/doc/jsbignum.pdf new file mode 100644 index 0000000000000000000000000000000000000000..16a0a35d374493f79c148c97211c68f74343da10 GIT binary patch literal 199698 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

9iNtsY(AoP)sJ!u?uq*5QvR z9KW9T)UgDww)q5C6vWB&;}#>spO}Xgw|@cv!uIyN66M2hVE_=ff(amK4ZQVutlQkj zQz5d^i;bnfc)65cPVFBLgVLASJZ?7> +@code{Symbol.operatorShr} + +@item < +@code{Symbol.operatorCmpLT} + +@item > +@code{Symbol.operatorCmpLT}, operands swapped + +@item <= +@code{Symbol.operatorCmpLE} + +@item >= +@code{Symbol.operatorCmpLE}, operands swapped + +@item ==, != +@code{Symbol.operatorCmpEQ} + +@end table + +The return value of @code{Symbol.operatorCmpLT}, @code{Symbol.operatorCmpLE} and +@code{Symbol.operatorCmpEQ} is converted to @code{Boolean}. + +@section Builtin Object changes + +@subsection @code{Symbol} constructor + +The following global symbols are added for the operator overloading: +@table @code +@item operatorOrder +@item operatorAdd +@item operatorSub +@item operatorMul +@item operatorDiv +@item operatorMod +@item operatorPow +@item operatorShl +@item operatorShr +@item operatorAnd +@item operatorOr +@item operatorXor +@item operatorCmpLT +@item operatorCmpLE +@item operatorCmpEQ +@item operatorPlus +@item operatorNeg +@item operatorNot +@item operatorInc +@item operatorDec +@end table + + +@chapter The BigInt Mode + +@section Introduction + +The bigint mode is enabled with the @code{"use bigint"} directive. It +propagates the same way as the strict mode. In bigint mode, all +integers are considered as @code{bigint} (arbitrarily large integer, +similar to the TC39 BigInt +proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}}) +instead of @code{number} (floating point number). In order to be able +to exchange data between standard and bigint modes, numbers are +internally represented as 3 different types: + +@itemize + +@item Small integer (SmallInt): 32 bit integer@footnote{Could be extended to 53 bits without changing the principle.}. + +@item Big integer (BigInt): arbitrarily large integer. + +@item Floating point number (Float). + +@end itemize + +In standard mode, the semantics of each operation is modified so that +when it returns a @code{number}, it is either of SmallInt or +Float. But the difference between SmallInt and Float is not observable +in standard mode. + +In bigint mode, each operation behaves differently whether its +operands are integer or float. The difference between SmallInt and +BigInt is not observable (i.e. they are both integers). + +The following table summarizes the observable types: + +@multitable @columnfractions .3 .3 .3 +@headitem Internal type @tab Observable type@* (standard mode) @tab Observable type@* (bigint mode) +@item SmallInt @tab number @tab bigint +@item BigInt @tab bigint @tab bigint +@item Float @tab number @tab number +@end multitable + +@section Changes that introduce incompatibilities with Javascript + +@subsection Standard mode + +There is no incompatibility with Javascript. + +@subsection Bigint mode + +The following changes are visible: + +@itemize + +@item Integer and Float are different types. Constants are typed. For example: @code{typeof 1.0 === "number"} and @code{typeof 1 === "bigint"}. Another consequence is that @code{1.0 === 1} is false. + +@item The range of integers is unlimited. In standard mode: @code{2**53 + 1 === 2**53}. This is no longer true with the bignum extensions. + +@item Binary bitwise operators do not truncate to 32 bits i.e. @code{0x800000000 | 1 === 0x800000001} while it gives @code{1} in standard mode. + +@item Bitwise shift operators do not truncate to 32 bits and do not mask the shift count with @code{0x1f} i.e. @code{1 << 32 === 4294967296} while it gives @code{1} in standard mode. However, the @code{>>>} operator (unsigned right shift) which is useless with bignums keeps its standard mode behavior@footnote{The unsigned right right operator could be removed in bigint mode.}. + +@item Operators with integer operands never return the minus zero floating point value as result. Hence @code{Object.is(0, -0) === true}. Use @code{-0.0} to create a minus zero floating point value. + +@item The @code{ToPrimitive} abstract operation is called with the @code{"integer"} preferred type when an integer is required (e.g. for bitwise binary or shift operations). + +@item The prototype of integers is no longer @code{Number.prototype}. Instead@* @code{Object.getPrototypeOf(1) === BigInt.prototype}. The prototype of floats remains Number.prototype. + +@item If the TC39 BigInt proposal is supported, there is no observable difference between integers and @code{bigint}s. + +@end itemize + +@section Operators + +@subsection Arithmetic operators + +The operands are converted to number values as in normal +Javascript. Then the general case is that an Integer is returned if +both operands are Integer. Otherwise, a float is returned. + +The @code{+} operator also accepts strings as input and behaves like +standard Javascript in this case. + +The binary operator @code{%} returns the truncated remainder of the +division. When the result is an Integer type, a dividend of zero yields a +RangeError exception. + +The binary operator @code{%} in math mode returns the Euclidian +remainder of the division i.e. it is always positive. + +The binary operator @code{/} returns a float. + +The binary operator @code{/} in math mode returns a float if one of +the operands is float. Otherwise, @code{BigInt[Symbol.operatorDiv]} is +invoked. + +The returned type of @code{a ** b} is Float if @math{a} or @math{b} +are Float. If @math{a} and @math{b} are integers: +@itemize +@item @math{b < 0} returns a Float in bigint mode. In math mode, @code{BigInt[Symbol.operatorPow]} is invoked. + +@item @math{b >= 0} returns an integer. +@end itemize + +The unary @code{-} and unary @code{+} return the same type as their +operand. They performs no floating point rounding when the result is a +float. + +The unary operators @code{++} and @code{--} return the same type as +their operand. + +In standard mode: + +If the operator returns an Integer and that the result fits a +SmallInt, it is converted to SmallInt. Otherwise, the Integer is +converted to a Float. + +In bigint mode: + +If the operator returns an Integer and that the result fits a +SmallInt, it is converted to SmallInt. Otherwise it is a BigInt. + +@subsection Logical operators + +In standard mode: + +The operands have their standard behavior. If the result fits a +SmallInt it is converted to a SmallInt. Otherwise it is a Float. + +In bigint mode: + +The operands are converted to integer values. The floating point +values are converted to integer by rounding them to zero. + +The logical operators are defined assuming the integers are +represented in two complement notation. + +For @code{<<} and @code{<<}, the shift can be positive or negative. So +@code{a << b} is defined as @math{\lfloor a/2^{-b} \rfloor} and +@code{a >> b} is defined as @math{\lfloor a/2^{b} \rfloor}. + +The operator @code{>>>} is supported for backward compatibility and +behaves the same way as Javascript i.e. implicit conversion to @code{Uint32}. + +If the result fits a SmallInt it is converted to a SmallInt. Otherwise +it is a BigInt. + +@subsection Relational operators + +The relational operators <, <=, >, >=, ==, != work as expected with +integers and floating point numbers (e.g. @code{1.0 == 1} is true). + +The strict equality operators === and !== have the usual Javascript +semantics. In particular, different types never equal, so @code{1.0 +=== 1} is false. + +@section Number literals + +Number literals in bigint mode have a slightly different behavior than +in standard Javascript: + +@enumerate + +@item +A number literal without a decimal point or an exponent is considered +as an Integer. Otherwise it is a Float. + +@item +Hexadecimal, octal or binary floating point literals are accepted with +a decimal point or an exponent. The exponent is specified with the +@code{p} letter assuming a base 2. The same convention is used by +C99. Example: @code{0x1p3} is the same as @code{8.0}. + +@end enumerate + +@section Builtin Object changes + +@subsection @code{BigInt} function + +The @code{BigInt} function cannot be invoked as a constructor. When +invoked as a function, it converts its first parameter to an +integer. When a floating point number is given as parameter, it is +truncated to an integer with infinite precision. + +@code{BigInt} properties: + +@table @code + +@item asIntN(bits, a) +Set @math{b=a \pmod{2^{bits}}}. Return @math{b} if @math{b < 2^{bits-1}} +otherwise @math{b-2^{bits}}. + +@item asUintN(bits, a) +Return @math{a \pmod{2^{bits}}}. + +@item tdiv(a, b) +Return @math{trunc(a/b)}. @code{b = 0} raises a RangeError +exception. + +@item fdiv(a, b) +Return @math{\lfloor a/b \rfloor}. @code{b = 0} raises a RangeError +exception. + +@item cdiv(a, b) +Return @math{\lceil a/b \rceil}. @code{b = 0} raises a RangeError +exception. + +@item ediv(a, b) +Return @math{sgn(b) \lfloor a/{|b|} \rfloor} (Euclidian +division). @code{b = 0} raises a RangeError exception. + +@item tdivrem(a, b) +@item fdivrem(a, b) +@item cdivrem(a, b) +@item edivrem(a, b) +Return an array of two elements. The first element is the quotient, +the second is the remainder. The same rounding is done as the +corresponding division operation. + +@item sqrt(a) +Return @math{\lfloor \sqrt(a) \rfloor}. A RangeError exception is +raised if @math{a < 0}. + +@item sqrtrem(a) +Return an array of two elements. The first element is @math{\lfloor +\sqrt{a} \rfloor}. The second element is @math{a-\lfloor \sqrt{a} +\rfloor^2}. A RangeError exception is raised if @math{a < 0}. + +@item floorLog2(a) +Return -1 if @math{a \leq 0} otherwise return @math{\lfloor \log2(a) \rfloor}. + +@item ctz(a) +Return the number of trailing zeros in the two's complement binary representation of a. Return -1 if @math{a=0}. + +@end table + +@subsection @code{BigInt.prototype} + +It is a normal object. + +@subsection @code{Number} constructor + +The number constructor returns its argument rounded to a Float using +the global floating point environment. In bigint mode, the Number +constructor returns a Float. In standard mode, it returns a SmallInt +if the value fits it, otherwise a Float. + +@subsection @code{Number.prototype} + +The following properties are modified: + +@table @code +@item toString(radix) + +In bigint mode, integers are converted to the specified radix with +infinite precision. + +@item toPrecision(p) +@item toFixed(p) +@item toExponential(p) + +In bigint mode, integers are accepted and converted to string with +infinite precision. + +@item parseInt(string, radix) + +In bigint mode, an integer is returned and the conversion is done with +infinite precision. + +@end table + +@subsection @code{Math} object + +The following properties are modified: + +@table @code +@item abs(x) +Absolute value. Return an integer if @code{x} is an Integer. Otherwise +return a Float. No rounding is performed. + +@item min(a, b) +@item max(a, b) +No rounding is performed. The returned type is the same one as the +minimum (resp. maximum) value. + +@end table + +@chapter Arbitrarily large floating point numbers + +@section Introduction + +This extension adds the @code{BigFloat} primitive type. The +@code{BigFloat} type represents floating point numbers are in base 2 +with the IEEE 754 semantics. A floating +point number is represented as a sign, mantissa and exponent. The +special values @code{NaN}, @code{+/-Infinity}, @code{+0} and @code{-0} +are supported. The mantissa and exponent can have any bit length with +an implementation specific minimum and maximum. + +@section Floating point rounding + +Each floating point operation operates with infinite precision and +then rounds the result according to the specified floating point +environment (@code{BigFloatEnv} object). The status flags of the +environment are also set according to the result of the operation. + +If no floating point environment is provided, the global floating +point environment is used. + +The rounding mode of the global floating point environment is always +@code{RNDN} (``round to nearest with ties to even'')@footnote{The +rationale is that the rounding mode changes must always be +explicit.}. The status flags of the global environment cannot be +read@footnote{The rationale is to avoid side effects for the built-in +operators.}. The precision of the global environment is +@code{BigFloatEnv.prec}. The number of exponent bits of the global +environment is @code{BigFloatEnv.expBits}. If @code{BigFloatEnv.expBits} is +strictly smaller than the maximum allowed number of exponent bits +(@code{BigFloatEnv.expBitsMax}), then the global environment subnormal +flag is set to @code{true}. Otherwise it is set to @code{false}; + +For example, @code{prec = 53} and @code{ expBits = 11} give exactly +the same precision as the IEEE 754 64 bit floating point. The +default precision is @code{prec = 113} and @code{ expBits = 15} (IEEE +754 128 bit floating point). + +The global floating point environment can only be modified temporarily +when calling a function (see @code{BigFloatEnv.setPrec}). Hence a +function can change the global floating point environment for its +callees but not for its caller. + +@section Operators + +The builtin operators are extended so that a BigFloat is returned if +at least one operand is a BigFloat. The computations are always done +with infinite precision and rounded according to the global floating +point environment. + +@code{typeof} applied on a @code{BigFloat} returns @code{bigfloat}. + +BigFloat can be compared with all the other numeric types and the +result follows the expected mathematical relations. + +However, since BigFloat and Number are different types they are never +equal when using the strict comparison operators (e.g. @code{0.0 === +0.0l} is false). + +@section BigFloat literals + +BigFloat literals are floating point numbers with a trailing @code{l} +suffix. BigFloat literals have an infinite precision. They are rounded +according to the global floating point environment when they are +evaluated.@footnote{Base 10 floating point literals cannot usually be +exactly represented as base 2 floating point number. In order to +ensure that the literal is represented accurately with the current +precision, it must be evaluated at runtime.} + +@section Builtin Object changes + +@subsection @code{BigFloat} function + +The @code{BigFloat} function cannot be invoked as a constructor. When +invoked as a function: the parameter is converted to a primitive +type. If the result is a numeric type, it is converted to BigFloat +without rounding. If the result is a string, it is converted to +BigFloat using the precision of the global floating point environment. + +@code{BigFloat} properties: + +@table @code + +@item LN2 +@item PI +Getter. Return the value of the corresponding mathematical constant +rounded to nearest, ties to even with the current global +precision. The constant values are cached for small precisions. + +@item MIN_VALUE +@item MAX_VALUE +@item EPSILON +Getter. Return the minimum, maximum and epsilon @code{BigFloat} values +(same definition as the corresponding @code{Number} constants). + +@item fpRound(a[, e]) +Round the floating point number @code{a} according to the floating +point environment @code{e} or the global environment if @code{e} is +undefined. + +@item parseFloat(a[, radix[, e]]) +Parse the string @code{a} as a floating point number in radix +@code{radix}. The radix is 0 (default) or from 2 to 36. The radix 0 +means radix 10 unless there is a hexadecimal or binary prefix. The +result is rounded according to the floating point environment @code{e} +or the global environment if @code{e} is undefined. + +@item isFinite(a) +Return true if @code{a} is a finite bigfloat. + +@item isNaN(a) +Return true if @code{a} is a NaN bigfloat. + +@item add(a, b[, e]) +@item sub(a, b[, e]) +@item mul(a, b[, e]) +@item div(a, b[, e]) +Perform the specified floating point operation and round the floating +point number @code{a} according to the floating point environment +@code{e} or the global environment if @code{e} is undefined. If +@code{e} is specified, the floating point status flags are updated. + +@item floor(x) +@item ceil(x) +@item round(x) +@item trunc(x) +Round to an integer. No additional rounding is performed. + +@item fmod(x, y[, e]) +@item remainder(x, y[, e]) +Floating point remainder. The quotient is truncated to zero (fmod) or +to the nearest integer with ties to even (remainder). @code{e} is an +optional floating point environment. + +@item sqrt(x[, e]) +Square root. Return a rounded floating point number. @code{e} is an +optional floating point environment. + +@item sin(x[, e]) +@item cos(x[, e]) +@item tan(x[, e]) +@item asin(x[, e]) +@item acos(x[, e]) +@item atan(x[, e]) +@item atan2(x, y[, e]) +@item exp(x[, e]) +@item log(x[, e]) +@item pow(x, y[, e]) +Transcendental operations. Return a rounded floating point +number. @code{e} is an optional floating point environment. + +@end table + +@subsection @code{BigFloat.prototype} + +The following properties are modified: + +@table @code +@item toString(radix) + +For floating point numbers: + +@itemize +@item +If the radix is a power of two, the conversion is done with infinite +precision. +@item +Otherwise, the number is rounded to nearest with ties to even using +the global precision. It is then converted to string using the minimum +number of digits so that its conversion back to a floating point using +the global precision and round to nearest gives the same number. + +@end itemize + +@item toPrecision(p[, rnd_mode]) +@item toFixed(p[, rnd_mode]) +@item toExponential(p[, rnd_mode]) +Same semantics as the corresponding @code{Number} functions with +BigFloats. There is no limit on the accepted precision @code{p}. The +rounding mode can be optionally specified. It is set by default to +@code{BigFloatEnv.RNDNA}. + +@end table + +@subsection @code{BigFloatEnv} constructor + +The @code{BigFloatEnv([p, [,rndMode]]} constructor cannot be invoked as a +function. The floating point environment contains: + +@itemize +@item the mantissa precision in bits + +@item the exponent size in bits assuming an IEEE 754 representation; + +@item the subnormal flag (if true, subnormal floating point numbers can +be generated by the floating point operations). + +@item the rounding mode + +@item the floating point status. The status flags can only be set by the floating point operations. They can be reset with @code{BigFloatEnv.prototype.clearStatus()} or with the various status flag setters. + +@end itemize + +@code{new BigFloatEnv([p, [,rndMode]]} creates a new floating point +environment. The status flags are reset. If no parameter is given the +precision, exponent bits and subnormal flags are copied from the +global floating point environment. Otherwise, the precision is set to +@code{p}, the number of exponent bits is set to @code{expBitsMax} and the +subnormal flags is set to @code{false}. If @code{rndMode} is +@code{undefined}, the rounding mode is set to @code{RNDN}. + +@code{BigFloatEnv} properties: + +@table @code + +@item prec +Getter. Return the precision in bits of the global floating point +environment. The initial value is @code{53}. + +@item expBits +Getter. Return the exponent size in bits of the global floating point +environment assuming an IEEE 754 representation. If @code{expBits < +expBitsMax}, then subnormal numbers are supported. The initial value +is @code{11}. + +@item setPrec(f, p[, e]) +Set the precision of the global floating point environment to @code{p} +and the exponent size to @code{e} then call the function +@code{f}. Then the Float precision and exponent size are reset to +their precious value and the return value of @code{f} is returned (or +an exception is raised if @code{f} raised an exception). If @code{e} +is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}. @code{p} +must be >= 53 and @code{e} must be >= 11 so that the global precision +is at least equivalent to the IEEE 754 64 bit doubles. + +@item precMin +Read-only integer. Return the minimum allowed precision. Must be at least 2. + +@item precMax +Read-only integer. Return the maximum allowed precision. Must be at least 53. + +@item expBitsMin +Read-only integer. Return the minimum allowed exponent size in +bits. Must be at least 3. + +@item expBitsMax +Read-only integer. Return the maximum allowed exponent size in +bits. Must be at least 11. + +@item RNDN +Read-only integer. Round to nearest, with ties to even rounding mode. + +@item RNDZ +Read-only integer. Round to zero rounding mode. + +@item RNDD +Read-only integer. Round to -Infinity rounding mode. + +@item RNDU +Read-only integer. Round to +Infinity rounding mode. + +@item RNDNA +Read-only integer. Round to nearest, with ties away from zero rounding mode. + +@item RNDNU +Read-only integer. Round to nearest, with ties to +Infinity rounding mode. + +@item RNDF@footnote{Could be removed in case a deterministic behavior for floating point operations is required.} +Read-only integer. Faithful rounding mode. The result is +non-deterministically rounded to -Infinity or +Infinity. This rounding +mode usually gives a faster and deterministic running time for the +floating point operations. + +@end table + +@code{BigFloatEnv.prototype} properties: + +@table @code + +@item prec +Getter and setter (Integer). Return or set the precision in bits. + +@item expBits +Getter and setter (Integer). Return or set the exponent size in bits +assuming an IEEE 754 representation. + +@item rndMode +Getter and setter (Integer). Return or set the rounding mode. + +@item subnormal +Getter and setter (Boolean). subnormal flag. It is false when +@code{expBits = expBitsMax}. + +@item clearStatus() +Clear the status flags. + +@item invalidOperation +@item divideByZero +@item overflow +@item underflow +@item inexact +Getter and setter (Boolean). Status flags. + +@end table + +@subsection @code{Math} object + +The following properties are modified: + +@table @code +@item abs(x) +Absolute value. If @code{x} is a BigFloat, its absolute value is +returned as a BigFloat. No rounding is performed. + +@item min(a, b) +@item max(a, b) +The returned type is the same one as the minimum (resp. maximum) +value, so @code{BigFloat} values are accepted. When a @code{BigFloat} +is returned, no rounding is performed. + +@end table + +@chapter Math mode + +@section Introduction + +A new @emph{math mode} is enabled with the @code{"use math"} +directive. @code{"use bigint"} is implied in math mode. With this +mode, writing mathematical expressions is more intuitive, exact +results (e.g. fractions) can be computed for all operators and floating +point literals have the @code{BigFloat} type by default. + +It propagates the same way as the @emph{strict mode}. In +this mode: + +@itemize + +@item The @code{^} operator is a similar to the power operator (@code{**}). + +@item The power operator (both @code{^} and @code{**}) grammar is modified so that @code{-2^2} is allowed and yields @code{-4}. + +@item The logical xor operator is still available with the @code{^^} operator. + +@item The division operator invokes @code{BigInt[Symbol.operatorDiv]} in case both operands are integers. + +@item The power operator invokes @code{BigInt[Symbol.operatorPow]} in case both operands are integers and the exponent is strictly negative. + +@item The modulo operator returns the Euclidian remainder (always positive) instead of the truncated remainder. + +@item Floating point literals are @code{BigFloat} by default (i.e. a @code{l} suffix is implied). + +@end itemize + +@section Builtin Object changes + +@subsection @code{Symbol} constructor + +The following global symbol is added for the operator overloading: +@table @code +@item operatorMathMod +@end table + +@section Remaining issues + +@enumerate + +@item A new floating point literal suffix could be added for @code{Number} literals. + +@end enumerate + +@bye diff --git a/doc/quickjs.html b/doc/quickjs.html new file mode 100644 index 0000000..f3522d7 --- /dev/null +++ b/doc/quickjs.html @@ -0,0 +1,1254 @@ + + + + +QuickJS Javascript Engine + + + + + + + + + + + + + + + +

QuickJS Javascript Engine

+ + +

Table of Contents

+ + + + + +

1 Introduction

+ +

QuickJS is a small and embeddable Javascript engine. It supports the +upcoming ES2020 specification +1 +including modules, asynchronous generators, proxies and BigInt. +

+

It supports mathematical extensions such as big decimal float float +numbers (BigDecimal), big binary floating point numbers (BigFloat), +and operator overloading. +

+ +

1.1 Main Features

+ +
    +
  • Small and easily embeddable: just a few C files, no external dependency, 210 KiB of x86 code for a simple “hello world” program. + +
  • Fast interpreter with very low startup time: runs the 69000 tests of the ECMAScript Test Suite2 in about 95 seconds on a single core of a desktop PC. The complete life cycle of a runtime instance completes in less than 300 microseconds. + +
  • Almost complete ES2019 support including modules, asynchronous +generators and full Annex B support (legacy web compatibility). Many +features from the upcoming ES2020 specification +3 are also supported. + +
  • Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2019 features. + +
  • Compile Javascript sources to executables with no external dependency. + +
  • Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal. + +
  • Mathematical extensions: BigDecimal, BigFloat, operator overloading, bigint mode, math mode. + +
  • Command line interpreter with contextual colorization and completion implemented in Javascript. + +
  • Small built-in standard library with C library wrappers. + +
+ + +

2 Usage

+ + +

2.1 Installation

+ +

A Makefile is provided to compile the engine on Linux or MacOS/X. A +preliminary Windows support is available thru cross compilation on a +Linux host with the MingGW tools. +

+

Edit the top of the Makefile if you wish to select specific +options then run make. +

+

You can type make install as root if you wish to install the binaries and support files to +/usr/local (this is not necessary to use QuickJS). +

+ +

2.2 Quick start

+ +

qjs is the command line interpreter (Read-Eval-Print Loop). You can pass +Javascript files and/or expressions as arguments to execute them: +

+
+
./qjs examples/hello.js
+
+ +

qjsc is the command line compiler: +

+
+
./qjsc -o hello examples/hello.js
+./hello
+
+ +

generates a hello executable with no external dependency. +

+ +

2.3 Command line options

+ + +

2.3.1 qjs interpreter

+ +
usage: qjs [options] [files]
+
+

Options are: +

+
-h
+
--help
+

List options. +

+
+
-e EXPR
+
--eval EXPR
+

Evaluate EXPR. +

+
+
-i
+
--interactive
+

Go to interactive mode (it is not the default when files are provided on the command line). +

+
+
-m
+
--module
+

Load as ES6 module (default=autodetect). A module is autodetected if +the filename extension is .mjs or if the first keyword of the +source is import. +

+
+
--script
+

Load as ES6 script (default=autodetect). +

+
+
--bignum
+

Enable the bignum extensions: BigDecimal object, BigFloat object and +the "use bigint" and "use math" directives. +

+
+
+ +

Advanced options are: +

+
+
--std
+

Make the std and os modules available to the loaded +script even if it is not a module. +

+
+
-d
+
--dump
+

Dump the memory usage stats. +

+
+
-q
+
--quit
+

just instantiate the interpreter and quit. +

+
+
+ + +

2.3.2 qjsc compiler

+ +
usage: qjsc [options] [files]
+
+

Options are: +

+
-c
+

Only output bytecode in a C file. The default is to output an executable file. +

+
-e
+

Output main() and bytecode in a C file. The default is to output an +executable file. +

+
-o output
+

Set the output filename (default = out.c or a.out). +

+
+
-N cname
+

Set the C name of the generated data. +

+
+
-m
+

Compile as Javascript module (default=autodetect). +

+
+
-M module_name[,cname]
+

Add initialization code for an external C module. See the +c_module example. +

+
+
-x
+

Byte swapped output (only used for cross compilation). +

+
+
-flto
+

Use link time optimization. The compilation is slower but the +executable is smaller and faster. This option is automatically set +when the -fno-x options are used. +

+
+
-fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint]
+

Disable selected language features to produce a smaller executable file. +

+
+
-fbignum
+

Enable the bignum extensions: BigDecimal object, BigFloat object and +the "use bigint" and "use math" directives. +

+
+
+ + +

2.4 qjscalc application

+ +

The qjscalc application is a superset of the qjs +command line interpreter implementing a Javascript calculator with +arbitrarily large integer and floating point numbers, fractions, +complex numbers, polynomials and matrices. The source code is in +qjscalc.js. More documentation and a web version are available at +http://numcalc.com. +

+ +

2.5 Built-in tests

+ +

Run make test to run the few built-in tests included in the +QuickJS archive. +

+ +

2.6 Test262 (ECMAScript Test Suite)

+ +

A test262 runner is included in the QuickJS archive. The test262 tests +can be installed in the QuickJS source directory with: +

+
+
git clone https://github.com/tc39/test262.git test262
+cd test262
+patch -p1 < ../tests/test262.patch
+cd ..
+
+ +

The patch adds the implementation specific harness functions +and optimizes the inefficient RegExp character classes and Unicode +property escapes tests (the tests themselves are not modified, only a +slow string initialization function is optimized). +

+

The tests can be run with +

+
make test2
+
+ +

The configuration files test262.conf +(resp. test262o.conf for the old ES5.1 tests4)) +contain the options to run the various tests. Tests can be excluded +based on features or filename. +

+

The file test262_errors.txt contains the current list of +errors. The runner displays a message when a new error appears or when +an existing error is corrected or modified. Use the -u option +to update the current list of errors (or make test2-update). +

+

The file test262_report.txt contains the logs of all the +tests. It is useful to have a clearer analysis of a particular +error. In case of crash, the last line corresponds to the failing +test. +

+

Use the syntax ./run-test262 -c test262.conf -f filename.js to +run a single test. Use the syntax ./run-test262 -c test262.conf +N to start testing at test number N. +

+

For more information, run ./run-test262 to see the command line +options of the test262 runner. +

+

run-test262 accepts the -N option to be invoked from +test262-harness5 +thru eshost. Unless you want to compare QuickJS with other +engines under the same conditions, we do not recommend to run the +tests this way as it is much slower (typically half an hour instead of +about 100 seconds). +

+ +

3 Specifications

+ + +

3.1 Language support

+ + +

3.1.1 ES2019 support

+ +

The ES2019 specification is almost fully supported including the Annex +B (legacy web compatibility) and the Unicode related features. +

+

The following features are not supported yet: +

+
    +
  • Realms (although the C API supports different runtimes and contexts) + +
  • Tail calls6 + +
+ + +

3.1.2 JSON

+ +

The JSON parser is currently more tolerant than the specification. +

+ +

3.1.3 ECMA402

+ +

ECMA402 (Internationalization API) is not supported. +

+ +

3.1.4 Extensions

+ +
    +
  • The directive "use strip" indicates that the debug information (including the source code of the functions) should not be retained to save memory. As "use strict", the directive can be global to a script or local to a function. + +
  • The first line of a script beginning with #! is ignored. + +
+ + +

3.1.5 Mathematical extensions

+ +

The mathematical extensions are fully backward compatible with +standard Javascript. See jsbignum.pdf for more information. +

+
    +
  • BigDecimal support: arbitrary large floating point numbers in base 10. + +
  • BigFloat support: arbitrary large floating point numbers in base 2. + +
  • Operator overloading. + +
  • The directive "use bigint" enables the bigint mode where integers are BigInt by default. + +
  • The directive "use math" enables the math mode where the division and power operators on integers produce fractions. Floating point literals are BigFloat by default and integers are BigInt by default. + +
+ + +

3.2 Modules

+ +

ES6 modules are fully supported. The default name resolution is the +following: +

+
    +
  • Module names with a leading . or .. are relative +to the current module path. + +
  • Module names without a leading . or .. are system +modules, such as std or os. + +
  • Module names ending with .so are native modules using the +QuickJS C API. + +
+ + +

3.3 Standard library

+ +

The standard library is included by default in the command line +interpreter. It contains the two modules std and os and +a few global objects. +

+ +

3.3.1 Global objects

+ +
+
scriptArgs
+

Provides the command line arguments. The first argument is the script name. +

+
print(...args)
+

Print the arguments separated by spaces and a trailing newline. +

+
console.log(...args)
+

Same as print(). +

+
+
+ + +

3.3.2 std module

+ +

The std module provides wrappers to the libc stdlib.h +and stdio.h and a few other utilities. +

+

Available exports: +

+
+
exit(n)
+

Exit the process. +

+
+
evalScript(str, options = undefined)
+

Evaluate the string str as a script (global +eval). options is an optional object containing the following +optional properties: +

+
+
backtrace_barrier
+

Boolean (default = false). If true, error backtraces do not list the + stack frames below the evalScript. +

+
+ +
+
loadScript(filename)
+

Evaluate the file filename as a script (global eval). +

+
+
Error(errno)
+
+

std.Error constructor. Error instances contain the field +errno (error code) and message (result of +std.Error.strerror(errno)). +

+

The constructor contains the following fields: +

+
+
EINVAL
+
EIO
+
EACCES
+
EEXIST
+
ENOSPC
+
ENOSYS
+
EBUSY
+
ENOENT
+
EPERM
+
EPIPE
+

Integer value of common errors (additional error codes may be defined). +

+
strerror(errno)
+

Return a string that describes the error errno. +

+
+ +
+
open(filename, flags)
+

Open a file (wrapper to the libc fopen()). Throws +std.Error in case of I/O error. +

+
+
popen(command, flags)
+

Open a process by creating a pipe (wrapper to the libc popen()). Throws +std.Error in case of I/O error. +

+
+
fdopen(fd, flags)
+

Open a file from a file handle (wrapper to the libc +fdopen()). Throws std.Error in case of I/O error. +

+
+
tmpfile()
+

Open a temporary file. Throws std.Error in case of I/O error. +

+
+
puts(str)
+

Equivalent to std.out.puts(str). +

+
+
printf(fmt, ...args)
+

Equivalent to std.out.printf(fmt, ...args) +

+
+
sprintf(fmt, ...args)
+

Equivalent to the libc sprintf(). +

+
+
in
+
out
+
err
+

Wrappers to the libc file stdin, stdout, stderr. +

+
+
SEEK_SET
+
SEEK_CUR
+
SEEK_END
+

Constants for seek(). +

+
+
gc()
+

Manually invoke the cycle removal algorithm. The cycle removal +algorithm is automatically started when needed, so this function is +useful in case of specific memory constraints or for testing. +

+
+
getenv(name)
+

Return the value of the environment variable name or +undefined if it is not defined. +

+
+
urlGet(url, options = undefined)
+
+

Download url using the curl command line +utility. options is an optional object containing the following +optional properties: +

+
+
binary
+

Boolean (default = false). If true, the response is an ArrayBuffer + instead of a string. When a string is returned, the data is assumed + to be UTF-8 encoded. +

+
+
full
+

Boolean (default = false). If true, return the an object contains + the properties response (response content), + responseHeaders (headers separated by CRLF), status + (status code). If full is false, only the response is + returned if the status is between 200 and 299. Otherwise an + std.Error exception is raised. +

+
+
+ +
+
+ +

FILE prototype: +

+
+
close()
+

Close the file. +

+
puts(str)
+

Outputs the string with the UTF-8 encoding. +

+
printf(fmt, ...args)
+

Formatted printf, same formats as the libc printf. +

+
flush()
+

Flush the buffered file. +

+
seek(offset, whence)
+

Seek to a give file position (whence is std.SEEK_*). Throws a +std.Error in case of I/O error. +

+
tell()
+

Return the current file position. +

+
eof()
+

Return true if end of file. +

+
fileno()
+

Return the associated OS handle. +

+
+
read(buffer, position, length)
+

Read length bytes from the file to the ArrayBuffer buffer at byte +position position (wrapper to the libc fread). +

+
+
write(buffer, position, length)
+

Write length bytes to the file from the ArrayBuffer buffer at byte +position position (wrapper to the libc fread). +

+
+
getline()
+

Return the next line from the file, assuming UTF-8 encoding, excluding +the trailing line feed. +

+
+
readAsString(max_size = undefined)
+

Read max_size bytes from the file and return them as a string +assuming UTF-8 encoding. If max_size is not present, the file +is read up its end. +

+
+
getByte()
+

Return the next byte from the file. Return -1 if the end of file is reached. +

+
+
putByte(c)
+

Write one byte to the file. +

+
+ + +

3.3.3 os module

+ +

The os module provides Operating System specific functions: +

+
    +
  • low level file access +
  • signals +
  • timers +
  • asynchronous I/O +
+ +

The OS functions usually return 0 if OK or an OS specific negative +error code. +

+

Available exports: +

+
+
open(filename, flags, mode = 0o666)
+

Open a file. Return a handle or < 0 if error. +

+
+
O_RDONLY
+
O_WRONLY
+
O_RDWR
+
O_APPEND
+
O_CREAT
+
O_EXCL
+
O_TRUNC
+

POSIX open flags. +

+
+
O_TEXT
+

(Windows specific). Open the file in text mode. The default is binary mode. +

+
+
close(fd)
+

Close the file handle fd. +

+
+
seek(fd, offset, whence)
+

Seek in the file. Use std.SEEK_* for whence. +

+
+
read(fd, buffer, offset, length)
+

Read length bytes from the file handle fd to the +ArrayBuffer buffer at byte position offset. +Return the number of read bytes or < 0 if error. +

+
+
write(fd, buffer, offset, length)
+

Write length bytes to the file handle fd from the +ArrayBuffer buffer at byte position offset. +Return the number of written bytes or < 0 if error. +

+
+
isatty(fd)
+

Return true is fd is a TTY (terminal) handle. +

+
+
ttyGetWinSize(fd)
+

Return the TTY size as [width, height] or null if not available. +

+
+
ttySetRaw(fd)
+

Set the TTY in raw mode. +

+
+
remove(filename)
+

Remove a file. Return 0 if OK or < 0 if error. +

+
+
rename(oldname, newname)
+

Rename a file. Return 0 if OK or < 0 if error. +

+
+
realpath(path)
+

Return [str, err] where str is the canonicalized absolute +pathname of path and err the error code. +

+
+
getcwd()
+

Return [str, err] where str is the current working directory +and err the error code. +

+
+
mkdir(path, mode = 0o777)
+

Create a directory at path. Return the error code. +

+
+
stat(path)
+
lstat(path)
+
+

Return [obj, err] where obj is an object containing the +file status of path. err is the error code. The +following fields are defined in obj: dev, ino, mode, nlink, +uid, gid, rdev, size, blocks, atime, mtime, ctime. The times are +specified in milliseconds since 1970. lstat() is the same as +stat() excepts that it returns information about the link +itself. +

+
+
S_IFMT
+
S_IFIFO
+
S_IFCHR
+
S_IFDIR
+
S_IFBLK
+
S_IFREG
+
S_IFSOCK
+
S_IFLNK
+
S_ISGID
+
S_ISUID
+

Constants to interpret the mode property returned by +stat(). They have the same value as in the C system header +sys/stat.h. +

+
+
utimes(path, atime, mtime)
+

Change the access and modification times of the file path. The +times are specified in milliseconds since 1970. +

+
+
symlink(target, linkpath)
+

Create a link at linkpath containing the string target. +

+
+
readlink(path)
+

Return [str, err] where str is the link target and err +the error code. +

+
+
readdir(path)
+

Return [array, err] where array is an array of strings +containing the filenames of the directory path. err is +the error code. +

+
+
setReadHandler(fd, func)
+

Add a read handler to the file handle fd. func is called +each time there is data pending for fd. A single read handler +per file handle is supported. Use func = null to remove the +handler. +

+
+
setWriteHandler(fd, func)
+

Add a write handler to the file handle fd. func is +called each time data can be written to fd. A single write +handler per file handle is supported. Use func = null to remove +the handler. +

+
+
signal(signal, func)
+

Call the function func when the signal signal +happens. Only a single handler per signal number is supported. Use +null to set the default handler or undefined to ignore +the signal. +

+
+
SIGINT
+
SIGABRT
+
SIGFPE
+
SIGILL
+
SIGSEGV
+
SIGTERM
+

POSIX signal numbers. +

+
+
kill(pid, sig)
+

Send the signal sig to the process pid. +

+
+
exec(args[, options])
+

Execute a process with the arguments args. options is an +object containing optional parameters: +

+
+
block
+

Boolean (default = true). If true, wait until the process is + terminated. In this case, exec return the exit code if positive + or the negated signal number if the process was interrupted by a + signal. If false, do not block and return the process id of the child. +

+
+
usePath
+

Boolean (default = true). If true, the file is searched in the + PATH environment variable. +

+
+
file
+

String (default = args[0]). Set the file to be executed. +

+
+
cwd
+

String. If present, set the working directory of the new process. +

+
+
stdin
+
stdout
+
stderr
+

If present, set the handle in the child for stdin, stdout or stderr. +

+
+
+ +
+
waitpid(pid, options)
+

waitpid Unix system call. Return the array [ret, status]. +

+
+
WNOHANG
+

Constant for the options argument of waitpid. +

+
+
dup(fd)
+

dup Unix system call. +

+
+
dup2(oldfd, newfd)
+

dup2 Unix system call. +

+
+
pipe()
+

pipe Unix system call. Return two handles as [read_fd, +write_fd] or null in case of error. +

+
+
sleep(delay_ms)
+

Sleep during delay_ms milliseconds. +

+
+
setTimeout(func, delay)
+

Call the function func after delay ms. Return a handle +to the timer. +

+
+
clearTimeout(handle)
+

Cancel a timer. +

+
+
platform
+

Return a string representing the platform: "linux", "darwin", +"win32" or "js". +

+
+
+ + +

3.4 QuickJS C API

+ +

The C API was designed to be simple and efficient. The C API is +defined in the header quickjs.h. +

+ +

3.4.1 Runtime and contexts

+ +

JSRuntime represents a Javascript runtime corresponding to an +object heap. Several runtimes can exist at the same time but they +cannot exchange objects. Inside a given runtime, no multi-threading is +supported. +

+

JSContext represents a Javascript context (or Realm). Each +JSContext has its own global objects and system objects. There can be +several JSContexts per JSRuntime and they can share objects, similar +to frames of the same origin sharing Javascript objects in a +web browser. +

+ +

3.4.2 JSValue

+ +

JSValue represents a Javascript value which can be a primitive +type or an object. Reference counting is used, so it is important to +explicitly duplicate (JS_DupValue(), increment the reference +count) or free (JS_FreeValue(), decrement the reference count) +JSValues. +

+ +

3.4.3 C functions

+ +

C functions can be created with +JS_NewCFunction(). JS_SetPropertyFunctionList() is a +shortcut to easily add functions, setters and getters properties to a +given object. +

+

Unlike other embedded Javascript engines, there is no implicit stack, +so C functions get their parameters as normal C parameters. As a +general rule, C functions take constant JSValues as parameters +(so they don’t need to free them) and return a newly allocated (=live) +JSValue. +

+ +

3.4.4 Exceptions

+ +

Exceptions: most C functions can return a Javascript exception. It +must be explicitly tested and handled by the C code. The specific +JSValue JS_EXCEPTION indicates that an exception +occurred. The actual exception object is stored in the +JSContext and can be retrieved with JS_GetException(). +

+ +

3.4.5 Script evaluation

+ +

Use JS_Eval() to evaluate a script or module source. +

+

If the script or module was compiled to bytecode with qjsc, it +can be evaluated by calling js_std_eval_binary(). The advantage +is that no compilation is needed so it is faster and smaller because +the compiler can be removed from the executable if no eval is +required. +

+

Note: the bytecode format is linked to a given QuickJS +version. Moreover, no security check is done before its +execution. Hence the bytecode should not be loaded from untrusted +sources. That’s why there is no option to output the bytecode to a +binary file in qjsc. +

+ +

3.4.6 JS Classes

+ +

C opaque data can be attached to a Javascript object. The type of the +C opaque data is determined with the class ID (JSClassID) of +the object. Hence the first step is to register a new class ID and JS +class (JS_NewClassID(), JS_NewClass()). Then you can +create objects of this class with JS_NewObjectClass() and get or +set the C opaque point with +JS_GetOpaque()/JS_SetOpaque(). +

+

When defining a new JS class, it is possible to declare a finalizer +which is called when the object is destroyed. A gc_mark method +can be provided so that the cycle removal algorithm can find the other +objects referenced by this object. Other methods are available to +define exotic object behaviors. +

+

The Class ID are globally allocated (i.e. for all runtimes). The +JSClass are allocated per JSRuntime. JS_SetClassProto() +is used to define a prototype for a given class in a given +JSContext. JS_NewObjectClass() sets this prototype in the +created object. +

+

Examples are available in quickjs-libc.c. +

+ +

3.4.7 C Modules

+ +

Native ES6 modules are supported and can be dynamically or statically +linked. Look at the test_bjson and bjson.so +examples. The standard library quickjs-libc.c is also a good example +of a native module. +

+ +

3.4.8 Memory handling

+ +

Use JS_SetMemoryLimit() to set a global memory allocation limit +to a given JSRuntime. +

+

Custom memory allocation functions can be provided with +JS_NewRuntime2(). +

+

The maximum system stack size can be set with JS_SetMaxStackSize(). +

+ +

3.4.9 Execution timeout and interrupts

+ +

Use JS_SetInterruptHandler() to set a callback which is +regularly called by the engine when it is executing code. This +callback can be used to implement an execution timeout. +

+

It is used by the command line interpreter to implement a +Ctrl-C handler. +

+ +

4 Internals

+ + +

4.1 Bytecode

+ +

The compiler generates bytecode directly with no intermediate +representation such as a parse tree, hence it is very fast. Several +optimizations passes are done over the generated bytecode. +

+

A stack-based bytecode was chosen because it is simple and generates +compact code. +

+

For each function, the maximum stack size is computed at compile time so that +no runtime stack overflow tests are needed. +

+

A separate compressed line number table is maintained for the debug +information. +

+

Access to closure variables is optimized and is almost as fast as local +variables. +

+

Direct eval in strict mode is optimized. +

+ +

4.2 Executable generation

+ + +

4.2.1 qjsc compiler

+ +

The qjsc compiler generates C sources from Javascript files. By +default the C sources are compiled with the system compiler +(gcc or clang). +

+

The generated C source contains the bytecode of the compiled functions +or modules. If a full complete executable is needed, it also +contains a main() function with the necessary C code to initialize the +Javascript engine and to load and execute the compiled functions and +modules. +

+

Javascript code can be mixed with C modules. +

+

In order to have smaller executables, specific Javascript features can +be disabled, in particular eval or the regular expressions. The +code removal relies on the Link Time Optimization of the system +compiler. +

+ +

4.2.2 Binary JSON

+ +

qjsc works by compiling scripts or modules and then serializing +them to a binary format. A subset of this format (without functions or +modules) can be used as binary JSON. The example test_bjson.js +shows how to use it. +

+

Warning: the binary JSON format may change without notice, so it +should not be used to store persistent data. The test_bjson.js +example is only used to test the binary object format functions. +

+ +

4.3 Runtime

+ + +

4.3.1 Strings

+ +

Strings are stored either as an 8 bit or a 16 bit array of +characters. Hence random access to characters is always fast. +

+

The C API provides functions to convert Javascript Strings to C UTF-8 encoded +strings. The most common case where the Javascript string contains +only ASCII characters involves no copying. +

+ +

4.3.2 Objects

+ +

The object shapes (object prototype, property names and flags) are shared +between objects to save memory. +

+

Arrays with no holes (except at the end of the array) are optimized. +

+

TypedArray accesses are optimized. +

+ +

4.3.3 Atoms

+ +

Object property names and some strings are stored as Atoms (unique +strings) to save memory and allow fast comparison. Atoms are +represented as a 32 bit integer. Half of the atom range is reserved for +immediate integer literals from 0 to 2^{31}-1. +

+ +

4.3.4 Numbers

+ +

Numbers are represented either as 32-bit signed integers or 64-bit IEEE-754 +floating point values. Most operations have fast paths for the 32-bit +integer case. +

+ +

4.3.5 Garbage collection

+ +

Reference counting is used to free objects automatically and +deterministically. A separate cycle removal pass is done when the allocated +memory becomes too large. The cycle removal algorithm only uses the +reference counts and the object content, so no explicit garbage +collection roots need to be manipulated in the C code. +

+ +

4.3.6 JSValue

+ +

It is a Javascript value which can be a primitive type (such as +Number, String, ...) or an Object. NaN boxing is used in the 32-bit version +to store 64-bit floating point numbers. The representation is +optimized so that 32-bit integers and reference counted values can be +efficiently tested. +

+

In 64-bit code, JSValue are 128-bit large and no NaN boxing is used. The +rationale is that in 64-bit code memory usage is less critical. +

+

In both cases (32 or 64 bits), JSValue exactly fits two CPU registers, +so it can be efficiently returned by C functions. +

+ +

4.3.7 Function call

+ +

The engine is optimized so that function calls are fast. The system +stack holds the Javascript parameters and local variables. +

+ +

4.4 RegExp

+ +

A specific regular expression engine was developed. It is both small +and efficient and supports all the ES2020 features including the +Unicode properties. As the Javascript compiler, it directly generates +bytecode without a parse tree. +

+

Backtracking with an explicit stack is used so that there is no +recursion on the system stack. Simple quantifiers are specifically +optimized to avoid recursions. +

+

Infinite recursions coming from quantifiers with empty terms are +avoided. +

+

The full regexp library weights about 15 KiB (x86 code), excluding the +Unicode library. +

+ +

4.5 Unicode

+ +

A specific Unicode library was developed so that there is no +dependency on an external large Unicode library such as ICU. All the +Unicode tables are compressed while keeping a reasonable access +speed. +

+

The library supports case conversion, Unicode normalization, Unicode +script queries, Unicode general category queries and all Unicode +binary properties. +

+

The full Unicode library weights about 45 KiB (x86 code). +

+ +

4.6 BigInt, BigFloat, BigDecimal

+ +

BigInt, BigFloat and BigDecimal are implemented with the libbf +library7. It weights about 90 +KiB (x86 code) and provides arbitrary precision IEEE 754 floating +point operations and transcendental functions with exact rounding. +

+ +

5 License

+ +

QuickJS is released under the MIT license. +

+

Unless otherwise specified, the QuickJS sources are copyright Fabrice +Bellard and Charlie Gordon. +

+
+
+

Footnotes

+ +

(1)

+

https://www.ecma-international.org/ecma-262/10.0

+

(2)

+

https://github.com/tc39/test262

+

(3)

+

https://tc39.github.io/ecma262/

+

(4)

+

The old +ES5.1 tests can be extracted with git clone --single-branch +--branch es5-tests https://github.com/tc39/test262.git test262o

+

(5)

+

https://github.com/bterlson/test262-harness

+

(6)

+

We believe the current specification of tails calls is too complicated and presents limited practical interests.

+

(7)

+

https://bellard.org/libbf

+
+
+ + + + + diff --git a/doc/quickjs.pdf b/doc/quickjs.pdf new file mode 100644 index 0000000000000000000000000000000000000000..68352cfc33d26e37707a87ba001545ec9255825e GIT binary patch literal 162866 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
QFh89G{of*(6&@ZP7j~~=tMv!fhfycF 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 U> 1n; + [P1, Q1, G1] = chud_bs(a, c, true); + [P2, Q2, G2] = chud_bs(c, b, need_G); + P = P1 * Q2 + P2 * G1; + Q = Q1 * Q2; + if (need_G) + G = G1 * G2; + else + G = 0d; + } + return [P, Q, G]; + } + + var n, P, Q, G; + /* number of serie terms */ + n = BigInt(Math.ceil(prec / CHUD_DIGITS_PER_TERM)) + 10n; + [P, Q, G] = chud_bs(0n, n, false); + Q = BigDecimal.div(Q, (P + Q * CHUD_A), + { roundingMode: "half-even", + maximumSignificantDigits: prec }); + G = (CHUD_C / 12d) * BigDecimal.sqrt(CHUD_C, + { roundingMode: "half-even", + maximumSignificantDigits: prec }); + return Q * G; +} + +(function() { + var r, n_digits, n_bits; + if (typeof scriptArgs != "undefined") { + if (scriptArgs.length < 2) { + print("usage: pi n_digits"); + return; + } + n_digits = scriptArgs[1] | 0; + } else { + n_digits = 1000; + } + /* we add more digits to reduce the probability of bad rounding for + the last digits */ + r = calc_pi(n_digits + 20); + r = BigDecimal.round(r, { roundingMode: "down", + maximumFractionDigits: n_digits }) + print(r); +})(); diff --git a/examples/pi_bigfloat.js b/examples/pi_bigfloat.js new file mode 100644 index 0000000..2bcda22 --- /dev/null +++ b/examples/pi_bigfloat.js @@ -0,0 +1,66 @@ +/* + * PI computation in Javascript using the QuickJS bigfloat type + * (binary floating point) + */ +"use strict"; + +/* compute PI with a precision of 'prec' bits */ +function calc_pi() { + const CHUD_A = 13591409n; + const CHUD_B = 545140134n; + const CHUD_C = 640320n; + const CHUD_C3 = 10939058860032000n; /* C^3/24 */ + const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */ + + /* return [P, Q, G] */ + function chud_bs(a, b, need_G) { + var c, P, Q, G, P1, Q1, G1, P2, Q2, G2; + if (a == (b - 1n)) { + G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n); + P = BigFloat(G * (CHUD_B * b + CHUD_A)); + if (b & 1n) + P = -P; + G = BigFloat(G); + Q = BigFloat(b * b * b * CHUD_C3); + } else { + c = (a + b) >> 1n; + [P1, Q1, G1] = chud_bs(a, c, true); + [P2, Q2, G2] = chud_bs(c, b, need_G); + P = P1 * Q2 + P2 * G1; + Q = Q1 * Q2; + if (need_G) + G = G1 * G2; + else + G = 0l; + } + return [P, Q, G]; + } + + var n, P, Q, G; + /* number of serie terms */ + n = BigInt(Math.ceil(BigFloatEnv.prec / CHUD_BITS_PER_TERM)) + 10n; + [P, Q, G] = chud_bs(0n, n, false); + Q = Q / (P + Q * BigFloat(CHUD_A)); + G = BigFloat((CHUD_C / 12n)) * BigFloat.sqrt(BigFloat(CHUD_C)); + return Q * G; +} + +(function() { + var r, n_digits, n_bits; + if (typeof scriptArgs != "undefined") { + if (scriptArgs.length < 2) { + print("usage: pi n_digits"); + return; + } + n_digits = scriptArgs[1]; + } else { + n_digits = 1000; + } + n_bits = Math.ceil(n_digits * Math.log2(10)); + /* we add more bits to reduce the probability of bad rounding for + the last digits */ + BigFloatEnv.setPrec( () => { + r = calc_pi(); + print(r.toFixed(n_digits, BigFloatEnv.RNDZ)); + }, n_bits + 32); +})(); diff --git a/examples/pi_bigint.js b/examples/pi_bigint.js new file mode 100644 index 0000000..cbbb2c4 --- /dev/null +++ b/examples/pi_bigint.js @@ -0,0 +1,118 @@ +/* + * PI computation in Javascript using the BigInt type + */ +"use strict"; + +/* return floor(log2(a)) for a > 0 and 0 for a = 0 */ +function floor_log2(a) +{ + var k_max, a1, k, i; + k_max = 0n; + while ((a >> (2n ** k_max)) != 0n) { + k_max++; + } + k = 0n; + a1 = a; + for(i = k_max - 1n; i >= 0n; i--) { + a1 = a >> (2n ** i); + if (a1 != 0n) { + a = a1; + k |= (1n << i); + } + } + return k; +} + +/* return ceil(log2(a)) for a > 0 */ +function ceil_log2(a) +{ + return floor_log2(a - 1n) + 1n; +} + +/* return floor(sqrt(a)) (not efficient but simple) */ +function int_sqrt(a) +{ + var l, u, s; + if (a == 0n) + return a; + l = ceil_log2(a); + u = 1n << ((l + 1n) / 2n); + /* u >= floor(sqrt(a)) */ + for(;;) { + s = u; + u = ((a / s) + s) / 2n; + if (u >= s) + break; + } + return s; +} + +/* return pi * 2**prec */ +function calc_pi(prec) { + const CHUD_A = 13591409n; + const CHUD_B = 545140134n; + const CHUD_C = 640320n; + const CHUD_C3 = 10939058860032000n; /* C^3/24 */ + const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */ + + /* return [P, Q, G] */ + function chud_bs(a, b, need_G) { + var c, P, Q, G, P1, Q1, G1, P2, Q2, G2; + if (a == (b - 1n)) { + G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n); + P = G * (CHUD_B * b + CHUD_A); + if (b & 1n) + P = -P; + Q = b * b * b * CHUD_C3; + } else { + c = (a + b) >> 1n; + [P1, Q1, G1] = chud_bs(a, c, true); + [P2, Q2, G2] = chud_bs(c, b, need_G); + P = P1 * Q2 + P2 * G1; + Q = Q1 * Q2; + if (need_G) + G = G1 * G2; + else + G = 0n; + } + return [P, Q, G]; + } + + var n, P, Q, G; + /* number of serie terms */ + n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n; + [P, Q, G] = chud_bs(0n, n, false); + Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A); + G = int_sqrt(CHUD_C << (2n * prec)); + return (Q * G) >> prec; +} + +function main(args) { + var r, n_digits, n_bits, out; + if (args.length < 1) { + print("usage: pi n_digits"); + return; + } + n_digits = args[0] | 0; + + /* we add more bits to reduce the probability of bad rounding for + the last digits */ + n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n; + r = calc_pi(n_bits); + r = ((10n ** BigInt(n_digits)) * r) >> n_bits; + out = r.toString(); + print(out[0] + "." + out.slice(1)); +} + +var args; +if (typeof scriptArgs != "undefined") { + args = scriptArgs; + args.shift(); +} else if (typeof arguments != "undefined") { + args = arguments; +} else { + /* default: 1000 digits */ + args=[1000]; +} + +main(args); diff --git a/examples/point.c b/examples/point.c new file mode 100644 index 0000000..049d135 --- /dev/null +++ b/examples/point.c @@ -0,0 +1,151 @@ +/* + * QuickJS: Example of C module with a class + * + * Copyright (c) 2019 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 "../quickjs.h" +#include + +#define countof(x) (sizeof(x) / sizeof((x)[0])) + +/* Point Class */ + +typedef struct { + int x; + int y; +} JSPointData; + +static JSClassID js_point_class_id; + +static void js_point_finalizer(JSRuntime *rt, JSValue val) +{ + JSPointData *s = JS_GetOpaque(val, js_point_class_id); + /* Note: 's' can be NULL in case JS_SetOpaque() was not called */ + js_free_rt(rt, s); +} + +static JSValue js_point_ctor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSPointData *s; + JSValue obj = JS_UNDEFINED; + JSValue proto; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &s->x, argv[0])) + goto fail; + if (JS_ToInt32(ctx, &s->y, argv[1])) + goto fail; + /* using new_target to get the prototype is necessary when the + class is extended. */ + proto = JS_GetPropertyStr(ctx, new_target, "prototype"); + if (JS_IsException(proto)) + goto fail; + obj = JS_NewObjectProtoClass(ctx, proto, js_point_class_id); + JS_FreeValue(ctx, proto); + if (JS_IsException(obj)) + goto fail; + JS_SetOpaque(obj, s); + return obj; + fail: + js_free(ctx, s); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_point_get_xy(JSContext *ctx, JSValueConst this_val, int magic) +{ + JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id); + if (!s) + return JS_EXCEPTION; + if (magic == 0) + return JS_NewInt32(ctx, s->x); + else + return JS_NewInt32(ctx, s->y); +} + +static JSValue js_point_set_xy(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ + JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id); + int v; + if (!s) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &v, val)) + return JS_EXCEPTION; + if (magic == 0) + s->x = v; + else + s->y = v; + return JS_UNDEFINED; +} + +static JSValue js_point_norm(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id); + if (!s) + return JS_EXCEPTION; + return JS_NewFloat64(ctx, sqrt((double)s->x * s->x + (double)s->y * s->y)); +} + +static JSClassDef js_point_class = { + "Point", + .finalizer = js_point_finalizer, +}; + +static const JSCFunctionListEntry js_point_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0), + JS_CGETSET_MAGIC_DEF("y", js_point_get_xy, js_point_set_xy, 1), + JS_CFUNC_DEF("norm", 0, js_point_norm), +}; + +static int js_point_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue point_proto, point_class; + + /* create the Point class */ + JS_NewClassID(&js_point_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class); + + point_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, point_proto, js_point_proto_funcs, countof(js_point_proto_funcs)); + JS_SetClassProto(ctx, js_point_class_id, point_proto); + + point_class = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0); + /* set proto.constructor and ctor.prototype */ + JS_SetConstructor(ctx, point_class, point_proto); + + JS_SetModuleExport(ctx, m, "Point", point_class); + return 0; +} + +JSModuleDef *js_init_module(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_point_init); + if (!m) + return NULL; + JS_AddModuleExport(ctx, m, "Point"); + return m; +} diff --git a/examples/test_fib.js b/examples/test_fib.js new file mode 100644 index 0000000..70d26bd --- /dev/null +++ b/examples/test_fib.js @@ -0,0 +1,6 @@ +/* example of JS module importing a C module */ + +import { fib } from "./fib.so"; + +console.log("Hello World"); +console.log("fib(10)=", fib(10)); diff --git a/examples/test_point.js b/examples/test_point.js new file mode 100644 index 0000000..0659bc3 --- /dev/null +++ b/examples/test_point.js @@ -0,0 +1,40 @@ +/* example of JS module importing a C module */ +import { Point } from "./point.so"; + +function assert(b, str) +{ + if (b) { + return; + } else { + throw Error("assertion failed: " + str); + } +} + +class ColorPoint extends Point { + constructor(x, y, color) { + super(x, y); + this.color = color; + } + get_color() { + return this.color; + } +}; + +function main() +{ + var pt, pt2; + + pt = new Point(2, 3); + assert(pt.x === 2); + assert(pt.y === 3); + pt.x = 4; + assert(pt.x === 4); + assert(pt.norm() == 5); + + pt2 = new ColorPoint(2, 3, 0xffffff); + assert(pt2.x === 2); + assert(pt2.color === 0xffffff); + assert(pt2.get_color() === 0xffffff); +} + +main(); diff --git a/jscompress.c b/jscompress.c new file mode 100644 index 0000000..a68c0e8 --- /dev/null +++ b/jscompress.c @@ -0,0 +1,918 @@ +/* + * Javascript Compressor + * + * Copyright (c) 2008-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. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" + +typedef struct JSToken { + int tok; + char buf[20]; + char *str; + int len; + int size; + int line_num; /* line number for start of token */ + int lines; /* number of embedded linefeeds in token */ +} JSToken; + +enum { + TOK_EOF = 256, + TOK_IDENT, + TOK_STR1, + TOK_STR2, + TOK_STR3, + TOK_NUM, + TOK_COM, + TOK_LCOM, +}; + +void tok_reset(JSToken *tt) +{ + if (tt->str != tt->buf) { + free(tt->str); + tt->str = tt->buf; + tt->size = sizeof(tt->buf); + } + tt->len = 0; +} + +void tok_add_ch(JSToken *tt, int c) +{ + if (tt->len + 1 > tt->size) { + tt->size *= 2; + if (tt->str == tt->buf) { + tt->str = malloc(tt->size); + memcpy(tt->str, tt->buf, tt->len); + } else { + tt->str = realloc(tt->str, tt->size); + } + } + tt->str[tt->len++] = c; +} + +FILE *infile; +const char *filename; +int output_line_num; +int line_num; +int ch; +JSToken tokc; + +int skip_mask; +#define DEFINE_MAX 20 +char *define_tab[DEFINE_MAX]; +int define_len; + +void error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (filename) { + fprintf(stderr, "%s:%d: ", filename, line_num); + } else { + fprintf(stderr, "jscompress: "); + } + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +void define_symbol(const char *def) +{ + int i; + for (i = 0; i < define_len; i++) { + if (!strcmp(tokc.str, define_tab[i])) + return; + } + if (define_len >= DEFINE_MAX) + error("too many defines"); + define_tab[define_len++] = strdup(def); +} + +void undefine_symbol(const char *def) +{ + int i, j; + for (i = j = 0; i < define_len; i++) { + if (!strcmp(tokc.str, define_tab[i])) { + free(define_tab[i]); + } else { + define_tab[j++] = define_tab[i]; + } + } + define_len = j; +} + +const char *find_symbol(const char *def) +{ + int i; + for (i = 0; i < define_len; i++) { + if (!strcmp(tokc.str, define_tab[i])) + return "1"; + } + return NULL; +} + +void next(void); + +void nextch(void) +{ + ch = fgetc(infile); + if (ch == '\n') + line_num++; +} + +int skip_blanks(void) +{ + for (;;) { + next(); + if (tokc.tok != ' ' && tokc.tok != '\t' && + tokc.tok != TOK_COM && tokc.tok != TOK_LCOM) + return tokc.tok; + } +} + +void parse_directive(void) +{ + int ifdef, mask = skip_mask; + /* simplistic preprocessor: + #define / #undef / #ifdef / #ifndef / #else / #endif + no symbol substitution. + */ + skip_mask = 0; /* disable skipping to parse preprocessor line */ + nextch(); + if (skip_blanks() != TOK_IDENT) + error("expected preprocessing directive after #"); + + if (!strcmp(tokc.str, "define")) { + if (skip_blanks() != TOK_IDENT) + error("expected identifier after #define"); + define_symbol(tokc.str); + } else if (!strcmp(tokc.str, "undef")) { + if (skip_blanks() != TOK_IDENT) + error("expected identifier after #undef"); + undefine_symbol(tokc.str); + } else if ((ifdef = 1, !strcmp(tokc.str, "ifdef")) || + (ifdef = 0, !strcmp(tokc.str, "ifndef"))) { + if (skip_blanks() != TOK_IDENT) + error("expected identifier after #ifdef/#ifndef"); + mask = (mask << 2) | 2 | ifdef; + if (find_symbol(tokc.str)) + mask ^= 1; + } else if (!strcmp(tokc.str, "else")) { + if (!(mask & 2)) + error("#else without a #if"); + mask ^= 1; + } else if (!strcmp(tokc.str, "endif")) { + if (!(mask & 2)) + error("#endif without a #if"); + mask >>= 2; + } else { + error("unsupported preprocessing directive"); + } + if (skip_blanks() != '\n') + error("extra characters on preprocessing line"); + skip_mask = mask; +} + +/* return -1 if invalid char */ +static int hex_to_num(int ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else if (ch >= '0' && ch <= '9') + return ch - '0'; + else + return -1; +} + +void next(void) +{ +again: + tok_reset(&tokc); + tokc.line_num = line_num; + tokc.lines = 0; + switch(ch) { + case EOF: + tokc.tok = TOK_EOF; + if (skip_mask) + error("missing #endif"); + break; + case 'a' ... 'z': + case 'A' ... 'Z': + case '_': + case '$': + tok_add_ch(&tokc, ch); + nextch(); + while ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + (ch == '_' || ch == '$')) { + tok_add_ch(&tokc, ch); + nextch(); + } + tok_add_ch(&tokc, '\0'); + tokc.tok = TOK_IDENT; + break; + case '.': + nextch(); + if (ch >= '0' && ch <= '9') { + tok_add_ch(&tokc, '.'); + goto has_dot; + } + tokc.tok = '.'; + break; + case '0': + tok_add_ch(&tokc, ch); + nextch(); + if (ch == 'x' || ch == 'X') { + /* hexa */ + tok_add_ch(&tokc, ch); + nextch(); + while ((ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F') || + (ch >= '0' && ch <= '9')) { + tok_add_ch(&tokc, ch); + nextch(); + } + tok_add_ch(&tokc, '\0'); + tokc.tok = TOK_NUM; + break; + } + goto has_digit; + + case '1' ... '9': + tok_add_ch(&tokc, ch); + nextch(); + has_digit: + /* decimal */ + while (ch >= '0' && ch <= '9') { + tok_add_ch(&tokc, ch); + nextch(); + } + if (ch == '.') { + tok_add_ch(&tokc, ch); + nextch(); + has_dot: + while (ch >= '0' && ch <= '9') { + tok_add_ch(&tokc, ch); + nextch(); + } + } + if (ch == 'e' || ch == 'E') { + tok_add_ch(&tokc, ch); + nextch(); + if (ch == '+' || ch == '-') { + tok_add_ch(&tokc, ch); + nextch(); + } + while (ch >= '0' && ch <= '9') { + tok_add_ch(&tokc, ch); + nextch(); + } + } + tok_add_ch(&tokc, '\0'); + tokc.tok = TOK_NUM; + break; + case '`': + { + nextch(); + while (ch != '`' && ch != EOF) { + if (ch == '\\') { + tok_add_ch(&tokc, ch); + nextch(); + if (ch == EOF) { + error("unexpected char after '\\'"); + } + tok_add_ch(&tokc, ch); + } else { + tok_add_ch(&tokc, ch); + nextch(); + } + } + nextch(); + tok_add_ch(&tokc, 0); + tokc.tok = TOK_STR3; + } + break; + case '\"': + case '\'': + { + int n, i, c, hex_digit_count; + int quote_ch; + quote_ch = ch; + nextch(); + while (ch != quote_ch && ch != EOF) { + if (ch == '\\') { + nextch(); + switch(ch) { + case 'n': + tok_add_ch(&tokc, '\n'); + nextch(); + break; + case 'r': + tok_add_ch(&tokc, '\r'); + nextch(); + break; + case 't': + tok_add_ch(&tokc, '\t'); + nextch(); + break; + case 'v': + tok_add_ch(&tokc, '\v'); + nextch(); + break; + case '\"': + case '\'': + case '\\': + tok_add_ch(&tokc, ch); + nextch(); + break; + case '0' ... '7': + n = 0; + while (ch >= '0' && ch <= '7') { + n = n * 8 + (ch - '0'); + nextch(); + } + tok_add_ch(&tokc, n); + break; + case 'x': + case 'u': + if (ch == 'x') + hex_digit_count = 2; + else + hex_digit_count = 4; + nextch(); + n = 0; + for(i = 0; i < hex_digit_count; i++) { + c = hex_to_num(ch); + if (c < 0) + error("unexpected char after '\\x'"); + n = n * 16 + c; + nextch(); + } + if (n >= 256) + error("unicode is currently unsupported"); + tok_add_ch(&tokc, n); + break; + + default: + error("unexpected char after '\\'"); + } + } else { + /* XXX: should refuse embedded newlines */ + tok_add_ch(&tokc, ch); + nextch(); + } + } + nextch(); + tok_add_ch(&tokc, 0); + if (quote_ch == '\'') + tokc.tok = TOK_STR1; + else + tokc.tok = TOK_STR2; + } + break; + case '/': + nextch(); + if (ch == '/') { + tok_add_ch(&tokc, '/'); + tok_add_ch(&tokc, ch); + nextch(); + while (ch != '\n' && ch != EOF) { + tok_add_ch(&tokc, ch); + nextch(); + } + tok_add_ch(&tokc, '\0'); + tokc.tok = TOK_LCOM; + } else if (ch == '*') { + int last; + tok_add_ch(&tokc, '/'); + tok_add_ch(&tokc, ch); + last = 0; + for(;;) { + nextch(); + if (ch == EOF) + error("unterminated comment"); + if (ch == '\n') + tokc.lines++; + tok_add_ch(&tokc, ch); + if (last == '*' && ch == '/') + break; + last = ch; + } + nextch(); + tok_add_ch(&tokc, '\0'); + tokc.tok = TOK_COM; + } else { + tokc.tok = '/'; + } + break; + case '#': + parse_directive(); + goto again; + case '\n': + /* adjust line number */ + tokc.line_num--; + tokc.lines++; + /* fall thru */ + default: + tokc.tok = ch; + nextch(); + break; + } + if (skip_mask & 1) + goto again; +} + +void print_tok(FILE *f, JSToken *tt) +{ + /* keep output lines in sync with input lines */ + while (output_line_num < tt->line_num) { + putc('\n', f); + output_line_num++; + } + + switch(tt->tok) { + case TOK_IDENT: + case TOK_COM: + case TOK_LCOM: + fprintf(f, "%s", tt->str); + break; + case TOK_NUM: + { + unsigned long a; + char *p; + a = strtoul(tt->str, &p, 0); + if (*p == '\0' && a <= 0x7fffffff) { + /* must be an integer */ + fprintf(f, "%d", (int)a); + } else { + fprintf(f, "%s", tt->str); + } + } + break; + case TOK_STR3: + fprintf(f, "`%s`", tt->str); + break; + case TOK_STR1: + case TOK_STR2: + { + int i, c, quote_ch; + if (tt->tok == TOK_STR1) + quote_ch = '\''; + else + quote_ch = '\"'; + fprintf(f, "%c", quote_ch); + for(i = 0; i < tt->len - 1; i++) { + c = (uint8_t)tt->str[i]; + switch(c) { + case '\r': + fprintf(f, "\\r"); + break; + case '\n': + fprintf(f, "\\n"); + break; + case '\t': + fprintf(f, "\\t"); + break; + case '\v': + fprintf(f, "\\v"); + break; + case '\"': + case '\'': + if (c == quote_ch) + fprintf(f, "\\%c", c); + else + fprintf(f, "%c", c); + break; + case '\\': + fprintf(f, "\\\\"); + break; + default: + /* XXX: no utf-8 support! */ + if (c >= 32 && c <= 255) { + fprintf(f, "%c", c); + } else if (c <= 255) + fprintf(f, "\\x%02x", c); + else + fprintf(f, "\\u%04x", c); + break; + } + } + fprintf(f, "%c", quote_ch); + } + break; + default: + if (tokc.tok >= 256) + error("unsupported token in print_tok: %d", tt->tok); + fprintf(f, "%c", tt->tok); + break; + } + output_line_num += tt->lines; +} + +/* check if token pasting could occur */ +static BOOL compat_token(int c1, int c2) +{ + if ((c1 == TOK_IDENT || c1 == TOK_NUM) && + (c2 == TOK_IDENT || c2 == TOK_NUM)) + return FALSE; + + if ((c1 == c2 && strchr("+-<>&|=*/.", c1)) + || (c2 == '=' && strchr("+-<>&|!*/^%", c1)) + || (c1 == '=' && c2 == '>') + || (c1 == '/' && c2 == '*') + || (c1 == '.' && c2 == TOK_NUM) + || (c1 == TOK_NUM && c2 == '.')) + return FALSE; + + return TRUE; +} + +void js_compress(const char *filename, const char *outfilename, + BOOL do_strip, BOOL keep_header) +{ + FILE *outfile; + int ltok, seen_space; + + line_num = 1; + infile = fopen(filename, "rb"); + if (!infile) { + perror(filename); + exit(1); + } + + output_line_num = 1; + outfile = fopen(outfilename, "wb"); + if (!outfile) { + perror(outfilename); + exit(1); + } + + nextch(); + next(); + ltok = 0; + seen_space = 0; + if (do_strip) { + if (keep_header) { + while (tokc.tok == ' ' || + tokc.tok == '\n' || + tokc.tok == '\t' || + tokc.tok == '\v' || + tokc.tok == '\b' || + tokc.tok == '\f') { + seen_space = 1; + next(); + } + if (tokc.tok == TOK_COM) { + print_tok(outfile, &tokc); + //fprintf(outfile, "\n"); + ltok = tokc.tok; + seen_space = 0; + next(); + } + } + + for(;;) { + if (tokc.tok == TOK_EOF) + break; + if (tokc.tok == ' ' || + tokc.tok == '\r' || + tokc.tok == '\t' || + tokc.tok == '\v' || + tokc.tok == '\b' || + tokc.tok == '\f' || + tokc.tok == TOK_LCOM || + tokc.tok == TOK_COM) { + /* don't print spaces or comments */ + seen_space = 1; + } else if (tokc.tok == TOK_STR3) { + print_tok(outfile, &tokc); + ltok = tokc.tok; + seen_space = 0; + } else if (tokc.tok == TOK_STR1 || tokc.tok == TOK_STR2) { + int count, i; + /* find the optimal quote char */ + count = 0; + for(i = 0; i < tokc.len; i++) { + if (tokc.str[i] == '\'') + count++; + else if (tokc.str[i] == '\"') + count--; + } + if (count > 0) + tokc.tok = TOK_STR2; + else if (count < 0) + tokc.tok = TOK_STR1; + print_tok(outfile, &tokc); + ltok = tokc.tok; + seen_space = 0; + } else { + if (seen_space && !compat_token(ltok, tokc.tok)) { + fprintf(outfile, " "); + } + print_tok(outfile, &tokc); + ltok = tokc.tok; + seen_space = 0; + } + next(); + } + } else { + /* just handle preprocessing */ + while (tokc.tok != TOK_EOF) { + print_tok(outfile, &tokc); + next(); + } + } + + fclose(outfile); + fclose(infile); +} + +#define HASH_SIZE 30011 +#define MATCH_LEN_MIN 3 +#define MATCH_LEN_MAX (4 + 63) +#define DIST_MAX 65535 + +static int find_longest_match(int *pdist, const uint8_t *src, int src_len, + const int *hash_next, int cur_pos) +{ + int pos, i, match_len, match_pos, pos_min, len_max; + + len_max = min_int(src_len - cur_pos, MATCH_LEN_MAX); + match_len = 0; + match_pos = 0; + pos_min = max_int(cur_pos - DIST_MAX - 1, 0); + pos = hash_next[cur_pos]; + while (pos >= pos_min) { + for(i = 0; i < len_max; i++) { + if (src[cur_pos + i] != src[pos + i]) + break; + } + if (i > match_len) { + match_len = i; + match_pos = pos; + } + pos = hash_next[pos]; + } + *pdist = cur_pos - match_pos - 1; + return match_len; +} + +int lz_compress(uint8_t **pdst, const uint8_t *src, int src_len) +{ + int *hash_table, *hash_next; + uint32_t h, v; + int i, dist, len, len1, dist1; + uint8_t *dst, *q; + + /* build the hash table */ + + hash_table = malloc(sizeof(hash_table[0]) * HASH_SIZE); + for(i = 0; i < HASH_SIZE; i++) + hash_table[i] = -1; + hash_next = malloc(sizeof(hash_next[0]) * src_len); + for(i = 0; i < src_len; i++) + hash_next[i] = -1; + + for(i = 0; i < src_len - MATCH_LEN_MIN + 1; i++) { + h = ((src[i] << 16) | (src[i + 1] << 8) | src[i + 2]) % HASH_SIZE; + hash_next[i] = hash_table[h]; + hash_table[h] = i; + } + for(;i < src_len; i++) { + hash_next[i] = -1; + } + free(hash_table); + + dst = malloc(src_len + 4); /* never larger than the source */ + q = dst; + *q++ = src_len >> 24; + *q++ = src_len >> 16; + *q++ = src_len >> 8; + *q++ = src_len >> 0; + /* compress */ + i = 0; + while (i < src_len) { + if (src[i] >= 128) + return -1; + len = find_longest_match(&dist, src, src_len, hash_next, i); + if (len >= MATCH_LEN_MIN) { + /* heuristic: see if better length just after */ + len1 = find_longest_match(&dist1, src, src_len, hash_next, i + 1); + if (len1 > len) + goto no_match; + } + if (len < MATCH_LEN_MIN) { + no_match: + *q++ = src[i]; + i++; + } else if (len <= (3 + 15) && dist < (1 << 10)) { + v = 0x8000 | ((len - 3) << 10) | dist; + *q++ = v >> 8; + *q++ = v; + i += len; + } else if (len >= 4 && len <= (4 + 63) && dist < (1 << 16)) { + v = 0xc00000 | ((len - 4) << 16) | dist; + *q++ = v >> 16; + *q++ = v >> 8; + *q++ = v; + i += len; + } else { + goto no_match; + } + } + free(hash_next); + *pdst = dst; + return q - dst; +} + +static int load_file(uint8_t **pbuf, const char *filename) +{ + FILE *f; + uint8_t *buf; + int buf_len; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + fseek(f, 0, SEEK_END); + buf_len = ftell(f); + fseek(f, 0, SEEK_SET); + buf = malloc(buf_len + 1); + fread(buf, 1, buf_len, f); + buf[buf_len] = '\0'; + fclose(f); + *pbuf = buf; + return buf_len; +} + +static void save_file(const char *filename, const uint8_t *buf, int buf_len) +{ + FILE *f; + + f = fopen(filename, "wb"); + if (!f) { + perror(filename); + exit(1); + } + fwrite(buf, 1, buf_len, f); + fclose(f); +} + +static void save_c_source(const char *filename, const uint8_t *buf, int buf_len, + const char *var_name) +{ + FILE *f; + int i; + + f = fopen(filename, "wb"); + if (!f) { + perror(filename); + exit(1); + } + fprintf(f, "/* This file is automatically generated - do not edit */\n\n"); + fprintf(f, "const uint8_t %s[] = {\n", var_name); + for(i = 0; i < buf_len; i++) { + fprintf(f, " 0x%02x,", buf[i]); + if ((i % 8) == 7 || (i == buf_len - 1)) + fprintf(f, "\n"); + } + fprintf(f, "};\n"); + fclose(f); +} + +#define DEFAULT_OUTPUT_FILENAME "out.js" + +void help(void) +{ + printf("jscompress version 1.0 Copyright (c) 2008-2018 Fabrice Bellard\n" + "usage: jscompress [options] filename\n" + "Javascript compressor\n" + "\n" + "-h print this help\n" + "-n do not compress spaces\n" + "-H keep the first comment\n" + "-c compress to file\n" + "-C name compress to C source ('name' is the variable name)\n" + "-D symbol define preprocessor symbol\n" + "-U symbol undefine preprocessor symbol\n" + "-o outfile set the output filename (default=%s)\n", + DEFAULT_OUTPUT_FILENAME); + exit(1); +} + +int main(int argc, char **argv) +{ + int c, do_strip, keep_header, compress; + const char *out_filename, *c_var, *fname; + char tmpfilename[1024]; + + do_strip = 1; + keep_header = 0; + out_filename = DEFAULT_OUTPUT_FILENAME; + compress = 0; + c_var = NULL; + for(;;) { + c = getopt(argc, argv, "hno:HcC:D:U:"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + break; + case 'n': + do_strip = 0; + break; + case 'o': + out_filename = optarg; + break; + case 'H': + keep_header = 1; + break; + case 'c': + compress = 1; + break; + case 'C': + c_var = optarg; + compress = 1; + break; + case 'D': + define_symbol(optarg); + break; + case 'U': + undefine_symbol(optarg); + break; + } + } + if (optind >= argc) + help(); + + filename = argv[optind++]; + + if (compress) { +#if defined(__ANDROID__) + /* XXX: use another directory ? */ + snprintf(tmpfilename, sizeof(tmpfilename), "out.%d", getpid()); +#else + snprintf(tmpfilename, sizeof(tmpfilename), "/tmp/out.%d", getpid()); +#endif + fname = tmpfilename; + } else { + fname = out_filename; + } + js_compress(filename, fname, do_strip, keep_header); + + if (compress) { + uint8_t *buf1, *buf2; + int buf1_len, buf2_len; + + buf1_len = load_file(&buf1, fname); + unlink(fname); + buf2_len = lz_compress(&buf2, buf1, buf1_len); + if (buf2_len < 0) { + fprintf(stderr, "Could not compress file (UTF8 chars are forbidden)\n"); + exit(1); + } + + if (c_var) { + save_c_source(out_filename, buf2, buf2_len, c_var); + } else { + save_file(out_filename, buf2, buf2_len); + } + free(buf1); + free(buf2); + } + return 0; +} diff --git a/libbf.c b/libbf.c new file mode 100644 index 0000000..25e856d --- /dev/null +++ b/libbf.c @@ -0,0 +1,8166 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2020 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#ifdef __AVX2__ +#include +#endif + +#include "cutils.h" +#include "libbf.h" + +/* enable it to check the multiplication result */ +//#define USE_MUL_CHECK +/* enable it to use FFT/NTT multiplication */ +#define USE_FFT_MUL +/* enable decimal floating point support */ +#define USE_BF_DEC + +//#define inline __attribute__((always_inline)) + +#ifdef __AVX2__ +#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ +#else +#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ +#endif + +/* XXX: adjust */ +#define DIVNORM_LARGE_THRESHOLD 50 +#define UDIV1NORM_THRESHOLD 3 + +#if LIMB_BITS == 64 +#define FMT_LIMB1 "%" PRIx64 +#define FMT_LIMB "%016" PRIx64 +#define PRId_LIMB PRId64 +#define PRIu_LIMB PRIu64 + +#else + +#define FMT_LIMB1 "%x" +#define FMT_LIMB "%08x" +#define PRId_LIMB "d" +#define PRIu_LIMB "u" + +#endif + +typedef intptr_t mp_size_t; + +typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); + +#ifdef USE_FFT_MUL + +#define FFT_MUL_R_OVERLAP_A (1 << 0) +#define FFT_MUL_R_OVERLAP_B (1 << 1) +#define FFT_MUL_R_NORESIZE (1 << 2) + +static no_inline int fft_mul(bf_context_t *s, + bf_t *res, limb_t *a_tab, limb_t a_len, + limb_t *b_tab, limb_t b_len, int mul_flags); +static void fft_clear_cache(bf_context_t *s); +#endif +#ifdef USE_BF_DEC +static void mp_pow_init(void); +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos); +#endif + + +/* could leading zeros */ +static inline int clz(limb_t a) +{ + if (a == 0) { + return LIMB_BITS; + } else { +#if LIMB_BITS == 64 + return clz64(a); +#else + return clz32(a); +#endif + } +} + +static inline int ctz(limb_t a) +{ + if (a == 0) { + return LIMB_BITS; + } else { +#if LIMB_BITS == 64 + return ctz64(a); +#else + return ctz32(a); +#endif + } +} + +static inline int ceil_log2(limb_t a) +{ + if (a <= 1) + return 0; + else + return LIMB_BITS - clz(a - 1); +} + +/* b must be >= 1 */ +static inline slimb_t ceil_div(slimb_t a, slimb_t b) +{ + if (a >= 0) + return (a + b - 1) / b; + else + return a / b; +} + +/* b must be >= 1 */ +static inline slimb_t floor_div(slimb_t a, slimb_t b) +{ + if (a >= 0) { + return a / b; + } else { + return (a - b + 1) / b; + } +} + +/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ +static inline limb_t smod(slimb_t a, slimb_t b) +{ + a = a % (slimb_t)b; + if (a < 0) + a += b; + return a; +} + +#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; +#ifdef USE_BF_DEC + mp_pow_init(); +#endif +} + +void bf_context_end(bf_context_t *s) +{ + bf_clear_cache(s); +} + +void bf_init(bf_context_t *s, bf_t *r) +{ + r->ctx = s; + r->sign = 0; + r->expn = BF_EXP_ZERO; + r->len = 0; + r->tab = NULL; +} + +/* return 0 if OK, -1 if alloc error */ +int bf_resize(bf_t *r, limb_t len) +{ + limb_t *tab; + + if (len != r->len) { + tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t)); + if (!tab && len != 0) + return -1; + r->tab = tab; + r->len = len; + } + return 0; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_ui(bf_t *r, uint64_t a) +{ + r->sign = 0; + if (a == 0) { + r->expn = BF_EXP_ZERO; + bf_resize(r, 0); /* cannot fail */ + } +#if LIMB_BITS == 32 + else if (a <= 0xffffffff) +#else + else +#endif + { + int shift; + if (bf_resize(r, 1)) + goto fail; + shift = clz(a); + r->tab[0] = a << shift; + r->expn = LIMB_BITS - shift; + } +#if LIMB_BITS == 32 + else { + uint32_t a1, a0; + int shift; + if (bf_resize(r, 2)) + goto fail; + a0 = a; + a1 = a >> 32; + shift = clz(a1); + r->tab[0] = a0 << shift; + r->tab[1] = (a1 << shift) | (a0 >> (LIMB_BITS - shift)); + r->expn = 2 * LIMB_BITS - shift; + } +#endif + return 0; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_si(bf_t *r, int64_t a) +{ + int ret; + + if (a < 0) { + ret = bf_set_ui(r, -a); + r->sign = 1; + } else { + ret = bf_set_ui(r, a); + } + return ret; +} + +void bf_set_nan(bf_t *r) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_NAN; + r->sign = 0; +} + +void bf_set_zero(bf_t *r, int is_neg) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_ZERO; + r->sign = is_neg; +} + +void bf_set_inf(bf_t *r, int is_neg) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_INF; + r->sign = is_neg; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set(bf_t *r, const bf_t *a) +{ + if (r == a) + return 0; + if (bf_resize(r, a->len)) { + bf_set_nan(r); + return BF_ST_MEM_ERROR; + } + r->sign = a->sign; + r->expn = a->expn; + memcpy(r->tab, a->tab, a->len * sizeof(limb_t)); + return 0; +} + +/* equivalent to bf_set(r, a); bf_delete(a) */ +void bf_move(bf_t *r, bf_t *a) +{ + bf_context_t *s = r->ctx; + if (r == a) + return; + bf_free(s, r->tab); + *r = *a; +} + +static limb_t get_limbz(const bf_t *a, limb_t idx) +{ + if (idx >= a->len) + return 0; + else + return a->tab[idx]; +} + +/* get LIMB_BITS at bit position 'pos' in tab */ +static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos) +{ + limb_t i, a0, a1; + int p; + + i = pos >> LIMB_LOG2_BITS; + p = pos & (LIMB_BITS - 1); + if (i < len) + a0 = tab[i]; + else + a0 = 0; + if (p == 0) { + return a0; + } else { + i++; + if (i < len) + a1 = tab[i]; + else + a1 = 0; + return (a0 >> p) | (a1 << (LIMB_BITS - p)); + } +} + +static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos) +{ + slimb_t i; + i = pos >> LIMB_LOG2_BITS; + if (i < 0 || i >= len) + return 0; + return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1; +} + +static inline limb_t limb_mask(int start, int last) +{ + limb_t v; + int n; + n = last - start + 1; + if (n == LIMB_BITS) + v = -1; + else + v = (((limb_t)1 << n) - 1) << start; + return v; +} + +static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n) +{ + mp_size_t i; + for(i = 0; i < n; i++) { + if (tab[i] != 0) + return 1; + } + return 0; +} + +/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */ +static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos) +{ + slimb_t pos; + limb_t v; + + pos = bit_pos >> LIMB_LOG2_BITS; + if (pos < 0) + return 0; + v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1)); + if (v != 0) + return 1; + pos--; + while (pos >= 0) { + if (r->tab[pos] != 0) + return 1; + pos--; + } + return 0; +} + +/* return the addend for rounding. Note that prec can be <= 0 (for + BF_FLAG_RADPNT_PREC) */ +static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, + slimb_t prec, int rnd_mode) +{ + int add_one, inexact; + limb_t bit1, bit0; + + if (rnd_mode == BF_RNDF) { + bit0 = 1; /* faithful rounding does not honor the INEXACT flag */ + } else { + /* starting limb for bit 'prec + 1' */ + bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1)); + } + + /* get the bit at 'prec' */ + bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec); + inexact = (bit1 | bit0) != 0; + + add_one = 0; + switch(rnd_mode) { + case BF_RNDZ: + break; + case BF_RNDN: + if (bit1) { + if (bit0) { + add_one = 1; + } else { + /* round to even */ + add_one = + get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1)); + } + } + break; + case BF_RNDD: + case BF_RNDU: + if (r->sign == (rnd_mode == BF_RNDD)) + add_one = inexact; + break; + case BF_RNDNA: + case BF_RNDF: + add_one = bit1; + break; + case BF_RNDNU: + if (bit1) { + if (r->sign) + add_one = bit0; + else + add_one = 1; + } + 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_RNDNU || + (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). 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) +{ + limb_t v, a; + int shift, add_one, ret, 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; + ret = 0; + 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); + } + // 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 || + rnd_mode == BF_RNDNU); + 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); +} + +/* 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; +} + +#define BF_CMP_EQ 1 +#define BF_CMP_LT 2 +#define BF_CMP_LE 3 + +static int bf_cmp(const bf_t *a, const bf_t *b, int op) +{ + BOOL is_both_zero; + int res; + + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) + return 0; + if (a->sign != b->sign) { + is_both_zero = (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO); + if (is_both_zero) { + return op & BF_CMP_EQ; + } else if (op & BF_CMP_LT) { + return a->sign; + } else { + return FALSE; + } + } else { + res = bf_cmpu(a, b); + if (res == 0) { + return op & BF_CMP_EQ; + } else if (op & BF_CMP_LT) { + return (res < 0) ^ a->sign; + } else { + return FALSE; + } + } +} + +int bf_cmp_eq(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b, BF_CMP_EQ); +} + +int bf_cmp_le(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b, BF_CMP_LE); +} + +int bf_cmp_lt(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b, BF_CMP_LT); +} + +/* Compute the number of bits 'n' matching the pattern: + a= X1000..0 + b= X0111..1 + + When computing a-b, the result will have at least n leading zero + bits. + + Precondition: a > b and a.expn - b.expn = 0 or 1 +*/ +static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b) +{ + slimb_t bit_offset, b_offset, n; + int p, p1; + limb_t v1, v2, mask; + + bit_offset = a->len * LIMB_BITS - 1; + b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) + + a->expn - b->expn; + n = 0; + + /* first search the equals bits */ + for(;;) { + v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); + v2 = get_bits(b->tab, b->len, bit_offset + b_offset); + // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); + if (v1 != v2) + break; + n += LIMB_BITS; + bit_offset -= LIMB_BITS; + } + /* find the position of the first different bit */ + p = clz(v1 ^ v2) + 1; + n += p; + /* then search for '0' in a and '1' in b */ + p = LIMB_BITS - p; + if (p > 0) { + /* search in the trailing p bits of v1 and v2 */ + mask = limb_mask(0, p - 1); + p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p); + n += p1; + if (p1 != p) + goto done; + } + bit_offset -= LIMB_BITS; + for(;;) { + v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); + v2 = get_bits(b->tab, b->len, bit_offset + b_offset); + // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); + if (v1 != 0 || v2 != -1) { + /* different: count the matching bits */ + p1 = bf_min(clz(v1), clz(~v2)); + n += p1; + break; + } + n += LIMB_BITS; + bit_offset -= LIMB_BITS; + } + done: + return n; +} + +static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int b_neg) +{ + const bf_t *tmp; + int is_sub, ret, cmp_res, a_sign, b_sign; + + a_sign = a->sign; + b_sign = b->sign ^ b_neg; + is_sub = a_sign ^ b_sign; + cmp_res = bf_cmpu(a, b); + if (cmp_res < 0) { + tmp = a; + a = b; + b = tmp; + a_sign = b_sign; /* b_sign is never used later */ + } + /* abs(a) >= abs(b) */ + if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { + /* zero result */ + bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); + ret = 0; + } else if (a->len == 0 || b->len == 0) { + ret = 0; + if (a->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN) { + /* at least one operand is NaN */ + bf_set_nan(r); + } else if (b->expn == BF_EXP_INF && is_sub) { + /* infinities with different signs */ + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bf_set_inf(r, a_sign); + } + } else { + /* at least one zero and not subtract */ + bf_set(r, a); + r->sign = a_sign; + goto renorm; + } + } else { + slimb_t d, a_offset, b_bit_offset, i, cancelled_bits; + limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask; + + r->sign = a_sign; + r->expn = a->expn; + d = a->expn - b->expn; + /* must add more precision for the leading cancelled bits in + subtraction */ + if (is_sub) { + if (d <= 1) + cancelled_bits = count_cancelled_bits(a, b); + else + cancelled_bits = 1; + } else { + cancelled_bits = 0; + } + + /* add two extra bits for rounding */ + precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS); + r_len = bf_min(precl, tot_len); + if (bf_resize(r, r_len)) + goto fail; + a_offset = a->len - r_len; + b_bit_offset = (b->len - r_len) * LIMB_BITS + d; + + /* compute the bits before for the rounding */ + carry = is_sub; + z = 0; + sub_mask = -is_sub; + i = r_len - tot_len; + while (i < 0) { + slimb_t ap, bp; + BOOL inflag; + + ap = a_offset + i; + bp = b_bit_offset + i * LIMB_BITS; + inflag = FALSE; + if (ap >= 0 && ap < a->len) { + v1 = a->tab[ap]; + inflag = TRUE; + } else { + v1 = 0; + } + if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) { + v2 = get_bits(b->tab, b->len, bp); + inflag = TRUE; + } else { + v2 = 0; + } + if (!inflag) { + /* outside 'a' and 'b': go directly to the next value + inside a or b so that the running time does not + depend on the exponent difference */ + i = 0; + if (ap < 0) + i = bf_min(i, -a_offset); + /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1 + equivalent to + i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS) + */ + if (bp + LIMB_BITS <= 0) + i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS); + } else { + i++; + } + v2 ^= sub_mask; + u = v1 + v2; + carry1 = u < v1; + u += carry; + carry = (u < carry) | carry1; + z |= u; + } + /* and the result */ + for(i = 0; i < r_len; i++) { + v1 = get_limbz(a, a_offset + i); + v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS); + v2 ^= sub_mask; + u = v1 + v2; + carry1 = u < v1; + u += carry; + carry = (u < carry) | carry1; + r->tab[i] = u; + } + /* set the extra bits for the rounding */ + r->tab[0] |= (z != 0); + + /* carry is only possible in add case */ + if (!is_sub && carry) { + if (bf_resize(r, r_len + 1)) + goto fail; + r->tab[r_len] = 1; + r->expn += LIMB_BITS; + } + renorm: + ret = bf_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_add_internal(r, a, b, prec, flags, 0); +} + +static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_add_internal(r, a, b, prec, flags, 1); +} + +limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, + limb_t n, limb_t carry) +{ + slimb_t i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2 */ +static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry) +{ + int i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n) +{ + mp_size_t i; + limb_t k, a, v; + + k=b; + for(i=0;i v; + tab[i] = a; + if (k == 0) + break; + } + return k; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t high) +{ + mp_size_t i; + limb_t l, a; + + assert(shift >= 1 && shift < LIMB_BITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); + l = a; + } + return l & (((limb_t)1 << shift) - 1); +} + +/* tabr[] = taba[] * b + l. Return the high carry */ +static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t l) +{ + limb_t i; + dlimb_t t; + + for(i = 0; i < n; i++) { + t = (dlimb_t)taba[i] * (dlimb_t)b + l; + tabr[i] = t; + l = t >> LIMB_BITS; + } + return l; +} + +/* tabr[] += taba[] * b, return the high word. */ +static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b) +{ + limb_t i, l; + dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> LIMB_BITS; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +static void mp_mul_basecase(limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size) +{ + limb_t i, r; + + result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i= FFT_MUL_THRESHOLD)) { + bf_t r_s, *r = &r_s; + r->tab = result; + /* XXX: optimize memory usage in API */ + if (fft_mul(s, r, (limb_t *)op1, op1_size, + (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE)) + return -1; + } else +#endif + { + mp_mul_basecase(result, op1, op1_size, op2, op2_size); + } + return 0; +} + +/* tabr[] -= taba[] * b. Return the value to substract to the high + word. */ +static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b) +{ + limb_t i, l; + dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l; + tabr[i] = t; + l = -(t >> LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(LIMB_BITS-1) */ +static inline limb_t udiv1norm_init(limb_t d) +{ + limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, + limb_t d, limb_t d_inv) +{ + limb_t n1m, n_adj, q, r, ah; + dlimb_t a; + n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((dlimb_t)a1 << LIMB_BITS) | a0; + a = a - (dlimb_t)q * d - d; + ah = a >> LIMB_BITS; + q += 1 + ah; + r = (limb_t)a + (ah & d); + *pr = r; + return q; +} + +/* b must be >= 1 << (LIMB_BITS - 1) */ +static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t r) +{ + slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); + } + } else { + dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + } + return r; +} + +static int mp_divnorm_large(bf_context_t *s, + limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb); + +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb) +{ + limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + slimb_t i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = mp_div1norm(tabq, taba, na, b1, 0); + return 0; + } + n = na - nb; + if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) { + return mp_divnorm_large(s, tabq, taba, na, tabb, nb); + } + + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } + } + tabq[n] = q; + if (q) { + mp_sub(taba + n, taba + n, tabb, nb, 0); + } + + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + dlimb_t al; + al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; + } + r = mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } + } + } + tabq[i] = q; + } + return 0; +} + +/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a' + has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1. + + See Modern Computer Arithmetic by Richard P. Brent and Paul + Zimmermann, algorithm 3.5 */ +int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n) +{ + mp_size_t l, h, k, i; + limb_t *tabxh, *tabt, c, *tabu; + + if (n <= 2) { + /* return ceil(B^(2*n)/a) - 1 */ + /* XXX: could avoid allocation */ + tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1)); + tabt = bf_malloc(s, sizeof(limb_t) * (n + 2)); + if (!tabt || !tabu) + goto fail; + for(i = 0; i < 2 * n; i++) + tabu[i] = 0; + tabu[2 * n] = 1; + if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n)) + goto fail; + for(i = 0; i < n + 1; i++) + tabr[i] = tabt[i]; + if (mp_scan_nz(tabu, n) == 0) { + /* only happens for a=B^n/2 */ + mp_sub_ui(tabr, 1, n + 1); + } + } else { + l = (n - 1) / 2; + h = n - l; + /* n=2p -> l=p-1, h = p + 1, k = p + 3 + n=2p+1-> l=p, h = p + 1; k = p + 2 + */ + tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1)); + tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2)); + if (!tabt || !tabu) + goto fail; + tabxh = tabr + l; + if (mp_recip(s, tabxh, taba + l, h)) + goto fail; + if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */ + goto fail; + while (tabt[n + h] != 0) { + mp_sub_ui(tabxh, 1, h + 1); + c = mp_sub(tabt, tabt, taba, n, 0); + mp_sub_ui(tabt + n, c, h + 1); + } + /* T = B^(n+h) - T */ + mp_neg(tabt, tabt, n + h + 1, 0); + tabt[n + h]++; + if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1)) + goto fail; + /* n + 2*h - l + 2 limbs */ + k = 2 * h - l; + for(i = 0; i < l; i++) + tabr[i] = tabu[i + k]; + mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0); + } + bf_free(s, tabt); + bf_free(s, tabu); + return 0; + fail: + bf_free(s, tabt); + bf_free(s, tabu); + return -1; +} + +/* return -1, 0 or 1 */ +static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n) +{ + mp_size_t i; + for(i = n - 1; i >= 0; i--) { + if (taba[i] != tabb[i]) { + if (taba[i] < tabb[i]) + return -1; + else + return 1; + } + } + return 0; +} + +//#define DEBUG_DIVNORM_LARGE +//#define DEBUG_DIVNORM_LARGE2 + +/* subquadratic divnorm */ +static int mp_divnorm_large(bf_context_t *s, + limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb) +{ + limb_t *tabb_inv, nq, *tabt, i, n; + nq = na - nb; +#ifdef DEBUG_DIVNORM_LARGE + printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq); + mp_print_str("a", taba, na); + mp_print_str("b", tabb, nb); +#endif + assert(nq >= 1); + n = nq; + if (nq < nb) + n++; + tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1)); + tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1)); + if (!tabb_inv || !tabt) + goto fail; + + if (n >= nb) { + for(i = 0; i < n - nb; i++) + tabt[i] = 0; + for(i = 0; i < nb; i++) + tabt[i + n - nb] = tabb[i]; + } else { + /* truncate B: need to increment it so that the approximate + inverse is smaller that the exact inverse */ + for(i = 0; i < n; i++) + tabt[i] = tabb[i + nb - n]; + if (mp_add_ui(tabt, 1, n)) { + /* tabt = B^n : tabb_inv = B^n */ + memset(tabb_inv, 0, n * sizeof(limb_t)); + tabb_inv[n] = 1; + goto recip_done; + } + } + if (mp_recip(s, tabb_inv, tabt, n)) + goto fail; + recip_done: + /* Q=A*B^-1 */ + if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1)) + goto fail; + + for(i = 0; i < nq + 1; i++) + tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)]; +#ifdef DEBUG_DIVNORM_LARGE + mp_print_str("q", tabq, nq + 1); +#endif + + bf_free(s, tabt); + bf_free(s, tabb_inv); + tabb_inv = NULL; + + /* R=A-B*Q */ + tabt = bf_malloc(s, sizeof(limb_t) * (na + 1)); + if (!tabt) + goto fail; + if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb)) + goto fail; + /* we add one more limb for the result */ + mp_sub(taba, taba, tabt, nb + 1, 0); + bf_free(s, tabt); + /* the approximated quotient is smaller than than the exact one, + hence we may have to increment it */ +#ifdef DEBUG_DIVNORM_LARGE2 + int cnt = 0; + static int cnt_max; +#endif + for(;;) { + if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0) + break; + taba[nb] -= mp_sub(taba, taba, tabb, nb, 0); + mp_add_ui(tabq, 1, nq + 1); +#ifdef DEBUG_DIVNORM_LARGE2 + cnt++; +#endif + } +#ifdef DEBUG_DIVNORM_LARGE2 + if (cnt > cnt_max) { + cnt_max = cnt; + printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb); + } +#endif + return 0; + fail: + bf_free(s, tabb_inv); + bf_free(s, tabt); + return -1; +} + +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + int ret, r_sign; + + if (a->len < b->len) { + const bf_t *tmp = a; + a = b; + b = tmp; + } + r_sign = a->sign ^ b->sign; + /* here b->len <= a->len */ + if (b->len == 0) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + ret = 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { + if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || + (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bf_set_inf(r, r_sign); + ret = 0; + } + } else { + bf_set_zero(r, r_sign); + ret = 0; + } + } else { + bf_t tmp, *r1 = NULL; + limb_t a_len, b_len, precl; + limb_t *a_tab, *b_tab; + + a_len = a->len; + b_len = b->len; + + if ((flags & BF_RND_MASK) == BF_RNDF) { + /* faithful rounding does not require using the full inputs */ + precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + a_len = bf_min(a_len, precl); + b_len = bf_min(b_len, precl); + } + a_tab = a->tab + a->len - a_len; + b_tab = b->tab + b->len - b_len; + +#ifdef USE_FFT_MUL + if (b_len >= FFT_MUL_THRESHOLD) { + int mul_flags = 0; + if (r == a) + mul_flags |= FFT_MUL_R_OVERLAP_A; + if (r == b) + mul_flags |= FFT_MUL_R_OVERLAP_B; + if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags)) + goto fail; + } else +#endif + { + if (r == a || r == b) { + bf_init(r->ctx, &tmp); + r1 = r; + r = &tmp; + } + if (bf_resize(r, a_len + b_len)) { + fail: + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; + } + mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len); + } + r->sign = r_sign; + r->expn = a->expn + b->expn; + ret = bf_normalize_and_round(r, prec, flags); + done: + if (r == &tmp) + bf_move(r1, &tmp); + } + return ret; +} + +/* multiply 'r' by 2^e */ +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags) +{ + slimb_t e_max; + if (r->len == 0) + return 0; + e_max = ((limb_t)1 << BF_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); +} + +/* 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 fail; + if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) + 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 || + rnd_mode == BF_RNDNU); + 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_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + case BF_RNDNU: + /* XXX: unsupported yet */ + abort(); + } + + 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_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, BF_RNDZ); + bf_delete(q); + return ret; +} + +int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, BF_RNDN); + 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) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, BF_RNDN); + bf_get_limb(pq, q, BF_GET_INT_MOD); + bf_delete(q); + return ret; +} + +static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m) +{ + dlimb_t t; + t = (dlimb_t)a * (dlimb_t)b; + return t % m; +} + +#if defined(USE_MUL_CHECK) +static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r) +{ + slimb_t i; + dlimb_t t; + + for(i = n - 1; i >= 0; i--) { + t = ((dlimb_t)r << LIMB_BITS) | tab[i]; + r = t % m; + } + return r; +} +#endif + +static const uint16_t sqrt_table[192] = { +128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255, +}; + +/* a >= 2^(LIMB_BITS - 2). Return (s, r) with s=floor(sqrt(a)) and + r=a-s^2. 0 <= r <= 2 * s */ +static limb_t mp_sqrtrem1(limb_t *pr, limb_t a) +{ + limb_t s1, r1, s, r, q, u, num; + + /* use a table for the 16 -> 8 bit sqrt */ + s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64]; + r1 = (a >> (LIMB_BITS - 16)) - s1 * s1; + if (r1 > 2 * s1) { + r1 -= 2 * s1 + 1; + s1++; + } + + /* one iteration to get a 32 -> 16 bit sqrt */ + num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff); + q = num / (2 * s1); /* q <= 2^8 */ + u = num % (2 * s1); + s = (s1 << 8) + q; + r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff); + r -= q * q; + if ((slimb_t)r < 0) { + s--; + r += 2 * s + 1; + } + +#if LIMB_BITS == 64 + s1 = s; + r1 = r; + /* one more iteration for 64 -> 32 bit sqrt */ + num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff); + q = num / (2 * s1); /* q <= 2^16 */ + u = num % (2 * s1); + s = (s1 << 16) + q; + r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff); + r -= q * q; + if ((slimb_t)r < 0) { + s--; + r += 2 * s + 1; + } +#endif + *pr = r; + return s; +} + +/* return floor(sqrt(a)) */ +limb_t bf_isqrt(limb_t a) +{ + limb_t s, r; + int k; + + if (a == 0) + return 0; + k = clz(a) & ~1; + s = mp_sqrtrem1(&r, a << k); + s >>= (k >> 1); + return s; +} + +static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba) +{ + limb_t s1, r1, s, q, u, a0, a1; + dlimb_t r, num; + int l; + + a0 = taba[0]; + a1 = taba[1]; + s1 = mp_sqrtrem1(&r1, a1); + l = LIMB_BITS / 2; + num = ((dlimb_t)r1 << l) | (a0 >> l); + q = num / (2 * s1); + u = num % (2 * s1); + s = (s1 << l) + q; + r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1)); + if (unlikely((q >> l) != 0)) + r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */ + else + r -= q * q; + if ((slimb_t)(r >> LIMB_BITS) < 0) { + s--; + r += 2 * (dlimb_t)s + 1; + } + tabs[0] = s; + taba[0] = r; + return r >> LIMB_BITS; +} + +//#define DEBUG_SQRTREM + +/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest + limb of the remainder. */ +static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n, + limb_t *tmp_buf, limb_t *prh) +{ + limb_t l, h, rh, ql, qh, c, i; + + if (n == 1) { + *prh = mp_sqrtrem2(tabs, taba); + return 0; + } +#ifdef DEBUG_SQRTREM + mp_print_str("a", taba, 2 * n); +#endif + l = n / 2; + h = n - l; + if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh)) + return -1; +#ifdef DEBUG_SQRTREM + mp_print_str("s1", tabs + l, h); + mp_print_str_h("r1", taba + 2 * l, h, qh); + mp_print_str_h("r2", taba + l, n, qh); +#endif + + /* the remainder is in taba + 2 * l. Its high bit is in qh */ + if (qh) { + mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); + } + /* instead of dividing by 2*s, divide by s (which is normalized) + and update q and r */ + if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h)) + return -1; + qh += tmp_buf[l]; + for(i = 0; i < l; i++) + tabs[i] = tmp_buf[i]; + ql = mp_shr(tabs, tabs, l, 1, qh & 1); + qh = qh >> 1; /* 0 or 1 */ + if (ql) + rh = mp_add(taba + l, taba + l, tabs + l, h, 0); + else + rh = 0; +#ifdef DEBUG_SQRTREM + mp_print_str_h("q", tabs, l, qh); + mp_print_str_h("u", taba + l, h, rh); +#endif + + mp_add_ui(tabs + l, qh, h); +#ifdef DEBUG_SQRTREM + mp_print_str_h("s2", tabs, n, sh); +#endif + + /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ + /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ + if (qh) { + c = qh; + } else { + if (mp_mul(s, taba + n, tabs, l, tabs, l)) + return -1; + c = mp_sub(taba, taba, taba + n, 2 * l, 0); + } + rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l); + if ((slimb_t)rh < 0) { + mp_sub_ui(tabs, 1, n); + rh += mp_add_mul1(taba, tabs, n, 2); + rh += mp_add_ui(taba, 1, n); + } + *prh = rh; + return 0; +} + +/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS + - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 + * s. tabs has n limbs. r is returned in the lower n limbs of + taba. Its r[n] is the returned value of the function. */ +/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and + inspirated from its GMP implementation */ +int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) +{ + limb_t tmp_buf1[8]; + limb_t *tmp_buf; + mp_size_t n2; + int ret; + n2 = n / 2 + 1; + if (n2 <= countof(tmp_buf1)) { + tmp_buf = tmp_buf1; + } else { + tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); + if (!tmp_buf) + return -1; + } + ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n); + if (tmp_buf != tmp_buf1) + bf_free(s, tmp_buf); + return ret; +} + +/* Integer square root with remainder. 'a' must be an integer. r = + floor(sqrt(a)) and rem = a - r^2. BF_ST_INEXACT is set if the result + is inexact. 'rem' can be NULL if the remainder is not needed. */ +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a) +{ + int ret; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bf_set(r, a); + } + if (rem1) + bf_set_ui(rem1, 0); + ret = 0; + } else if (a->sign) { + invalid_op: + bf_set_nan(r); + if (rem1) + bf_set_ui(rem1, 0); + ret = BF_ST_INVALID_OP; + } else { + bf_t rem_s, *rem; + + bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ); + bf_rint(r, BF_RNDZ); + /* see if the result is exact by computing the remainder */ + if (rem1) { + rem = rem1; + } else { + rem = &rem_s; + bf_init(r->ctx, rem); + } + /* XXX: could avoid recomputing the remainder */ + bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ); + bf_neg(rem); + bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ); + if (bf_is_nan(rem)) { + ret = BF_ST_MEM_ERROR; + goto done; + } + if (rem->len != 0) { + ret = BF_ST_INEXACT; + } else { + ret = 0; + } + done: + if (!rem1) + bf_delete(rem); + } + return ret; +} + +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = a->ctx; + int ret; + + assert(r != a); + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bf_set(r, a); + } + ret = 0; + } else if (a->sign) { + invalid_op: + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + limb_t *a1; + slimb_t n, n1; + limb_t res; + + /* convert the mantissa to an integer with at least 2 * + prec + 4 bits */ + n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS); + if (bf_resize(r, n)) + goto fail; + a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); + if (!a1) + goto fail; + n1 = bf_min(2 * n, a->len); + memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); + memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); + if (a->expn & 1) { + res = mp_shr(a1, a1, 2 * n, 1, 0); + } else { + res = 0; + } + if (mp_sqrtrem(s, r->tab, a1, n)) { + bf_free(s, a1); + goto fail; + } + if (!res) { + res = mp_scan_nz(a1, n + 1); + } + bf_free(s, a1); + if (!res) { + res = mp_scan_nz(a->tab, a->len - n1); + } + if (res != 0) + r->tab[0] |= 1; + r->sign = 0; + r->expn = (a->expn + 1) >> 1; + ret = bf_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, bf_op2_func_t *func) +{ + bf_t tmp; + int ret; + + if (r == a || r == b) { + bf_init(r->ctx, &tmp); + ret = func(&tmp, a, b, prec, flags); + bf_move(r, &tmp); + } else { + ret = func(r, a, b, prec, flags); + } + return ret; +} + +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_add); +} + +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_sub); +} + +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_div); +} + +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + bf_init(r->ctx, &b); + ret = bf_set_ui(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + bf_init(r->ctx, &b); + ret = bf_set_si(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + + bf_init(r->ctx, &b); + ret = bf_set_si(&b, b1); + ret |= bf_add(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, + bf_flags_t flags) +{ + int ret, n_bits, i; + + assert(r != a); + if (b == 0) + return bf_set_ui(r, 1); + ret = bf_set(r, a); + n_bits = LIMB_BITS - clz(b); + for(i = n_bits - 2; i >= 0; i--) { + ret |= bf_mul(r, r, r, prec, flags); + if ((b >> i) & 1) + ret |= bf_mul(r, r, a, prec, flags); + } + return ret; +} + +static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, + limb_t prec, bf_flags_t flags) +{ + bf_t a; + int ret; + + if (a1 == 10 && b <= LIMB_DIGITS) { + /* use precomputed powers. We do not round at this point + because we expect the caller to do it */ + ret = bf_set_ui(r, mp_pow_dec[b]); + } else { + bf_init(r->ctx, &a); + ret = bf_set_ui(&a, a1); + ret |= bf_pow_ui(r, &a, b, prec, flags); + bf_delete(&a); + } + return ret; +} + +/* convert to integer (infinite precision) */ +int bf_rint(bf_t *r, int rnd_mode) +{ + return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); +} + +/* logical operations */ +#define BF_LOGIC_OR 0 +#define BF_LOGIC_XOR 1 +#define BF_LOGIC_AND 2 + +static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op) +{ + switch(op) { + case BF_LOGIC_OR: + return a | b; + case BF_LOGIC_XOR: + return a ^ b; + default: + case BF_LOGIC_AND: + return a & b; + } +} + +static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) +{ + bf_t b1_s, a1_s, *a, *b; + limb_t a_sign, b_sign, r_sign; + slimb_t l, i, a_bit_offset, b_bit_offset; + limb_t v1, v2, v1_mask, v2_mask, r_mask; + int ret; + + assert(r != a1 && r != b1); + + if (a1->expn <= 0) + a_sign = 0; /* minus zero is considered as positive */ + else + a_sign = a1->sign; + + if (b1->expn <= 0) + b_sign = 0; /* minus zero is considered as positive */ + else + b_sign = b1->sign; + + if (a_sign) { + a = &a1_s; + bf_init(r->ctx, a); + if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) { + b = NULL; + goto fail; + } + } else { + a = (bf_t *)a1; + } + + if (b_sign) { + b = &b1_s; + bf_init(r->ctx, b); + if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ)) + goto fail; + } else { + b = (bf_t *)b1; + } + + r_sign = bf_logic_op1(a_sign, b_sign, op); + if (op == BF_LOGIC_AND && r_sign == 0) { + /* no need to compute extra zeros for and */ + if (a_sign == 0 && b_sign == 0) + l = bf_min(a->expn, b->expn); + else if (a_sign == 0) + l = a->expn; + else + l = b->expn; + } else { + l = bf_max(a->expn, b->expn); + } + /* Note: a or b can be zero */ + l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS; + if (bf_resize(r, l)) + goto fail; + a_bit_offset = a->len * LIMB_BITS - a->expn; + b_bit_offset = b->len * LIMB_BITS - b->expn; + v1_mask = -a_sign; + v2_mask = -b_sign; + r_mask = -r_sign; + for(i = 0; i < l; i++) { + v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask; + v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask; + r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask; + } + r->expn = l * LIMB_BITS; + r->sign = r_sign; + bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */ + if (r_sign) { + if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ)) + goto fail; + } + ret = 0; + done: + if (a == &a1_s) + bf_delete(a); + if (b == &b1_s) + bf_delete(b); + return ret; + fail: + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_OR); +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_XOR); +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_AND); +} + +/* conversion between fixed size types */ + +typedef union { + double d; + uint64_t u; +} Float64Union; + +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode) +{ + Float64Union u; + int e, ret; + uint64_t m; + + ret = 0; + if (a->expn == BF_EXP_NAN) { + u.u = 0x7ff8000000000000; /* quiet nan */ + } else { + bf_t b_s, *b = &b_s; + + bf_init(a->ctx, b); + bf_set(b, a); + if (bf_is_finite(b)) { + ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11)); + } + if (b->expn == BF_EXP_INF) { + e = (1 << 11) - 1; + m = 0; + } else if (b->expn == BF_EXP_ZERO) { + e = 0; + m = 0; + } else { + e = b->expn + 1023 - 1; +#if LIMB_BITS == 32 + if (b->len == 2) { + m = ((uint64_t)b->tab[1] << 32) | b->tab[0]; + } else { + m = ((uint64_t)b->tab[0] << 32); + } +#else + m = b->tab[0]; +#endif + if (e <= 0) { + /* subnormal */ + m = m >> (12 - e); + e = 0; + } else { + m = (m << 1) >> 12; + } + } + u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63); + bf_delete(b); + } + *pres = u.d; + return ret; +} + +int bf_set_float64(bf_t *a, double d) +{ + Float64Union u; + uint64_t m; + int shift, e, sgn; + + u.d = d; + sgn = u.u >> 63; + e = (u.u >> 52) & ((1 << 11) - 1); + m = u.u & (((uint64_t)1 << 52) - 1); + if (e == ((1 << 11) - 1)) { + if (m != 0) { + bf_set_nan(a); + } else { + bf_set_inf(a, sgn); + } + } else if (e == 0) { + if (m == 0) { + bf_set_zero(a, sgn); + } else { + /* subnormal number */ + m <<= 12; + shift = clz64(m); + m <<= shift; + e = -shift; + goto norm; + } + } else { + m = (m << 11) | ((uint64_t)1 << 63); + norm: + a->expn = e - 1023 + 1; +#if LIMB_BITS == 32 + if (bf_resize(a, 2)) + goto fail; + a->tab[0] = m; + a->tab[1] = m >> 32; +#else + if (bf_resize(a, 1)) + goto fail; + a->tab[0] = m; +#endif + a->sign = sgn; + } + return 0; +fail: + bf_set_nan(a); + return BF_ST_MEM_ERROR; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW 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 = 0; + if (flags & BF_GET_INT_MOD) { + v = 0; + } else 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 <= 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_OVERFLOW; + 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_OVERFLOW 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 = 0; + 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_OVERFLOW; + 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; +} + +/* 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); + 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); + else + ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN); + 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 { + 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 > ((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 void 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; + 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]; + if (B->len == 0) { + /* compute BASE^n2 */ + 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 */ + bf_set_ui(&R, 1); + 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; + bf_mul(&Q, a, B_inv, q_prec, BF_RNDN); + 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); + /* adjust if necessary */ + q_add = 0; + while (R.sign && R.len != 0) { + bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ); + q_add--; + } + while (bf_cmpu(&R, B) >= 0) { + bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ); + q_add++; + } + if (q_add != 0) { + bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ); + } + bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0, + radixl, radixl_bits); + bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0, + radixl, radixl_bits); + bf_delete(&Q); + bf_delete(&R); + } +} + +static void 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; + + 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); + for(i = 0; i < pow_tab_len; i++) + bf_init(r->ctx, &pow_tab[i]); + + 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); +} + +/* 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); + if (!e_sign) + ret |= bf_mul(r, r, a, prec + extra_bits, BF_RNDN); + else + ret |= bf_div(r, a, r, prec + extra_bits, BF_RNDN); + 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) { + 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; + bf_resize(a, n); + bf_integer_to_radix(a, a1, radixl); + radix_bits = 0; + pos = n; + pos_incr = 1; + first_buf_pos = pos * digits_per_limb - n_digits; + } else { + a = (bf_t *)a1; + radix_bits = ceil_log2(radix); + digits_per_limb = LIMB_BITS / radix_bits; + pos_incr = digits_per_limb * radix_bits; + pos = a->len * LIMB_BITS - a->expn + n_digits * radix_bits; + first_buf_pos = 0; + } + 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; + } + 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) +{ + DynBuf s_s, *s = &s_s; + int radix_bits; + + // bf_print_str("ftoa", a2); + // printf("radix=%d\n", radix); + dbuf_init2(s, a2->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; + 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 ((radix & (radix - 1)) != 0) + radix_bits = 0; + else + radix_bits = ceil_log2(radix); + + fmt = flags & BF_FTOA_FORMAT_MASK; + a1 = &a1_s; + bf_init(a2->ctx, a1); + if (fmt == BF_FTOA_FORMAT_FRAC) { + size_t pos, start; + assert(!is_dec); + /* 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 (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + } else { + /* prec is ignored */ + prec = n_digits = a->len * LIMB_DIGITS; + /* remove trailing zero digits */ + while (n_digits > 1 && + get_digit(a->tab, a->len, prec - n_digits) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + bf_init(a2->ctx, a1); + bf_set(a1, a); + n = a1->expn; + } else +#endif + { + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + } else { + slimb_t n_digits_max, n_digits_min; + + if (prec == BF_PREC_INF) { + assert(radix_bits != 0); + /* XXX: could use the exact number of bits */ + prec = a->len * LIMB_BITS; + } + 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(a2->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(a2->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)) +{ + 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); + 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); +} + +int bf_const_pi(bf_t *T, 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); +} + +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; + } + } + return bf_round(r, prec, flags) | 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); + } + + /* undo the argument reduction */ + bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ); + + return BF_ST_INEXACT; +} + +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + 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; + } + + /* crude overflow and underflow tests */ + if (a->expn > 0) { + bf_t T_s, *T = &T_s; + bf_t log2_s, *log2 = &log2_s; + slimb_t e_min, e_max; + 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 > e_max * log(2) implies exp(a) > e_max */ + if (bf_cmp_lt(T, a) > 0) { + /* overflow */ + bf_delete(T); + bf_delete(log2); + return bf_set_overflow(r, 0, prec, flags); + } + /* a < e_min * log(2) implies exp(a) < e_min */ + bf_mul_si(T, log2, e_min, LIMB_BITS, BF_RNDD); + if (bf_cmp_lt(a, 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 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 */ +/* XXX: overflow/underflow handling */ +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_mul(T, T, y, prec1, BF_RNDF); + bf_exp(r, T, prec1, BF_RNDF); + bf_delete(T); + return BF_ST_INEXACT; +} + +/* x and y finite, x > 0, y integer and y fits on one limb */ +/* XXX: overflow/underflow handling */ +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); + if (y->sign) { + bf_init(s, T); + bf_set_ui(T, 1); + ret |= bf_div(r, T, r, prec1, BF_RNDN); + 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_QUICKS) && + (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 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=%ld\n", -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); + } + 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); +} + +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); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + if (c) + bf_set_nan(c); + if (s) + bf_set_nan(s); + return 0; + } else if (a->expn == BF_EXP_INF) { + if (c) + bf_set_nan(c); + if (s) + bf_set_nan(s); + return BF_ST_INVALID_OP; + } else { + if (c) + bf_set_ui(c, 1); + if (s) + bf_set_zero(s, a->sign); + return 0; + } + } + + 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 + 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); + // 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) +{ + 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) +{ + 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; + + 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; + } + } + + /* 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) +{ + 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; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else { + if (a->expn == BF_EXP_INF) + i = 1 - 2 * a->sign; + else + i = 0; + i += add_pi2; + /* return i*(pi/2) with -1 <= i <= 2 */ + if (i == 0) { + bf_set_zero(r, add_pi2 ? 0 : a->sign); + return 0; + } else { + /* PI or PI/2 */ + bf_const_pi(r, prec, BF_RNDF); + if (i != 2) + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + r->sign = (i < 0); + return BF_ST_INEXACT; + } + } + } + + bf_init(s, T); + bf_set_ui(T, 1); + cmp_1 = bf_cmpu(a, T); + if (cmp_1 == 0 && !add_pi2) { + /* short cut: abs(a) == 1 -> +/-pi/4 */ + bf_const_pi(r, prec, BF_RNDF); + bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); + r->sign = a->sign; + bf_delete(T); + return BF_ST_INEXACT; + } + + /* XXX: precision analysis */ + K = bf_isqrt((prec + 1) / 2); + l = prec / (2 * K) + 1; + prec1 = prec + K + 2 * l + 32; + // printf("prec=%ld K=%ld l=%ld prec1=%ld\n", prec, K, l, prec1); + + if (cmp_1 > 0) { + 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) +{ + 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; + 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 { + if (is_acos) { + bf_const_pi(r, prec, BF_RNDF); + 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); + if (res > 0) { + bf_delete(T); + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (res == 0 && a->sign == 0 && is_acos) { + bf_set_zero(r, 0); + bf_delete(T); + return 0; + } + + /* 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_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) +{ + 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) +{ + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE); +} + +/***************************************************************/ +/* decimal floating point numbers */ + +#ifdef USE_BF_DEC + +#define adddq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 += (a0); \ + r1 += (a1) + (r0 < __t); \ + } while (0) + +#define subdq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 -= (a0); \ + r1 -= (a1) + (r0 > __t); \ + } while (0) + +#if LIMB_BITS == 64 + +/* Note: we assume __int128 is available */ +#define muldq(r1, r0, a, b) \ + do { \ + unsigned __int128 __t; \ + __t = (unsigned __int128)(a) * (unsigned __int128)(b); \ + r0 = __t; \ + r1 = __t >> 64; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + unsigned __int128 __t; \ + limb_t __b = (b); \ + __t = ((unsigned __int128)(a1) << 64) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#else + +#define muldq(r1, r0, a, b) \ + do { \ + uint64_t __t; \ + __t = (uint64_t)(a) * (uint64_t)(b); \ + r0 = __t; \ + r1 = __t >> 32; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + uint64_t __t; \ + limb_t __b = (b); \ + __t = ((uint64_t)(a1) << 32) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#endif /* LIMB_BITS != 64 */ + +static inline 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 limb_t shld(limb_t a1, limb_t a0, long shift) +{ + if (shift != 0) + return (a1 << shift) | (a0 >> (LIMB_BITS - shift)); + else + return a1; +} + +#if LIMB_DIGITS == 19 + +/* WARNING: hardcoded for b = 1e19. It is assumed that: + 0 <= a1 < 2^63 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \ + __a0 = a0;\ + __a1 = a1;\ + __t0 = __a1;\ + __t0 = shld(__t0, __a0, 1);\ + muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \ + muldq(__t1, __t0, q, __b);\ + subdq(__a1, __a0, __t1, __t0);\ + subdq(__a1, __a0, 1, __b * 2); \ + __t0 = (slimb_t)__a1 >> 1; \ + q += 2 + __t0;\ + adddq(__a1, __a0, 0, __b & __t0);\ + q += __a1; \ + __a0 += __b & __a1; \ + r = __a0;\ +} while(0) + +#elif LIMB_DIGITS == 9 + +/* WARNING: hardcoded for b = 1e9. It is assumed that: + 0 <= a1 < 2^29 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint32_t __t0, __t1, __b = BF_DEC_BASE; \ + __t0 = a1;\ + __t1 = a0;\ + __t0 = (__t0 << 3) | (__t1 >> (32 - 3)); \ + muldq(q, __t1, __t0, 2305843009U);\ + r = a0 - q * __b;\ + __t1 = (r >= __b);\ + q += __t1;\ + if (__t1)\ + r -= __b;\ +} while(0) + +#endif + +/* fast integer division by a fixed constant */ + +typedef struct FastDivData { + limb_t d; /* divisor (only user visible field) */ + limb_t m1; /* multiplier */ + int shift1; + int shift2; +} FastDivData; + +#if 1 +/* From "Division by Invariant Integers using Multiplication" by + Torborn Granlund and Peter L. Montgomery */ +/* d must be != 0 */ +static inline void fast_udiv_init(FastDivData *s, limb_t d) +{ + int l; + limb_t q, r, m1; + s->d = d; + 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; +} + +static inline limb_t fast_urem(limb_t a, const FastDivData *s) +{ + limb_t q; + q = fast_udiv(a, s); + return a - q * s->d; +} + +#define fast_udivrem(q, r, a, s) q = fast_udiv(a, s), r = a - q * (s)->d + +#else + +static inline void fast_udiv_init(FastDivData *s, limb_t d) +{ + s->d = d; +} +static inline limb_t fast_udiv(limb_t a, const FastDivData *s) +{ + return a / s->d; +} +static inline limb_t fast_urem(limb_t a, const FastDivData *s) +{ + return a % s->d; +} + +#define fast_udivrem(q, r, a, s) q = a / (s)->d, r = a % (s)->d + +#endif + +/* contains 10^i */ +/* XXX: make it const */ +limb_t mp_pow_dec[LIMB_DIGITS + 1]; +static FastDivData mp_pow_div[LIMB_DIGITS + 1]; + +static void mp_pow_init(void) +{ + limb_t a; + int i; + + a = 1; + for(i = 0; i <= LIMB_DIGITS; i++) { + mp_pow_dec[i] = a; + fast_udiv_init(&mp_pow_div[i], a); + a = a * 10; + } +} + +limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, a, v; + + k=carry; + for(i=0;i v; + if (k) + a += base; + res[i] = a; + } + return k; +} + +limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, v, a; + + k=b; + for(i=0;i v; + if (k) + a += base; + tab[i]=a; + if (k == 0) + break; + } + return k; +} + +/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */ +limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b, limb_t l) +{ + mp_size_t i; + limb_t t0, t1, r; + + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add + to the high word */ +limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + mp_size_t i; + limb_t l, t0, t1, r; + + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + adddq(t1, t0, 0, tabr[i]); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to + substract to the high word. */ +limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t l, t0, t1, r, a, v, c; + + /* XXX: optimize */ + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + v = tabr[i]; + a = v - r; + c = a > v; + if (c) + a += base; + /* never bigger than base because r = 0 when l = base - 1 */ + l += c; + tabr[i] = a; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +void mp_mul_basecase_dec(limb_t *result, + const limb_t *op1, mp_size_t op1_size, + const limb_t *op2, mp_size_t op2_size) +{ + mp_size_t i; + limb_t r; + + result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0); + + for(i=1;i> 1; + if (r) + r = base_div2; + for(i = na - 1; i >= 0; i--) { + t0 = taba[i]; + tabr[i] = (t0 >> 1) + r; + r = 0; + if (t0 & 1) + r = base_div2; + } + if (r) + r = 1; + } else +#endif + if (na >= UDIV1NORM_THRESHOLD) { + shift = clz(b); + if (shift == 0) { + /* normalized case: b >= 2^(LIMB_BITS-1) */ + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + q = udiv1norm(&r, t1, t0, b, b_inv); + tabr[i] = q; + } + } else { + limb_t b_inv; + b <<= shift; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift)); + t0 <<= shift; + q = udiv1norm(&r, t1, t0, b, b_inv); + r >>= shift; + tabr[i] = q; + } + } + } else { + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + divdq(q, r, t1, t0, b); + tabr[i] = q; + } + } + return r; +} + +static __maybe_unused void mp_print_str_dec(const char *str, + const limb_t *tab, slimb_t n) +{ + slimb_t i; + printf("%s=", str); + for(i = n - 1; i >= 0; i--) { + if (i != n - 1) + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +static __maybe_unused void mp_print_str_h_dec(const char *str, + const limb_t *tab, slimb_t n, + limb_t high) +{ + slimb_t i; + printf("%s=", str); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, high); + for(i = n - 1; i >= 0; i--) { + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +//#define DEBUG_DIV_SLOW + +#define DIV_STATIC_ALLOC_LEN 16 + +/* return q = a / b and r = a % b. + + taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1] + must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1] + >= B / 2. + + The remainder is is returned in taba and contains nb libms. tabq + contains na - nb + 1 limbs. No overlap is permitted. + + Running time of the standard method: (na - nb + 1) * nb + Return 0 if OK, -1 if memory alloc error +*/ +/* XXX: optimize */ +static int mp_div_dec(bf_context_t *s, limb_t *tabq, + limb_t *taba, mp_size_t na, + const limb_t *tabb1, mp_size_t nb) +{ + limb_t base = BF_DEC_BASE; + limb_t r, mult, t0, t1, a, c, q, v, *tabb; + mp_size_t i, j; + limb_t static_tabb[DIV_STATIC_ALLOC_LEN]; + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("a", taba, na); + mp_print_str_dec("b", tabb1, nb); +#endif + + /* normalize tabb */ + r = tabb1[nb - 1]; + assert(r != 0); + i = na - nb; + if (r >= BF_DEC_BASE / 2) { + mult = 1; + tabb = (limb_t *)tabb1; + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[i + j] != tabb[j]) { + if (taba[i + j] < tabb[j]) + q = 0; + break; + } + } + tabq[i] = q; + if (q) { + mp_sub_dec(taba + i, taba + i, tabb, nb, 0); + } + i--; + } else { + mult = base / (r + 1); + if (likely(nb <= DIV_STATIC_ALLOC_LEN)) { + tabb = static_tabb; + } else { + tabb = bf_malloc(s, sizeof(limb_t) * nb); + if (!tabb) + return -1; + } + mp_mul1_dec(tabb, tabb1, nb, mult, 0); + taba[na] = mp_mul1_dec(taba, taba, na, mult, 0); + } + +#ifdef DEBUG_DIV_SLOW + printf("mult=" FMT_LIMB "\n", mult); + mp_print_str_dec("a_norm", taba, na + 1); + mp_print_str_dec("b_norm", tabb, nb); +#endif + + for(; i >= 0; i--) { + if (unlikely(taba[i + nb] >= tabb[nb - 1])) { + /* XXX: check if it is really possible */ + q = base - 1; + } else { + muldq(t1, t0, taba[i + nb], base); + adddq(t1, t0, 0, taba[i + nb - 1]); + divdq(q, r, t1, t0, tabb[nb - 1]); + } + // printf("i=%d q1=%ld\n", i, q); + + r = mp_sub_mul1_dec(taba + i, tabb, nb, q); + // mp_dump("r1", taba + i, nb, bd); + // printf("r2=%ld\n", r); + + v = taba[i + nb]; + a = v - r; + c = a > v; + if (c) + a += base; + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add_dec(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == base) { + break; + } + } + } + } + tabq[i] = q; + } + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("q", tabq, na - nb + 1); + mp_print_str_dec("r", taba, nb); +#endif + + /* remove the normalization */ + if (mult != 1) { + mp_div1_dec(taba, taba, nb, mult, 0); + if (unlikely(tabb != static_tabb)) + bf_free(s, tabb); + } + return 0; +} + +/* divide by 10^shift */ +static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t high) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + fast_udivrem(q, r, a, &mp_pow_div[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_udivrem(q, r, a, &mp_pow_div[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_udivrem(q, v, r->tab[pos], &mp_pow_div[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_udiv(tab[i], &mp_pow_div[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_udiv(a0, &mp_pow_div[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_RNDNU: + if (digit1 >= 5) { + if (r->sign) + add_one = (digit0 != 0); + else + add_one = 1; + } + 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; + + /* e_min and e_max are computed to match the IEEE 754 conventions */ + /* XXX: does not matter 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 { + 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)) { + 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_udiv(r->tab[i], &mp_pow_div[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 + + 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 || + rnd_mode == BF_RNDNU); + 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_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + case BF_RNDNU: + /* XXX: unsupported yet */ + abort(); + } + + 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_fmod(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + bfdec_t q_s, *q = &q_s; + int ret; + + bfdec_init(r->ctx, q); + ret = bfdec_divrem(q, r, a, b, prec, flags, BF_RNDZ); + 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; + 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 { + /* convert the mantissa to an integer with at least 2 * + prec + 4 digits */ + n = (2 * (prec + 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_udiv(a->tab[a->len - 1], &mp_pow_div[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_udiv(a->tab[a->len - 1], &mp_pow_div[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/libbf.h b/libbf.h new file mode 100644 index 0000000..500124c --- /dev/null +++ b/libbf.h @@ -0,0 +1,499 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2020 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBBF_H +#define LIBBF_H + +#include +#include + +#if defined(__x86_64__) +#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 EXP_MIN INT64_MIN +#define 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 EXP_MIN INT32_MIN +#define EXP_MAX INT32_MAX + +#define LIMB_DIGITS 9 +#define BF_DEC_BASE 1000000000U + +#endif + +/* in bits */ +#define BF_EXP_BITS_MIN 3 +#define BF_EXP_BITS_MAX (LIMB_BITS - 2) +#define BF_PREC_MIN 2 +#define BF_PREC_MAX (((limb_t)1 << BF_EXP_BITS_MAX) - 2) +#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 EXP_MIN +#define BF_EXP_INF (EXP_MAX - 1) +#define BF_EXP_NAN 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 */ + BF_RNDU, /* round to +inf */ + BF_RNDNA, /* round to nearest, ties away from zero */ + BF_RNDNU, /* round to nearest, ties to +inf */ + 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_MAX and prec != BF_PREC_INF. Not supported + for decimal floating point numbers. */ +#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(), bfdec_round() and + bfdev_div(). */ +#define BF_FLAG_RADPNT_PREC (1 << 4) + +#define BF_RND_MASK 0x7 +#define BF_EXP_BITS_SHIFT 5 +#define BF_EXP_BITS_MASK 0x3f + +/* 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) +{ + return BF_EXP_BITS_MAX - ((flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK); +} + +static inline bf_flags_t bf_set_exp_bits(int n) +{ + return (BF_EXP_BITS_MAX - n) << 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_eq(const bf_t *a, const bf_t *b); +int bf_cmp_le(const bf_t *a, const bf_t *b); +int bf_cmp_lt(const bf_t *a, const bf_t *b); +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_fmod(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +int bf_remainder(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); +/* 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 the 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: use as many digits as necessary so that bf_atof() + return the same number when using precision 'prec', rounding to + nearest and the subnormal+exponent configuration of 'flags'. The + result is meaningful only if 'a' is already rounded to the wanted + precision. + + Infinite precision (BF_PREC_INF) is supported when the radix is a + power of two. */ +#define BF_FTOA_FORMAT_FREE (2 << 16) +/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits + (takes more computation time). */ +#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); + +/* 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_QUICKS (1 << 16) +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_eq(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp_eq((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp_le((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp_lt((const bf_t *)a, (const bf_t *)b); +} + +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_fmod(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +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 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/libregexp-opcode.h b/libregexp-opcode.h new file mode 100644 index 0000000..f90c23b --- /dev/null +++ b/libregexp-opcode.h @@ -0,0 +1,58 @@ +/* + * 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(bne_char_pos, 5) /* pop one stack element and jump if equal to the character + position */ +DEF(prev, 1) /* go to the previous char */ +DEF(simple_greedy_quant, 17) + +#endif /* DEF */ diff --git a/libregexp.c b/libregexp.c new file mode 100644 index 0000000..4425ce5 --- /dev/null +++ b/libregexp.c @@ -0,0 +1,2541 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "libregexp.h" + +/* + TODO: + + - Add full unicode canonicalize rules for character ranges (not + really useful but needed for exact "ignorecase" compatibility). + + - Add a lock step execution mode (=linear time execution guaranteed) + when the regular expression is "simple" i.e. no backreference nor + complicated lookahead. The opcodes are designed for this execution + model. +*/ + +#if defined(TEST) +#define DUMP_REOP +#endif + +typedef enum { +#define DEF(id, size) REOP_ ## id, +#include "libregexp-opcode.h" +#undef DEF + REOP_COUNT, +} REOPCodeEnum; + +#define CAPTURE_COUNT_MAX 255 +#define STACK_SIZE_MAX 255 + +/* unicode code points */ +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +#define TMP_BUF_SIZE 128 + +typedef struct { + DynBuf byte_code; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + const uint8_t *buf_start; + int re_flags; + BOOL is_utf16; + BOOL ignore_case; + BOOL dotall; + int capture_count; + int total_capture_count; /* -1 = not computed yet */ + int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */ + void *mem_opaque; + DynBuf group_names; + union { + char error_msg[TMP_BUF_SIZE]; + char tmp_buf[TMP_BUF_SIZE]; + } u; +} REParseState; + +typedef struct { +#ifdef DUMP_REOP + const char *name; +#endif + uint8_t size; +} REOpCode; + +static const REOpCode reopcode_info[REOP_COUNT] = { +#ifdef DUMP_REOP +#define DEF(id, size) { #id, size }, +#else +#define DEF(id, size) { size }, +#endif +#include "libregexp-opcode.h" +#undef DEF +}; + +#define RE_HEADER_FLAGS 0 +#define RE_HEADER_CAPTURE_COUNT 1 +#define RE_HEADER_STACK_SIZE 2 + +#define RE_HEADER_LEN 7 + +static inline int is_digit(int c) { + return c >= '0' && c <= '9'; +} + +/* insert 'len' bytes at position 'pos' */ +static void dbuf_insert(DynBuf *s, int pos, int len) +{ + dbuf_realloc(s, s->size + len); + memmove(s->buf + pos + len, s->buf + pos, s->size - pos); + s->size += len; +} + +/* canonicalize with the specific JS regexp rules */ +static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + int len; + if (is_utf16) { + if (likely(c < 128)) { + if (c >= 'A' && c <= 'Z') + c = c - 'A' + 'a'; + } else { + lre_case_conv(res, c, 2); + c = res[0]; + } + } else { + if (likely(c < 128)) { + if (c >= 'a' && c <= 'z') + c = c - 'a' + 'A'; + } else { + /* legacy regexp: to upper case if single char >= 128 */ + len = lre_case_conv(res, c, FALSE); + if (len == 1 && res[0] >= 128) + c = res[0]; + } + } + return c; +} + +static const uint16_t char_range_d[] = { + 1, + 0x0030, 0x0039 + 1, +}; + +/* code point ranges for Zs,Zl or Zp property */ +static const uint16_t char_range_s[] = { + 10, + 0x0009, 0x000D + 1, + 0x0020, 0x0020 + 1, + 0x00A0, 0x00A0 + 1, + 0x1680, 0x1680 + 1, + 0x2000, 0x200A + 1, + /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ + /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ + 0x2028, 0x2029 + 1, + 0x202F, 0x202F + 1, + 0x205F, 0x205F + 1, + 0x3000, 0x3000 + 1, + /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ + 0xFEFF, 0xFEFF + 1, +}; + +BOOL lre_is_space(int c) +{ + int i, n, low, high; + n = (countof(char_range_s) - 1) / 2; + for(i = 0; i < n; i++) { + low = char_range_s[2 * i + 1]; + if (c < low) + return FALSE; + high = char_range_s[2 * i + 2]; + if (c < high) + return TRUE; + } + return FALSE; +} + +uint32_t const lre_id_start_table_ascii[4] = { + /* $ A-Z _ a-z */ + 0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE +}; + +uint32_t const lre_id_continue_table_ascii[4] = { + /* $ 0-9 A-Z _ a-z */ + 0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE +}; + + +static const uint16_t char_range_w[] = { + 4, + 0x0030, 0x0039 + 1, + 0x0041, 0x005A + 1, + 0x005F, 0x005F + 1, + 0x0061, 0x007A + 1, +}; + +#define CLASS_RANGE_BASE 0x40000000 + +typedef enum { + CHAR_RANGE_d, + CHAR_RANGE_D, + CHAR_RANGE_s, + CHAR_RANGE_S, + CHAR_RANGE_w, + CHAR_RANGE_W, +} CharRangeEnum; + +static const uint16_t *char_range_table[] = { + char_range_d, + char_range_s, + char_range_w, +}; + +static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c) +{ + BOOL invert; + const uint16_t *c_pt; + int len, i; + + invert = c & 1; + c_pt = char_range_table[c >> 1]; + len = *c_pt++; + cr_init(cr, s->mem_opaque, lre_realloc); + for(i = 0; i < len * 2; i++) { + if (cr_add_point(cr, c_pt[i])) + goto fail; + } + if (invert) { + if (cr_invert(cr)) + goto fail; + } + return 0; + fail: + cr_free(cr); + return -1; +} + +static int cr_canonicalize(CharRange *cr) +{ + CharRange a; + uint32_t pt[2]; + int i, ret; + + cr_init(&a, cr->mem_opaque, lre_realloc); + pt[0] = 'a'; + pt[1] = 'z' + 1; + ret = cr_op(&a, cr->points, cr->len, pt, 2, CR_OP_INTER); + if (ret) + goto fail; + /* convert to upper case */ + /* XXX: the generic unicode case would be much more complicated + and not really useful */ + for(i = 0; i < a.len; i++) { + a.points[i] += 'A' - 'a'; + } + /* Note: for simplicity we keep the lower case ranges */ + ret = cr_union1(cr, a.points, a.len); + fail: + cr_free(&a); + return ret; +} + +#ifdef DUMP_REOP +static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, + int buf_len) +{ + int pos, len, opcode, bc_len, re_flags, i; + uint32_t val; + + assert(buf_len >= RE_HEADER_LEN); + + re_flags= buf[0]; + bc_len = get_u32(buf + 3); + assert(bc_len + RE_HEADER_LEN <= buf_len); + printf("flags: 0x%x capture_count=%d stack_size=%d\n", + re_flags, buf[1], buf[2]); + if (re_flags & LRE_FLAG_NAMED_GROUPS) { + const char *p; + p = (char *)buf + RE_HEADER_LEN + bc_len; + printf("named groups: "); + for(i = 1; i < buf[1]; i++) { + if (i != 1) + printf(","); + printf("<%s>", p); + p += strlen(p) + 1; + } + printf("\n"); + assert(p == (char *)(buf + buf_len)); + } + printf("bytecode_len=%d\n", bc_len); + + buf += RE_HEADER_LEN; + pos = 0; + while (pos < bc_len) { + printf("%5u: ", pos); + opcode = buf[pos]; + len = reopcode_info[opcode].size; + if (opcode >= REOP_COUNT) { + printf(" invalid opcode=0x%02x\n", opcode); + break; + } + if ((pos + len) > bc_len) { + printf(" buffer overflow (opcode=0x%02x)\n", opcode); + break; + } + printf("%s", reopcode_info[opcode].name); + switch(opcode) { + case REOP_char: + val = get_u16(buf + pos + 1); + if (val >= ' ' && val <= 126) + printf(" '%c'", val); + else + printf(" 0x%04x", val); + break; + case REOP_char32: + val = get_u32(buf + pos + 1); + if (val >= ' ' && val <= 126) + printf(" '%c'", val); + else + printf(" 0x%08x", val); + break; + case REOP_goto: + case REOP_split_goto_first: + case REOP_split_next_first: + case REOP_loop: + case REOP_lookahead: + case REOP_negative_lookahead: + case REOP_bne_char_pos: + val = get_u32(buf + pos + 1); + val += (pos + 5); + printf(" %u", val); + break; + case REOP_simple_greedy_quant: + printf(" %u %u %u %u", + get_u32(buf + pos + 1) + (pos + 17), + get_u32(buf + pos + 1 + 4), + get_u32(buf + pos + 1 + 8), + get_u32(buf + pos + 1 + 12)); + break; + case REOP_save_start: + case REOP_save_end: + case REOP_back_reference: + case REOP_backward_back_reference: + printf(" %u", buf[pos + 1]); + break; + case REOP_save_reset: + printf(" %u %u", buf[pos + 1], buf[pos + 2]); + break; + case REOP_push_i32: + val = get_u32(buf + pos + 1); + printf(" %d", val); + break; + case REOP_range: + { + int n, i; + n = get_u16(buf + pos + 1); + len += n * 4; + for(i = 0; i < n * 2; i++) { + val = get_u16(buf + pos + 3 + i * 2); + printf(" 0x%04x", val); + } + } + break; + case REOP_range32: + { + int n, i; + n = get_u16(buf + pos + 1); + len += n * 8; + for(i = 0; i < n * 2; i++) { + val = get_u32(buf + pos + 3 + i * 4); + printf(" 0x%08x", val); + } + } + break; + default: + break; + } + printf("\n"); + pos += len; + } +} +#endif + +static void re_emit_op(REParseState *s, int op) +{ + dbuf_putc(&s->byte_code, op); +} + +/* return the offset of the u32 value */ +static int re_emit_op_u32(REParseState *s, int op, uint32_t val) +{ + int pos; + dbuf_putc(&s->byte_code, op); + pos = s->byte_code.size; + dbuf_put_u32(&s->byte_code, val); + return pos; +} + +static int re_emit_goto(REParseState *s, int op, uint32_t val) +{ + int pos; + dbuf_putc(&s->byte_code, op); + pos = s->byte_code.size; + dbuf_put_u32(&s->byte_code, val - (pos + 4)); + return pos; +} + +static void re_emit_op_u8(REParseState *s, int op, uint32_t val) +{ + dbuf_putc(&s->byte_code, op); + dbuf_putc(&s->byte_code, val); +} + +static void re_emit_op_u16(REParseState *s, int op, uint32_t val) +{ + dbuf_putc(&s->byte_code, op); + dbuf_put_u16(&s->byte_code, val); +} + +static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap); + va_end(ap); + return -1; +} + +/* Return -1 in case of overflow */ +static int parse_digits(const uint8_t **pp) +{ + const uint8_t *p; + uint64_t v; + int c; + + p = *pp; + v = 0; + for(;;) { + c = *p; + if (c < '0' || c > '9') + break; + v = v * 10 + c - '0'; + if (v >= INT32_MAX) + return -1; + p++; + } + *pp = p; + return v; +} + +static int re_parse_expect(REParseState *s, const uint8_t **pp, int c) +{ + const uint8_t *p; + p = *pp; + if (*p != c) + return re_parse_error(s, "expecting '%c'", c); + p++; + *pp = p; + return 0; +} + +/* Parse an escape sequence, *pp points after the '\': + allow_utf16 value: + 0 : no UTF-16 escapes allowed + 1 : UTF-16 escapes allowed + 2 : UTF-16 escapes allowed and escapes of surrogate pairs are + converted to a unicode character (unicode regexp case). + + Return the unicode char and update *pp if recognized, + return -1 if malformed escape, + return -2 otherwise. */ +int lre_parse_escape(const uint8_t **pp, int allow_utf16) +{ + const uint8_t *p; + uint32_t c; + + p = *pp; + c = *p++; + switch(c) { + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'x': + case 'u': + { + int h, n, i; + uint32_t c1; + + if (*p == '{' && allow_utf16) { + p++; + c = 0; + for(;;) { + h = from_hex(*p++); + if (h < 0) + return -1; + c = (c << 4) | h; + if (c > 0x10FFFF) + return -1; + if (*p == '}') + break; + } + p++; + } else { + if (c == 'x') { + n = 2; + } else { + n = 4; + } + + c = 0; + for(i = 0; i < n; i++) { + h = from_hex(*p++); + if (h < 0) { + return -1; + } + c = (c << 4) | h; + } + if (c >= 0xd800 && c < 0xdc00 && + allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { + /* convert an escaped surrogate pair into a + unicode char */ + c1 = 0; + for(i = 0; i < 4; i++) { + h = from_hex(p[2 + i]); + if (h < 0) + break; + c1 = (c1 << 4) | h; + } + if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) { + p += 6; + c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + } + } + } + } + break; + case '0' ... '7': + c -= '0'; + if (allow_utf16 == 2) { + /* only accept \0 not followed by digit */ + if (c != 0 || is_digit(*p)) + return -1; + } else { + /* parse a legacy octal sequence */ + uint32_t v; + v = *p - '0'; + if (v > 7) + break; + c = (c << 3) | v; + p++; + if (c >= 32) + break; + v = *p - '0'; + if (v > 7) + break; + c = (c << 3) | v; + p++; + } + break; + default: + return -2; + } + *pp = p; + return c; +} + +#ifdef CONFIG_ALL_UNICODE +/* XXX: we use the same chars for name and value */ +static BOOL is_unicode_char(int c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c == '_')); +} + +static int parse_unicode_property(REParseState *s, CharRange *cr, + const uint8_t **pp, BOOL is_inv) +{ + const uint8_t *p; + char name[64], value[64]; + char *q; + BOOL script_ext; + int ret; + + p = *pp; + if (*p != '{') + return re_parse_error(s, "expecting '{' after \\p"); + p++; + q = name; + while (is_unicode_char(*p)) { + if ((q - name) > sizeof(name) - 1) + goto unknown_property_name; + *q++ = *p++; + } + *q = '\0'; + q = value; + if (*p == '=') { + p++; + while (is_unicode_char(*p)) { + if ((q - value) > sizeof(value) - 1) + return re_parse_error(s, "unknown unicode property value"); + *q++ = *p++; + } + } + *q = '\0'; + if (*p != '}') + return re_parse_error(s, "expecting '}'"); + p++; + // printf("name=%s value=%s\n", name, value); + + if (!strcmp(name, "Script") || !strcmp(name, "sc")) { + script_ext = FALSE; + goto do_script; + } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { + script_ext = TRUE; + do_script: + cr_init(cr, s->mem_opaque, lre_realloc); + ret = unicode_script(cr, value, script_ext); + if (ret) { + cr_free(cr); + if (ret == -2) + return re_parse_error(s, "unknown unicode script"); + else + goto out_of_memory; + } + } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { + cr_init(cr, s->mem_opaque, lre_realloc); + ret = unicode_general_category(cr, value); + if (ret) { + cr_free(cr); + if (ret == -2) + return re_parse_error(s, "unknown unicode general category"); + else + goto out_of_memory; + } + } else if (value[0] == '\0') { + cr_init(cr, s->mem_opaque, lre_realloc); + ret = unicode_general_category(cr, name); + if (ret == -1) { + cr_free(cr); + goto out_of_memory; + } + if (ret < 0) { + ret = unicode_prop(cr, name); + if (ret) { + cr_free(cr); + if (ret == -2) + goto unknown_property_name; + else + goto out_of_memory; + } + } + } else { + unknown_property_name: + return re_parse_error(s, "unknown unicode property name"); + } + + if (is_inv) { + if (cr_invert(cr)) { + cr_free(cr); + return -1; + } + } + *pp = p; + return 0; + out_of_memory: + return re_parse_error(s, "out of memory"); +} +#endif /* CONFIG_ALL_UNICODE */ + +/* return -1 if error otherwise the character or a class range + (CLASS_RANGE_BASE). In case of class range, 'cr' is + initialized. Otherwise, it is ignored. */ +static int get_class_atom(REParseState *s, CharRange *cr, + const uint8_t **pp, BOOL inclass) +{ + const uint8_t *p; + uint32_t c; + int ret; + + p = *pp; + + c = *p; + switch(c) { + case '\\': + p++; + if (p >= s->buf_end) + goto unexpected_end; + c = *p++; + switch(c) { + case 'd': + c = CHAR_RANGE_d; + goto class_range; + case 'D': + c = CHAR_RANGE_D; + goto class_range; + case 's': + c = CHAR_RANGE_s; + goto class_range; + case 'S': + c = CHAR_RANGE_S; + goto class_range; + case 'w': + c = CHAR_RANGE_w; + goto class_range; + case 'W': + c = CHAR_RANGE_W; + class_range: + if (cr_init_char_range(s, cr, c)) + return -1; + c = CLASS_RANGE_BASE; + break; + case 'c': + c = *p; + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (((c >= '0' && c <= '9') || c == '_') && + inclass && !s->is_utf16)) { /* Annex B.1.4 */ + c &= 0x1f; + p++; + } else if (s->is_utf16) { + goto invalid_escape; + } else { + /* otherwise return '\' and 'c' */ + p--; + c = '\\'; + } + break; +#ifdef CONFIG_ALL_UNICODE + case 'p': + case 'P': + if (s->is_utf16) { + if (parse_unicode_property(s, cr, &p, (c == 'P'))) + return -1; + c = CLASS_RANGE_BASE; + break; + } + /* fall thru */ +#endif + default: + p--; + ret = lre_parse_escape(&p, s->is_utf16 * 2); + if (ret >= 0) { + c = ret; + } else { + if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) { + /* always valid to escape these characters */ + goto normal_char; + } else if (s->is_utf16) { + invalid_escape: + return re_parse_error(s, "invalid escape sequence in regular expression"); + } else { + /* just ignore the '\' */ + goto normal_char; + } + } + break; + } + break; + case '\0': + if (p >= s->buf_end) { + unexpected_end: + return re_parse_error(s, "unexpected end"); + } + /* fall thru */ + default: + normal_char: + /* normal char */ + if (c >= 128) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + if ((unsigned)c > 0xffff && !s->is_utf16) { + /* XXX: should handle non BMP-1 code points */ + return re_parse_error(s, "malformed unicode char"); + } + } else { + p++; + } + break; + } + *pp = p; + return c; +} + +static int re_emit_range(REParseState *s, const CharRange *cr) +{ + int len, i; + uint32_t high; + + len = (unsigned)cr->len / 2; + if (len >= 65535) + return re_parse_error(s, "too many ranges"); + if (len == 0) { + /* not sure it can really happen. Emit a match that is always + false */ + re_emit_op_u32(s, REOP_char32, -1); + } else { + high = cr->points[cr->len - 1]; + if (high == UINT32_MAX) + high = cr->points[cr->len - 2]; + if (high <= 0xffff) { + /* can use 16 bit ranges with the conversion that 0xffff = + infinity */ + re_emit_op_u16(s, REOP_range, len); + for(i = 0; i < cr->len; i += 2) { + dbuf_put_u16(&s->byte_code, cr->points[i]); + high = cr->points[i + 1] - 1; + if (high == UINT32_MAX - 1) + high = 0xffff; + dbuf_put_u16(&s->byte_code, high); + } + } else { + re_emit_op_u16(s, REOP_range32, len); + for(i = 0; i < cr->len; i += 2) { + dbuf_put_u32(&s->byte_code, cr->points[i]); + dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1); + } + } + } + return 0; +} + +static int re_parse_char_class(REParseState *s, const uint8_t **pp) +{ + const uint8_t *p; + uint32_t c1, c2; + CharRange cr_s, *cr = &cr_s; + CharRange cr1_s, *cr1 = &cr1_s; + BOOL invert; + + cr_init(cr, s->mem_opaque, lre_realloc); + p = *pp; + p++; /* skip '[' */ + invert = FALSE; + if (*p == '^') { + p++; + invert = TRUE; + } + for(;;) { + if (*p == ']') + break; + c1 = get_class_atom(s, cr1, &p, TRUE); + if ((int)c1 < 0) + goto fail; + if (*p == '-' && p[1] != ']') { + const uint8_t *p0 = p + 1; + if (c1 >= CLASS_RANGE_BASE) { + if (s->is_utf16) { + cr_free(cr1); + goto invalid_class_range; + } + /* Annex B: match '-' character */ + goto class_atom; + } + c2 = get_class_atom(s, cr1, &p0, TRUE); + if ((int)c2 < 0) + goto fail; + if (c2 >= CLASS_RANGE_BASE) { + cr_free(cr1); + if (s->is_utf16) { + goto invalid_class_range; + } + /* Annex B: match '-' character */ + goto class_atom; + } + p = p0; + if (c2 < c1) { + invalid_class_range: + re_parse_error(s, "invalid class range"); + goto fail; + } + if (cr_union_interval(cr, c1, c2)) + goto memory_error; + } else { + class_atom: + if (c1 >= CLASS_RANGE_BASE) { + int ret; + ret = cr_union1(cr, cr1->points, cr1->len); + cr_free(cr1); + if (ret) + goto memory_error; + } else { + if (cr_union_interval(cr, c1, c1)) + goto memory_error; + } + } + } + if (s->ignore_case) { + if (cr_canonicalize(cr)) + goto memory_error; + } + if (invert) { + if (cr_invert(cr)) + goto memory_error; + } + if (re_emit_range(s, cr)) + goto fail; + cr_free(cr); + p++; /* skip ']' */ + *pp = p; + return 0; + memory_error: + re_parse_error(s, "out of memory"); + fail: + cr_free(cr); + return -1; +} + +/* Return: + 1 if the opcodes in bc_buf[] always advance the character pointer. + 0 if the character pointer may not be advanced. + -1 if the code may depend on side effects of its previous execution (backreference) +*/ +static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len) +{ + int pos, opcode, ret, len, i; + uint32_t val, last; + BOOL has_back_reference; + uint8_t capture_bitmap[CAPTURE_COUNT_MAX]; + + ret = -2; /* not known yet */ + pos = 0; + has_back_reference = FALSE; + memset(capture_bitmap, 0, sizeof(capture_bitmap)); + + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + switch(opcode) { + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + goto simple_char; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + goto simple_char; + case REOP_char: + case REOP_char32: + case REOP_dot: + case REOP_any: + simple_char: + if (ret == -2) + ret = 1; + break; + case REOP_line_start: + case REOP_line_end: + case REOP_push_i32: + case REOP_push_char_pos: + case REOP_drop: + case REOP_word_boundary: + case REOP_not_word_boundary: + case REOP_prev: + /* no effect */ + break; + case REOP_save_start: + case REOP_save_end: + val = bc_buf[pos + 1]; + capture_bitmap[val] |= 1; + break; + case REOP_save_reset: + { + val = bc_buf[pos + 1]; + last = bc_buf[pos + 2]; + while (val < last) + capture_bitmap[val++] |= 1; + } + break; + case REOP_back_reference: + case REOP_backward_back_reference: + val = bc_buf[pos + 1]; + capture_bitmap[val] |= 2; + has_back_reference = TRUE; + break; + default: + /* safe behvior: we cannot predict the outcome */ + if (ret == -2) + ret = 0; + break; + } + pos += len; + } + if (has_back_reference) { + /* check if there is back reference which references a capture + made in the some code */ + for(i = 0; i < CAPTURE_COUNT_MAX; i++) { + if (capture_bitmap[i] == 3) + return -1; + } + } + if (ret == -2) + ret = 0; + return ret; +} + +/* return -1 if a simple quantifier cannot be used. Otherwise return + the number of characters in the atom. */ +static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len) +{ + int pos, opcode, len, count; + uint32_t val; + + count = 0; + pos = 0; + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + switch(opcode) { + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + goto simple_char; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + goto simple_char; + case REOP_char: + case REOP_char32: + case REOP_dot: + case REOP_any: + simple_char: + count++; + break; + case REOP_line_start: + case REOP_line_end: + case REOP_word_boundary: + case REOP_not_word_boundary: + break; + default: + return -1; + } + pos += len; + } + return count; +} + +/* '*pp' is the first char after '<' */ +static int re_parse_group_name(char *buf, int buf_size, + const uint8_t **pp, BOOL is_utf16) +{ + const uint8_t *p; + uint32_t c; + char *q; + + p = *pp; + q = buf; + for(;;) { + c = *p; + if (c == '\\') { + p++; + if (*p != 'u') + return -1; + c = lre_parse_escape(&p, is_utf16 * 2); + } else if (c == '>') { + break; + } else if (c >= 128) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + } else { + p++; + } + if (c > 0x10FFFF) + return -1; + if (q == buf) { + if (!lre_js_is_ident_first(c)) + return -1; + } else { + if (!lre_js_is_ident_next(c)) + return -1; + } + if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size) + return -1; + if (c < 128) { + *q++ = c; + } else { + q += unicode_to_utf8((uint8_t*)q, c); + } + } + if (q == buf) + return -1; + *q = '\0'; + p++; + *pp = p; + return 0; +} + +/* if capture_name = NULL: return the number of captures + 1. + Otherwise, return the capture index corresponding to capture_name + or -1 if none */ +static int re_parse_captures(REParseState *s, int *phas_named_captures, + const char *capture_name) +{ + const uint8_t *p; + int capture_index; + char name[TMP_BUF_SIZE]; + + capture_index = 1; + *phas_named_captures = 0; + for (p = s->buf_start; p < s->buf_end; p++) { + switch (*p) { + case '(': + if (p[1] == '?') { + if (p[2] == '<' && p[3] != '=' && p[3] != '!') { + *phas_named_captures = 1; + /* potential named capture */ + if (capture_name) { + p += 3; + if (re_parse_group_name(name, sizeof(name), &p, + s->is_utf16) == 0) { + if (!strcmp(name, capture_name)) + return capture_index; + } + } + capture_index++; + } + } else { + capture_index++; + } + break; + case '\\': + p++; + break; + case '[': + for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) { + if (*p == '\\') + p++; + } + break; + } + } + if (capture_name) + return -1; + else + return capture_index; +} + +static int re_count_captures(REParseState *s) +{ + if (s->total_capture_count < 0) { + s->total_capture_count = re_parse_captures(s, &s->has_named_captures, + NULL); + } + return s->total_capture_count; +} + +static BOOL re_has_named_captures(REParseState *s) +{ + if (s->has_named_captures < 0) + re_count_captures(s); + return s->has_named_captures; +} + +static int find_group_name(REParseState *s, const char *name) +{ + const char *p, *buf_end; + size_t len, name_len; + int capture_index; + + name_len = strlen(name); + p = (char *)s->group_names.buf; + buf_end = (char *)s->group_names.buf + s->group_names.size; + capture_index = 1; + while (p < buf_end) { + len = strlen(p); + if (len == name_len && memcmp(name, p, name_len) == 0) + return capture_index; + p += len + 1; + capture_index++; + } + return -1; +} + +static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir); + +static int re_parse_term(REParseState *s, BOOL is_backward_dir) +{ + const uint8_t *p; + int c, last_atom_start, quant_min, quant_max, last_capture_count; + BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead; + CharRange cr_s, *cr = &cr_s; + + last_atom_start = -1; + last_capture_count = 0; + p = s->buf_ptr; + c = *p; + switch(c) { + case '^': + p++; + re_emit_op(s, REOP_line_start); + break; + case '$': + p++; + re_emit_op(s, REOP_line_end); + break; + case '.': + p++; + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + re_emit_op(s, s->dotall ? REOP_any : REOP_dot); + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + case '{': + /* As an extension (see ES6 annex B), we accept '{' not + followed by digits as a normal atom */ + if (!is_digit(p[1])) { + if (s->is_utf16) + goto invalid_quant_count; + goto parse_class_atom; + } + /* fall tru */ + case '*': + case '+': + case '?': + return re_parse_error(s, "nothing to repeat"); + case '(': + if (p[1] == '?') { + if (p[2] == ':') { + p += 3; + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_dir)) + return -1; + p = s->buf_ptr; + if (re_parse_expect(s, &p, ')')) + return -1; + } else if ((p[2] == '=' || p[2] == '!')) { + is_neg = (p[2] == '!'); + is_backward_lookahead = FALSE; + p += 3; + goto lookahead; + } else if (p[2] == '<' && + (p[3] == '=' || p[3] == '!')) { + int pos; + is_neg = (p[3] == '!'); + is_backward_lookahead = TRUE; + p += 4; + /* lookahead */ + lookahead: + /* Annex B allows lookahead to be used as an atom for + the quantifiers */ + if (!s->is_utf16 && !is_backward_lookahead) { + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + } + pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0); + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_lookahead)) + return -1; + p = s->buf_ptr; + if (re_parse_expect(s, &p, ')')) + return -1; + re_emit_op(s, REOP_match); + /* jump after the 'match' after the lookahead is successful */ + put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4)); + } else if (p[2] == '<') { + p += 3; + if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), + &p, s->is_utf16)) { + return re_parse_error(s, "invalid group name"); + } + if (find_group_name(s, s->u.tmp_buf) > 0) { + return re_parse_error(s, "duplicate group name"); + } + /* group name with a trailing zero */ + dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf, + strlen(s->u.tmp_buf) + 1); + s->has_named_captures = 1; + goto parse_capture; + } else { + return re_parse_error(s, "invalid group"); + } + } else { + int capture_index; + p++; + /* capture without group name */ + dbuf_putc(&s->group_names, 0); + parse_capture: + if (s->capture_count >= CAPTURE_COUNT_MAX) + return re_parse_error(s, "too many captures"); + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + capture_index = s->capture_count++; + re_emit_op_u8(s, REOP_save_start + is_backward_dir, + capture_index); + + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_dir)) + return -1; + p = s->buf_ptr; + + re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir, + capture_index); + + if (re_parse_expect(s, &p, ')')) + return -1; + } + break; + case '\\': + switch(p[1]) { + case 'b': + case 'B': + re_emit_op(s, REOP_word_boundary + (p[1] != 'b')); + p += 2; + break; + case 'k': + { + const uint8_t *p1; + int dummy_res; + + p1 = p; + if (p1[2] != '<') { + /* annex B: we tolerate invalid group names in non + unicode mode if there is no named capture + definition */ + if (s->is_utf16 || re_has_named_captures(s)) + return re_parse_error(s, "expecting group name"); + else + goto parse_class_atom; + } + p1 += 3; + if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), + &p1, s->is_utf16)) { + if (s->is_utf16 || re_has_named_captures(s)) + return re_parse_error(s, "invalid group name"); + else + goto parse_class_atom; + } + c = find_group_name(s, s->u.tmp_buf); + if (c < 0) { + /* no capture name parsed before, try to look + after (inefficient, but hopefully not common */ + c = re_parse_captures(s, &dummy_res, s->u.tmp_buf); + if (c < 0) { + if (s->is_utf16 || re_has_named_captures(s)) + return re_parse_error(s, "group name not defined"); + else + goto parse_class_atom; + } + } + p = p1; + } + goto emit_back_reference; + case '0': + p += 2; + c = 0; + if (s->is_utf16) { + if (is_digit(*p)) { + return re_parse_error(s, "invalid decimal escape in regular expression"); + } + } else { + /* Annex B.1.4: accept legacy octal */ + if (*p >= '0' && *p <= '7') { + c = *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + } + } + } + goto normal_char; + case '1' ... '9': + { + const uint8_t *q = ++p; + + c = parse_digits(&p); + if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { + if (!s->is_utf16) { + /* Annex B.1.4: accept legacy octal */ + p = q; + if (*p <= '7') { + c = 0; + if (*p <= '3') + c = *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + } + } + } else { + c = *p++; + } + goto normal_char; + } + return re_parse_error(s, "back reference out of range in reguar expression"); + } + emit_back_reference: + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + re_emit_op_u8(s, REOP_back_reference + is_backward_dir, c); + } + break; + default: + goto parse_class_atom; + } + break; + case '[': + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + if (re_parse_char_class(s, &p)) + return -1; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + case ']': + case '}': + if (s->is_utf16) + return re_parse_error(s, "syntax error"); + goto parse_class_atom; + default: + parse_class_atom: + c = get_class_atom(s, cr, &p, FALSE); + if ((int)c < 0) + return -1; + normal_char: + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + if (c >= CLASS_RANGE_BASE) { + int ret; + /* Note: canonicalization is not needed */ + ret = re_emit_range(s, cr); + cr_free(cr); + if (ret) + return -1; + } else { + if (s->ignore_case) + c = lre_canonicalize(c, s->is_utf16); + if (c <= 0xffff) + re_emit_op_u16(s, REOP_char, c); + else + re_emit_op_u32(s, REOP_char32, c); + } + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + } + + /* quantifier */ + if (last_atom_start >= 0) { + c = *p; + switch(c) { + case '*': + p++; + quant_min = 0; + quant_max = INT32_MAX; + goto quantifier; + case '+': + p++; + quant_min = 1; + quant_max = INT32_MAX; + goto quantifier; + case '?': + p++; + quant_min = 0; + quant_max = 1; + goto quantifier; + case '{': + /* As an extension (see ES6 annex B), we accept '{' not + followed by digits as a normal atom */ + if (!is_digit(p[1])) { + if (s->is_utf16) + goto invalid_quant_count; + break; + } + p++; + quant_min = parse_digits(&p); + if (quant_min < 0) { + invalid_quant_count: + return re_parse_error(s, "invalid repetition count"); + } + quant_max = quant_min; + if (*p == ',') { + p++; + if (is_digit(*p)) { + quant_max = parse_digits(&p); + if (quant_max < 0 || quant_max < quant_min) + goto invalid_quant_count; + } else { + quant_max = INT32_MAX; /* infinity */ + } + } + 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 */ + 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); + + dbuf_insert(&s->byte_code, last_atom_start, 17); + 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; + } + } + + add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start, + s->byte_code.size - last_atom_start) == 0); + } else { + add_zero_advance_check = FALSE; + } + + { + int len, pos; + len = s->byte_code.size - last_atom_start; + if (quant_min == 0) { + /* need to reset the capture in case the atom is + not executed */ + if (last_capture_count != s->capture_count) { + dbuf_insert(&s->byte_code, last_atom_start, 3); + 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) { + dbuf_insert(&s->byte_code, last_atom_start, 5); + s->byte_code.buf[last_atom_start] = REOP_split_goto_first + + greedy; + put_u32(s->byte_code.buf + last_atom_start + 1, len); + } else if (quant_max == INT32_MAX) { + dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check); + s->byte_code.buf[last_atom_start] = REOP_split_goto_first + + greedy; + put_u32(s->byte_code.buf + last_atom_start + 1, + len + 5 + add_zero_advance_check); + if (add_zero_advance_check) { + /* avoid infinite loop by stoping the + recursion if no advance was made in the + atom (only works if the atom has no + side effect) */ + s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos; + re_emit_goto(s, REOP_bne_char_pos, last_atom_start); + } else { + re_emit_goto(s, REOP_goto, last_atom_start); + } + } else { + dbuf_insert(&s->byte_code, last_atom_start, 10); + pos = last_atom_start; + s->byte_code.buf[pos++] = REOP_push_i32; + put_u32(s->byte_code.buf + pos, quant_max); + pos += 4; + s->byte_code.buf[pos++] = REOP_split_goto_first + greedy; + put_u32(s->byte_code.buf + pos, len + 5); + re_emit_goto(s, REOP_loop, last_atom_start + 5); + re_emit_op(s, REOP_drop); + } + } else if (quant_min == 1 && quant_max == INT32_MAX && + !add_zero_advance_check) { + re_emit_goto(s, REOP_split_next_first - greedy, + last_atom_start); + } else { + if (quant_min == 1) { + /* nothing to add */ + } else { + dbuf_insert(&s->byte_code, last_atom_start, 5); + s->byte_code.buf[last_atom_start] = REOP_push_i32; + put_u32(s->byte_code.buf + last_atom_start + 1, + quant_min); + last_atom_start += 5; + re_emit_goto(s, REOP_loop, last_atom_start); + re_emit_op(s, REOP_drop); + } + if (quant_max == INT32_MAX) { + pos = s->byte_code.size; + re_emit_op_u32(s, REOP_split_goto_first + greedy, + len + 5 + add_zero_advance_check); + if (add_zero_advance_check) + re_emit_op(s, REOP_push_char_pos); + /* copy the atom */ + dbuf_put_self(&s->byte_code, last_atom_start, len); + if (add_zero_advance_check) + re_emit_goto(s, REOP_bne_char_pos, pos); + else + re_emit_goto(s, REOP_goto, pos); + } else if (quant_max > quant_min) { + re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min); + pos = s->byte_code.size; + re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5); + /* copy the atom */ + dbuf_put_self(&s->byte_code, last_atom_start, len); + + re_emit_goto(s, REOP_loop, pos); + re_emit_op(s, REOP_drop); + } + } + last_atom_start = -1; + } + break; + default: + break; + } + } + done: + s->buf_ptr = p; + return 0; +} + +static int re_parse_alternative(REParseState *s, BOOL is_backward_dir) +{ + const uint8_t *p; + int ret; + size_t start, term_start, end, term_size; + + start = s->byte_code.size; + for(;;) { + p = s->buf_ptr; + if (p >= s->buf_end) + break; + if (*p == '|' || *p == ')') + break; + term_start = s->byte_code.size; + ret = re_parse_term(s, is_backward_dir); + if (ret) + return ret; + if (is_backward_dir) { + /* reverse the order of the terms (XXX: inefficient, but + speed is not really critical here) */ + end = s->byte_code.size; + term_size = end - term_start; + if (dbuf_realloc(&s->byte_code, end + term_size)) + return -1; + memmove(s->byte_code.buf + start + term_size, + s->byte_code.buf + start, + end - start); + memcpy(s->byte_code.buf + start, s->byte_code.buf + end, + term_size); + } + } + return 0; +} + +static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) +{ + int start, len, pos; + + start = s->byte_code.size; + if (re_parse_alternative(s, is_backward_dir)) + return -1; + while (*s->buf_ptr == '|') { + s->buf_ptr++; + + len = s->byte_code.size - start; + + /* insert a split before the first alternative */ + dbuf_insert(&s->byte_code, start, 5); + s->byte_code.buf[start] = REOP_split_next_first; + put_u32(s->byte_code.buf + start + 1, len + 5); + + pos = re_emit_op_u32(s, REOP_goto, 0); + + if (re_parse_alternative(s, is_backward_dir)) + return -1; + + /* patch the goto */ + len = s->byte_code.size - (pos + 4); + put_u32(s->byte_code.buf + pos, len); + } + return 0; +} + +/* the control flow is recursive so the analysis can be linear */ +static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len) +{ + int stack_size, stack_size_max, pos, opcode, len; + uint32_t val; + + stack_size = 0; + stack_size_max = 0; + bc_buf += RE_HEADER_LEN; + bc_buf_len -= RE_HEADER_LEN; + pos = 0; + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + assert(opcode < REOP_COUNT); + assert((pos + len) <= bc_buf_len); + switch(opcode) { + case REOP_push_i32: + case REOP_push_char_pos: + stack_size++; + if (stack_size > stack_size_max) { + if (stack_size > STACK_SIZE_MAX) + return -1; + stack_size_max = stack_size; + } + break; + case REOP_drop: + case REOP_bne_char_pos: + assert(stack_size > 0); + stack_size--; + break; + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + break; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + break; + } + pos += len; + } + return stack_size_max; +} + +/* 'buf' must be a zero terminated UTF-8 string of length buf_len. + Return NULL if error and allocate an error message in *perror_msg, + otherwise the compiled bytecode and its length in plen. +*/ +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque) +{ + REParseState s_s, *s = &s_s; + int stack_size; + BOOL is_sticky; + + memset(s, 0, sizeof(*s)); + s->mem_opaque = opaque; + s->buf_ptr = (const uint8_t *)buf; + s->buf_end = s->buf_ptr + buf_len; + s->buf_start = s->buf_ptr; + s->re_flags = re_flags; + s->is_utf16 = ((re_flags & LRE_FLAG_UTF16) != 0); + is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0); + s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); + s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); + s->capture_count = 1; + s->total_capture_count = -1; + s->has_named_captures = -1; + + dbuf_init2(&s->byte_code, opaque, lre_realloc); + dbuf_init2(&s->group_names, opaque, lre_realloc); + + dbuf_putc(&s->byte_code, re_flags); /* first element is the flags */ + dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */ + dbuf_putc(&s->byte_code, 0); /* stack size */ + dbuf_put_u32(&s->byte_code, 0); /* bytecode length */ + + if (!is_sticky) { + /* iterate thru all positions (about the same as .*?( ... ) ) + . We do it without an explicit loop so that lock step + thread execution will be possible in an optimized + implementation */ + re_emit_op_u32(s, REOP_split_goto_first, 1 + 5); + re_emit_op(s, REOP_any); + re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5)); + } + re_emit_op_u8(s, REOP_save_start, 0); + + if (re_parse_disjunction(s, FALSE)) { + error: + dbuf_free(&s->byte_code); + dbuf_free(&s->group_names); + pstrcpy(error_msg, error_msg_size, s->u.error_msg); + *plen = 0; + return NULL; + } + + re_emit_op_u8(s, REOP_save_end, 0); + + re_emit_op(s, REOP_match); + + if (*s->buf_ptr != '\0') { + re_parse_error(s, "extraneous characters at the end"); + goto error; + } + + if (dbuf_error(&s->byte_code)) { + re_parse_error(s, "out of memory"); + goto error; + } + + stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size); + if (stack_size < 0) { + re_parse_error(s, "too many imbricated quantifiers"); + goto error; + } + + s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; + s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; + put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_LEN); + + /* add the named groups if needed */ + if (s->group_names.size > (s->capture_count - 1)) { + dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size); + s->byte_code.buf[RE_HEADER_FLAGS] |= LRE_FLAG_NAMED_GROUPS; + } + dbuf_free(&s->group_names); + +#ifdef DUMP_REOP + lre_dump_bytecode(s->byte_code.buf, s->byte_code.size); +#endif + + error_msg[0] = '\0'; + *plen = s->byte_code.size; + return s->byte_code.buf; +} + +static BOOL is_line_terminator(uint32_t c) +{ + return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS); +} + +static BOOL is_word_char(uint32_t c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c == '_')); +} + +#define GET_CHAR(c, cptr, cbuf_end) \ + do { \ + if (cbuf_type == 0) { \ + c = *cptr++; \ + } else { \ + uint32_t __c1; \ + c = *(uint16_t *)cptr; \ + cptr += 2; \ + if (c >= 0xd800 && c < 0xdc00 && \ + cbuf_type == 2 && cptr < cbuf_end) { \ + __c1 = *(uint16_t *)cptr; \ + if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ + c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ + cptr += 2; \ + } \ + } \ + } \ + } while (0) + +#define PEEK_CHAR(c, cptr, cbuf_end) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[0]; \ + } else { \ + uint32_t __c1; \ + c = ((uint16_t *)cptr)[0]; \ + if (c >= 0xd800 && c < 0xdc00 && \ + cbuf_type == 2 && (cptr + 2) < cbuf_end) { \ + __c1 = ((uint16_t *)cptr)[1]; \ + if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ + c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ + } \ + } \ + } \ + } while (0) + +#define PEEK_PREV_CHAR(c, cptr, cbuf_start) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[-1]; \ + } else { \ + uint32_t __c1; \ + c = ((uint16_t *)cptr)[-1]; \ + if (c >= 0xdc00 && c < 0xe000 && \ + cbuf_type == 2 && (cptr - 4) >= cbuf_start) { \ + __c1 = ((uint16_t *)cptr)[-2]; \ + if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \ + c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \ + } \ + } \ + } \ + } while (0) + +#define GET_PREV_CHAR(c, cptr, cbuf_start) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + c = cptr[0]; \ + } else { \ + uint32_t __c1; \ + cptr -= 2; \ + c = ((uint16_t *)cptr)[0]; \ + if (c >= 0xdc00 && c < 0xe000 && \ + cbuf_type == 2 && cptr > cbuf_start) { \ + __c1 = ((uint16_t *)cptr)[-1]; \ + if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \ + cptr -= 2; \ + c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \ + } \ + } \ + } \ + } while (0) + +#define PREV_CHAR(cptr, cbuf_start) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + } else { \ + cptr -= 2; \ + if (cbuf_type == 2) { \ + c = ((uint16_t *)cptr)[0]; \ + if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) { \ + c = ((uint16_t *)cptr)[-1]; \ + if (c >= 0xd800 && c < 0xdc00) \ + cptr -= 2; \ + } \ + } \ + } \ + } while (0) + +typedef uintptr_t StackInt; + +typedef enum { + RE_EXEC_STATE_SPLIT, + RE_EXEC_STATE_LOOKAHEAD, + RE_EXEC_STATE_NEGATIVE_LOOKAHEAD, + RE_EXEC_STATE_GREEDY_QUANT, +} REExecStateEnum; + +typedef struct REExecState { + REExecStateEnum type : 8; + uint8_t stack_len; + size_t count; /* only used for RE_EXEC_STATE_GREEDY_QUANT */ + const uint8_t *cptr; + const uint8_t *pc; + void *buf[0]; +} REExecState; + +typedef struct { + const uint8_t *cbuf; + const uint8_t *cbuf_end; + /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */ + int cbuf_type; + int capture_count; + int stack_size_max; + BOOL multi_line; + BOOL ignore_case; + BOOL is_utf16; + void *opaque; /* used for stack overflow check */ + + size_t state_size; + uint8_t *state_stack; + size_t state_stack_size; + size_t state_stack_len; +} REExecContext; + +static int push_state(REExecContext *s, + uint8_t **capture, + StackInt *stack, size_t stack_len, + const uint8_t *pc, const uint8_t *cptr, + REExecStateEnum type, size_t count) +{ + REExecState *rs; + uint8_t *new_stack; + size_t new_size, i, n; + StackInt *stack_buf; + + if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) { + /* reallocate the stack */ + new_size = s->state_stack_size * 3 / 2; + if (new_size < 8) + new_size = 8; + new_stack = lre_realloc(s->opaque, s->state_stack, new_size * s->state_size); + if (!new_stack) + return -1; + s->state_stack_size = new_size; + s->state_stack = new_stack; + } + rs = (REExecState *)(s->state_stack + s->state_stack_len * s->state_size); + s->state_stack_len++; + rs->type = type; + rs->count = count; + rs->stack_len = stack_len; + rs->cptr = cptr; + rs->pc = pc; + n = 2 * s->capture_count; + for(i = 0; i < n; i++) + rs->buf[i] = capture[i]; + stack_buf = (StackInt *)(rs->buf + n); + for(i = 0; i < stack_len; i++) + stack_buf[i] = stack[i]; + return 0; +} + +/* return 1 if match, 0 if not match or -1 if error. */ +static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, + StackInt *stack, int stack_len, + const uint8_t *pc, const uint8_t *cptr, + BOOL no_recurse) +{ + int opcode, ret; + int cbuf_type; + uint32_t val, c; + const uint8_t *cbuf_end; + + cbuf_type = s->cbuf_type; + cbuf_end = s->cbuf_end; + + for(;;) { + // printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN))); + opcode = *pc++; + switch(opcode) { + case REOP_match: + { + REExecState *rs; + if (no_recurse) + return (intptr_t)cptr; + ret = 1; + goto recurse; + no_match: + if (no_recurse) + return 0; + ret = 0; + recurse: + for(;;) { + if (s->state_stack_len == 0) + return ret; + rs = (REExecState *)(s->state_stack + + (s->state_stack_len - 1) * s->state_size); + if (rs->type == RE_EXEC_STATE_SPLIT) { + if (!ret) { + pop_state: + memcpy(capture, rs->buf, + sizeof(capture[0]) * 2 * s->capture_count); + pop_state1: + pc = rs->pc; + cptr = rs->cptr; + stack_len = rs->stack_len; + memcpy(stack, rs->buf + 2 * s->capture_count, + stack_len * sizeof(stack[0])); + s->state_stack_len--; + break; + } + } else if (rs->type == RE_EXEC_STATE_GREEDY_QUANT) { + if (!ret) { + uint32_t char_count, i; + memcpy(capture, rs->buf, + sizeof(capture[0]) * 2 * s->capture_count); + stack_len = rs->stack_len; + memcpy(stack, rs->buf + 2 * s->capture_count, + stack_len * sizeof(stack[0])); + pc = rs->pc; + cptr = rs->cptr; + /* go backward */ + char_count = get_u32(pc + 12); + for(i = 0; i < char_count; i++) { + PREV_CHAR(cptr, s->cbuf); + } + pc = (pc + 16) + (int)get_u32(pc); + rs->cptr = cptr; + rs->count--; + if (rs->count == 0) { + s->state_stack_len--; + } + break; + } + } else { + ret = ((rs->type == RE_EXEC_STATE_LOOKAHEAD && ret) || + (rs->type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD && !ret)); + if (ret) { + /* keep the capture in case of positive lookahead */ + if (rs->type == RE_EXEC_STATE_LOOKAHEAD) + goto pop_state1; + else + goto pop_state; + } + } + s->state_stack_len--; + } + } + break; + case REOP_char32: + val = get_u32(pc); + pc += 4; + goto test_char; + case REOP_char: + val = get_u16(pc); + pc += 2; + test_char: + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_utf16); + } + if (val != c) + goto no_match; + break; + case REOP_split_goto_first: + case REOP_split_next_first: + { + const uint8_t *pc1; + + val = get_u32(pc); + pc += 4; + if (opcode == REOP_split_next_first) { + pc1 = pc + (int)val; + } else { + pc1 = pc; + pc = pc + (int)val; + } + ret = push_state(s, capture, stack, stack_len, + pc1, cptr, RE_EXEC_STATE_SPLIT, 0); + if (ret < 0) + return -1; + break; + } + case REOP_lookahead: + case REOP_negative_lookahead: + val = get_u32(pc); + pc += 4; + ret = push_state(s, capture, stack, stack_len, + pc + (int)val, cptr, + RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, + 0); + if (ret < 0) + return -1; + break; + + case REOP_goto: + val = get_u32(pc); + pc += 4 + (int)val; + break; + case REOP_line_start: + if (cptr == s->cbuf) + break; + if (!s->multi_line) + goto no_match; + PEEK_PREV_CHAR(c, cptr, s->cbuf); + if (!is_line_terminator(c)) + goto no_match; + break; + case REOP_line_end: + if (cptr == cbuf_end) + break; + if (!s->multi_line) + goto no_match; + PEEK_CHAR(c, cptr, cbuf_end); + if (!is_line_terminator(c)) + goto no_match; + break; + case REOP_dot: + if (cptr == cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + if (is_line_terminator(c)) + goto no_match; + break; + case REOP_any: + if (cptr == cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + break; + case REOP_save_start: + case REOP_save_end: + val = *pc++; + assert(val < s->capture_count); + capture[2 * val + opcode - REOP_save_start] = (uint8_t *)cptr; + break; + case REOP_save_reset: + { + uint32_t val2; + val = pc[0]; + val2 = pc[1]; + pc += 2; + assert(val2 < s->capture_count); + while (val <= val2) { + capture[2 * val] = NULL; + capture[2 * val + 1] = NULL; + val++; + } + } + break; + case REOP_push_i32: + val = get_u32(pc); + pc += 4; + stack[stack_len++] = val; + break; + case REOP_drop: + stack_len--; + break; + case REOP_loop: + val = get_u32(pc); + pc += 4; + if (--stack[stack_len - 1] != 0) { + pc += (int)val; + } + break; + case REOP_push_char_pos: + stack[stack_len++] = (uintptr_t)cptr; + break; + case REOP_bne_char_pos: + val = get_u32(pc); + pc += 4; + if (stack[--stack_len] != (uintptr_t)cptr) + pc += (int)val; + break; + case REOP_word_boundary: + case REOP_not_word_boundary: + { + BOOL v1, v2; + /* char before */ + if (cptr == s->cbuf) { + v1 = FALSE; + } else { + PEEK_PREV_CHAR(c, cptr, s->cbuf); + v1 = is_word_char(c); + } + /* current char */ + if (cptr >= cbuf_end) { + v2 = FALSE; + } else { + PEEK_CHAR(c, cptr, cbuf_end); + v2 = is_word_char(c); + } + if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode)) + goto no_match; + } + break; + case REOP_back_reference: + case REOP_backward_back_reference: + { + const uint8_t *cptr1, *cptr1_end, *cptr1_start; + uint32_t c1, c2; + + val = *pc++; + if (val >= s->capture_count) + goto no_match; + cptr1_start = capture[2 * val]; + cptr1_end = capture[2 * val + 1]; + if (!cptr1_start || !cptr1_end) + break; + if (opcode == REOP_back_reference) { + cptr1 = cptr1_start; + while (cptr1 < cptr1_end) { + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c1, cptr1, cptr1_end); + GET_CHAR(c2, cptr, cbuf_end); + if (s->ignore_case) { + c1 = lre_canonicalize(c1, s->is_utf16); + c2 = lre_canonicalize(c2, s->is_utf16); + } + if (c1 != c2) + goto no_match; + } + } else { + cptr1 = cptr1_end; + while (cptr1 > cptr1_start) { + if (cptr == s->cbuf) + goto no_match; + GET_PREV_CHAR(c1, cptr1, cptr1_start); + GET_PREV_CHAR(c2, cptr, s->cbuf); + if (s->ignore_case) { + c1 = lre_canonicalize(c1, s->is_utf16); + c2 = lre_canonicalize(c2, s->is_utf16); + } + if (c1 != c2) + goto no_match; + } + } + } + break; + case REOP_range: + { + int n; + uint32_t low, high, idx_min, idx_max, idx; + + n = get_u16(pc); /* n must be >= 1 */ + pc += 2; + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_utf16); + } + idx_min = 0; + low = get_u16(pc + 0 * 4); + if (c < low) + goto no_match; + idx_max = n - 1; + high = get_u16(pc + idx_max * 4 + 2); + /* 0xffff in for last value means +infinity */ + if (unlikely(c >= 0xffff) && high == 0xffff) + goto range_match; + if (c > high) + goto no_match; + while (idx_min <= idx_max) { + idx = (idx_min + idx_max) / 2; + low = get_u16(pc + idx * 4); + high = get_u16(pc + idx * 4 + 2); + if (c < low) + idx_max = idx - 1; + else if (c > high) + idx_min = idx + 1; + else + goto range_match; + } + goto no_match; + range_match: + pc += 4 * n; + } + break; + case REOP_range32: + { + int n; + uint32_t low, high, idx_min, idx_max, idx; + + n = get_u16(pc); /* n must be >= 1 */ + pc += 2; + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_utf16); + } + idx_min = 0; + low = get_u32(pc + 0 * 8); + if (c < low) + goto no_match; + idx_max = n - 1; + high = get_u32(pc + idx_max * 8 + 4); + if (c > high) + goto no_match; + while (idx_min <= idx_max) { + idx = (idx_min + idx_max) / 2; + low = get_u32(pc + idx * 8); + high = get_u32(pc + idx * 8 + 4); + if (c < low) + idx_max = idx - 1; + else if (c > high) + idx_min = idx + 1; + else + goto range32_match; + } + goto no_match; + range32_match: + pc += 8 * n; + } + break; + case REOP_prev: + /* go to the previous char */ + if (cptr == s->cbuf) + goto no_match; + PREV_CHAR(cptr, s->cbuf); + break; + case REOP_simple_greedy_quant: + { + uint32_t next_pos, quant_min, quant_max; + size_t q; + intptr_t res; + const uint8_t *pc1; + + next_pos = get_u32(pc); + quant_min = get_u32(pc + 4); + quant_max = get_u32(pc + 8); + pc += 16; + pc1 = pc; + pc += (int)next_pos; + + q = 0; + for(;;) { + res = lre_exec_backtrack(s, capture, stack, stack_len, + pc1, cptr, TRUE); + if (res == -1) + return res; + if (!res) + break; + cptr = (uint8_t *)res; + q++; + if (q >= quant_max && quant_max != INT32_MAX) + break; + } + if (q < quant_min) + goto no_match; + if (q > quant_min) { + /* will examine all matches down to quant_min */ + ret = push_state(s, capture, stack, stack_len, + pc1 - 16, cptr, + RE_EXEC_STATE_GREEDY_QUANT, + q - quant_min); + if (ret < 0) + return -1; + } + } + break; + default: + abort(); + } + } +} + +/* Return 1 if match, 0 if not match or -1 if error. cindex is the + starting position of the match and must be such as 0 <= cindex <= + clen. */ +int lre_exec(uint8_t **capture, + const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, + int cbuf_type, void *opaque) +{ + REExecContext s_s, *s = &s_s; + int re_flags, i, alloca_size, ret; + StackInt *stack_buf; + + re_flags = bc_buf[RE_HEADER_FLAGS]; + s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0; + s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0; + s->is_utf16 = (re_flags & LRE_FLAG_UTF16) != 0; + s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT]; + s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE]; + s->cbuf = cbuf; + s->cbuf_end = cbuf + (clen << cbuf_type); + s->cbuf_type = cbuf_type; + if (s->cbuf_type == 1 && s->is_utf16) + s->cbuf_type = 2; + s->opaque = opaque; + + s->state_size = sizeof(REExecState) + + s->capture_count * sizeof(capture[0]) * 2 + + s->stack_size_max * sizeof(stack_buf[0]); + s->state_stack = NULL; + s->state_stack_len = 0; + s->state_stack_size = 0; + + for(i = 0; i < s->capture_count * 2; i++) + capture[i] = NULL; + alloca_size = s->stack_size_max * sizeof(stack_buf[0]); + stack_buf = alloca(alloca_size); + ret = lre_exec_backtrack(s, capture, stack_buf, 0, bc_buf + RE_HEADER_LEN, + cbuf + (cindex << cbuf_type), FALSE); + lre_realloc(s->opaque, s->state_stack, 0); + return ret; +} + +int lre_get_capture_count(const uint8_t *bc_buf) +{ + return bc_buf[RE_HEADER_CAPTURE_COUNT]; +} + +int lre_get_flags(const uint8_t *bc_buf) +{ + return bc_buf[RE_HEADER_FLAGS]; +} + +#ifdef TEST + +BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) +{ + return FALSE; +} + +void *lre_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +int main(int argc, char **argv) +{ + int len, ret, i; + uint8_t *bc; + char error_msg[64]; + uint8_t *capture[CAPTURE_COUNT_MAX * 2]; + const char *input; + int input_len, capture_count; + + if (argc < 3) { + printf("usage: %s regexp input\n", argv[0]); + exit(1); + } + bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], + strlen(argv[1]), 0, NULL); + if (!bc) { + fprintf(stderr, "error: %s\n", error_msg); + exit(1); + } + + input = argv[2]; + input_len = strlen(input); + + ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); + printf("ret=%d\n", ret); + if (ret == 1) { + capture_count = lre_get_capture_count(bc); + for(i = 0; i < 2 * capture_count; i++) { + uint8_t *ptr; + ptr = capture[i]; + printf("%d: ", i); + if (!ptr) + printf(""); + else + printf("%u", (int)(ptr - (uint8_t *)input)); + printf("\n"); + } + } + return 0; +} +#endif diff --git a/libregexp.h b/libregexp.h new file mode 100644 index 0000000..cd0b24f --- /dev/null +++ b/libregexp.h @@ -0,0 +1,91 @@ +/* + * 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 + +#include "libunicode.h" + +#define LRE_BOOL int /* for documentation purposes */ + +#define LRE_FLAG_GLOBAL (1 << 0) +#define LRE_FLAG_IGNORECASE (1 << 1) +#define LRE_FLAG_MULTILINE (1 << 2) +#define LRE_FLAG_DOTALL (1 << 3) +#define LRE_FLAG_UTF16 (1 << 4) +#define LRE_FLAG_STICKY (1 << 5) + +#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); +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); +LRE_BOOL lre_is_space(int c); + +/* must be provided by the user */ +LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); +void *lre_realloc(void *opaque, void *ptr, size_t size); + +/* JS identifier test */ +extern uint32_t const lre_id_start_table_ascii[4]; +extern uint32_t const lre_id_continue_table_ascii[4]; + +static inline int lre_js_is_ident_first(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_start(c); +#else + return !lre_is_space(c); +#endif + } +} + +static inline int lre_js_is_ident_next(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; +#else + return !lre_is_space(c) || c == 0x200C || c == 0x200D; +#endif + } +} + +#undef LRE_BOOL + +#endif /* LIBREGEXP_H */ diff --git a/libunicode-table.h b/libunicode-table.h new file mode 100644 index 0000000..521f2f3 --- /dev/null +++ b/libunicode-table.h @@ -0,0 +1,4313 @@ +/* Compressed unicode tables */ +/* Automatically generated file - do not edit */ + +#include + +static const uint32_t case_conv_table1[359] = { + 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, 0x16002f31, + 0x16182f01, 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, 0x53da0c40, 0x53e10240, + 0x53e20131, 0x53e28130, 0x53e30130, 0x55a98101, + 0x55b85020, 0x7d8001b2, 0x7d8081b2, 0x7d8101b2, + 0x7d8181da, 0x7d8201da, 0x7d8281b3, 0x7d8301b3, + 0x7d8981bb, 0x7d8a01bb, 0x7d8a81bb, 0x7d8b01bc, + 0x7d8b81bb, 0x7f909a31, 0x7fa09a01, 0x82002831, + 0x82142801, 0x82582431, 0x826c2401, 0x86403331, + 0x86603301, 0x8c502031, 0x8c602001, 0xb7202031, + 0xb7302001, 0xf4802231, 0xf4912201, +}; + +static const uint8_t case_conv_table2[359] = { + 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, 0x49, + 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, + 0x4c, 0xaa, 0xa9, 0x12, 0x14, 0x1e, 0x21, 0x22, + 0x22, 0x2a, 0x34, 0x35, 0xa6, 0xa7, 0x36, 0x1f, + 0x4a, 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, 0x48, 0x00, 0x5b, 0x56, 0x58, + 0x60, 0x5e, 0x70, 0x69, 0x6f, 0x4b, 0x00, 0x00, + 0x3b, 0x67, 0xb8, 0x45, 0xa8, 0x8a, 0x8b, 0x8c, + 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, 0xb0, 0x6f, + 0xb2, 0x5a, 0x59, 0x5c, 0x5b, 0x5e, 0x5d, 0x60, + 0x5f, 0x62, 0x61, 0x64, 0x63, 0x66, 0x65, +}; + +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[172] = { + 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, 0x57, 0x76, + 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, 0xc7, 0x82, 0x43, 0x34, + 0xa2, 0x06, 0x80, 0x8b, 0x61, 0x28, 0x97, 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, 0x59, 0x63, 0x99, + 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Cased1_index[18] = { + 0xb9, 0x02, 0xe0, 0xa0, 0x1e, 0x40, 0x9e, 0xa6, + 0x40, 0xba, 0xd4, 0x01, 0x89, 0xd7, 0x01, 0x8a, + 0xf1, 0x01, +}; + +static const uint8_t unicode_prop_Case_Ignorable_table[678] = { + 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, 0x80, 0xb6, 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, 0xf6, 0xaf, 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, 0x07, 0x80, 0x8a, 0x81, 0x9d, + 0x80, 0xbc, 0x80, 0x8b, 0x80, 0xb1, 0x02, 0x80, + 0xb8, 0x14, 0x10, 0x1e, 0x81, 0x8a, 0x81, 0x9c, + 0x80, 0xb9, 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, + 0x9b, 0x81, 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, + 0xe5, 0x06, 0x10, 0x80, 0xd9, 0x01, 0x86, 0x8a, + 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x85, 0xc9, + 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, + 0x82, 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37, + 0x01, 0x8a, 0x10, 0x20, 0xac, 0x83, 0xb3, 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, + 0x8e, 0xc0, 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, 0xde, 0x04, + 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, 0xec, 0x0f, 0x02, + 0x03, 0x80, 0x98, 0x81, 0x40, 0x9c, 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, 0x40, 0x84, 0x01, 0x03, 0x80, 0x60, + 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x8f, 0x42, 0x3d, + 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, 0x46, 0x85, 0x10, 0x0c, 0x83, 0xa7, + 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83, + 0x42, 0x1d, 0x8a, 0x40, 0xaf, 0x80, 0xb5, 0x8e, + 0xb7, 0x82, 0xb0, 0x19, 0x09, 0x80, 0x8e, 0x80, + 0xb1, 0x82, 0xa3, 0x20, 0x87, 0xbd, 0x80, 0x8b, + 0x81, 0xb3, 0x88, 0x89, 0x83, 0xe1, 0x11, 0x00, + 0x0d, 0x80, 0x40, 0x9f, 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, 0x41, 0x98, 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, 0x55, 0x3a, 0x88, 0x60, 0x36, 0xb6, + 0x84, 0xba, 0x86, 0x88, 0x83, 0x44, 0x0a, 0x80, + 0xbe, 0x90, 0xbf, 0x08, 0x80, 0x60, 0x4c, 0xb8, + 0x08, 0x83, 0x54, 0xc2, 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, 0x41, + 0x04, 0x8d, 0x41, 0xad, 0x83, 0x45, 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[66] = { + 0xc0, 0x05, 0x00, 0x2e, 0x08, 0x20, 0x52, 0x0a, + 0x00, 0x05, 0x0c, 0x00, 0x4f, 0x0e, 0x20, 0x75, + 0x10, 0x20, 0x44, 0x18, 0x00, 0x43, 0x1b, 0x00, + 0x00, 0x1e, 0x00, 0x7e, 0x2c, 0x00, 0x7e, 0xa6, + 0x40, 0x83, 0xa9, 0x20, 0xf7, 0xaa, 0x00, 0x41, + 0xff, 0x20, 0x28, 0x0d, 0x01, 0x3f, 0x12, 0x41, + 0xde, 0x15, 0x21, 0x5c, 0x1a, 0x01, 0xf5, 0x6a, + 0x21, 0x37, 0xda, 0x01, 0x02, 0x00, 0x2e, 0xf0, + 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_ID_Start_table[1024] = { + 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, + 0xb4, 0x94, 0x07, 0xc5, 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, 0x14, 0x81, 0x9d, 0x03, 0x38, 0x10, 0x96, + 0x80, 0x89, 0x04, 0x10, 0x9f, 0x00, 0x81, 0x8e, + 0x81, 0x91, 0x38, 0x10, 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, 0x8c, 0x03, 0x8d, 0x91, 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, + 0x86, 0xb6, 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, 0xae, 0x80, 0xae, 0x80, 0x40, 0x84, + 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, 0x9a, 0xb4, + 0x8f, 0x41, 0xff, 0x59, 0xb5, 0xc9, 0x60, 0x51, + 0xef, 0x8f, 0x44, 0x8c, 0xc2, 0xad, 0x81, 0x41, + 0x0c, 0x82, 0x8f, 0x89, 0x81, 0x93, 0xae, 0x8f, + 0x9e, 0x81, 0xcf, 0xa6, 0x88, 0x81, 0xe6, 0x81, + 0xb4, 0x0c, 0xaf, 0x8a, 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, 0x8b, 0x87, 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, 0x40, 0x9b, 0x41, 0x36, + 0x88, 0x95, 0x89, 0x87, 0x40, 0x97, 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, 0xdb, 0x9c, 0x89, 0x07, 0x95, 0x40, 0x99, + 0x96, 0x8b, 0xb4, 0xca, 0xac, 0x9f, 0x98, 0x99, + 0xa3, 0x9c, 0x80, 0x8a, 0xa2, 0x10, 0x8b, 0xaf, + 0x8d, 0x83, 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, + 0x98, 0xd3, 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, 0x80, 0x9f, 0xaf, 0x93, + 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, 0xa3, + 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, 0xc6, + 0x9a, 0x40, 0xe4, 0xab, 0xf3, 0xbf, 0x9e, 0x80, + 0x40, 0x9f, 0x39, 0xa6, 0x8f, 0x00, 0x80, 0x9b, + 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, 0xad, + 0x92, 0x80, 0xa1, 0xb8, 0x41, 0x06, 0x88, 0x80, + 0xa4, 0x90, 0x80, 0xb0, 0x9d, 0xef, 0x30, 0x08, + 0xa5, 0x94, 0x80, 0x98, 0x28, 0x08, 0x9f, 0x8d, + 0x80, 0x41, 0x46, 0x92, 0x41, 0x0c, 0x43, 0x99, + 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, 0xbb, 0x44, + 0x2e, 0x4f, 0xd0, 0x42, 0x46, 0x60, 0x21, 0xb8, + 0x42, 0x38, 0x86, 0x9e, 0xf0, 0x9d, 0x91, 0xaf, + 0x8f, 0x83, 0x9e, 0x94, 0x84, 0x92, 0x42, 0xaf, + 0xbf, 0xff, 0xca, 0x20, 0xc1, 0x8c, 0xbf, 0x08, + 0x80, 0x9b, 0x57, 0xf7, 0x87, 0x42, 0xf2, 0x60, + 0x25, 0x0c, 0x41, 0x1e, 0xb0, 0x82, 0x90, 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, 0x49, 0x33, 0xac, 0x89, 0x86, + 0x8f, 0x80, 0x41, 0x70, 0xab, 0x45, 0x13, 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, 0xd6, + 0xa8, 0x50, 0x34, 0x8a, 0x40, 0xdd, 0x81, 0x56, + 0x81, 0x8d, 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, +}; + +static const uint8_t unicode_prop_ID_Start_index[96] = { + 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xb1, 0x09, + 0x00, 0xba, 0x0a, 0x00, 0xd1, 0x0b, 0x20, 0x62, + 0x0d, 0x40, 0x01, 0x0f, 0x20, 0x5e, 0x12, 0x00, + 0xf9, 0x16, 0x00, 0x17, 0x1a, 0x20, 0xc0, 0x1d, + 0x20, 0x9d, 0x20, 0x00, 0x68, 0x2d, 0x00, 0x00, + 0x32, 0x20, 0xc0, 0xa7, 0x20, 0x29, 0xaa, 0x00, + 0xa4, 0xd7, 0x20, 0xc8, 0xfd, 0x20, 0x75, 0x01, + 0x01, 0x37, 0x07, 0x01, 0x36, 0x0a, 0x21, 0xf7, + 0x0f, 0x21, 0xa9, 0x12, 0x01, 0x30, 0x16, 0x21, + 0x8a, 0x1a, 0x01, 0x9a, 0x23, 0x01, 0x80, 0x6e, + 0x21, 0x89, 0xbc, 0x21, 0xc1, 0xd6, 0x01, 0xc5, + 0xe8, 0x21, 0x73, 0xee, 0x01, 0x1e, 0xfa, 0x02, +}; + +static const uint8_t unicode_prop_ID_Continue1_table[607] = { + 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, 0xf6, 0x8e, 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, 0x17, + 0x81, 0x89, 0x09, 0x89, 0x91, 0x80, 0xba, 0x22, + 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89, 0x8f, 0x84, + 0xb8, 0x30, 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, + 0x90, 0x82, 0xb7, 0x00, 0x30, 0x10, 0x1e, 0x81, + 0x8a, 0x09, 0x89, 0x8f, 0x83, 0xb6, 0x08, 0x30, + 0x10, 0x83, 0x88, 0x80, 0x89, 0x09, 0x89, 0x91, + 0x81, 0xc5, 0x03, 0x28, 0x00, 0x3d, 0x89, 0x09, + 0xbc, 0x01, 0x86, 0x8b, 0x38, 0x89, 0xd6, 0x01, + 0x88, 0x8a, 0x29, 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, 0x82, 0x9c, 0x82, 0x9c, 0x81, 0x9d, + 0x81, 0xbf, 0x9f, 0x88, 0x01, 0x89, 0xa0, 0x11, + 0x89, 0x40, 0x8e, 0x80, 0xf5, 0x8b, 0x83, 0x8b, + 0x89, 0x89, 0xff, 0x8a, 0xbb, 0x84, 0xb8, 0x89, + 0x80, 0x9c, 0x81, 0x8a, 0x85, 0x89, 0x95, 0x8d, + 0xc1, 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, 0xb9, 0x04, + 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, + 0x84, 0xd7, 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, 0x42, 0x0b, 0x8a, 0x40, 0xae, + 0x82, 0xb4, 0x8e, 0x9e, 0x89, 0x8e, 0x83, 0xac, + 0x8a, 0xb4, 0x89, 0x2a, 0xa3, 0x8d, 0x80, 0x89, + 0x21, 0xab, 0x80, 0x8b, 0x82, 0xaf, 0x8d, 0x3b, + 0x82, 0x89, 0xd1, 0x8b, 0x28, 0x40, 0x9f, 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, + 0x40, 0xe6, 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, + 0x60, 0x4b, 0x68, 0x89, 0x40, 0x85, 0x84, 0xba, + 0x86, 0x98, 0x89, 0x43, 0xf4, 0x00, 0xb6, 0x33, + 0x60, 0x4d, 0x09, 0x81, 0x54, 0xc5, 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, 0x41, 0x04, 0x86, 0x88, + 0x89, 0x41, 0xa1, 0x8d, 0x45, 0xd5, 0x86, 0xec, + 0x34, 0x89, 0x6c, 0x17, 0xa5, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_ID_Continue1_index[57] = { + 0xfa, 0x06, 0x00, 0x84, 0x09, 0x00, 0xf0, 0x0a, + 0x00, 0x70, 0x0c, 0x00, 0xf4, 0x0d, 0x00, 0x4a, + 0x10, 0x20, 0x1a, 0x18, 0x20, 0x74, 0x1b, 0x00, + 0xe2, 0x20, 0x00, 0x28, 0xa8, 0x20, 0x7e, 0xaa, + 0x20, 0x40, 0xff, 0x00, 0x03, 0x10, 0x21, 0xeb, + 0x12, 0x01, 0x41, 0x16, 0x01, 0x40, 0x1c, 0x61, + 0x37, 0x6b, 0x21, 0x76, 0xda, 0x01, 0xf0, 0x01, + 0x0e, +}; + +#ifdef CONFIG_ALL_UNICODE + +static const uint8_t unicode_cc_table[831] = { + 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, 0x46, 0x00, 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, 0x4e, 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, 0x00, 0x09, 0x9e, 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, 0xb0, + 0x45, 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, + 0x80, 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, 0xb0, 0x8c, 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, 0xed, 0x01, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, + 0x03, 0xdc, 0xb0, 0xc4, 0x00, 0x09, 0xb0, 0x07, + 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, 0xb1, 0x74, 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, 0xb8, + 0x45, 0x27, 0x04, 0x01, 0xb0, 0x0a, 0xc6, 0xb8, + 0x49, 0x36, 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, 0xd4, 0xc6, 0xb1, 0x84, 0xc3, 0xb5, 0xaf, + 0x06, 0xdc, 0xb0, 0x3c, 0xc5, 0x00, 0x07, +}; + +static const uint8_t unicode_cc_index[78] = { + 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, + 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0xe2, + 0x08, 0x00, 0x53, 0x09, 0x00, 0xcd, 0x0b, 0x20, + 0x38, 0x0e, 0x00, 0x73, 0x0f, 0x20, 0x5d, 0x13, + 0x20, 0x60, 0x1a, 0x20, 0xe6, 0x1b, 0x20, 0xfa, + 0x1c, 0x00, 0x00, 0x1e, 0x20, 0x80, 0x2d, 0x00, + 0x06, 0xa8, 0x00, 0xbe, 0xaa, 0x00, 0x76, 0x03, + 0x01, 0x4d, 0x0f, 0x01, 0xcb, 0x11, 0x21, 0x5e, + 0x14, 0x01, 0x3b, 0x18, 0x21, 0xf0, 0x6a, 0x41, + 0xaa, 0xd1, 0x01, 0x4b, 0xe9, 0x01, +}; + +static const uint32_t unicode_decomp_table1[687] = { + 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, 0x29fe0103, 0x2ad70203, 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, 0x44268192, 0x442ac092, 0x444b8112, + 0x44d2c112, 0x452ec212, 0x456e8112, 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, 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, 0xbe0001ac, 0xbe00d110, + 0xbe0947ac, 0xbe0d3910, 0xbe29872c, 0xbe2d022c, + 0xbe2e3790, 0xbe49ff90, 0xbe69bc10, +}; + +static const uint16_t unicode_decomp_table2[687] = { + 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, 0x11b3, 0x11bb, 0x120d, 0x130b, 0x1409, 0x148d, + 0x1492, 0x1550, 0x1569, 0x156f, 0x1575, 0x157b, 0x1587, 0x1593, + 0x002b, 0x159e, 0x15b6, 0x15ba, 0x15be, 0x15c2, 0x15c6, 0x15ca, + 0x15de, 0x15e2, 0x1646, 0x165f, 0x1685, 0x168b, 0x1749, 0x174f, + 0x1754, 0x1774, 0x1874, 0x187a, 0x190e, 0x19d0, 0x1a74, 0x1a7c, + 0x1a9a, 0x1a9f, 0x1ab3, 0x1abd, 0x1ac3, 0x1ad7, 0x1adc, 0x1ae2, + 0x1af0, 0x1b20, 0x1b2d, 0x1b35, 0x1b39, 0x1b4f, 0x1bc6, 0x1bd8, + 0x1bda, 0x1bdc, 0x3164, 0x1c1d, 0x1c1f, 0x1c21, 0x1c23, 0x1c25, + 0x1c27, 0x1c45, 0x1c53, 0x1c58, 0x1c61, 0x1c6a, 0x1c7c, 0x1c85, + 0x1ca5, 0x1cc0, 0x1cc2, 0x1cc4, 0x1cc6, 0x1cc8, 0x1cca, 0x1ccc, + 0x1cce, 0x1cee, 0x1cf0, 0x1cf2, 0x1cf4, 0x1cf6, 0x1cfd, 0x1cff, + 0x1d01, 0x1d03, 0x1d12, 0x1d14, 0x1d16, 0x1d18, 0x1d1a, 0x1d1c, + 0x1d1e, 0x1d20, 0x1d22, 0x1d24, 0x1d26, 0x1d28, 0x1d2a, 0x1d2c, + 0x1d2e, 0x1d32, 0x03f4, 0x1d34, 0x2207, 0x1d36, 0x2202, 0x1d38, + 0x1d40, 0x03f4, 0x1d42, 0x2207, 0x1d44, 0x2202, 0x1d46, 0x1d4e, + 0x03f4, 0x1d50, 0x2207, 0x1d52, 0x2202, 0x1d54, 0x1d5c, 0x03f4, + 0x1d5e, 0x2207, 0x1d60, 0x2202, 0x1d62, 0x1d6a, 0x03f4, 0x1d6c, + 0x2207, 0x1d6e, 0x2202, 0x1d70, 0x1d7a, 0x1d7c, 0x1d7e, 0x1d80, + 0x1d82, 0x1d84, 0x1d8a, 0x1da7, 0x062d, 0x1daf, 0x1dbb, 0x062c, + 0x1dcb, 0x1e3b, 0x1e47, 0x1e5a, 0x1e6c, 0x1e7f, 0x1e81, 0x1e85, + 0x1e8b, 0x1e91, 0x1e93, 0x1e97, 0x1e99, 0x1ea1, 0x1ea4, 0x1ea6, + 0x1eac, 0x1eae, 0x30b5, 0x1eb4, 0x1f0c, 0x1f22, 0x1f26, 0x1f2b, + 0x1f78, 0x1f89, 0x208a, 0x209a, 0x20a0, 0x219a, 0x22b8, +}; + +static const uint8_t unicode_decomp_data[9158] = { + 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, 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, 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, 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, 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, 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[944] = { + 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, 0x8e40, 0x8e42, 0x8e80, 0x8ec0, + 0x8ec1, 0x8f00, 0x8f01, 0x8f41, 0x8f40, 0x8f43, 0x8f80, 0x8f81, +}; + +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[3719] = { + 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, 0x00, 0x36, 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, + 0xe0, 0x2d, 0xe5, 0x0d, 0x00, 0xe5, 0x00, 0xe0, + 0x0d, 0xe6, 0x07, 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, 0xe0, 0x00, 0x06, + 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, 0x40, 0x05, + 0x46, 0x67, 0x00, 0x46, 0x00, 0x66, 0xc0, 0x26, + 0x00, 0x45, 0x80, 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, 0xc0, 0x05, 0x00, 0x25, 0x26, 0x20, + 0xe9, 0x02, 0x00, 0x25, 0xe0, 0x05, 0x26, 0x27, + 0x00, 0xe5, 0x00, 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, + 0x20, 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, 0xa6, 0x20, 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, 0x05, + 0x00, 0x65, 0x46, 0xe0, 0x03, 0xe5, 0x0a, 0x46, + 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, 0x00, 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, 0xe0, 0x39, 0x66, 0x07, + 0xe5, 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, + 0x06, 0x27, 0xc5, 0x60, 0xe9, 0x02, 0xd6, 0xef, + 0x02, 0xe6, 0x01, 0xef, 0x01, 0x40, 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, 0x32, 0x00, 0x86, 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, 0x18, 0xe0, + 0x08, 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, 0x20, 0xef, 0x60, + 0xe1, 0x27, 0x00, 0xe2, 0x27, 0x00, 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, 0xe0, 0x28, 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, + 0x13, 0x80, 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, 0x2e, 0xe0, 0x02, 0xef, 0x38, 0xe5, 0xc0, + 0x11, 0x68, 0xe0, 0x08, 0xe5, 0x0d, 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, 0x02, 0x20, 0x5f, 0x21, 0xe0, 0x28, + 0x05, 0x24, 0x02, 0xc5, 0x06, 0x45, 0x06, 0x65, + 0x06, 0xe5, 0x0f, 0x27, 0x26, 0x07, 0x6f, 0x60, + 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, 0x00, 0xe0, 0x00, + 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, 0x08, 0xe0, 0x09, 0xe5, 0x80, 0xe3, 0x13, + 0x12, 0xe0, 0x08, 0xe5, 0x38, 0x20, 0xe5, 0x2e, + 0xe0, 0x20, 0xe5, 0x04, 0x0d, 0x0f, 0x20, 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, 0x04, 0x60, 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, 0xe0, 0x80, 0x08, 0xe5, + 0x80, 0xaf, 0xe0, 0x01, 0xe5, 0x0e, 0xe0, 0x02, + 0xe5, 0x00, 0xe0, 0x80, 0x10, 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, 0xe0, 0x79, + 0xe5, 0x15, 0xeb, 0x02, 0x05, 0xe0, 0x00, 0xe5, + 0x0e, 0xe6, 0x03, 0x6b, 0x96, 0xe0, 0x7e, 0xe5, + 0x0f, 0xe0, 0x01, 0x07, 0x06, 0x07, 0xe5, 0x2d, + 0xe6, 0x07, 0xd6, 0x60, 0xeb, 0x0c, 0xe9, 0x02, + 0xe0, 0x07, 0x46, 0x07, 0xe5, 0x25, 0x47, 0x66, + 0x27, 0x26, 0x36, 0x1b, 0x76, 0xe0, 0x03, 0x1b, + 0x20, 0xe5, 0x11, 0xc0, 0xe9, 0x02, 0xa0, 0x46, + 0xe5, 0x1c, 0x86, 0x07, 0xe6, 0x00, 0x00, 0xe9, + 0x02, 0x76, 0x05, 0x27, 0xe0, 0x01, 0xe5, 0x1b, + 0x06, 0x36, 0x05, 0xe0, 0x01, 0x26, 0x07, 0xe5, + 0x28, 0x47, 0xe6, 0x01, 0x27, 0x65, 0x76, 0x66, + 0x16, 0x20, 0xe9, 0x02, 0x05, 0x16, 0x05, 0x56, + 0x00, 0xeb, 0x0c, 0xe0, 0x03, 0xe5, 0x0a, 0x00, + 0xe5, 0x11, 0x47, 0x46, 0x27, 0x06, 0x07, 0x26, + 0xb6, 0x06, 0xe0, 0x39, 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, + 0x00, 0x16, 0x00, 0x16, 0x06, 0x05, 0xe0, 0x18, + 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, 0xc0, 0xe9, 0x02, 0xe0, 0x2e, 0xe5, 0x13, + 0x20, 0x46, 0x27, 0x66, 0x07, 0x86, 0x60, 0xe9, + 0x02, 0x2b, 0x56, 0x0f, 0xe0, 0x80, 0x38, 0xe5, + 0x24, 0x47, 0xe6, 0x01, 0x07, 0x26, 0x16, 0xe0, + 0x5c, 0xe1, 0x18, 0xe2, 0x18, 0xe9, 0x02, 0xeb, + 0x01, 0xe0, 0x04, 0x05, 0xe0, 0x80, 0x18, 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, 0x15, 0xe5, 0x31, 0xe0, + 0x80, 0x7f, 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, 0xe0, 0x80, 0x3f, 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, 0x8a, + 0x34, 0xe5, 0x83, 0xa7, 0x00, 0xfb, 0x01, 0xe0, + 0x8f, 0x3f, 0xe5, 0x81, 0xbf, 0xe0, 0xa1, 0x31, + 0xe5, 0x81, 0xb1, 0xc0, 0xe5, 0x17, 0x00, 0xe9, + 0x02, 0x60, 0x36, 0xe0, 0x58, 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, 0xe0, 0x14, 0xe5, 0x97, + 0x70, 0xe0, 0x00, 0xe5, 0x82, 0x6b, 0xe0, 0xa4, + 0x85, 0xe5, 0x80, 0x97, 0xe0, 0x29, 0x45, 0xe0, + 0x09, 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, 0x92, 0xd4, 0xef, 0x80, 0x6e, + 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef, 0x34, 0x27, + 0x46, 0x4f, 0xa7, 0xfb, 0x00, 0xe6, 0x00, 0x2f, + 0xc6, 0xef, 0x16, 0x66, 0xef, 0x33, 0xe0, 0x0f, + 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x80, 0x12, 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, 0x84, + 0xc8, 0xc6, 0x00, 0xe6, 0x09, 0x20, 0xc6, 0x00, + 0x26, 0x00, 0x86, 0xe0, 0x80, 0x4d, 0xe5, 0x25, + 0x40, 0xc6, 0xc4, 0x20, 0xe9, 0x02, 0x60, 0x05, + 0x0f, 0xe0, 0x80, 0xe8, 0xe5, 0x24, 0x66, 0xe9, + 0x02, 0x80, 0x0d, 0xe0, 0x84, 0x78, 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, 0x40, + 0xef, 0x55, 0x40, 0xef, 0x35, 0xe0, 0x31, 0xef, + 0x15, 0xe0, 0x05, 0xef, 0x24, 0x60, 0xef, 0x01, + 0xc0, 0x2f, 0xe0, 0x06, 0xaf, 0xe0, 0x80, 0x12, + 0xef, 0x80, 0x73, 0x8e, 0xef, 0x82, 0x4e, 0xe0, + 0x02, 0xef, 0x05, 0x40, 0xef, 0x03, 0x80, 0xef, + 0x6c, 0xe0, 0x04, 0xef, 0x51, 0xc0, 0xef, 0x04, + 0xe0, 0x0c, 0xef, 0x04, 0x60, 0xef, 0x30, 0xe0, + 0x00, 0xef, 0x02, 0xa0, 0xef, 0x20, 0xe0, 0x00, + 0xef, 0x16, 0xe0, 0x4a, 0xef, 0x04, 0x00, 0xef, + 0x5d, 0x00, 0x6f, 0x40, 0xef, 0x21, 0x20, 0xaf, + 0x40, 0xef, 0x15, 0x20, 0xef, 0x7f, 0xe0, 0x04, + 0xef, 0x06, 0x20, 0x6f, 0x60, 0x4f, 0x80, 0x4f, + 0xe0, 0x05, 0xaf, 0xe0, 0x84, 0xe2, 0xe5, 0xc0, + 0x66, 0x4f, 0xe0, 0x21, 0xe5, 0x8f, 0xad, 0xe0, + 0x03, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, 0xfa, + 0xe0, 0x06, 0xe5, 0x9c, 0xa9, 0xe0, 0x8b, 0x97, + 0xe5, 0x81, 0x96, 0xe0, 0xca, 0xc5, 0x5b, 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_Common, + UNICODE_SCRIPT_Coptic, + UNICODE_SCRIPT_Cuneiform, + UNICODE_SCRIPT_Cypriot, + UNICODE_SCRIPT_Cyrillic, + UNICODE_SCRIPT_Deseret, + UNICODE_SCRIPT_Devanagari, + 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_Kayah_Li, + UNICODE_SCRIPT_Kharoshthi, + UNICODE_SCRIPT_Khmer, + UNICODE_SCRIPT_Khojki, + 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_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_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_Ugaritic, + UNICODE_SCRIPT_Vai, + UNICODE_SCRIPT_Wancho, + UNICODE_SCRIPT_Warang_Citi, + 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" + "Common,Zyyy" "\0" + "Coptic,Copt,Qaac" "\0" + "Cuneiform,Xsux" "\0" + "Cypriot,Cprt" "\0" + "Cyrillic,Cyrl" "\0" + "Deseret,Dsrt" "\0" + "Devanagari,Deva" "\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" + "Kayah_Li,Kali" "\0" + "Kharoshthi,Khar" "\0" + "Khmer,Khmr" "\0" + "Khojki,Khoj" "\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" + "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" + "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" + "Ugaritic,Ugar" "\0" + "Vai,Vaii" "\0" + "Wancho,Wcho" "\0" + "Warang_Citi,Wara" "\0" + "Yi,Yiii" "\0" + "Zanabazar_Square,Zanb" "\0" +; + +static const uint8_t unicode_script_table[2565] = { + 0xc0, 0x18, 0x99, 0x42, 0x85, 0x18, 0x99, 0x42, + 0xae, 0x18, 0x80, 0x42, 0x8e, 0x18, 0x80, 0x42, + 0x84, 0x18, 0x96, 0x42, 0x80, 0x18, 0x9e, 0x42, + 0x80, 0x18, 0xe1, 0x60, 0x42, 0xa6, 0x18, 0x84, + 0x42, 0x84, 0x18, 0x81, 0x0d, 0x93, 0x18, 0xe0, + 0x0f, 0x35, 0x83, 0x29, 0x80, 0x18, 0x82, 0x29, + 0x01, 0x83, 0x29, 0x80, 0x18, 0x80, 0x29, 0x03, + 0x80, 0x29, 0x80, 0x18, 0x80, 0x29, 0x80, 0x18, + 0x82, 0x29, 0x00, 0x80, 0x29, 0x00, 0x93, 0x29, + 0x00, 0xbe, 0x29, 0x8d, 0x19, 0x8f, 0x29, 0xe0, + 0x24, 0x1c, 0x81, 0x35, 0xe0, 0x48, 0x1c, 0x00, + 0xa5, 0x05, 0x01, 0xaf, 0x05, 0x80, 0x18, 0x80, + 0x05, 0x01, 0x82, 0x05, 0x00, 0xb6, 0x32, 0x07, + 0x9a, 0x32, 0x03, 0x85, 0x32, 0x0a, 0x84, 0x04, + 0x80, 0x18, 0x85, 0x04, 0x80, 0x18, 0x8d, 0x04, + 0x80, 0x18, 0x80, 0x04, 0x00, 0x80, 0x04, 0x80, + 0x18, 0x9f, 0x04, 0x80, 0x18, 0x89, 0x04, 0x8a, + 0x35, 0x99, 0x04, 0x80, 0x35, 0xe0, 0x0b, 0x04, + 0x80, 0x18, 0xa1, 0x04, 0x8d, 0x84, 0x00, 0xbb, + 0x84, 0x01, 0x82, 0x84, 0xaf, 0x04, 0xb1, 0x8e, + 0x0d, 0xba, 0x60, 0x01, 0x82, 0x60, 0xad, 0x78, + 0x01, 0x8e, 0x78, 0x00, 0x9b, 0x4d, 0x01, 0x80, + 0x4d, 0x00, 0x8a, 0x84, 0x34, 0x94, 0x04, 0x00, + 0x87, 0x04, 0x14, 0x8e, 0x04, 0x80, 0x18, 0x9c, + 0x04, 0xd0, 0x1e, 0x83, 0x35, 0x8e, 0x1e, 0x81, + 0x18, 0x99, 0x1e, 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, 0x2c, 0x00, + 0x85, 0x2c, 0x03, 0x81, 0x2c, 0x01, 0x95, 0x2c, + 0x00, 0x86, 0x2c, 0x00, 0x81, 0x2c, 0x00, 0x81, + 0x2c, 0x00, 0x81, 0x2c, 0x01, 0x80, 0x2c, 0x00, + 0x84, 0x2c, 0x03, 0x81, 0x2c, 0x01, 0x82, 0x2c, + 0x02, 0x80, 0x2c, 0x06, 0x83, 0x2c, 0x00, 0x80, + 0x2c, 0x06, 0x90, 0x2c, 0x09, 0x82, 0x2a, 0x00, + 0x88, 0x2a, 0x00, 0x82, 0x2a, 0x00, 0x95, 0x2a, + 0x00, 0x86, 0x2a, 0x00, 0x81, 0x2a, 0x00, 0x84, + 0x2a, 0x01, 0x89, 0x2a, 0x00, 0x82, 0x2a, 0x00, + 0x82, 0x2a, 0x01, 0x80, 0x2a, 0x0e, 0x83, 0x2a, + 0x01, 0x8b, 0x2a, 0x06, 0x86, 0x2a, 0x00, 0x82, + 0x6d, 0x00, 0x87, 0x6d, 0x01, 0x81, 0x6d, 0x01, + 0x95, 0x6d, 0x00, 0x86, 0x6d, 0x00, 0x81, 0x6d, + 0x00, 0x84, 0x6d, 0x01, 0x88, 0x6d, 0x01, 0x81, + 0x6d, 0x01, 0x82, 0x6d, 0x07, 0x81, 0x6d, 0x03, + 0x81, 0x6d, 0x00, 0x84, 0x6d, 0x01, 0x91, 0x6d, + 0x09, 0x81, 0x8b, 0x00, 0x85, 0x8b, 0x02, 0x82, + 0x8b, 0x00, 0x83, 0x8b, 0x02, 0x81, 0x8b, 0x00, + 0x80, 0x8b, 0x00, 0x81, 0x8b, 0x02, 0x81, 0x8b, + 0x02, 0x82, 0x8b, 0x02, 0x8b, 0x8b, 0x03, 0x84, + 0x8b, 0x02, 0x82, 0x8b, 0x00, 0x83, 0x8b, 0x01, + 0x80, 0x8b, 0x05, 0x80, 0x8b, 0x0d, 0x94, 0x8b, + 0x04, 0x8c, 0x8d, 0x00, 0x82, 0x8d, 0x00, 0x96, + 0x8d, 0x00, 0x8f, 0x8d, 0x02, 0x87, 0x8d, 0x00, + 0x82, 0x8d, 0x00, 0x83, 0x8d, 0x06, 0x81, 0x8d, + 0x00, 0x82, 0x8d, 0x04, 0x83, 0x8d, 0x01, 0x89, + 0x8d, 0x06, 0x88, 0x8d, 0x8c, 0x3a, 0x00, 0x82, + 0x3a, 0x00, 0x96, 0x3a, 0x00, 0x89, 0x3a, 0x00, + 0x84, 0x3a, 0x01, 0x88, 0x3a, 0x00, 0x82, 0x3a, + 0x00, 0x83, 0x3a, 0x06, 0x81, 0x3a, 0x06, 0x80, + 0x3a, 0x00, 0x83, 0x3a, 0x01, 0x89, 0x3a, 0x00, + 0x81, 0x3a, 0x0c, 0x83, 0x4c, 0x00, 0x87, 0x4c, + 0x00, 0x82, 0x4c, 0x00, 0xb2, 0x4c, 0x00, 0x82, + 0x4c, 0x00, 0x85, 0x4c, 0x03, 0x8f, 0x4c, 0x01, + 0x99, 0x4c, 0x01, 0x81, 0x7e, 0x00, 0x91, 0x7e, + 0x02, 0x97, 0x7e, 0x00, 0x88, 0x7e, 0x00, 0x80, + 0x7e, 0x01, 0x86, 0x7e, 0x02, 0x80, 0x7e, 0x03, + 0x85, 0x7e, 0x00, 0x80, 0x7e, 0x00, 0x87, 0x7e, + 0x05, 0x89, 0x7e, 0x01, 0x82, 0x7e, 0x0b, 0xb9, + 0x8f, 0x03, 0x80, 0x18, 0x9b, 0x8f, 0x24, 0x81, + 0x41, 0x00, 0x80, 0x41, 0x00, 0x84, 0x41, 0x00, + 0x97, 0x41, 0x00, 0x80, 0x41, 0x00, 0x96, 0x41, + 0x01, 0x84, 0x41, 0x00, 0x80, 0x41, 0x00, 0x85, + 0x41, 0x01, 0x89, 0x41, 0x01, 0x83, 0x41, 0x1f, + 0xc7, 0x90, 0x00, 0xa3, 0x90, 0x03, 0xa6, 0x90, + 0x00, 0xa3, 0x90, 0x00, 0x8e, 0x90, 0x00, 0x86, + 0x90, 0x83, 0x18, 0x81, 0x90, 0x24, 0xe0, 0x3f, + 0x5b, 0xa5, 0x25, 0x00, 0x80, 0x25, 0x04, 0x80, + 0x25, 0x01, 0xaa, 0x25, 0x80, 0x18, 0x83, 0x25, + 0xe0, 0x9f, 0x2e, 0xc8, 0x24, 0x00, 0x83, 0x24, + 0x01, 0x86, 0x24, 0x00, 0x80, 0x24, 0x00, 0x83, + 0x24, 0x01, 0xa8, 0x24, 0x00, 0x83, 0x24, 0x01, + 0xa0, 0x24, 0x00, 0x83, 0x24, 0x01, 0x86, 0x24, + 0x00, 0x80, 0x24, 0x00, 0x83, 0x24, 0x01, 0x8e, + 0x24, 0x00, 0xb8, 0x24, 0x00, 0x83, 0x24, 0x01, + 0xc2, 0x24, 0x01, 0x9f, 0x24, 0x02, 0x99, 0x24, + 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, 0xe2, + 0x1f, 0x12, 0x9c, 0x63, 0x02, 0xca, 0x77, 0x82, + 0x18, 0x8a, 0x77, 0x06, 0x8c, 0x85, 0x00, 0x86, + 0x85, 0x0a, 0x94, 0x30, 0x81, 0x18, 0x08, 0x93, + 0x11, 0x0b, 0x8c, 0x86, 0x00, 0x82, 0x86, 0x00, + 0x81, 0x86, 0x0b, 0xdd, 0x3e, 0x01, 0x89, 0x3e, + 0x05, 0x89, 0x3e, 0x05, 0x81, 0x58, 0x81, 0x18, + 0x80, 0x58, 0x80, 0x18, 0x88, 0x58, 0x00, 0x89, + 0x58, 0x05, 0xd8, 0x58, 0x06, 0xaa, 0x58, 0x04, + 0xc5, 0x12, 0x09, 0x9e, 0x44, 0x00, 0x8b, 0x44, + 0x03, 0x8b, 0x44, 0x03, 0x80, 0x44, 0x02, 0x8b, + 0x44, 0x9d, 0x87, 0x01, 0x84, 0x87, 0x0a, 0xab, + 0x5e, 0x03, 0x99, 0x5e, 0x05, 0x8a, 0x5e, 0x02, + 0x81, 0x5e, 0x9f, 0x3e, 0x9b, 0x10, 0x01, 0x81, + 0x10, 0xbe, 0x88, 0x00, 0x9c, 0x88, 0x01, 0x8a, + 0x88, 0x05, 0x89, 0x88, 0x05, 0x8d, 0x88, 0x01, + 0x8e, 0x35, 0x40, 0xcb, 0x07, 0x03, 0xac, 0x07, + 0x02, 0xbf, 0x82, 0xb3, 0x0a, 0x07, 0x83, 0x0a, + 0xb7, 0x43, 0x02, 0x8e, 0x43, 0x02, 0x82, 0x43, + 0xaf, 0x64, 0x88, 0x1c, 0x06, 0xaa, 0x25, 0x01, + 0x82, 0x25, 0x87, 0x82, 0x07, 0x82, 0x35, 0x80, + 0x18, 0x8c, 0x35, 0x80, 0x18, 0x86, 0x35, 0x83, + 0x18, 0x80, 0x35, 0x85, 0x18, 0x80, 0x35, 0x82, + 0x18, 0x81, 0x35, 0x80, 0x18, 0x04, 0xa5, 0x42, + 0x84, 0x29, 0x80, 0x1c, 0xb0, 0x42, 0x84, 0x29, + 0x83, 0x42, 0x84, 0x29, 0x8c, 0x42, 0x80, 0x1c, + 0xc5, 0x42, 0x80, 0x29, 0xb9, 0x35, 0x00, 0x84, + 0x35, 0xe0, 0x9f, 0x42, 0x95, 0x29, 0x01, 0x85, + 0x29, 0x01, 0xa5, 0x29, 0x01, 0x85, 0x29, 0x01, + 0x87, 0x29, 0x00, 0x80, 0x29, 0x00, 0x80, 0x29, + 0x00, 0x80, 0x29, 0x00, 0x9e, 0x29, 0x01, 0xb4, + 0x29, 0x00, 0x8e, 0x29, 0x00, 0x8d, 0x29, 0x01, + 0x85, 0x29, 0x00, 0x92, 0x29, 0x01, 0x82, 0x29, + 0x00, 0x88, 0x29, 0x00, 0x8b, 0x18, 0x81, 0x35, + 0xd6, 0x18, 0x00, 0x8a, 0x18, 0x80, 0x42, 0x01, + 0x8a, 0x18, 0x80, 0x42, 0x8e, 0x18, 0x00, 0x8c, + 0x42, 0x02, 0x9f, 0x18, 0x0f, 0xa0, 0x35, 0x0e, + 0xa5, 0x18, 0x80, 0x29, 0x82, 0x18, 0x81, 0x42, + 0x85, 0x18, 0x80, 0x42, 0x9a, 0x18, 0x80, 0x42, + 0x90, 0x18, 0xa8, 0x42, 0x82, 0x18, 0x03, 0xe2, + 0x36, 0x18, 0x18, 0x8a, 0x18, 0x14, 0xe3, 0x3f, + 0x18, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x18, 0x01, + 0x9f, 0x18, 0x01, 0xe0, 0x07, 0x18, 0xae, 0x26, + 0x00, 0xae, 0x26, 0x00, 0x9f, 0x42, 0xe0, 0x13, + 0x19, 0x04, 0x86, 0x19, 0xa5, 0x25, 0x00, 0x80, + 0x25, 0x04, 0x80, 0x25, 0x01, 0xb7, 0x91, 0x06, + 0x81, 0x91, 0x0d, 0x80, 0x91, 0x96, 0x24, 0x08, + 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, + 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, + 0x24, 0x00, 0x86, 0x24, 0x00, 0x86, 0x24, 0x00, + 0x9f, 0x1c, 0xcf, 0x18, 0x2f, 0x99, 0x2d, 0x00, + 0xd8, 0x2d, 0x0b, 0xe0, 0x75, 0x2d, 0x19, 0x8b, + 0x18, 0x03, 0x84, 0x18, 0x80, 0x2d, 0x80, 0x18, + 0x80, 0x2d, 0x98, 0x18, 0x88, 0x2d, 0x83, 0x35, + 0x81, 0x2e, 0x87, 0x18, 0x83, 0x2d, 0x83, 0x18, + 0x00, 0xd5, 0x33, 0x01, 0x81, 0x35, 0x81, 0x18, + 0x82, 0x33, 0x80, 0x18, 0xd9, 0x3b, 0x81, 0x18, + 0x82, 0x3b, 0x04, 0xaa, 0x0d, 0x00, 0xdd, 0x2e, + 0x00, 0x8f, 0x18, 0x9a, 0x0d, 0x04, 0xa3, 0x18, + 0x0b, 0x8f, 0x3b, 0x9e, 0x2e, 0x00, 0xbf, 0x18, + 0x9e, 0x2e, 0xd0, 0x18, 0xae, 0x3b, 0x80, 0x18, + 0xd7, 0x3b, 0xe0, 0x47, 0x18, 0xf0, 0x09, 0x55, + 0x2d, 0x09, 0xbf, 0x18, 0xf0, 0x41, 0x8f, 0x2d, + 0x0f, 0xe4, 0x2c, 0x97, 0x02, 0xb6, 0x97, 0x08, + 0xaf, 0x47, 0xe0, 0xcb, 0x94, 0x13, 0xdf, 0x1c, + 0xd7, 0x08, 0x07, 0xa1, 0x18, 0xe0, 0x05, 0x42, + 0x82, 0x18, 0xb4, 0x42, 0x01, 0x84, 0x42, 0x2f, + 0x88, 0x42, 0xab, 0x83, 0x03, 0x89, 0x18, 0x05, + 0xb7, 0x73, 0x07, 0xc5, 0x79, 0x07, 0x8b, 0x79, + 0x05, 0x9f, 0x1e, 0xad, 0x3c, 0x80, 0x18, 0x80, + 0x3c, 0xa3, 0x76, 0x0a, 0x80, 0x76, 0x9c, 0x2e, + 0x02, 0xcd, 0x38, 0x00, 0x80, 0x18, 0x89, 0x38, + 0x03, 0x81, 0x38, 0x9e, 0x5b, 0x00, 0xb6, 0x16, + 0x08, 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, 0x83, + 0x16, 0x9f, 0x5b, 0xc2, 0x89, 0x17, 0x84, 0x89, + 0x96, 0x52, 0x09, 0x85, 0x24, 0x01, 0x85, 0x24, + 0x01, 0x85, 0x24, 0x08, 0x86, 0x24, 0x00, 0x86, + 0x24, 0x00, 0xaa, 0x42, 0x80, 0x18, 0x88, 0x42, + 0x80, 0x29, 0x81, 0x42, 0x07, 0xcf, 0x17, 0xad, + 0x52, 0x01, 0x89, 0x52, 0x05, 0xf0, 0x1b, 0x43, + 0x2e, 0x0b, 0x96, 0x2e, 0x03, 0xb0, 0x2e, 0x70, + 0x10, 0xa3, 0xe1, 0x0d, 0x2d, 0x01, 0xe0, 0x09, + 0x2d, 0x25, 0x86, 0x42, 0x0b, 0x84, 0x05, 0x04, + 0x99, 0x32, 0x00, 0x84, 0x32, 0x00, 0x80, 0x32, + 0x00, 0x81, 0x32, 0x00, 0x81, 0x32, 0x00, 0x89, + 0x32, 0xe0, 0x11, 0x04, 0x10, 0xe1, 0x0a, 0x04, + 0x81, 0x18, 0x0f, 0xbf, 0x04, 0x01, 0xb5, 0x04, + 0x27, 0x8d, 0x04, 0x01, 0x8f, 0x35, 0x89, 0x18, + 0x05, 0x8d, 0x35, 0x81, 0x1c, 0xa2, 0x18, 0x00, + 0x92, 0x18, 0x00, 0x83, 0x18, 0x03, 0x84, 0x04, + 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, 0x18, 0x00, + 0x9f, 0x18, 0x99, 0x42, 0x85, 0x18, 0x99, 0x42, + 0x8a, 0x18, 0x89, 0x3b, 0x80, 0x18, 0xac, 0x3b, + 0x81, 0x18, 0x9e, 0x2e, 0x02, 0x85, 0x2e, 0x01, + 0x85, 0x2e, 0x01, 0x85, 0x2e, 0x01, 0x82, 0x2e, + 0x02, 0x86, 0x18, 0x00, 0x86, 0x18, 0x09, 0x84, + 0x18, 0x01, 0x8b, 0x46, 0x00, 0x99, 0x46, 0x00, + 0x92, 0x46, 0x00, 0x81, 0x46, 0x00, 0x8e, 0x46, + 0x01, 0x8d, 0x46, 0x21, 0xe0, 0x1a, 0x46, 0x04, + 0x82, 0x18, 0x03, 0xac, 0x18, 0x02, 0x88, 0x18, + 0xce, 0x29, 0x00, 0x8b, 0x18, 0x03, 0x80, 0x29, + 0x2e, 0xac, 0x18, 0x80, 0x35, 0x60, 0x21, 0x9c, + 0x48, 0x02, 0xb0, 0x13, 0x0e, 0x80, 0x35, 0x9a, + 0x18, 0x03, 0xa3, 0x66, 0x08, 0x82, 0x66, 0x9a, + 0x27, 0x04, 0xaa, 0x68, 0x04, 0x9d, 0x93, 0x00, + 0x80, 0x93, 0xa3, 0x69, 0x03, 0x8d, 0x69, 0x29, + 0xcf, 0x1d, 0xaf, 0x7b, 0x9d, 0x6f, 0x01, 0x89, + 0x6f, 0x05, 0xa3, 0x6e, 0x03, 0xa3, 0x6e, 0x03, + 0xa7, 0x22, 0x07, 0xb3, 0x14, 0x0a, 0x80, 0x14, + 0x60, 0x2f, 0xe0, 0xd6, 0x45, 0x08, 0x95, 0x45, + 0x09, 0x87, 0x45, 0x60, 0x37, 0x85, 0x1b, 0x01, + 0x80, 0x1b, 0x00, 0xab, 0x1b, 0x00, 0x81, 0x1b, + 0x02, 0x80, 0x1b, 0x01, 0x80, 0x1b, 0x95, 0x34, + 0x00, 0x88, 0x34, 0x9f, 0x71, 0x9e, 0x5c, 0x07, + 0x88, 0x5c, 0x2f, 0x92, 0x31, 0x00, 0x81, 0x31, + 0x04, 0x84, 0x31, 0x9b, 0x74, 0x02, 0x80, 0x74, + 0x99, 0x49, 0x04, 0x80, 0x49, 0x3f, 0x9f, 0x55, + 0x97, 0x54, 0x03, 0x93, 0x54, 0x01, 0xad, 0x54, + 0x83, 0x3d, 0x00, 0x81, 0x3d, 0x04, 0x87, 0x3d, + 0x00, 0x82, 0x3d, 0x00, 0x9c, 0x3d, 0x01, 0x82, + 0x3d, 0x03, 0x89, 0x3d, 0x06, 0x88, 0x3d, 0x06, + 0x9f, 0x6b, 0x9f, 0x67, 0x1f, 0xa6, 0x4e, 0x03, + 0x8b, 0x4e, 0x08, 0xb5, 0x06, 0x02, 0x86, 0x06, + 0x95, 0x37, 0x01, 0x87, 0x37, 0x92, 0x36, 0x04, + 0x87, 0x36, 0x91, 0x75, 0x06, 0x83, 0x75, 0x0b, + 0x86, 0x75, 0x4f, 0xc8, 0x6c, 0x36, 0xb2, 0x65, + 0x0c, 0xb2, 0x65, 0x06, 0x85, 0x65, 0xa7, 0x2f, + 0x07, 0x89, 0x2f, 0x60, 0xc5, 0x9e, 0x04, 0x60, + 0x20, 0xa7, 0x6a, 0x07, 0xa9, 0x7f, 0x60, 0x25, + 0x96, 0x23, 0x08, 0xcd, 0x0e, 0x03, 0x9d, 0x0e, + 0x0e, 0x80, 0x0e, 0xc1, 0x39, 0x0a, 0x80, 0x39, + 0x01, 0x98, 0x80, 0x06, 0x89, 0x80, 0x05, 0xb4, + 0x15, 0x00, 0x90, 0x15, 0x08, 0xa6, 0x4b, 0x08, + 0xcd, 0x7a, 0x01, 0x8f, 0x7a, 0x00, 0x93, 0x7e, + 0x0a, 0x91, 0x3f, 0x00, 0xab, 0x3f, 0x40, 0x86, + 0x5a, 0x00, 0x80, 0x5a, 0x00, 0x83, 0x5a, 0x00, + 0x8e, 0x5a, 0x00, 0x8a, 0x5a, 0x05, 0xba, 0x40, + 0x04, 0x89, 0x40, 0x05, 0x83, 0x28, 0x00, 0x87, + 0x28, 0x01, 0x81, 0x28, 0x01, 0x95, 0x28, 0x00, + 0x86, 0x28, 0x00, 0x81, 0x28, 0x00, 0x84, 0x28, + 0x00, 0x80, 0x35, 0x88, 0x28, 0x01, 0x81, 0x28, + 0x01, 0x82, 0x28, 0x01, 0x80, 0x28, 0x05, 0x80, + 0x28, 0x04, 0x86, 0x28, 0x01, 0x86, 0x28, 0x02, + 0x84, 0x28, 0x60, 0x2a, 0xd9, 0x5f, 0x00, 0x80, + 0x5f, 0x00, 0x82, 0x5f, 0x1f, 0xc7, 0x92, 0x07, + 0x89, 0x92, 0x60, 0x45, 0xb5, 0x7c, 0x01, 0xa5, + 0x7c, 0x21, 0xc4, 0x57, 0x0a, 0x89, 0x57, 0x05, + 0x8c, 0x58, 0x12, 0xb8, 0x8a, 0x06, 0x89, 0x8a, + 0x35, 0x9a, 0x02, 0x01, 0x8e, 0x02, 0x03, 0x8f, + 0x02, 0x60, 0x5f, 0xbb, 0x1f, 0x60, 0x03, 0xd2, + 0x96, 0x0b, 0x80, 0x96, 0x60, 0x3f, 0x87, 0x5d, + 0x01, 0xad, 0x5d, 0x01, 0x8a, 0x5d, 0x1a, 0xc7, + 0x98, 0x07, 0xd2, 0x81, 0x1c, 0xb8, 0x72, 0x60, + 0xa6, 0x88, 0x0c, 0x00, 0xac, 0x0c, 0x00, 0x8d, + 0x0c, 0x09, 0x9c, 0x0c, 0x02, 0x9f, 0x4f, 0x01, + 0x95, 0x4f, 0x00, 0x8d, 0x4f, 0x48, 0x86, 0x50, + 0x00, 0x81, 0x50, 0x00, 0xab, 0x50, 0x02, 0x80, + 0x50, 0x00, 0x81, 0x50, 0x00, 0x88, 0x50, 0x07, + 0x89, 0x50, 0x05, 0x85, 0x2b, 0x00, 0x81, 0x2b, + 0x00, 0xa4, 0x2b, 0x00, 0x81, 0x2b, 0x00, 0x85, + 0x2b, 0x06, 0x89, 0x2b, 0x60, 0xd5, 0x98, 0x4a, + 0x60, 0x66, 0xb1, 0x8b, 0x0c, 0x80, 0x8b, 0xe3, + 0x39, 0x1a, 0x60, 0x05, 0xe0, 0x0e, 0x1a, 0x00, + 0x84, 0x1a, 0x0a, 0xe0, 0x63, 0x1a, 0x6a, 0x5b, + 0xe3, 0xce, 0x21, 0x00, 0x88, 0x21, 0x6f, 0x66, + 0xe1, 0xe6, 0x03, 0x70, 0x11, 0x58, 0xe1, 0xd8, + 0x08, 0x06, 0x9e, 0x59, 0x00, 0x89, 0x59, 0x03, + 0x81, 0x59, 0x5f, 0x9d, 0x09, 0x01, 0x85, 0x09, + 0x09, 0xc5, 0x70, 0x09, 0x89, 0x70, 0x00, 0x86, + 0x70, 0x00, 0x94, 0x70, 0x04, 0x92, 0x70, 0x62, + 0x4f, 0xda, 0x51, 0x60, 0x04, 0xca, 0x56, 0x03, + 0xb8, 0x56, 0x06, 0x90, 0x56, 0x3f, 0x80, 0x8c, + 0x80, 0x61, 0x81, 0x18, 0x1b, 0xf0, 0x07, 0x97, + 0x8c, 0x07, 0xe2, 0x92, 0x8c, 0x70, 0x14, 0xac, + 0x80, 0x3b, 0xe0, 0xbd, 0x33, 0x30, 0x82, 0x33, + 0x10, 0x83, 0x3b, 0x07, 0xe1, 0x2b, 0x61, 0x68, + 0xa3, 0xe0, 0x0a, 0x20, 0x04, 0x8c, 0x20, 0x02, + 0x88, 0x20, 0x06, 0x89, 0x20, 0x01, 0x83, 0x20, + 0x83, 0x18, 0x70, 0x02, 0xfb, 0xe0, 0x95, 0x18, + 0x09, 0xa6, 0x18, 0x01, 0xbd, 0x18, 0x82, 0x35, + 0x90, 0x18, 0x87, 0x35, 0x81, 0x18, 0x86, 0x35, + 0x9d, 0x18, 0x83, 0x35, 0xba, 0x18, 0x16, 0xc5, + 0x29, 0x60, 0x39, 0x93, 0x18, 0x0b, 0xd6, 0x18, + 0x08, 0x98, 0x18, 0x60, 0x26, 0xd4, 0x18, 0x00, + 0xc6, 0x18, 0x00, 0x81, 0x18, 0x01, 0x80, 0x18, + 0x01, 0x81, 0x18, 0x01, 0x83, 0x18, 0x00, 0x8b, + 0x18, 0x00, 0x80, 0x18, 0x00, 0x86, 0x18, 0x00, + 0xc0, 0x18, 0x00, 0x83, 0x18, 0x01, 0x87, 0x18, + 0x00, 0x86, 0x18, 0x00, 0x9b, 0x18, 0x00, 0x83, + 0x18, 0x00, 0x84, 0x18, 0x00, 0x80, 0x18, 0x02, + 0x86, 0x18, 0x00, 0xe0, 0xf3, 0x18, 0x01, 0xe0, + 0xc3, 0x18, 0x01, 0xb1, 0x18, 0xe2, 0x2b, 0x7d, + 0x0e, 0x84, 0x7d, 0x00, 0x8e, 0x7d, 0x64, 0xef, + 0x86, 0x26, 0x00, 0x90, 0x26, 0x01, 0x86, 0x26, + 0x00, 0x81, 0x26, 0x00, 0x84, 0x26, 0x60, 0x74, + 0xac, 0x62, 0x02, 0x8d, 0x62, 0x01, 0x89, 0x62, + 0x03, 0x81, 0x62, 0x61, 0x0f, 0xb9, 0x95, 0x04, + 0x80, 0x95, 0x64, 0x9f, 0xe0, 0x64, 0x53, 0x01, + 0x8f, 0x53, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01, + 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x18, 0x4b, + 0xbc, 0x18, 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, 0x18, 0x03, 0xe0, 0x03, + 0x18, 0x0b, 0x8e, 0x18, 0x01, 0x8e, 0x18, 0x00, + 0x8e, 0x18, 0x00, 0xa4, 0x18, 0x09, 0x8c, 0x18, + 0x02, 0xdc, 0x18, 0x02, 0xbc, 0x18, 0x38, 0x99, + 0x18, 0x80, 0x33, 0x81, 0x18, 0x0c, 0xab, 0x18, + 0x03, 0x88, 0x18, 0x06, 0x81, 0x18, 0x0d, 0x85, + 0x18, 0x60, 0x39, 0xe3, 0x75, 0x18, 0x09, 0x8c, + 0x18, 0x02, 0x8a, 0x18, 0x04, 0xe0, 0x13, 0x18, + 0x0b, 0xd8, 0x18, 0x06, 0x8b, 0x18, 0x13, 0x8b, + 0x18, 0x03, 0xb7, 0x18, 0x07, 0x89, 0x18, 0x05, + 0xa7, 0x18, 0x07, 0x9d, 0x18, 0x51, 0x8b, 0x18, + 0x00, 0xe0, 0x04, 0x18, 0x00, 0x83, 0x18, 0x02, + 0xa8, 0x18, 0x01, 0x85, 0x18, 0x02, 0x9c, 0x18, + 0x01, 0xe0, 0x26, 0x18, 0x0b, 0x8d, 0x18, 0x01, + 0x83, 0x18, 0x03, 0x82, 0x18, 0x04, 0x82, 0x18, + 0x0c, 0x85, 0x18, 0x65, 0x09, 0xf0, 0x96, 0x76, + 0x2d, 0x28, 0xef, 0xd4, 0x2d, 0x0a, 0xe0, 0x7d, + 0x2d, 0x01, 0xf0, 0x06, 0x21, 0x2d, 0x0d, 0xf0, + 0x0c, 0xd0, 0x2d, 0x6b, 0xbe, 0xe1, 0xbd, 0x2d, + 0x7a, 0xf5, 0x82, 0x80, 0x18, 0x1d, 0xdf, 0x18, + 0x60, 0x1f, 0xe0, 0x8f, 0x35, +}; + +static const uint8_t unicode_script_ext_table[789] = { + 0x82, 0xc1, 0x00, 0x00, 0x01, 0x29, 0x01, 0x00, + 0x00, 0x01, 0x29, 0x1c, 0x00, 0x0c, 0x01, 0x42, + 0x80, 0x92, 0x00, 0x00, 0x02, 0x1c, 0x68, 0x00, + 0x02, 0x1c, 0x26, 0x01, 0x02, 0x1c, 0x42, 0x00, + 0x02, 0x1c, 0x26, 0x80, 0x80, 0x00, 0x00, 0x02, + 0x05, 0x25, 0x80, 0x01, 0x00, 0x00, 0x04, 0x04, + 0x2f, 0x84, 0x8e, 0x0d, 0x00, 0x00, 0x04, 0x04, + 0x2f, 0x84, 0x8e, 0x00, 0x03, 0x04, 0x84, 0x8e, + 0x01, 0x00, 0x00, 0x04, 0x04, 0x2f, 0x84, 0x8e, + 0x1f, 0x00, 0x00, 0x08, 0x01, 0x04, 0x4d, 0x4e, + 0x75, 0x2f, 0x7f, 0x84, 0x09, 0x00, 0x0a, 0x02, + 0x04, 0x84, 0x09, 0x00, 0x09, 0x02, 0x04, 0x8e, + 0x05, 0x00, 0x00, 0x02, 0x04, 0x84, 0x62, 0x00, + 0x00, 0x02, 0x04, 0x2f, 0x81, 0xfb, 0x00, 0x00, + 0x0d, 0x0b, 0x1e, 0x28, 0x2a, 0x2c, 0x3a, 0x42, + 0x4c, 0x6d, 0x7a, 0x8b, 0x8d, 0x92, 0x00, 0x0c, + 0x0b, 0x1e, 0x28, 0x2a, 0x2c, 0x3a, 0x42, 0x4c, + 0x6d, 0x8b, 0x8d, 0x92, 0x10, 0x00, 0x00, 0x14, + 0x0b, 0x1e, 0x1f, 0x2b, 0x50, 0x28, 0x2a, 0x2c, + 0x3a, 0x4b, 0x4c, 0x5d, 0x6d, 0x40, 0x7e, 0x83, + 0x8a, 0x8b, 0x8d, 0x92, 0x00, 0x15, 0x0b, 0x1e, + 0x1f, 0x2b, 0x50, 0x28, 0x2a, 0x2c, 0x3a, 0x44, + 0x4b, 0x4c, 0x5d, 0x6d, 0x40, 0x7e, 0x83, 0x8a, + 0x8b, 0x8d, 0x92, 0x09, 0x04, 0x1e, 0x1f, 0x39, + 0x4b, 0x75, 0x00, 0x09, 0x03, 0x0b, 0x15, 0x83, + 0x75, 0x00, 0x09, 0x02, 0x2c, 0x5a, 0x75, 0x00, + 0x09, 0x02, 0x2a, 0x3f, 0x80, 0x75, 0x00, 0x0d, + 0x02, 0x28, 0x8b, 0x80, 0x71, 0x00, 0x09, 0x02, + 0x3a, 0x5d, 0x82, 0xcf, 0x00, 0x09, 0x03, 0x15, + 0x5b, 0x87, 0x80, 0x30, 0x00, 0x00, 0x02, 0x25, + 0x42, 0x85, 0xb8, 0x00, 0x01, 0x04, 0x11, 0x30, + 0x86, 0x85, 0x80, 0x4a, 0x00, 0x01, 0x02, 0x58, + 0x73, 0x00, 0x00, 0x00, 0x02, 0x58, 0x73, 0x84, + 0x49, 0x00, 0x00, 0x04, 0x0b, 0x1e, 0x28, 0x3a, + 0x00, 0x01, 0x1e, 0x00, 0x04, 0x0b, 0x1e, 0x28, + 0x3a, 0x00, 0x02, 0x1e, 0x28, 0x00, 0x01, 0x1e, + 0x01, 0x02, 0x0b, 0x1e, 0x00, 0x02, 0x1e, 0x7a, + 0x00, 0x02, 0x0b, 0x1e, 0x00, 0x02, 0x1e, 0x7a, + 0x00, 0x06, 0x1e, 0x3a, 0x4c, 0x6d, 0x8b, 0x8d, + 0x00, 0x01, 0x1e, 0x01, 0x02, 0x1e, 0x7a, 0x01, + 0x01, 0x1e, 0x00, 0x02, 0x1e, 0x7a, 0x00, 0x02, + 0x0b, 0x1e, 0x06, 0x01, 0x1e, 0x00, 0x02, 0x1e, + 0x5d, 0x00, 0x02, 0x0b, 0x1e, 0x01, 0x01, 0x1e, + 0x00, 0x02, 0x0b, 0x1e, 0x03, 0x01, 0x1e, 0x00, + 0x08, 0x0b, 0x1e, 0x28, 0x3a, 0x5d, 0x6d, 0x8d, + 0x92, 0x00, 0x02, 0x1e, 0x28, 0x00, 0x03, 0x1e, + 0x28, 0x3a, 0x01, 0x02, 0x0b, 0x1e, 0x00, 0x01, + 0x0b, 0x01, 0x02, 0x1e, 0x28, 0x00, 0x01, 0x5d, + 0x80, 0x44, 0x00, 0x01, 0x01, 0x29, 0x81, 0xec, + 0x00, 0x00, 0x02, 0x42, 0x58, 0x80, 0x3f, 0x00, + 0x00, 0x03, 0x1e, 0x28, 0x42, 0x8c, 0xd1, 0x00, + 0x00, 0x02, 0x1c, 0x26, 0x81, 0x3c, 0x00, 0x01, + 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x97, 0x00, + 0x05, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x01, 0x00, + 0x00, 0x01, 0x2d, 0x00, 0x00, 0x09, 0x06, 0x0d, + 0x2e, 0x2d, 0x33, 0x3b, 0x97, 0x00, 0x00, 0x00, + 0x05, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x07, 0x06, + 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x97, 0x03, 0x05, + 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x09, 0x00, 0x03, + 0x02, 0x0d, 0x2d, 0x01, 0x00, 0x00, 0x05, 0x0d, + 0x2e, 0x2d, 0x33, 0x3b, 0x04, 0x02, 0x33, 0x3b, + 0x00, 0x00, 0x00, 0x05, 0x0d, 0x2e, 0x2d, 0x33, + 0x3b, 0x03, 0x00, 0x01, 0x03, 0x2d, 0x33, 0x3b, + 0x01, 0x01, 0x2d, 0x58, 0x00, 0x03, 0x02, 0x33, + 0x3b, 0x02, 0x00, 0x00, 0x02, 0x33, 0x3b, 0x59, + 0x00, 0x00, 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, + 0x97, 0x00, 0x02, 0x33, 0x3b, 0x80, 0x12, 0x00, + 0x0f, 0x01, 0x2d, 0x1f, 0x00, 0x23, 0x01, 0x2d, + 0x3b, 0x00, 0x27, 0x01, 0x2d, 0x37, 0x00, 0x30, + 0x01, 0x2d, 0x0e, 0x00, 0x0b, 0x01, 0x2d, 0x32, + 0x00, 0x00, 0x01, 0x2d, 0x57, 0x00, 0x18, 0x01, + 0x2d, 0x09, 0x00, 0x04, 0x01, 0x2d, 0x5f, 0x00, + 0x1e, 0x01, 0x2d, 0xc0, 0x31, 0xef, 0x00, 0x00, + 0x02, 0x1c, 0x26, 0x81, 0x3f, 0x00, 0x02, 0x0e, + 0x1e, 0x1f, 0x2a, 0x2c, 0x3f, 0x3a, 0x39, 0x4b, + 0x4c, 0x57, 0x5d, 0x40, 0x8a, 0x92, 0x02, 0x0d, + 0x1e, 0x1f, 0x2a, 0x2c, 0x3f, 0x3a, 0x39, 0x4b, + 0x57, 0x5d, 0x40, 0x8a, 0x92, 0x03, 0x0b, 0x1e, + 0x1f, 0x2a, 0x2c, 0x3f, 0x39, 0x4b, 0x57, 0x40, + 0x8a, 0x92, 0x80, 0x36, 0x00, 0x00, 0x02, 0x0b, + 0x1e, 0x00, 0x00, 0x00, 0x02, 0x1e, 0x8b, 0x39, + 0x00, 0x00, 0x03, 0x3c, 0x42, 0x5b, 0x80, 0x1f, + 0x00, 0x00, 0x02, 0x10, 0x38, 0xc0, 0x13, 0xa1, + 0x00, 0x00, 0x02, 0x04, 0x8e, 0x09, 0x00, 0x00, + 0x02, 0x04, 0x8e, 0x46, 0x00, 0x01, 0x05, 0x0d, + 0x2e, 0x2d, 0x33, 0x3b, 0x80, 0x99, 0x00, 0x04, + 0x06, 0x0d, 0x2e, 0x2d, 0x33, 0x3b, 0x97, 0x09, + 0x00, 0x00, 0x02, 0x33, 0x3b, 0x2c, 0x00, 0x01, + 0x02, 0x33, 0x3b, 0x80, 0xdf, 0x00, 0x02, 0x02, + 0x1b, 0x46, 0x03, 0x00, 0x2c, 0x03, 0x1b, 0x45, + 0x46, 0x02, 0x00, 0x08, 0x02, 0x1b, 0x46, 0x81, + 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x19, 0x8f, 0x84, + 0x00, 0x00, 0x02, 0x28, 0x8b, 0x00, 0x00, 0x00, + 0x02, 0x28, 0x8b, 0x36, 0x00, 0x01, 0x02, 0x28, + 0x8b, 0x8c, 0x12, 0x00, 0x01, 0x02, 0x28, 0x8b, + 0x00, 0x00, 0x00, 0x02, 0x28, 0x8b, 0xc0, 0x5c, + 0x4b, 0x00, 0x03, 0x01, 0x20, 0x96, 0x3b, 0x00, + 0x11, 0x01, 0x2d, 0x9e, 0x5d, 0x00, 0x01, 0x01, + 0x2d, 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[396] = { + 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, 0x83, 0xb9, + 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82, + 0xb9, 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9b, + 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, 0x80, 0x89, + 0x81, 0x9d, 0x81, 0xca, 0x28, 0x00, 0x87, 0x91, + 0x81, 0xbc, 0x01, 0x86, 0x91, 0x80, 0xe2, 0x01, + 0x28, 0x81, 0x8f, 0x80, 0x40, 0xa2, 0x90, 0x8a, + 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, 0x40, 0x8a, 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, 0x42, 0xd7, 0x82, 0xb4, 0x8d, 0xbb, + 0x80, 0xac, 0x88, 0xc6, 0x82, 0xa3, 0x8b, 0x91, + 0x81, 0xb8, 0x82, 0xaf, 0x8c, 0xeb, 0x88, 0x08, + 0x28, 0x40, 0x9f, 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, 0x41, 0x97, 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, + 0x60, 0x50, 0x57, 0x00, 0xb6, 0x33, 0x60, 0x4d, + 0x0a, 0x80, 0x60, 0x23, 0x60, 0x30, 0x90, 0x0e, + 0x01, 0x04, 0x49, 0x1b, 0x80, 0x47, 0xe7, 0x99, + 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Lowercase_table[51] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, + 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x59, + 0xb0, 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, 0x86, 0x81, + 0x43, 0x61, 0x83, +}; + +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[62] = { + 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, 0x60, 0xbb, 0xb4, + 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[17] = { + 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, + 0x41, 0xd1, 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[33] = { + 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44, + 0x3c, 0x80, 0x59, 0x11, 0x80, 0x40, 0xe4, 0x3f, + 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, 0x11, 0x80, + 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b, + 0x84, +}; + +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[436] = { + 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, + 0x10, 0x82, 0x9f, 0x80, 0xcf, 0x01, 0x80, 0x8b, + 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80, + 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, + 0x81, 0x89, 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, + 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80, + 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, + 0xfb, 0x08, 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, + 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, + 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, + 0x03, 0x03, 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, + 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, + 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, + 0x46, 0x52, 0x81, 0xd4, 0x83, 0x45, 0x1c, 0x10, + 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, + 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x3f, 0x87, + 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80, + 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08, + 0x8f, 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, + 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, + 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, + 0x01, 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, + 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, + 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, + 0x34, 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, + 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, + 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, + 0xd5, 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, + 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, + 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, + 0x60, 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, + 0x86, 0x81, 0x43, 0x61, 0x83, 0x60, 0x4d, 0x9f, + 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89, 0x00, 0x00, + 0x09, 0x82, 0xc3, 0x81, 0xe9, 0xa5, 0x86, 0x8b, + 0x24, 0x00, 0x97, 0x04, 0x00, 0x01, 0x01, 0x80, + 0xeb, 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, + 0xa7, 0x8c, 0x82, 0x99, 0x95, 0x94, 0x81, 0x8b, + 0x80, 0x92, 0x03, 0x1a, 0x00, 0x80, 0x40, 0x86, + 0x08, 0x80, 0x9f, 0x99, 0x40, 0x83, 0x15, 0x0d, + 0x0d, 0x0a, 0x16, 0x06, 0x80, 0x88, 0x60, 0xbc, + 0xa6, 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, 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, + 0x42, 0x43, 0x8a, 0x84, 0x9e, 0x80, 0x9f, 0x99, + 0x82, 0xa2, 0x80, 0xee, 0x82, 0x8c, 0xab, 0x83, + 0x88, 0x31, 0x61, 0x05, 0xad, 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[50] = { + 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, + 0x41, 0xda, 0x80, 0x92, 0x80, 0xee, 0x80, 0x60, + 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89, 0x80, 0x40, + 0xa8, 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[350] = { + 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, 0x40, 0xc8, 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, 0x80, 0xfe, 0x80, 0xfe, + 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, 0x44, 0x68, 0x8a, 0x88, + 0x80, 0x41, 0x5a, 0x82, 0x41, 0x38, 0x39, 0x80, + 0xaf, 0x8d, 0xf5, 0x80, 0x8e, 0x80, 0xa5, 0x88, + 0xb5, 0x81, 0x40, 0x89, 0x81, 0xbf, 0x85, 0xd1, + 0x98, 0x18, 0x28, 0x0a, 0xb1, 0xbe, 0xd8, 0x8b, + 0xa4, 0x22, 0x82, 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, 0x40, 0x8b, 0x81, + 0x60, 0x4f, 0x2f, 0x80, 0x43, 0x00, 0x8f, 0x41, + 0x0d, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, + 0x80, 0x42, 0xfb, 0x80, 0x48, 0x03, 0x81, 0x42, + 0x3a, 0x85, 0x42, 0x1d, 0x8a, 0x41, 0x67, 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, 0xa4, + 0x80, 0xd2, 0x80, 0x91, 0x80, 0xd0, 0x80, 0x41, + 0xa4, 0x80, 0x41, 0x01, 0x00, 0x81, 0xd0, 0x80, + 0x60, 0x4d, 0x57, 0x84, 0xba, 0x86, 0x44, 0x57, + 0x90, 0x60, 0x61, 0xc6, 0x12, 0x2f, 0x39, 0x86, + 0x9d, 0x83, 0x4f, 0x81, 0x86, 0x41, 0xb4, 0x83, + 0x45, 0xdf, 0x86, 0xec, 0x10, 0x82, +}; + +static const uint8_t unicode_prop_Extender_table[86] = { + 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, + 0x80, 0x41, 0xb8, 0x80, 0x46, 0x4a, 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, 0x53, 0xeb, + 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[58] = { + 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, + 0x43, 0xc4, 0x59, 0xb5, 0xc9, 0x60, 0x51, 0xef, + 0x60, 0x59, 0x0f, 0x41, 0x6d, 0x81, 0xe9, 0x60, + 0x75, 0x25, 0x57, 0xf7, 0x87, 0x42, 0xf2, 0x60, + 0x26, 0x7c, 0x41, 0x8b, 0x60, 0x4d, 0x03, 0x60, + 0xa6, 0xd6, 0xa8, 0x50, 0x34, 0x8a, 0x40, 0xdd, + 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x4c, 0x1e, + 0x42, 0x1d, +}; + +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[184] = { + 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, + 0x80, 0x40, 0x93, 0x81, 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, 0x40, 0xda, + 0x81, 0xc0, 0x81, 0x43, 0xbb, 0x81, 0x88, 0x82, + 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x41, 0xc4, 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, 0x40, 0xec, 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, + 0x43, 0x02, 0x81, 0xd6, 0x81, 0x41, 0xa3, 0x81, + 0x42, 0xb3, 0x81, 0x60, 0x4b, 0x74, 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[71] = { + 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, +}; + +static const uint8_t unicode_prop_Terminal_Punctuation_table[237] = { + 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, + 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8, + 0x80, 0xc7, 0x80, 0x8d, 0x01, 0x81, 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, 0x40, 0xda, 0x84, 0xbd, 0x81, + 0x43, 0xbb, 0x81, 0x88, 0x82, 0x4d, 0xe3, 0x80, + 0x8c, 0x03, 0x80, 0x89, 0x00, 0x81, 0x41, 0xb0, + 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, + 0x40, 0xec, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, + 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x84, 0xeb, + 0x80, 0x41, 0xa0, 0x82, 0x8c, 0x80, 0x41, 0x65, + 0x1a, 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x43, + 0x02, 0x81, 0xd6, 0x0b, 0x81, 0x41, 0x9d, 0x82, + 0xac, 0x80, 0x42, 0x84, 0x81, 0x45, 0x76, 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[38] = { + 0x60, 0x33, 0xff, 0x59, 0xb5, 0xc9, 0x60, 0x51, + 0xef, 0x60, 0x5a, 0x1d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0x61, 0x05, 0xd5, 0x60, + 0xa6, 0xd6, 0xa8, 0x50, 0x34, 0x8a, 0x40, 0xdd, + 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, +}; + +static const uint8_t unicode_prop_Variation_Selector_table[12] = { + 0x58, 0x0a, 0x82, 0x60, 0xe5, 0xf1, 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[171] = { + 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, + 0x41, 0xdd, 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[236] = { + 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, + 0x0f, 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, 0x08, 0x89, 0x2a, + 0x00, 0x0a, 0x01, 0x87, 0x40, 0xe4, 0x8b, 0x41, + 0x20, 0xad, 0x80, 0x89, 0x80, 0xaa, 0x03, 0x82, + 0xa8, 0x0d, 0x82, 0x9c, 0x81, 0xb2, 0xef, 0x1b, + 0x14, 0x82, 0x8c, 0x85, +}; + +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[63] = { + 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, 0x41, 0x07, 0x3d, 0x80, 0x88, 0x89, 0x0a, + 0xf5, 0x08, 0x08, 0x80, 0x90, 0x10, 0x8c, +}; + +static const uint8_t unicode_prop_Emoji_Presentation_table[143] = { + 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, + 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b, + 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, + 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, + 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00, + 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, + 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, + 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd, + 0x01, 0x89, 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, 0x08, 0x94, 0x0e, 0x86, 0x40, + 0xe4, 0x8b, 0x41, 0x20, 0xad, 0x80, 0x89, 0x80, + 0xaa, 0x03, 0x82, 0xa8, 0x0d, 0x82, 0x9c, 0x81, + 0xb2, 0xef, 0x1b, 0x14, 0x82, 0x8c, 0x85, +}; + +static const uint8_t unicode_prop_Extended_Pictographic_table[152] = { + 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, 0x46, 0xb6, +}; + +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, + 0x83, 0x47, 0xfb, 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" "\0" + "Emoji_Modifier" "\0" + "Emoji_Modifier_Base" "\0" + "Emoji_Presentation" "\0" + "Extended_Pictographic" "\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 */ diff --git a/libunicode.c b/libunicode.c new file mode 100644 index 0000000..96ff002 --- /dev/null +++ b/libunicode.c @@ -0,0 +1,1538 @@ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "libunicode.h" +#include "libunicode-table.h" + +enum { + RUN_TYPE_U, + RUN_TYPE_L, + RUN_TYPE_UF, + RUN_TYPE_LF, + RUN_TYPE_UL, + RUN_TYPE_LSU, + RUN_TYPE_U2L_399_EXT2, + RUN_TYPE_UF_D20, + RUN_TYPE_UF_D1_EXT, + RUN_TYPE_U_EXT, + RUN_TYPE_LF_EXT, + RUN_TYPE_U_EXT2, + RUN_TYPE_L_EXT2, + RUN_TYPE_U_EXT3, +}; + +/* conv_type: + 0 = to upper + 1 = to lower + 2 = case folding (= to lower with modifications) +*/ +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) +{ + if (c < 128) { + if (conv_type) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + } else { + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } + } else { + uint32_t v, code, data, type, len, a, is_lower; + int idx, idx_min, idx_max; + + is_lower = (conv_type != 0); + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + data = ((v & 0xf) << 8) | case_conv_table2[idx]; + switch(type) { + case RUN_TYPE_U: + case RUN_TYPE_L: + case RUN_TYPE_UF: + case RUN_TYPE_LF: + if (conv_type == (type & 1) || + (type >= RUN_TYPE_UF && conv_type == 2)) { + c = c - code + (case_conv_table1[data] >> (32 - 17)); + } + break; + case RUN_TYPE_UL: + a = c - code; + if ((a & 1) != (1 - is_lower)) + break; + c = (a ^ 1) + code; + break; + case RUN_TYPE_LSU: + a = c - code; + if (a == 1) { + c += 2 * is_lower - 1; + } else if (a == (1 - is_lower) * 2) { + c += (2 * is_lower - 1) * 2; + } + break; + case RUN_TYPE_U2L_399_EXT2: + if (!is_lower) { + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = 0x399; + return 2; + } else { + c = c - code + case_conv_ext[data & 0x3f]; + } + break; + case RUN_TYPE_UF_D20: + if (conv_type == 1) + break; + c = data + (conv_type == 2) * 0x20; + break; + case RUN_TYPE_UF_D1_EXT: + if (conv_type == 1) + break; + c = case_conv_ext[data] + (conv_type == 2); + break; + case RUN_TYPE_U_EXT: + case RUN_TYPE_LF_EXT: + if (is_lower != (type - RUN_TYPE_U_EXT)) + break; + c = case_conv_ext[data]; + break; + case RUN_TYPE_U_EXT2: + case RUN_TYPE_L_EXT2: + if (conv_type != (type - RUN_TYPE_U_EXT2)) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + return 2; + default: + case RUN_TYPE_U_EXT3: + if (conv_type != 0) + break; + res[0] = case_conv_ext[data >> 8]; + res[1] = case_conv_ext[(data >> 4) & 0xf]; + res[2] = case_conv_ext[data & 0xf]; + return 3; + } + break; + } + } + } + res[0] = c; + return 1; +} + +static uint32_t get_le24(const uint8_t *ptr) +{ +#if defined(__x86__) || defined(__x86_64__) + return *(uint16_t *)ptr | (ptr[2] << 16); +#else + return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); +#endif +} + +#define UNICODE_INDEX_BLOCK_LEN 32 + +/* return -1 if not in table, otherwise the offset in the block */ +static int get_index_pos(uint32_t *pcode, uint32_t c, + const uint8_t *index_table, int index_table_len) +{ + uint32_t code, v; + int idx_min, idx_max, idx; + + idx_min = 0; + v = get_le24(index_table); + code = v & ((1 << 21) - 1); + if (c < code) { + *pcode = 0; + return 0; + } + idx_max = index_table_len - 1; + code = get_le24(index_table + idx_max * 3); + if (c >= code) + return -1; + /* invariant: tab[idx_min] <= c < tab2[idx_max] */ + while ((idx_max - idx_min) > 1) { + idx = (idx_max + idx_min) / 2; + v = get_le24(index_table + idx * 3); + code = v & ((1 << 21) - 1); + if (c < code) { + idx_max = idx; + } else { + idx_min = idx; + } + } + v = get_le24(index_table + idx_min * 3); + *pcode = v & ((1 << 21) - 1); + return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21); +} + +static BOOL lre_is_in_table(uint32_t c, const uint8_t *table, + const uint8_t *index_table, int index_table_len) +{ + uint32_t code, b, bit; + int pos; + const uint8_t *p; + + pos = get_index_pos(&code, c, index_table, index_table_len); + if (pos < 0) + return FALSE; /* outside the table */ + p = table + pos; + bit = 0; + for(;;) { + b = *p++; + if (b < 64) { + code += (b >> 3) + 1; + if (c < code) + return bit; + bit ^= 1; + code += (b & 7) + 1; + } else if (b >= 0x80) { + code += b - 0x80 + 1; + } else if (b < 0x60) { + code += (((b - 0x40) << 8) | p[0]) + 1; + p++; + } else { + code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; + p += 2; + } + if (c < code) + return bit; + bit ^= 1; + } +} + +BOOL lre_is_cased(uint32_t c) +{ + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return TRUE; + } + } + return lre_is_in_table(c, unicode_prop_Cased1_table, + unicode_prop_Cased1_index, + sizeof(unicode_prop_Cased1_index) / 3); +} + +BOOL lre_is_case_ignorable(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_Case_Ignorable_table, + unicode_prop_Case_Ignorable_index, + sizeof(unicode_prop_Case_Ignorable_index) / 3); +} + +/* character range */ + +static __maybe_unused void cr_dump(CharRange *cr) +{ + int i; + for(i = 0; i < cr->len; i++) + printf("%d: 0x%04x\n", i, cr->points[i]); +} + +static void *cr_default_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func) +{ + cr->len = cr->size = 0; + cr->points = NULL; + cr->mem_opaque = mem_opaque; + cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc; +} + +void cr_free(CharRange *cr) +{ + cr->realloc_func(cr->mem_opaque, cr->points, 0); +} + +int cr_realloc(CharRange *cr, int size) +{ + int new_size; + uint32_t *new_buf; + + if (size > cr->size) { + new_size = max_int(size, cr->size * 3 / 2); + new_buf = cr->realloc_func(cr->mem_opaque, cr->points, + new_size * sizeof(cr->points[0])); + if (!new_buf) + return -1; + cr->points = new_buf; + cr->size = new_size; + } + return 0; +} + +int cr_copy(CharRange *cr, const CharRange *cr1) +{ + if (cr_realloc(cr, cr1->len)) + return -1; + memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len); + cr->len = cr1->len; + return 0; +} + +/* merge consecutive intervals and remove empty intervals */ +static void cr_compress(CharRange *cr) +{ + int i, j, k, len; + uint32_t *pt; + + pt = cr->points; + len = cr->len; + i = 0; + j = 0; + k = 0; + while ((i + 1) < len) { + if (pt[i] == pt[i + 1]) { + /* empty interval */ + i += 2; + } else { + j = i; + while ((j + 3) < len && pt[j + 1] == pt[j + 2]) + j += 2; + /* just copy */ + pt[k] = pt[i]; + pt[k + 1] = pt[j + 1]; + k += 2; + i = j + 2; + } + } + cr->len = k; +} + +/* union or intersection */ +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op) +{ + int a_idx, b_idx, is_in; + uint32_t v; + + a_idx = 0; + b_idx = 0; + for(;;) { + /* get one more point from a or b in increasing order */ + if (a_idx < a_len && b_idx < b_len) { + if (a_pt[a_idx] < b_pt[b_idx]) { + goto a_add; + } else if (a_pt[a_idx] == b_pt[b_idx]) { + v = a_pt[a_idx]; + a_idx++; + b_idx++; + } else { + goto b_add; + } + } else if (a_idx < a_len) { + a_add: + v = a_pt[a_idx++]; + } else if (b_idx < b_len) { + b_add: + v = b_pt[b_idx++]; + } else { + break; + } + /* add the point if the in/out status changes */ + switch(op) { + case CR_OP_UNION: + is_in = (a_idx & 1) | (b_idx & 1); + break; + case CR_OP_INTER: + is_in = (a_idx & 1) & (b_idx & 1); + break; + case CR_OP_XOR: + is_in = (a_idx & 1) ^ (b_idx & 1); + break; + default: + abort(); + } + if (is_in != (cr->len & 1)) { + if (cr_add_point(cr, v)) + return -1; + } + } + cr_compress(cr); + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len) +{ + CharRange a = *cr; + int ret; + cr->len = 0; + cr->size = 0; + cr->points = NULL; + ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION); + cr_free(&a); + return ret; +} + +int cr_invert(CharRange *cr) +{ + int len; + len = cr->len; + if (cr_realloc(cr, len + 2)) + return -1; + memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0])); + cr->points[0] = 0; + cr->points[len + 1] = UINT32_MAX; + cr->len = len + 2; + cr_compress(cr); + return 0; +} + +#ifdef CONFIG_ALL_UNICODE + +BOOL lre_is_id_start(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_ID_Start_table, + unicode_prop_ID_Start_index, + sizeof(unicode_prop_ID_Start_index) / 3); +} + +BOOL lre_is_id_continue(uint32_t c) +{ + return lre_is_id_start(c) || + lre_is_in_table(c, unicode_prop_ID_Continue1_table, + unicode_prop_ID_Continue1_index, + sizeof(unicode_prop_ID_Continue1_index) / 3); +} + +#define UNICODE_DECOMP_LEN_MAX 18 + +typedef enum { + DECOMP_TYPE_C1, /* 16 bit char */ + DECOMP_TYPE_L1, /* 16 bit char table */ + DECOMP_TYPE_L2, + DECOMP_TYPE_L3, + DECOMP_TYPE_L4, + DECOMP_TYPE_L5, /* XXX: not used */ + DECOMP_TYPE_L6, /* XXX: could remove */ + DECOMP_TYPE_L7, /* XXX: could remove */ + DECOMP_TYPE_LL1, /* 18 bit char table */ + DECOMP_TYPE_LL2, + DECOMP_TYPE_S1, /* 8 bit char table */ + DECOMP_TYPE_S2, + DECOMP_TYPE_S3, + DECOMP_TYPE_S4, + DECOMP_TYPE_S5, + DECOMP_TYPE_I1, /* increment 16 bit char value */ + DECOMP_TYPE_I2_0, + DECOMP_TYPE_I2_1, + DECOMP_TYPE_I3_1, + DECOMP_TYPE_I3_2, + DECOMP_TYPE_I4_1, + DECOMP_TYPE_I4_2, + DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ + DECOMP_TYPE_B2, + DECOMP_TYPE_B3, + DECOMP_TYPE_B4, + DECOMP_TYPE_B5, + DECOMP_TYPE_B6, + DECOMP_TYPE_B7, + DECOMP_TYPE_B8, + DECOMP_TYPE_B18, + DECOMP_TYPE_LS2, + DECOMP_TYPE_PAT3, + DECOMP_TYPE_S2_UL, + DECOMP_TYPE_LS2_UL, +} DecompTypeEnum; + +static uint32_t unicode_get_short_code(uint32_t c) +{ + static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; + + if (c < 0x80) + return c; + else if (c < 0x80 + 0x50) + return c - 0x80 + 0x300; + else + return unicode_short_table[c - 0x80 - 0x50]; +} + +static uint32_t unicode_get_lower_simple(uint32_t c) +{ + if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) + c += 0x20; + else + c++; + return c; +} + +static uint16_t unicode_get16(const uint8_t *p) +{ + return p[0] | (p[1] << 8); +} + +static int unicode_decomp_entry(uint32_t *res, uint32_t c, + int idx, uint32_t code, uint32_t len, + uint32_t type) +{ + uint32_t c1; + int l, i, p; + const uint8_t *d; + + if (type == DECOMP_TYPE_C1) { + res[0] = unicode_decomp_table2[idx]; + return 1; + } else { + d = unicode_decomp_data + unicode_decomp_table2[idx]; + switch(type) { + case DECOMP_TYPE_L1 ... DECOMP_TYPE_L7: + l = type - DECOMP_TYPE_L1 + 1; + d += (c - code) * l * 2; + for(i = 0; i < l; i++) { + if ((res[i] = unicode_get16(d + 2 * i)) == 0) + return 0; + } + return l; + case DECOMP_TYPE_LL1 ... DECOMP_TYPE_LL2: + { + uint32_t k, p; + l = type - DECOMP_TYPE_LL1 + 1; + k = (c - code) * l; + p = len * l * 2; + for(i = 0; i < l; i++) { + c1 = unicode_get16(d + 2 * k) | + (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16); + if (!c1) + return 0; + res[i] = c1; + k++; + } + } + return l; + case DECOMP_TYPE_S1 ... DECOMP_TYPE_S5: + l = type - DECOMP_TYPE_S1 + 1; + d += (c - code) * l; + for(i = 0; i < l; i++) { + if ((res[i] = unicode_get_short_code(d[i])) == 0) + return 0; + } + return l; + case DECOMP_TYPE_I1: + l = 1; + p = 0; + goto decomp_type_i; + case DECOMP_TYPE_I2_0: + case DECOMP_TYPE_I2_1: + case DECOMP_TYPE_I3_1: + case DECOMP_TYPE_I3_2: + case DECOMP_TYPE_I4_1: + case DECOMP_TYPE_I4_2: + l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1); + p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2); + decomp_type_i: + for(i = 0; i < l; i++) { + c1 = unicode_get16(d + 2 * i); + if (i == p) + c1 += c - code; + res[i] = c1; + } + return l; + case DECOMP_TYPE_B18: + l = 18; + goto decomp_type_b; + case DECOMP_TYPE_B1 ... DECOMP_TYPE_B8: + l = type - DECOMP_TYPE_B1 + 1; + decomp_type_b: + { + uint32_t c_min; + c_min = unicode_get16(d); + d += 2 + (c - code) * l; + for(i = 0; i < l; i++) { + c1 = d[i]; + if (c1 == 0xff) + c1 = 0x20; + else + c1 += c_min; + res[i] = c1; + } + } + return l; + case DECOMP_TYPE_LS2: + d += (c - code) * 3; + if (!(res[0] = unicode_get16(d))) + return 0; + res[1] = unicode_get_short_code(d[2]); + return 2; + case DECOMP_TYPE_PAT3: + res[0] = unicode_get16(d); + res[2] = unicode_get16(d + 2); + d += 4 + (c - code) * 2; + res[1] = unicode_get16(d); + return 3; + case DECOMP_TYPE_S2_UL: + case DECOMP_TYPE_LS2_UL: + c1 = c - code; + if (type == DECOMP_TYPE_S2_UL) { + d += c1 & ~1; + c = unicode_get_short_code(*d); + d++; + } else { + d += (c1 >> 1) * 3; + c = unicode_get16(d); + d += 2; + } + if (c1 & 1) + c = unicode_get_lower_simple(c); + res[0] = c; + res[1] = unicode_get_short_code(*d); + return 2; + } + } + return 0; +} + + +/* return the length of the decomposition (length <= + UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */ +static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1) +{ + uint32_t v, type, is_compat, code, len; + int idx_min, idx_max, idx; + + idx_min = 0; + idx_max = countof(unicode_decomp_table1) - 1; + while (idx_min <= idx_max) { + idx = (idx_max + idx_min) / 2; + v = unicode_decomp_table1[idx]; + code = v >> (32 - 18); + len = (v >> (32 - 18 - 7)) & 0x7f; + // printf("idx=%d code=%05x len=%d\n", idx, code, len); + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + is_compat = v & 1; + if (is_compat1 < is_compat) + break; + type = (v >> (32 - 18 - 7 - 6)) & 0x3f; + return unicode_decomp_entry(res, c, idx, code, len, type); + } + } + return 0; +} + +/* return 0 if no pair found */ +static int unicode_compose_pair(uint32_t c0, uint32_t c1) +{ + uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; + int idx_min, idx_max, idx, d; + uint32_t pair[2]; + + idx_min = 0; + idx_max = countof(unicode_comp_table) - 1; + while (idx_min <= idx_max) { + idx = (idx_max + idx_min) / 2; + idx1 = unicode_comp_table[idx]; + + /* idx1 represent an entry of the decomposition table */ + d_idx = idx1 >> 6; + d_offset = idx1 & 0x3f; + v = unicode_decomp_table1[d_idx]; + code = v >> (32 - 18); + len = (v >> (32 - 18 - 7)) & 0x7f; + type = (v >> (32 - 18 - 7 - 6)) & 0x3f; + ch = code + d_offset; + unicode_decomp_entry(pair, ch, d_idx, code, len, type); + d = c0 - pair[0]; + if (d == 0) + d = c1 - pair[1]; + if (d < 0) { + idx_max = idx - 1; + } else if (d > 0) { + idx_min = idx + 1; + } else { + return ch; + } + } + return 0; +} + +/* return the combining class of character c (between 0 and 255) */ +static int unicode_get_cc(uint32_t c) +{ + uint32_t code, n, type, cc, c1, b; + int pos; + const uint8_t *p; + + pos = get_index_pos(&code, c, + unicode_cc_index, sizeof(unicode_cc_index) / 3); + if (pos < 0) + return 0; + p = unicode_cc_table + pos; + for(;;) { + b = *p++; + type = b >> 6; + n = b & 0x3f; + if (n < 48) { + } else if (n < 56) { + n = (n - 48) << 8; + n |= *p++; + n += 48; + } else { + n = (n - 56) << 8; + n |= *p++ << 8; + n |= *p++; + n += 48 + (1 << 11); + } + if (type <= 1) + p++; + c1 = code + n + 1; + if (c < c1) { + switch(type) { + case 0: + cc = p[-1]; + break; + case 1: + cc = p[-1] + c - code; + break; + case 2: + cc = 0; + break; + default: + case 3: + cc = 230; + break; + } + return cc; + } + code = c1; + } +} + +static void sort_cc(int *buf, int len) +{ + int i, j, k, cc, cc1, start, ch1; + + for(i = 0; i < len; i++) { + cc = unicode_get_cc(buf[i]); + if (cc != 0) { + start = i; + j = i + 1; + while (j < len) { + ch1 = buf[j]; + cc1 = unicode_get_cc(ch1); + if (cc1 == 0) + break; + k = j - 1; + while (k >= start) { + if (unicode_get_cc(buf[k]) <= cc1) + break; + buf[k + 1] = buf[k]; + k--; + } + buf[k + 1] = ch1; + j++; + } +#if 0 + printf("cc:"); + for(k = start; k < j; k++) { + printf(" %3d", unicode_get_cc(buf[k])); + } + printf("\n"); +#endif + i = j; + } + } +} + +static void to_nfd_rec(DynBuf *dbuf, + const int *src, int src_len, int is_compat) +{ + uint32_t c, v; + int i, l; + uint32_t res[UNICODE_DECOMP_LEN_MAX]; + + for(i = 0; i < src_len; i++) { + c = src[i]; + if (c >= 0xac00 && c < 0xd7a4) { + /* Hangul decomposition */ + c -= 0xac00; + dbuf_put_u32(dbuf, 0x1100 + c / 588); + dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28); + v = c % 28; + if (v != 0) + dbuf_put_u32(dbuf, 0x11a7 + v); + } else { + l = unicode_decomp_char(res, c, is_compat); + if (l) { + to_nfd_rec(dbuf, (int *)res, l, is_compat); + } else { + dbuf_put_u32(dbuf, c); + } + } + } +} + +/* return 0 if not found */ +static int compose_pair(uint32_t c0, uint32_t c1) +{ + /* Hangul composition */ + if (c0 >= 0x1100 && c0 < 0x1100 + 19 && + c1 >= 0x1161 && c1 < 0x1161 + 21) { + return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28; + } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 && + (c0 - 0xac00) % 28 == 0 && + c1 >= 0x11a7 && c1 < 0x11a7 + 28) { + return c0 + c1 - 0x11a7; + } else { + return unicode_compose_pair(c0, c1); + } +} + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, DynBufReallocFunc *realloc_func) +{ + int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; + BOOL is_compat; + DynBuf dbuf_s, *dbuf = &dbuf_s; + + is_compat = n_type >> 1; + + dbuf_init2(dbuf, opaque, realloc_func); + if (dbuf_realloc(dbuf, sizeof(int) * src_len)) + goto fail; + + /* common case: latin1 is unaffected by NFC */ + if (n_type == UNICODE_NFC) { + for(i = 0; i < src_len; i++) { + if (src[i] >= 0x100) + goto not_latin1; + } + buf = (int *)dbuf->buf; + memcpy(buf, src, src_len * sizeof(int)); + *pdst = (uint32_t *)buf; + return src_len; + not_latin1: ; + } + + to_nfd_rec(dbuf, (const int *)src, src_len, is_compat); + if (dbuf_error(dbuf)) { + fail: + *pdst = NULL; + return -1; + } + buf = (int *)dbuf->buf; + buf_len = dbuf->size / sizeof(int); + + sort_cc(buf, buf_len); + + if (buf_len <= 1 || (n_type & 1) != 0) { + /* NFD / NFKD */ + *pdst = (uint32_t *)buf; + return buf_len; + } + + i = 1; + out_len = 1; + while (i < buf_len) { + /* find the starter character and test if it is blocked from + the character at 'i' */ + last_cc = unicode_get_cc(buf[i]); + starter_pos = out_len - 1; + while (starter_pos >= 0) { + cc = unicode_get_cc(buf[starter_pos]); + if (cc == 0) + break; + if (cc >= last_cc) + goto next; + last_cc = 256; + starter_pos--; + } + if (starter_pos >= 0 && + (p = compose_pair(buf[starter_pos], buf[i])) != 0) { + buf[starter_pos] = p; + i++; + } else { + next: + buf[out_len++] = buf[i++]; + } + } + *pdst = (uint32_t *)buf; + return out_len; +} + +/* char ranges for various unicode properties */ + +static int unicode_find_name(const char *name_table, const char *name) +{ + const char *p, *r; + int pos; + size_t name_len, len; + + p = name_table; + pos = 0; + name_len = strlen(name); + while (*p) { + for(;;) { + r = strchr(p, ','); + if (!r) + len = strlen(p); + else + len = r - p; + if (len == name_len && !memcmp(p, name, name_len)) + return pos; + p += len + 1; + if (!r) + break; + } + pos++; + } + return -1; +} + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_script(CharRange *cr, + const char *script_name, BOOL is_ext) +{ + int script_idx; + const uint8_t *p, *p_end; + uint32_t c, c1, b, n, v, v_len, i, type; + CharRange cr1_s, *cr1; + CharRange cr2_s, *cr2 = &cr2_s; + BOOL is_common; + + script_idx = unicode_find_name(unicode_script_name_table, script_name); + if (script_idx < 0) + return -2; + /* Note: we remove the "Unknown" Script */ + script_idx += UNICODE_SCRIPT_Unknown + 1; + + is_common = (script_idx == UNICODE_SCRIPT_Common || + script_idx == UNICODE_SCRIPT_Inherited); + if (is_ext) { + cr1 = &cr1_s; + cr_init(cr1, cr->mem_opaque, cr->realloc_func); + cr_init(cr2, cr->mem_opaque, cr->realloc_func); + } else { + cr1 = cr; + } + + p = unicode_script_table; + p_end = unicode_script_table + countof(unicode_script_table); + c = 0; + while (p < p_end) { + b = *p++; + type = b >> 7; + n = b & 0x7f; + if (n < 96) { + } else if (n < 112) { + n = (n - 96) << 8; + n |= *p++; + n += 96; + } else { + n = (n - 112) << 16; + n |= *p++ << 8; + n |= *p++; + n += 96 + (1 << 12); + } + if (type == 0) + v = 0; + else + v = *p++; + c1 = c + n + 1; + if (v == script_idx) { + if (cr_add_interval(cr1, c, c1)) + goto fail; + } + c = c1; + } + + if (is_ext) { + /* add the script extensions */ + p = unicode_script_ext_table; + p_end = unicode_script_ext_table + countof(unicode_script_ext_table); + c = 0; + while (p < p_end) { + b = *p++; + if (b < 128) { + n = b; + } else if (b < 128 + 64) { + n = (b - 128) << 8; + n |= *p++; + n += 128; + } else { + n = (b - 128 - 64) << 16; + n |= *p++ << 8; + n |= *p++; + n += 128 + (1 << 14); + } + c1 = c + n + 1; + v_len = *p++; + if (is_common) { + if (v_len != 0) { + if (cr_add_interval(cr2, c, c1)) + goto fail; + } + } else { + for(i = 0; i < v_len; i++) { + if (p[i] == script_idx) { + if (cr_add_interval(cr2, c, c1)) + goto fail; + break; + } + } + } + p += v_len; + c = c1; + } + if (is_common) { + /* remove all the characters with script extensions */ + if (cr_invert(cr2)) + goto fail; + if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, + CR_OP_INTER)) + goto fail; + } else { + if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, + CR_OP_UNION)) + goto fail; + } + cr_free(cr1); + cr_free(cr2); + } + return 0; + fail: + if (is_ext) { + cr_free(cr1); + cr_free(cr2); + } + goto fail; +} + +#define M(id) (1U << UNICODE_GC_ ## id) + +static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) +{ + const uint8_t *p, *p_end; + uint32_t c, c0, b, n, v; + + p = unicode_gc_table; + p_end = unicode_gc_table + countof(unicode_gc_table); + c = 0; + while (p < p_end) { + b = *p++; + n = b >> 5; + v = b & 0x1f; + if (n == 7) { + n = *p++; + if (n < 128) { + n += 7; + } else if (n < 128 + 64) { + n = (n - 128) << 8; + n |= *p++; + n += 7 + 128; + } else { + n = (n - 128 - 64) << 16; + n |= *p++ << 8; + n |= *p++; + n += 7 + 128 + (1 << 14); + } + } + c0 = c; + c += n + 1; + if (v == 31) { + /* run of Lu / Ll */ + b = gc_mask & (M(Lu) | M(Ll)); + if (b != 0) { + if (b == (M(Lu) | M(Ll))) { + goto add_range; + } else { + c0 += ((gc_mask & M(Ll)) != 0); + for(; c0 < c; c0 += 2) { + if (cr_add_interval(cr, c0, c0 + 1)) + return -1; + } + } + } + } else if ((gc_mask >> v) & 1) { + add_range: + if (cr_add_interval(cr, c0, c)) + return -1; + } + } + return 0; +} + +static int unicode_prop1(CharRange *cr, int prop_idx) +{ + const uint8_t *p, *p_end; + uint32_t c, c0, b, bit; + + p = unicode_prop_table[prop_idx]; + p_end = p + unicode_prop_len_table[prop_idx]; + c = 0; + bit = 0; + while (p < p_end) { + c0 = c; + b = *p++; + if (b < 64) { + c += (b >> 3) + 1; + if (bit) { + if (cr_add_interval(cr, c0, c)) + return -1; + } + bit ^= 1; + c0 = c; + c += (b & 7) + 1; + } else if (b >= 0x80) { + c += b - 0x80 + 1; + } else if (b < 0x60) { + c += (((b - 0x40) << 8) | p[0]) + 1; + p++; + } else { + c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; + p += 2; + } + if (bit) { + if (cr_add_interval(cr, c0, c)) + return -1; + } + bit ^= 1; + } + return 0; +} + +#define CASE_U (1 << 0) +#define CASE_L (1 << 1) +#define CASE_F (1 << 2) + +/* use the case conversion table to generate range of characters. + CASE_U: set char if modified by uppercasing, + CASE_L: set char if modified by lowercasing, + CASE_F: set char if modified by case folding, + */ +static int unicode_case1(CharRange *cr, int case_mask) +{ +#define MR(x) (1 << RUN_TYPE_ ## x) + const uint32_t tab_run_mask[3] = { + MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | + MR(UF_D1_EXT) | MR(U_EXT) | MR(U_EXT2) | MR(U_EXT3), + + MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2), + + MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT), + }; +#undef MR + uint32_t mask, v, code, type, len, i, idx; + + if (case_mask == 0) + return 0; + mask = 0; + for(i = 0; i < 3; i++) { + if ((case_mask >> i) & 1) + mask |= tab_run_mask[i]; + } + for(idx = 0; idx < countof(case_conv_table1); idx++) { + v = case_conv_table1[idx]; + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if ((mask >> type) & 1) { + // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); + switch(type) { + case RUN_TYPE_UL: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + code += ((case_mask & CASE_U) != 0); + for(i = 0; i < len; i += 2) { + if (cr_add_interval(cr, code + i, code + i + 1)) + return -1; + } + break; + case RUN_TYPE_LSU: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + if (!(case_mask & CASE_U)) { + if (cr_add_interval(cr, code, code + 1)) + return -1; + } + if (cr_add_interval(cr, code + 1, code + 2)) + return -1; + if (case_mask & CASE_U) { + if (cr_add_interval(cr, code + 2, code + 3)) + return -1; + } + break; + default: + def_case: + if (cr_add_interval(cr, code, code + len)) + return -1; + break; + } + } + } + return 0; +} + +typedef enum { + POP_GC, + POP_PROP, + POP_CASE, + POP_UNION, + POP_INTER, + POP_XOR, + POP_INVERT, + POP_END, +} PropOPEnum; + +#define POP_STACK_LEN_MAX 4 + +static int unicode_prop_ops(CharRange *cr, ...) +{ + va_list ap; + CharRange stack[POP_STACK_LEN_MAX]; + int stack_len, op, ret, i; + uint32_t a; + + va_start(ap, cr); + stack_len = 0; + for(;;) { + op = va_arg(ap, int); + switch(op) { + case POP_GC: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_general_category1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_PROP: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_prop1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_CASE: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_case1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_UNION: + case POP_INTER: + case POP_XOR: + { + CharRange *cr1, *cr2, *cr3; + assert(stack_len >= 2); + assert(stack_len < POP_STACK_LEN_MAX); + cr1 = &stack[stack_len - 2]; + cr2 = &stack[stack_len - 1]; + cr3 = &stack[stack_len++]; + cr_init(cr3, cr->mem_opaque, cr->realloc_func); + if (cr_op(cr3, cr1->points, cr1->len, + cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) + goto fail; + cr_free(cr1); + cr_free(cr2); + *cr1 = *cr3; + stack_len -= 2; + } + break; + case POP_INVERT: + assert(stack_len >= 1); + if (cr_invert(&stack[stack_len - 1])) + goto fail; + break; + case POP_END: + goto done; + default: + abort(); + } + } + done: + assert(stack_len == 1); + ret = cr_copy(cr, &stack[0]); + cr_free(&stack[0]); + return ret; + fail: + for(i = 0; i < stack_len; i++) + cr_free(&stack[i]); + return -1; +} + +static const uint32_t unicode_gc_mask_table[] = { + M(Lu) | M(Ll) | M(Lt), /* LC */ + M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */ + M(Mn) | M(Mc) | M(Me), /* M */ + M(Nd) | M(Nl) | M(No), /* N */ + M(Sm) | M(Sc) | M(Sk) | M(So), /* S */ + M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */ + M(Zs) | M(Zl) | M(Zp), /* Z */ + M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */ +}; + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_general_category(CharRange *cr, const char *gc_name) +{ + int gc_idx; + uint32_t gc_mask; + + gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); + if (gc_idx < 0) + return -2; + if (gc_idx <= UNICODE_GC_Co) { + gc_mask = (uint64_t)1 << gc_idx; + } else { + gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC]; + } + return unicode_general_category1(cr, gc_mask); +} + + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_prop(CharRange *cr, const char *prop_name) +{ + int prop_idx, ret; + + prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); + if (prop_idx < 0) + return -2; + prop_idx += UNICODE_PROP_ASCII_Hex_Digit; + + ret = 0; + switch(prop_idx) { + case UNICODE_PROP_ASCII: + if (cr_add_interval(cr, 0x00, 0x7f + 1)) + return -1; + break; + case UNICODE_PROP_Any: + if (cr_add_interval(cr, 0x00000, 0x10ffff + 1)) + return -1; + break; + case UNICODE_PROP_Assigned: + ret = unicode_prop_ops(cr, + POP_GC, M(Cn), + POP_INVERT, + POP_END); + break; + case UNICODE_PROP_Math: + ret = unicode_prop_ops(cr, + POP_GC, M(Sm), + POP_PROP, UNICODE_PROP_Other_Math, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Lowercase: + ret = unicode_prop_ops(cr, + POP_GC, M(Ll), + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Uppercase: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Cased: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Alphabetic: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Alphabetic, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Grapheme_Base: + ret = unicode_prop_ops(cr, + POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn), + POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, + POP_UNION, + POP_INVERT, + POP_END); + break; + case UNICODE_PROP_Grapheme_Extend: + ret = unicode_prop_ops(cr, + POP_GC, M(Me) | M(Mn), + POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_XID_Start: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_PROP, UNICODE_PROP_XID_Start1, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_XID_Continue: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | + M(Mn) | M(Mc) | M(Nd) | M(Pc), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_ID_Continue, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_PROP, UNICODE_PROP_XID_Continue1, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_Changes_When_Uppercased: + ret = unicode_case1(cr, CASE_U); + break; + case UNICODE_PROP_Changes_When_Lowercased: + ret = unicode_case1(cr, CASE_L); + break; + case UNICODE_PROP_Changes_When_Casemapped: + ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F); + break; + case UNICODE_PROP_Changes_When_Titlecased: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_U, + POP_PROP, UNICODE_PROP_Changes_When_Titlecased1, + POP_XOR, + POP_END); + break; + case UNICODE_PROP_Changes_When_Casefolded: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_F, + POP_PROP, UNICODE_PROP_Changes_When_Casefolded1, + POP_XOR, + POP_END); + break; + case UNICODE_PROP_Changes_When_NFKC_Casefolded: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_F, + POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1, + POP_XOR, + POP_END); + break; +#if 0 + case UNICODE_PROP_ID_Start: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_ID_Continue: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | + M(Mn) | M(Mc) | M(Nd) | M(Pc), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_ID_Continue, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_Case_Ignorable: + ret = unicode_prop_ops(cr, + POP_GC, M(Mn) | M(Cf) | M(Lm) | M(Sk), + POP_PROP, UNICODE_PROP_Case_Ignorable1, + POP_XOR, + POP_END); + break; +#else + /* we use the existing tables */ + case UNICODE_PROP_ID_Continue: + ret = unicode_prop_ops(cr, + POP_PROP, UNICODE_PROP_ID_Start, + POP_PROP, UNICODE_PROP_ID_Continue1, + POP_XOR, + POP_END); + break; +#endif + default: + if (prop_idx >= countof(unicode_prop_table)) + return -2; + ret = unicode_prop1(cr, prop_idx); + break; + } + return ret; +} + +#endif /* CONFIG_ALL_UNICODE */ diff --git a/libunicode.h b/libunicode.h new file mode 100644 index 0000000..cfa600a --- /dev/null +++ b/libunicode.h @@ -0,0 +1,124 @@ +/* + * 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 + +#define LRE_BOOL int /* for documentation purposes */ + +/* define it to include all the unicode tables (40KB larger) */ +#define CONFIG_ALL_UNICODE + +#define LRE_CC_RES_LEN_MAX 3 + +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; + +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +LRE_BOOL lre_is_cased(uint32_t c); +LRE_BOOL lre_is_case_ignorable(uint32_t c); + +/* char ranges */ + +typedef struct { + 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); + +#ifdef CONFIG_ALL_UNICODE + +LRE_BOOL lre_is_id_start(uint32_t c); +LRE_BOOL lre_is_id_continue(uint32_t c); + +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, LRE_BOOL is_ext); +int unicode_general_category(CharRange *cr, const char *gc_name); +int unicode_prop(CharRange *cr, const char *prop_name); + +#endif /* CONFIG_ALL_UNICODE */ + +#undef LRE_BOOL + +#endif /* LIBUNICODE_H */ diff --git a/list.h b/list.h new file mode 100644 index 0000000..0a1bc5a --- /dev/null +++ b/list.h @@ -0,0 +1,100 @@ +/* + * 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 +#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) \ + ((type *)((uint8_t *)(el) - offsetof(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/qjs.c b/qjs.c new file mode 100644 index 0000000..e04552a --- /dev/null +++ b/qjs.c @@ -0,0 +1,520 @@ +/* + * QuickJS stand alone interpreter + * + * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2020 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#elif defined(__linux__) +#include +#endif + +#include "cutils.h" +#include "quickjs-libc.h" + +/* enable bignums */ +#define CONFIG_BIGNUM + +extern const uint8_t qjsc_repl[]; +extern const uint32_t qjsc_repl_size; +#ifdef CONFIG_BIGNUM +extern const uint8_t qjsc_qjscalc[]; +extern const uint32_t qjsc_qjscalc_size; +#endif + +static int eval_buf(JSContext *ctx, const void *buf, int buf_len, + const char *filename, int eval_flags) +{ + JSValue val; + int ret; + + if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { + /* for the modules, we compile then run to be able to set + import.meta */ + val = JS_Eval(ctx, buf, buf_len, filename, + eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); + if (!JS_IsException(val)) { + js_module_set_import_meta(ctx, val, TRUE, TRUE); + val = JS_EvalFunction(ctx, val); + } + } else { + val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + } + if (JS_IsException(val)) { + js_std_dump_error(ctx); + ret = -1; + } else { + ret = 0; + } + JS_FreeValue(ctx, val); + return ret; +} + +static int eval_file(JSContext *ctx, const char *filename, int module) +{ + uint8_t *buf; + int ret, eval_flags; + size_t buf_len; + + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + perror(filename); + exit(1); + } + + if (module < 0) { + module = (has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags = JS_EVAL_TYPE_MODULE; + else + eval_flags = JS_EVAL_TYPE_GLOBAL; + ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); + js_free(ctx, buf); + return ret; +} + +#if defined(__APPLE__) +#define MALLOC_OVERHEAD 0 +#else +#define MALLOC_OVERHEAD 8 +#endif + +struct trace_malloc_data { + uint8_t *base; +}; + +static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, + struct trace_malloc_data *dp) +{ + return ptr - dp->base; +} + +/* default memory allocation functions with memory limitation */ +static inline size_t js_trace_malloc_usable_size(void *ptr) +{ +#if defined(__APPLE__) + return malloc_size(ptr); +#elif defined(_WIN32) + return _msize(ptr); +#elif defined(EMSCRIPTEN) + return 0; +#elif defined(__linux__) + return malloc_usable_size(ptr); +#else + /* change this to `return 0;` if compilation fails */ + return malloc_usable_size(ptr); +#endif +} + +static void __attribute__((format(printf, 2, 3))) + js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...) +{ + va_list ap; + int c; + + va_start(ap, fmt); + while ((c = *fmt++) != '\0') { + if (c == '%') { + /* only handle %p and %zd */ + if (*fmt == 'p') { + uint8_t *ptr = va_arg(ap, void *); + if (ptr == NULL) { + printf("NULL"); + } else { + printf("H%+06lld.%zd", + js_trace_malloc_ptr_offset(ptr, s->opaque), + js_trace_malloc_usable_size(ptr)); + } + fmt++; + continue; + } + if (fmt[0] == 'z' && fmt[1] == 'd') { + size_t sz = va_arg(ap, size_t); + printf("%zd", sz); + fmt += 2; + continue; + } + } + putc(c, stdout); + } + va_end(ap); +} + +static void js_trace_malloc_init(struct trace_malloc_data *s) +{ + free(s->base = malloc(8)); +} + +static void *js_trace_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); + js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr); + if (ptr) { + s->malloc_count++; + s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + } + return ptr; +} + +static void js_trace_free(JSMallocState *s, void *ptr) +{ + if (!ptr) + return; + + js_trace_malloc_printf(s, "F %p\n", ptr); + s->malloc_count--; + s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + free(ptr); +} + +static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) +{ + size_t old_size; + + if (!ptr) { + if (size == 0) + return NULL; + return js_trace_malloc(s, size); + } + old_size = js_trace_malloc_usable_size(ptr); + if (size == 0) { + js_trace_malloc_printf(s, "R %zd %p\n", size, ptr); + 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; + + js_trace_malloc_printf(s, "R %zd %p", size, ptr); + + ptr = realloc(ptr, size); + js_trace_malloc_printf(s, " -> %p\n", ptr); + if (ptr) { + s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size; + } + return ptr; +} + +static const JSMallocFunctions trace_mf = { + js_trace_malloc, + js_trace_free, + js_trace_realloc, +#if defined(__APPLE__) + malloc_size, +#elif defined(_WIN32) + (size_t (*)(const void *))_msize, +#elif defined(EMSCRIPTEN) + NULL, +#elif defined(__linux__) + (size_t (*)(const void *))malloc_usable_size, +#else + /* change this to `NULL,` if compilation fails */ + malloc_usable_size, +#endif +}; + +#define PROG_NAME "qjs" + +void help(void) +{ + printf("QuickJS version " CONFIG_VERSION "\n" + "usage: " PROG_NAME " [options] [file]\n" + "-h --help list options\n" + "-e --eval EXPR evaluate EXPR\n" + "-i --interactive go to interactive mode\n" + "-m --module load as ES6 module (default=autodetect)\n" + " --script load as ES6 script (default=autodetect)\n" + " --std make 'std' and 'os' available to the loaded script\n" +#ifdef CONFIG_BIGNUM + " --bignum enable the bignum extensions (BigFloat, BigDecimal)\n" + " --qjscalc load the QJSCalc runtime (default if invoked as qjscalc)\n" +#endif + "-T --trace trace memory allocation\n" + "-d --dump dump the memory usage stats\n" + " --memory-limit n limit the memory usage to 'n' bytes\n" + " --unhandled-rejection dump unhandled promise rejections\n" + "-q --quit just instantiate the interpreter and quit\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + JSRuntime *rt; + JSContext *ctx; + struct trace_malloc_data trace_data = { NULL }; + int optind; + char *expr = NULL; + int interactive = 0; + int dump_memory = 0; + int trace_memory = 0; + int empty_run = 0; + int module = -1; + int load_std = 0; + int dump_unhandled_promise_rejection = 0; + size_t memory_limit = 0; +#ifdef CONFIG_BIGNUM + int load_jscalc, bignum_ext = 0; +#endif + +#ifdef CONFIG_BIGNUM + /* load jscalc runtime if invoked as 'qjscalc' */ + { + const char *p, *exename; + exename = argv[0]; + p = strrchr(exename, '/'); + if (p) + exename = p + 1; + load_jscalc = !strcmp(exename, "qjscalc"); + } +#endif + + /* cannot use getopt because we want to pass the command line to + the script */ + optind = 1; + while (optind < argc && *argv[optind] == '-') { + char *arg = argv[optind] + 1; + const char *longopt = ""; + /* a single - is not an option, it also stops argument scanning */ + if (!*arg) + break; + optind++; + if (*arg == '-') { + longopt = arg + 1; + arg += strlen(arg); + /* -- stops argument scanning */ + if (!*longopt) + break; + } + for (; *arg || *longopt; longopt = "") { + char opt = *arg; + if (opt) + arg++; + if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { + help(); + continue; + } + if (opt == 'e' || !strcmp(longopt, "eval")) { + if (*arg) { + expr = arg; + break; + } + if (optind < argc) { + expr = argv[optind++]; + break; + } + fprintf(stderr, "qjs: missing expression for -e\n"); + exit(2); + } + if (opt == 'i' || !strcmp(longopt, "interactive")) { + interactive++; + continue; + } + if (opt == 'm' || !strcmp(longopt, "module")) { + module = 1; + continue; + } + if (!strcmp(longopt, "script")) { + module = 0; + continue; + } + if (opt == 'd' || !strcmp(longopt, "dump")) { + dump_memory++; + continue; + } + if (opt == 'T' || !strcmp(longopt, "trace")) { + trace_memory++; + continue; + } + if (!strcmp(longopt, "std")) { + load_std = 1; + continue; + } + if (!strcmp(longopt, "unhandled-rejection")) { + dump_unhandled_promise_rejection = 1; + continue; + } +#ifdef CONFIG_BIGNUM + if (!strcmp(longopt, "bignum")) { + bignum_ext = 1; + continue; + } + if (!strcmp(longopt, "qjscalc")) { + load_jscalc = 1; + continue; + } +#endif + if (opt == 'q' || !strcmp(longopt, "quit")) { + empty_run++; + continue; + } + if (!strcmp(longopt, "memory-limit")) { + if (optind >= argc) { + fprintf(stderr, "expecting memory limit"); + exit(1); + } + memory_limit = (size_t)strtod(argv[optind++], NULL); + continue; + } + if (opt) { + fprintf(stderr, "qjs: unknown option '-%c'\n", opt); + } else { + fprintf(stderr, "qjs: unknown option '--%s'\n", longopt); + } + help(); + } + } + + if (trace_memory) { + js_trace_malloc_init(&trace_data); + rt = JS_NewRuntime2(&trace_mf, &trace_data); + } else { + rt = JS_NewRuntime(); + } + if (!rt) { + fprintf(stderr, "qjs: cannot allocate JS runtime\n"); + exit(2); + } + if (memory_limit != 0) + JS_SetMemoryLimit(rt, memory_limit); + ctx = JS_NewContext(rt); + if (!ctx) { + fprintf(stderr, "qjs: cannot allocate JS context\n"); + exit(2); + } + +#ifdef CONFIG_BIGNUM + if (bignum_ext || load_jscalc) { + JS_AddIntrinsicBigFloat(ctx); + JS_AddIntrinsicBigDecimal(ctx); + JS_EnableBignumExt(ctx, TRUE); + } +#endif + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + + if (dump_unhandled_promise_rejection) { + JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, + NULL); + } + + if (!empty_run) { +#ifdef CONFIG_BIGNUM + if (load_jscalc) { + js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0); + } +#endif + js_std_add_helpers(ctx, argc - optind, argv + optind); + + /* system modules */ + js_init_module_std(ctx, "std"); + js_init_module_os(ctx, "os"); + + /* make 'std' and 'os' visible to non module code */ + if (load_std) { + const char *str = "import * as std from 'std';\n" + "import * as os from 'os';\n" + "globalThis.std = std;\n" + "globalThis.os = os;\n"; + eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); + } + + if (expr) { + if (eval_buf(ctx, expr, strlen(expr), "", 0)) + goto fail; + } else + if (optind >= argc) { + /* interactive mode */ + interactive = 1; + } else { + const char *filename; + filename = argv[optind]; + if (eval_file(ctx, filename, module)) + goto fail; + } + if (interactive) { + js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); + } + js_std_loop(ctx); + } + + if (dump_memory) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(stdout, &stats, rt); + } + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + if (empty_run && dump_memory) { + clock_t t[5]; + double best[5]; + int i, j; + for (i = 0; i < 100; i++) { + t[0] = clock(); + rt = JS_NewRuntime(); + t[1] = clock(); + ctx = JS_NewContext(rt); + t[2] = clock(); + JS_FreeContext(ctx); + t[3] = clock(); + JS_FreeRuntime(rt); + t[4] = clock(); + for (j = 4; j > 0; j--) { + double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC; + if (i == 0 || best[j] > ms) + best[j] = ms; + } + } + printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n", + best[1] + best[2] + best[3] + best[4], + best[1], best[2], best[3], best[4]); + } + return 0; + fail: + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 1; +} diff --git a/qjsc.c b/qjsc.c new file mode 100644 index 0000000..0706887 --- /dev/null +++ b/qjsc.c @@ -0,0 +1,707 @@ +/* + * QuickJS command line compiler + * + * Copyright (c) 2018-2020 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(_WIN32) +#include +#endif + +#include "cutils.h" +#include "quickjs-libc.h" + +/* enable bignums */ +#define CONFIG_BIGNUM + +typedef struct { + char *name; + char *short_name; + int flags; +} namelist_entry_t; + +typedef struct namelist_t { + namelist_entry_t *array; + int count; + int size; +} namelist_t; + +typedef struct { + const char *option_name; + const char *init_name; +} FeatureEntry; + +static namelist_t cname_list; +static namelist_t cmodule_list; +static namelist_t init_module_list; +static uint64_t feature_bitmap; +static FILE *outfile; +static BOOL byte_swap; +static BOOL dynamic_export; +static const char *c_ident_prefix = "qjsc_"; + +#define FE_ALL (-1) + +static const FeatureEntry feature_list[] = { + { "date", "Date" }, + { "eval", "Eval" }, + { "string-normalize", "StringNormalize" }, + { "regexp", "RegExp" }, + { "json", "JSON" }, + { "proxy", "Proxy" }, + { "map", "MapSet" }, + { "typedarray", "TypedArrays" }, + { "promise", "Promise" }, +#define FE_MODULE_LOADER 9 + { "module-loader", NULL }, + { "bigint", "BigInt" }, +}; + +void namelist_add(namelist_t *lp, const char *name, const char *short_name, + int flags) +{ + namelist_entry_t *e; + if (lp->count == lp->size) { + size_t newsize = lp->size + (lp->size >> 1) + 4; + namelist_entry_t *a = + realloc(lp->array, sizeof(lp->array[0]) * newsize); + /* XXX: check for realloc failure */ + lp->array = a; + lp->size = newsize; + } + e = &lp->array[lp->count++]; + e->name = strdup(name); + if (short_name) + e->short_name = strdup(short_name); + else + e->short_name = NULL; + e->flags = flags; +} + +void namelist_free(namelist_t *lp) +{ + while (lp->count > 0) { + namelist_entry_t *e = &lp->array[--lp->count]; + free(e->name); + free(e->short_name); + } + free(lp->array); + lp->array = NULL; + lp->size = 0; +} + +namelist_entry_t *namelist_find(namelist_t *lp, const char *name) +{ + int i; + for(i = 0; i < lp->count; i++) { + namelist_entry_t *e = &lp->array[i]; + if (!strcmp(e->name, name)) + return e; + } + return NULL; +} + +static void get_c_name(char *buf, size_t buf_size, const char *file) +{ + const char *p, *r; + size_t len, i; + int c; + char *q; + + p = strrchr(file, '/'); + if (!p) + p = file; + else + p++; + r = strrchr(p, '.'); + if (!r) + len = strlen(p); + else + len = r - p; + pstrcpy(buf, buf_size, c_ident_prefix); + q = buf + strlen(buf); + for(i = 0; i < len; i++) { + c = p[i]; + if (!((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) { + c = '_'; + } + if ((q - buf) < buf_size - 1) + *q++ = c; + } + *q = '\0'; +} + +static void dump_hex(FILE *f, const uint8_t *buf, size_t len) +{ + size_t i, col; + col = 0; + for(i = 0; i < len; i++) { + fprintf(f, " 0x%02x,", buf[i]); + if (++col == 8) { + fprintf(f, "\n"); + col = 0; + } + } + if (col != 0) + fprintf(f, "\n"); +} + +static void output_object_code(JSContext *ctx, + FILE *fo, JSValueConst obj, const char *c_name, + BOOL load_only) +{ + uint8_t *out_buf; + size_t out_buf_len; + int flags; + flags = JS_WRITE_OBJ_BYTECODE; + if (byte_swap) + flags |= JS_WRITE_OBJ_BSWAP; + out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); + if (!out_buf) { + js_std_dump_error(ctx); + exit(1); + } + + namelist_add(&cname_list, c_name, NULL, load_only); + + fprintf(fo, "const uint32_t %s_size = %u;\n\n", + c_name, (unsigned int)out_buf_len); + fprintf(fo, "const uint8_t %s[%u] = {\n", + c_name, (unsigned int)out_buf_len); + dump_hex(fo, out_buf, out_buf_len); + fprintf(fo, "};\n\n"); + + js_free(ctx, out_buf); +} + +static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m) +{ + /* should never be called when compiling JS code */ + abort(); +} + +static void find_unique_cname(char *cname, size_t cname_size) +{ + char cname1[1024]; + int suffix_num; + size_t len, max_len; + assert(cname_size >= 32); + /* find a C name not matching an existing module C name by + adding a numeric suffix */ + len = strlen(cname); + max_len = cname_size - 16; + if (len > max_len) + cname[max_len] = '\0'; + suffix_num = 1; + for(;;) { + snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num); + if (!namelist_find(&cname_list, cname1)) + break; + suffix_num++; + } + pstrcpy(cname, cname_size, cname1); +} + +JSModuleDef *jsc_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + namelist_entry_t *e; + + /* check if it is a declared C or system module */ + e = namelist_find(&cmodule_list, module_name); + if (e) { + /* add in the static init module list */ + namelist_add(&init_module_list, e->name, e->short_name, 0); + /* create a dummy module */ + m = JS_NewCModule(ctx, module_name, js_module_dummy_init); + } else if (has_suffix(module_name, ".so")) { + fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n", module_name); + /* create a dummy module */ + m = JS_NewCModule(ctx, module_name, js_module_dummy_init); + /* the resulting executable will export its symbols for the + dynamic library */ + dynamic_export = TRUE; + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + char cname[1024]; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + get_c_name(cname, sizeof(cname), module_name); + if (namelist_find(&cname_list, cname)) { + find_unique_cname(cname, sizeof(cname)); + } + output_object_code(ctx, outfile, func_val, cname, TRUE); + + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static void compile_file(JSContext *ctx, FILE *fo, + const char *filename, + const char *c_name1, + int module) +{ + uint8_t *buf; + char c_name[1024]; + int eval_flags; + JSValue obj; + size_t buf_len; + + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + fprintf(stderr, "Could not load '%s'\n", filename); + exit(1); + } + eval_flags = JS_EVAL_FLAG_COMPILE_ONLY; + if (module < 0) { + module = (has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags |= JS_EVAL_TYPE_MODULE; + else + eval_flags |= JS_EVAL_TYPE_GLOBAL; + obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags); + if (JS_IsException(obj)) { + js_std_dump_error(ctx); + exit(1); + } + js_free(ctx, buf); + if (c_name1) { + pstrcpy(c_name, sizeof(c_name), c_name1); + } else { + get_c_name(c_name, sizeof(c_name), filename); + } + output_object_code(ctx, fo, obj, c_name, FALSE); + JS_FreeValue(ctx, obj); +} + +static const char main_c_template1[] = + "int main(int argc, char **argv)\n" + "{\n" + " JSRuntime *rt;\n" + " JSContext *ctx;\n" + " rt = JS_NewRuntime();\n" + ; + +static const char main_c_template2[] = + " js_std_loop(ctx);\n" + " JS_FreeContext(ctx);\n" + " JS_FreeRuntime(rt);\n" + " return 0;\n" + "}\n"; + +#define PROG_NAME "qjsc" + +void help(void) +{ + printf("QuickJS Compiler version " CONFIG_VERSION "\n" + "usage: " PROG_NAME " [options] [files]\n" + "\n" + "options are:\n" + "-c only output bytecode in a C file\n" + "-e output main() and bytecode in a C file (default = executable output)\n" + "-o output set the output filename\n" + "-N cname set the C name of the generated data\n" + "-m compile as Javascript module (default=autodetect)\n" + "-M module_name[,cname] add initialization code for an external C module\n" + "-x byte swapped output\n" + "-p prefix set the prefix of the generated C names\n" + ); +#ifdef CONFIG_LTO + { + int i; + printf("-flto use link time optimization\n"); + printf("-fbignum enable bignum extensions\n"); + printf("-fno-["); + for(i = 0; i < countof(feature_list); i++) { + if (i != 0) + printf("|"); + printf("%s", feature_list[i].option_name); + } + printf("]\n" + " disable selected language features (smaller code size)\n"); + } +#endif + exit(1); +} + +#if defined(CONFIG_CC) && !defined(_WIN32) + +int exec_cmd(char **argv) +{ + int pid, status, ret; + + pid = fork(); + if (pid == 0) { + execvp(argv[0], argv); + exit(1); + } + + for(;;) { + ret = waitpid(pid, &status, 0); + if (ret == pid && WIFEXITED(status)) + break; + } + return WEXITSTATUS(status); +} + +static int output_executable(const char *out_filename, const char *cfilename, + BOOL use_lto, BOOL verbose, const char *exename) +{ + const char *argv[64]; + const char **arg, *bn_suffix, *lto_suffix; + char libjsname[1024]; + char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p; + int ret; + + /* get the directory of the executable */ + pstrcpy(exe_dir, sizeof(exe_dir), exename); + p = strrchr(exe_dir, '/'); + if (p) { + *p = '\0'; + } else { + pstrcpy(exe_dir, sizeof(exe_dir), "."); + } + + /* if 'quickjs.h' is present at the same path as the executable, we + use it as include and lib directory */ + snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir); + if (access(buf, R_OK) == 0) { + pstrcpy(inc_dir, sizeof(inc_dir), exe_dir); + pstrcpy(lib_dir, sizeof(lib_dir), exe_dir); + } else { + snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX); + snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX); + } + + lto_suffix = ""; + bn_suffix = ""; + + arg = argv; + *arg++ = CONFIG_CC; + *arg++ = "-O2"; +#ifdef CONFIG_LTO + if (use_lto) { + *arg++ = "-flto"; + lto_suffix = ".lto"; + } +#endif + /* XXX: use the executable path to find the includes files and + libraries */ + *arg++ = "-D"; + *arg++ = "_GNU_SOURCE"; + *arg++ = "-I"; + *arg++ = inc_dir; + *arg++ = "-o"; + *arg++ = out_filename; + if (dynamic_export) + *arg++ = "-rdynamic"; + *arg++ = cfilename; + snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a", + lib_dir, bn_suffix, lto_suffix); + *arg++ = libjsname; + *arg++ = "-lm"; + *arg++ = "-ldl"; + *arg = NULL; + + if (verbose) { + for(arg = argv; *arg != NULL; arg++) + printf("%s ", *arg); + printf("\n"); + } + + ret = exec_cmd((char **)argv); + unlink(cfilename); + return ret; +} +#else +static int output_executable(const char *out_filename, const char *cfilename, + BOOL use_lto, BOOL verbose, const char *exename) +{ + fprintf(stderr, "Executable output is not supported for this target\n"); + exit(1); + return 0; +} +#endif + + +typedef enum { + OUTPUT_C, + OUTPUT_C_MAIN, + OUTPUT_EXECUTABLE, +} OutputTypeEnum; + +int main(int argc, char **argv) +{ + int c, i, verbose; + const char *out_filename, *cname; + char cfilename[1024]; + FILE *fo; + JSRuntime *rt; + JSContext *ctx; + BOOL use_lto, bignum_ext; + int module; + OutputTypeEnum output_type; + + out_filename = NULL; + output_type = OUTPUT_EXECUTABLE; + cname = NULL; + feature_bitmap = FE_ALL; + module = -1; + byte_swap = FALSE; + verbose = 0; + use_lto = FALSE; + bignum_ext = FALSE; + + /* add system modules */ + namelist_add(&cmodule_list, "std", "std", 0); + namelist_add(&cmodule_list, "os", "os", 0); + + for(;;) { + c = getopt(argc, argv, "ho:cN:f:mxevM:p:"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + case 'o': + out_filename = optarg; + break; + case 'c': + output_type = OUTPUT_C; + break; + case 'e': + output_type = OUTPUT_C_MAIN; + break; + case 'N': + cname = optarg; + break; + case 'f': + { + const char *p; + p = optarg; + if (!strcmp(optarg, "lto")) { + use_lto = TRUE; + } else if (strstart(p, "no-", &p)) { + use_lto = TRUE; + for(i = 0; i < countof(feature_list); i++) { + if (!strcmp(p, feature_list[i].option_name)) { + feature_bitmap &= ~((uint64_t)1 << i); + break; + } + } + if (i == countof(feature_list)) + goto bad_feature; + } else if (!strcmp(optarg, "bignum")) { + bignum_ext = TRUE; + } else { + bad_feature: + fprintf(stderr, "unsupported feature: %s\n", optarg); + exit(1); + } + } + break; + case 'm': + module = 1; + break; + case 'M': + { + char *p; + char path[1024]; + char cname[1024]; + pstrcpy(path, sizeof(path), optarg); + p = strchr(path, ','); + if (p) { + *p = '\0'; + pstrcpy(cname, sizeof(cname), p + 1); + } else { + get_c_name(cname, sizeof(cname), path); + } + namelist_add(&cmodule_list, path, cname, 0); + } + break; + case 'x': + byte_swap = TRUE; + break; + case 'v': + verbose++; + break; + case 'p': + c_ident_prefix = optarg; + break; + default: + break; + } + } + + if (optind >= argc) + help(); + + if (!out_filename) { + if (output_type == OUTPUT_EXECUTABLE) { + out_filename = "a.out"; + } else { + out_filename = "out.c"; + } + } + + if (output_type == OUTPUT_EXECUTABLE) { +#if defined(_WIN32) || defined(__ANDROID__) + /* XXX: find a /tmp directory ? */ + snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid()); +#else + snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid()); +#endif + } else { + pstrcpy(cfilename, sizeof(cfilename), out_filename); + } + + fo = fopen(cfilename, "w"); + if (!fo) { + perror(cfilename); + exit(1); + } + outfile = fo; + + rt = JS_NewRuntime(); + ctx = JS_NewContext(rt); +#ifdef CONFIG_BIGNUM + if (bignum_ext) { + JS_AddIntrinsicBigFloat(ctx); + JS_AddIntrinsicBigDecimal(ctx); + JS_EnableBignumExt(ctx, TRUE); + } +#endif + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); + + fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n" + "\n" + ); + + if (output_type != OUTPUT_C) { + fprintf(fo, "#include \"quickjs-libc.h\"\n" + "\n" + ); + } else { + fprintf(fo, "#include \n" + "\n" + ); + } + + for(i = optind; i < argc; i++) { + const char *filename = argv[i]; + compile_file(ctx, fo, filename, cname, module); + cname = NULL; + } + + if (output_type != OUTPUT_C) { + fputs(main_c_template1, fo); + fprintf(fo, " ctx = JS_NewContextRaw(rt);\n"); + + /* add the module loader if necessary */ + if (feature_bitmap & (1 << FE_MODULE_LOADER)) { + fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n"); + } + + /* add the basic objects */ + + fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n"); + for(i = 0; i < countof(feature_list); i++) { + if ((feature_bitmap & ((uint64_t)1 << i)) && + feature_list[i].init_name) { + fprintf(fo, " JS_AddIntrinsic%s(ctx);\n", + feature_list[i].init_name); + } + } + if (bignum_ext) { + fprintf(fo, + " JS_AddIntrinsicBigFloat(ctx);\n" + " JS_AddIntrinsicBigDecimal(ctx);\n" + " JS_EnableBignumExt(ctx, 1);\n"); + } + + fprintf(fo, " js_std_add_helpers(ctx, argc, argv);\n"); + + for(i = 0; i < init_module_list.count; i++) { + namelist_entry_t *e = &init_module_list.array[i]; + /* initialize the static C modules */ + + fprintf(fo, + " {\n" + " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" + " js_init_module_%s(ctx, \"%s\");\n" + " }\n", + e->short_name, e->short_name, e->name); + } + + for(i = 0; i < cname_list.count; i++) { + namelist_entry_t *e = &cname_list.array[i]; + fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, %s);\n", + e->name, e->name, + e->flags ? "1" : "0"); + } + fputs(main_c_template2, fo); + } + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + fclose(fo); + + if (output_type == OUTPUT_EXECUTABLE) { + return output_executable(out_filename, cfilename, use_lto, verbose, + argv[0]); + } + namelist_free(&cname_list); + namelist_free(&cmodule_list); + namelist_free(&init_module_list); + return 0; +} diff --git a/qjscalc.js b/qjscalc.js new file mode 100644 index 0000000..87382ec --- /dev/null +++ b/qjscalc.js @@ -0,0 +1,2442 @@ +/* + * QuickJS Javascript Calculator + * + * 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. + */ +"use strict"; +"use math"; + +var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunction, Series, Matrix; + +(function(global) { + /* the types index are used to dispatch the operator functions */ + var OT_INT = 0; + var OT_FRACTION = 10; + var OT_FLOAT64 = 19; + var OT_FLOAT = 20; + var OT_COMPLEX = 30; + var OT_MOD = 40; + var OT_POLY = 50; + var OT_POLYMOD = 55; + var OT_RFUNC = 60; + var OT_SERIES = 70; + var OT_ARRAY = 80; + + global.Integer = global.BigInt; + global.algebraicMode = true; + + /* add non enumerable properties */ + function add_props(obj, props) { + var i, val, prop, tab, desc; + tab = Reflect.ownKeys(props); + for(i = 0; i < tab.length; i++) { + prop = tab[i]; + desc = Object.getOwnPropertyDescriptor(props, prop); + desc.enumerable = false; + if ("value" in desc) { + if (typeof desc.value !== "function") { + desc.writable = false; + desc.configurable = false; + } + } else { + /* getter/setter */ + desc.configurable = false; + } + Object.defineProperty(obj, prop, desc); + } + } + + /* Integer */ + + function generic_pow(a, b) { + var r, is_neg, i; + if (!Integer.isInteger(b)) { + return exp(log(a) * b); + } + if (Array.isArray(a) && !(a instanceof Polynomial || + a instanceof Series)) { + r = idn(Matrix.check_square(a)); + } else { + r = 1; + } + if (b == 0) + return r; + is_neg = false; + if (b < 0) { + is_neg = true; + b = -b; + } + r = a; + for(i = Integer.floorLog2(b) - 1; i >= 0; i--) { + r *= r; + if ((b >> i) & 1) + r *= a; + } + if (is_neg) { + if (typeof r.inverse != "function") + throw "negative powers are not supported for this type"; + r = r.inverse(); + } + return r; + } + + var small_primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499 ]; + + function miller_rabin_test(n, t) { + var d, r, s, i, j, a; + d = n - 1; + s = 0; + while (d & 1) { + d >>= 1; + s++; + } + t = Math.min(t, small_primes.length); + loop: for(j = 0; j < t; j++) { + a = small_primes[j]; + r = Integer.pmod(a, d, n); + if (r == 1 || r == (n - 1)) + continue; + for(i = 1; i < s; i++) { + r = (r * r) % n; + if (r == 1) + return false; + if (r == (n - 1)) + continue loop; + } + return false; /* n is composite */ + } + return true; /* n is probably prime with probability (1-0.5^t) */ + } + + function fact_rec(a, b) { /* assumes a <= b */ + var i, r; + if ((b - a) <= 5) { + r = a; + for(i = a + 1; i <= b; i++) + r *= i; + return r; + } else { + /* to avoid a quadratic running time it is better to + multiply numbers of similar size */ + i = (a + b) >> 1; + return fact_rec(a, i) * fact_rec(i + 1, b); + } + } + + add_props(Integer, { + isInteger(a) { + return typeof a === "bigint"; + }, + [Symbol.operatorOrder]: OT_INT, + [Symbol.operatorDiv](a, b) { + if (algebraicMode) { + return Fraction.toFraction(a, b); + } else { + return Float(a) / Float(b); + } + }, + [Symbol.operatorPow](a, b) { + if (algebraicMode) { + return generic_pow(a, b); + } else { + return Float(a) ** Float(b); + } + }, + gcd(a, b) { + var r; + while (b != 0) { + r = a % b; + a = b; + b = r; + } + return a; + }, + fact(n) { + return n <= 0 ? 1 : fact_rec(1, n); + }, + /* binomial coefficient */ + comb(n, k) { + if (k < 0 || k > n) + return 0; + if (k > n - k) + k = n - k; + if (k == 0) + return 1; + return Integer.tdiv(fact_rec(n - k + 1, n), fact_rec(1, k)); + }, + /* inverse of x modulo y */ + invmod(x, y) { + var q, u, v, a, c, t; + u = x; + v = y; + c = 1; + a = 0; + while (u != 0) { + t = Integer.fdivrem(v, u); + q = t[0]; + v = u; + u = t[1]; + t = c; + c = a - q * c; + a = t; + } + /* v = gcd(x, y) */ + if (v != 1) + throw RangeError("not invertible"); + return a % y; + }, + /* return a ^ b modulo m */ + pmod(a, b, m) { + var r; + if (b == 0) + return 1; + if (b < 0) { + a = Integer.invmod(a, m); + b = -b; + } + r = 1; + for(;;) { + if (b & 1) { + r = (r * a) % m; + } + b >>= 1; + if (b == 0) + break; + a = (a * a) % m; + } + return r; + }, + + /* return true if n is prime (or probably prime with + probability 1-0.5^t) */ + isPrime(n, t) { + var i, d, n1; + if (!Integer.isInteger(n)) + throw TypeError("invalid type"); + if (n <= 1) + return false; + n1 = small_primes.length; + /* XXX: need Integer.sqrt() */ + for(i = 0; i < n1; i++) { + d = small_primes[i]; + if (d == n) + return true; + if (d > n) + return false; + if ((n % d) == 0) + return false; + } + if (n < d * d) + return true; + if (typeof t == "undefined") + t = 64; + return miller_rabin_test(n, t); + }, + nextPrime(n) { + if (!Integer.isInteger(n)) + throw TypeError("invalid type"); + if (n < 1) + n = 1; + for(;;) { + n++; + if (Integer.isPrime(n)) + return n; + } + }, + factor(n) { + var r, d; + if (!Integer.isInteger(n)) + throw TypeError("invalid type"); + r = []; + if (abs(n) <= 1) { + r.push(n); + return r; + } + if (n < 0) { + r.push(-1); + n = -n; + } + + while ((n % 2) == 0) { + n >>= 1; + r.push(2); + } + + d = 3; + while (n != 1) { + if (Integer.isPrime(n)) { + r.push(n); + break; + } + /* we are sure there is at least one divisor, so one test */ + for(;;) { + if ((n % d) == 0) + break; + d += 2; + } + for(;;) { + r.push(d); + n = Integer.tdiv(n, d); + if ((n % d) != 0) + break; + } + } + return r; + }, + }); + + add_props(Integer.prototype, { + inverse() { + return 1 / this; + }, + norm2() { + return this * this; + }, + abs() { + return Math.abs(this); + }, + conj() { + return this; + }, + arg() { + if (this >= 0) + return 0; + else + return Float.PI; + }, + exp() { + if (this == 0) + return 1; + else + return Float.exp(this); + }, + log() { + if (this == 1) + return 0; + else + return Float(this).log(); + }, + }); + + /* Fraction */ + + Fraction = function Fraction(a, b) + { + var d, r, obj; + + if (new.target) + throw TypeError("not a constructor"); + if (a instanceof Fraction) + return a; + if (!Integer.isInteger(a)) + throw TypeError("integer expected"); + if (typeof b === "undefined") { + b = 1; + } else { + if (!Integer.isInteger(b)) + throw TypeError("integer expected"); + if (b == 0) + throw RangeError("division by zero"); + d = Integer.gcd(a, b); + if (d != 1) { + a = Integer.tdiv(a, d); + b = Integer.tdiv(b, d); + } + + /* the fractions are normalized with den > 0 */ + if (b < 0) { + a = -a; + b = -b; + } + } + obj = Object.create(Fraction.prototype); + obj.num = a; + obj.den = b; + return obj; + } + + add_props(Fraction, { + [Symbol.operatorOrder]: OT_FRACTION, + /* (internal use) simplify 'a' to an integer when possible */ + toFraction(a, b) { + var r = Fraction(a, b); + if (algebraicMode && r.den == 1) + return r.num; + else + return r; + }, + + [Symbol.operatorAdd](a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den + a.den * b.num, a.den * b.den); + }, + [Symbol.operatorSub](a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den - a.den * b.num, a.den * b.den); + }, + [Symbol.operatorMul](a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.num, a.den * b.den); + }, + [Symbol.operatorDiv](a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den, a.den * b.num); + }, + [Symbol.operatorMathMod](a, b) { + var a1 = Fraction(a); + var b1 = Fraction(b); + return a - Integer.ediv(a1.num * b1.den, a1.den * b1.num) * b; + }, + [Symbol.operatorMod](a, b) { + var a1 = Fraction(a); + var b1 = Fraction(b); + return a - Integer.tdiv(a1.num * b1.den, a1.den * b1.num) * b; + }, + [Symbol.operatorPow]: generic_pow, + [Symbol.operatorCmpEQ](a, b) { + a = Fraction(a); + b = Fraction(b); + /* we assume the fractions are normalized */ + return (a.num == b.num && a.den == b.den); + }, + [Symbol.operatorCmpLT](a, b) { + a = Fraction(a); + b = Fraction(b); + return (a.num * b.den < b.num * a.den); + }, + [Symbol.operatorCmpLE](a, b) { + a = Fraction(a); + b = Fraction(b); + return (a.num * b.den <= b.num * a.den); + }, + }); + + add_props(Fraction.prototype, { + [Symbol.toPrimitive](hint) { + if (hint === "integer") { + return Integer.tdiv(this.num, this.den); + } else if (hint === "string") { + return this.toString(); + } else { + return Float(this.num) / this.den; + } + }, + [Symbol.operatorPlus]() { + return this; + }, + [Symbol.operatorNeg]() { + return Fraction(-this.num, this.den); + }, + inverse() { + return Fraction(this.den, this.num); + }, + toString() { + return this.num + "/" + this.den; + }, + norm2() { + return this * this; + }, + abs() { + if (this.num < 0) + return this[Symbol.operatorNeg](); + else + return this; + }, + conj() { + return this; + }, + arg() { + if (this.num >= 0) + return 0; + else + return Float.PI; + }, + exp() { + return Float.exp(Float(this)); + }, + log() { + return Float(this).log(); + }, + }); + + /* Number (Float64) */ + + add_props(Number, { + [Symbol.operatorOrder]: OT_FLOAT64, + /* operators are needed for fractions */ + [Symbol.operatorAdd](a, b) { + return Number(a) + Number(b); + }, + [Symbol.operatorSub](a, b) { + return Number(a) - Number(b); + }, + [Symbol.operatorMul](a, b) { + return Number(a) * Number(b); + }, + [Symbol.operatorDiv](a, b) { + return Number(a) / Number(b); + }, + [Symbol.operatorPow](a, b) { + return Number(a) ** Number(b); + }, + }); + + add_props(Number.prototype, { + inverse() { + return 1.0 / this; + }, + norm2() { + return this * this; + }, + abs() { + return Math.abs(this); + }, + conj() { + return this; + }, + arg() { + if (this >= 0) + return 0; + else + return Float.PI; + }, + exp() { + return Float.exp(this); + }, + log() { + if (this < 0) { + return Complex(this).log(); + } else { + return Float.log(this); + } + }, + }); + + /* Float */ + + global.Float = global.BigFloat; + + var const_tab = []; + + /* we cache the constants for small precisions */ + function get_const(n) { + var t, c, p; + t = const_tab[n]; + p = BigFloatEnv.prec; + if (t && t.prec == p) { + return t.val; + } else { + switch(n) { + case 0: c = Float.exp(1); break; + case 1: c = Float.log(10); break; +// case 2: c = Float.log(2); break; + case 3: c = 1/Float.log(2); break; + case 4: c = 1/Float.log(10); break; +// case 5: c = Float.atan(1) * 4; break; + case 6: c = Float.sqrt(0.5); break; + case 7: c = Float.sqrt(2); break; + } + if (p <= 1024) { + const_tab[n] = { prec: p, val: c }; + } + return c; + } + } + + add_props(Float, { + isFloat(a) { + return typeof a === "number" || typeof a === "bigfloat"; + }, + bestappr(u, b) { + var num1, num0, den1, den0, u, num, den, n; + + if (typeof b === "undefined") + throw TypeError("second argument expected"); + num1 = 1; + num0 = 0; + den1 = 0; + den0 = 1; + for(;;) { + n = Integer(Float.floor(u)); + num = n * num1 + num0; + den = n * den1 + den0; + if (den > b) + break; + u = 1.0 / (u - n); + num0 = num1; + num1 = num; + den0 = den1; + den1 = den; + } + return Fraction(num1, den1); + }, + /* similar constants as Math.x */ + get E() { return get_const(0); }, + get LN10() { return get_const(1); }, +// get LN2() { return get_const(2); }, + get LOG2E() { return get_const(3); }, + get LOG10E() { return get_const(4); }, +// get PI() { return get_const(5); }, + get SQRT1_2() { return get_const(6); }, + get SQRT2() { return get_const(7); }, + }); + + add_props(Float, { + [Symbol.operatorOrder]: OT_FLOAT, + /* operators are needed for fractions */ + [Symbol.operatorAdd](a, b) { + return Float(a) + Float(b); + }, + [Symbol.operatorSub](a, b) { + return Float(a) - Float(b); + }, + [Symbol.operatorMul](a, b) { + return Float(a) * Float(b); + }, + [Symbol.operatorDiv](a, b) { + return Float(a) / Float(b); + }, + [Symbol.operatorPow](a, b) { + return Float(a) ** Float(b); + }, + }); + + add_props(Float.prototype, { + inverse() { + return 1.0 / this; + }, + norm2() { + return this * this; + }, + abs() { + return Math.abs(this); + }, + conj() { + return this; + }, + arg() { + if (this >= 0) + return 0; + else + return Float.PI; + }, + exp() { + return Float.exp(this); + }, + log() { + if (this < 0) { + return Complex(this).log(); + } else { + return Float.log(this); + } + }, + }); + + /* Complex */ + + Complex = function Complex(re, im) + { + var obj; + if (new.target) + throw TypeError("not a constructor"); + if (re instanceof Complex) + return re; + if (typeof im === "undefined") { + im = 0; + } + obj = Object.create(Complex.prototype); + obj.re = re; + obj.im = im; + return obj; + } + + add_props(Complex, { + [Symbol.operatorOrder]: OT_COMPLEX, + /* simplify to real number when possible */ + toComplex(re, im) { + if (algebraicMode && im == 0) + return re; + else + return Complex(re, im); + }, + + [Symbol.operatorAdd](a, b) { + a = Complex(a); + b = Complex(b); + return Complex.toComplex(a.re + b.re, a.im + b.im); + }, + [Symbol.operatorSub](a, b) { + a = Complex(a); + b = Complex(b); + return Complex.toComplex(a.re - b.re, a.im - b.im); + }, + [Symbol.operatorMul](a, b) { + a = Complex(a); + b = Complex(b); + return Complex.toComplex(a.re * b.re - a.im * b.im, + a.re * b.im + a.im * b.re); + }, + [Symbol.operatorDiv](a, b) { + a = Complex(a); + b = Complex(b); + return a * b.inverse(); + }, + [Symbol.operatorPow]: generic_pow, + [Symbol.operatorCmpEQ](a, b) { + a = Complex(a); + b = Complex(b); + return a.re == b.re && a.im == b.im; + } + }); + + add_props(Complex.prototype, { + [Symbol.operatorPlus]() { + return this; + }, + [Symbol.operatorNeg]() { + return Complex(-this.re, -this.im); + }, + inverse() { + var c = this.norm2(); + return Complex(this.re / c, -this.im / c); + }, + toString() { + var v, s = "", a = this; + if (a.re != 0) + s += a.re.toString(); + if (a.im == 1) { + if (s != "") + s += "+"; + s += "I"; + } else if (a.im == -1) { + s += "-I"; + } else { + v = a.im.toString(); + if (v[0] != "-" && s != "") + s += "+"; + s += v + "*I"; + } + return s; + }, + norm2() { + return this.re * this.re + this.im * this.im; + }, + abs() { + return Float.sqrt(norm2(this)); + }, + conj() { + return Complex(this.re, -this.im); + }, + arg() { + return Float.atan2(this.im, this.re); + }, + exp() { + var arg = this.im, r = this.re.exp(); + return Complex(r * cos(arg), r * sin(arg)); + }, + log() { + return Complex(abs(this).log(), atan2(this.im, this.re)); + }, + }); + + /* Mod */ + + Mod = function Mod(a, m) { + var obj, t; + if (new.target) + throw TypeError("not a constructor"); + obj = Object.create(Mod.prototype); + if (Integer.isInteger(m)) { + if (m <= 0) + throw RangeError("the modulo cannot be <= 0"); + if (Integer.isInteger(a)) { + a %= m; + } else if (a instanceof Fraction) { + return Mod(a.num, m) / a.den; + } else { + throw TypeError("invalid types"); + } + } else { + throw TypeError("invalid types"); + } + obj.res = a; + obj.mod = m; + return obj; + }; + + add_props(Mod.prototype, { + [Symbol.operatorPlus]() { + return this; + }, + [Symbol.operatorNeg]() { + return Mod(-this.res, this.mod); + }, + inverse() { + var a = this, m = a.mod; + if (Integer.isInteger(m)) { + return Mod(Integer.invmod(a.res, m), m); + } else { + throw TypeError("unsupported type"); + } + }, + toString() { + return "Mod(" + this.res + "," + this.mod + ")"; + }, + }); + + add_props(Mod, { + [Symbol.operatorOrder]: OT_MOD, + [Symbol.operatorAdd](a, b) { + if (!(a instanceof Mod)) { + return Mod(a + b.res, b.mod); + } else if (!(b instanceof Mod)) { + return Mod(a.res + b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return Mod(a.res + b.res, a.mod); + } + }, + [Symbol.operatorSub](a, b) { + if (!(a instanceof Mod)) { + return Mod(a - b.res, b.mod); + } else if (!(b instanceof Mod)) { + return Mod(a.res - b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return Mod(a.res - b.res, a.mod); + } + }, + [Symbol.operatorMul](a, b) { + if (!(a instanceof Mod)) { + return Mod(a * b.res, b.mod); + } else if (!(b instanceof Mod)) { + return Mod(a.res * b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return Mod(a.res * b.res, a.mod); + } + }, + [Symbol.operatorDiv](a, b) { + if (!(b instanceof Mod)) + b = Mod(b, a.mod); + return Mod[Symbol.operatorMul](a, b.inverse()); + }, + [Symbol.operatorPow]: generic_pow, + [Symbol.operatorCmpEQ](a, b) { + if (!(a instanceof Mod) || + !(b instanceof Mod)) + return false; + return (a.mod == b.mod && a.res == b.res); + } + }); + + /* Polynomial */ + + Polynomial = function Polynomial(a) + { + if (new.target) + throw TypeError("not a constructor"); + if (a instanceof Polynomial) { + return a; + } else if (Array.isArray(a)) { + if (a.length == 0) + a = [ 0 ]; + Object.setPrototypeOf(a, Polynomial.prototype); + return a.trim(); + } else if (a.constructor[Symbol.operatorOrder] <= OT_MOD) { + a = [a]; + Object.setPrototypeOf(a, Polynomial.prototype); + return a; + } else { + throw TypeError("invalid type"); + } + } + + function number_need_paren(c) + { + return !(Integer.isInteger(c) || + Float.isFloat(c) || + c instanceof Fraction || + (c instanceof Complex && c.re == 0)); + } + + /* string for c*X^i */ + function monomial_toString(c, i) + { + var str1; + if (i == 0) { + str1 = c.toString(); + } else { + if (c == 1) { + str1 = ""; + } else if (c == -1) { + str1 = "-"; + } else { + if (number_need_paren(c)) { + str1 = "(" + c + ")"; + } else { + str1 = String(c); + } + str1 += "*"; + } + str1 += "X"; + if (i != 1) { + str1 += "^" + i; + } + } + return str1; + } + + /* find one complex root of 'p' starting from z at precision eps using + at most max_it iterations. Return null if could not find root. */ + function poly_root_laguerre1(p, z, max_it) + { + var p1, p2, i, z0, z1, z2, d, t0, t1, d1, d2, e, el, zl; + + d = p.deg(); + if (d == 1) { + /* monomial case */ + return -p[0] / p[1]; + } + /* trivial zero */ + if (p[0] == 0) + return 0.0; + + p1 = p.deriv(); + p2 = p1.deriv(); + el = 0.0; + zl = 0.0; + for(i = 0; i < max_it; i++) { + z0 = p.apply(z); + if (z0 == 0) + return z; /* simple exit case */ + + /* Ward stopping criteria */ + e = abs(z - zl); +// print("e", i, e); + if (i >= 2 && e >= el) { + if (abs(zl) < 1e-4) { + if (e < 1e-7) + return zl; + } else { + if (e < abs(zl) * 1e-3) + return zl; + } + } + el = e; + zl = z; + + z1 = p1.apply(z); + z2 = p2.apply(z); + t0 = (d - 1) * z1; + t0 = t0 * t0; + t1 = d * (d - 1) * z0 * z2; + t0 = sqrt(t0 - t1); + d1 = z1 + t0; + d2 = z1 - t0; + if (norm2(d2) > norm2(d1)) + d1 = d2; + if (d1 == 0) + return null; + z = z - d * z0 / d1; + } + return null; + } + + function poly_roots(p) + { + var d, i, roots, j, z, eps; + var start_points = [ 0.1, -1.4, 1.7 ]; + + if (!(p instanceof Polynomial)) + throw TypeError("polynomial expected"); + d = p.deg(); + if (d <= 0) + return []; + eps = 2.0 ^ (-BigFloatEnv.prec); + roots = []; + for(i = 0; i < d; i++) { + /* XXX: should select another start point if error */ + for(j = 0; j < 3; j++) { + z = poly_root_laguerre1(p, start_points[j], 100); + if (z !== null) + break; + } + if (j == 3) + throw RangeError("error in root finding algorithm"); + roots[i] = z; + p = Polynomial.divrem(p, X - z)[0]; + } + return roots; + } + + add_props(Polynomial.prototype, { + trim() { + var a = this, i; + i = a.length; + while (i > 1 && a[i - 1] == 0) + i--; + a.length = i; + return a; + }, + [Symbol.operatorPlus]() { + return this; + }, + [Symbol.operatorNeg]() { + var r, i, n, a; + a = this; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = -a[i]; + return Polynomial(r); + }, + conj() { + var r, i, n, a; + a = this; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = a[i].conj(); + return Polynomial(r); + }, + inverse() { + return RationalFunction(Polynomial([1]), this); + }, + toString() { + var i, str, str1, c, a = this; + if (a.length == 1) { + return a[0].toString(); + } + str=""; + for(i = a.length - 1; i >= 0; i--) { + c = a[i]; + if (c == 0 || + (c instanceof Mod) && c.res == 0) + continue; + str1 = monomial_toString(c, i); + if (str1[0] != "-") { + if (str != "") + str += "+"; + } + str += str1; + } + return str; + }, + deg() { + if (this.length == 1 && this[0] == 0) + return -Infinity; + else + return this.length - 1; + }, + apply(b) { + var i, n, r, a = this; + n = a.length - 1; + r = a[n]; + while (n > 0) { + n--; + r = r * b + a[n]; + } + return r; + }, + deriv() { + var a = this, n, r, i; + n = a.length; + if (n == 1) { + return Polynomial(0); + } else { + r = []; + for(i = 1; i < n; i++) { + r[i - 1] = i * a[i]; + } + return Polynomial(r); + } + }, + integ() { + var a = this, n, r, i; + n = a.length; + r = [0]; + for(i = 0; i < n; i++) { + r[i + 1] = a[i] / (i + 1); + } + return Polynomial(r); + }, + norm2() { + var a = this, n, r, i; + n = a.length; + r = 0; + for(i = 0; i < n; i++) { + r += a[i].norm2(); + } + return r; + }, + }); + + add_props(Polynomial, { + [Symbol.operatorOrder]: OT_POLY, + [Symbol.operatorAdd](a, b) { + var tmp, r, i, n1, n2; + a = Polynomial(a); + b = Polynomial(b); + if (a.length < b.length) { + tmp = a; + a = b; + b = tmp; + } + n1 = b.length; + n2 = a.length; + r = []; + for(i = 0; i < n1; i++) + r[i] = a[i] + b[i]; + for(i = n1; i < n2; i++) + r[i] = a[i]; + return Polynomial(r); + }, + [Symbol.operatorSub](a, b) { + return Polynomial[Symbol.operatorAdd](a, -b); + }, + [Symbol.operatorMul](a, b) { + var i, j, n1, n2, n, r; + a = Polynomial(a); + b = Polynomial(b); + n1 = a.length; + n2 = b.length; + n = n1 + n2 - 1; + r = []; + for(i = 0; i < n; i++) + r[i] = 0; + for(i = 0; i < n1; i++) { + for(j = 0; j < n2; j++) { + r[i + j] += a[i] * b[j]; + } + } + return Polynomial(r); + }, + [Symbol.operatorDiv](a, b) { + if (b.constructor[Symbol.operatorOrder] <= OT_COMPLEX) + return a * (1 / b); + else + return RationalFunction(Polynomial(a), + Polynomial(b)); + }, + [Symbol.operatorPow]: generic_pow, + [Symbol.operatorMathMod](a, b) { + return Polynomial.divrem(a, b)[1]; + }, + [Symbol.operatorMod](a, b) { + return Polynomial.divrem(a, b)[1]; + }, + [Symbol.operatorCmpEQ](a, b) { + var n, i; + if (!(a instanceof Polynomial) || + !(b instanceof Polynomial)) + return false; + n = a.length; + if (n != b.length) + return false; + for(i = 0; i < n; i++) { + if (a[i] != b[i]) + return false; + } + return true; + }, + divrem(a, b) { + var n1, n2, i, j, q, r, n, c; + if (b.deg() < 0) + throw RangeError("division by zero"); + n1 = a.length; + n2 = b.length; + if (n1 < n2) + return [Polynomial([0]), a]; + r = Array.prototype.dup.call(a); + q = []; + n2--; + n = n1 - n2; + for(i = 0; i < n; i++) + q[i] = 0; + for(i = n - 1; i >= 0; i--) { + c = r[i + n2]; + if (c != 0) { + c = c / b[n2]; + r[i + n2] = 0; + for(j = 0; j < n2; j++) { + r[i + j] -= b[j] * c; + } + q[i] = c; + } + } + return [Polynomial(q), Polynomial(r)]; + }, + gcd(a, b) { + var t; + while (b.deg() >= 0) { + t = Polynomial.divrem(a, b); + a = b; + b = t[1]; + } + /* convert to monic form */ + return a / a[a.length - 1]; + }, + invmod(x, y) { + var q, u, v, a, c, t; + u = x; + v = y; + c = Polynomial([1]); + a = Polynomial([0]); + while (u.deg() >= 0) { + t = Polynomial.divrem(v, u); + q = t[0]; + v = u; + u = t[1]; + t = c; + c = a - q * c; + a = t; + } + /* v = gcd(x, y) */ + if (v.deg() > 0) + throw RangeError("not invertible"); + return Polynomial.divrem(a, y)[1]; + }, + roots(p) { + return poly_roots(p); + } + }); + + /* Polynomial Modulo Q */ + + PolyMod = function PolyMod(a, m) { + var obj, t; + if (new.target) + throw TypeError("not a constructor"); + obj = Object.create(PolyMod.prototype); + if (m instanceof Polynomial) { + if (m.deg() <= 0) + throw RangeError("the modulo cannot have a degree <= 0"); + if (a instanceof RationalFunction) { + return PolyMod(a.num, m) / a.den; + } else { + a = Polynomial(a); + t = Polynomial.divrem(a, m); + a = t[1]; + } + } else { + throw TypeError("invalid types"); + } + obj.res = a; + obj.mod = m; + return obj; + }; + + add_props(PolyMod.prototype, { + [Symbol.operatorPlus]() { + return this; + }, + [Symbol.operatorNeg]() { + return PolyMod(-this.res, this.mod); + }, + inverse() { + var a = this, m = a.mod; + if (m instanceof Polynomial) { + return PolyMod(Polynomial.invmod(a.res, m), m); + } else { + throw TypeError("unsupported type"); + } + }, + toString() { + return "PolyMod(" + this.res + "," + this.mod + ")"; + }, + }); + + add_props(PolyMod, { + [Symbol.operatorOrder]: OT_POLYMOD, + [Symbol.operatorAdd](a, b) { + if (!(a instanceof PolyMod)) { + return PolyMod(a + b.res, b.mod); + } else if (!(b instanceof PolyMod)) { + return PolyMod(a.res + b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return PolyMod(a.res + b.res, a.mod); + } + }, + [Symbol.operatorSub](a, b) { + if (!(a instanceof PolyMod)) { + return PolyMod(a - b.res, b.mod); + } else if (!(b instanceof PolyMod)) { + return PolyMod(a.res - b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return PolyMod(a.res - b.res, a.mod); + } + }, + [Symbol.operatorMul](a, b) { + if (!(a instanceof PolyMod)) { + return PolyMod(a * b.res, b.mod); + } else if (!(b instanceof PolyMod)) { + return PolyMod(a.res * b, a.mod); + } else { + if (a.mod != b.mod) + throw TypeError("different modulo for binary operator"); + return PolyMod(a.res * b.res, a.mod); + } + }, + [Symbol.operatorDiv](a, b) { + if (!(b instanceof PolyMod)) + b = PolyMod(b, a.mod); + return PolyMod[Symbol.operatorMul](a, b.inverse()); + }, + [Symbol.operatorPow]: generic_pow, + [Symbol.operatorCmpEQ](a, b) { + if (!(a instanceof PolyMod) || + !(b instanceof PolyMod)) + return false; + return (a.mod == b.mod && a.res == b.res); + } + }); + + /* Rational function */ + + RationalFunction = function RationalFunction(a, b) + { + var t, r, d, obj; + if (new.target) + throw TypeError("not a constructor"); + if (!(a instanceof Polynomial) || + !(b instanceof Polynomial)) + throw TypeError("polynomial expected"); + t = Polynomial.divrem(a, b); + r = t[1]; + if (r.deg() < 0) + return t[0]; /* no need for a fraction */ + d = Polynomial.gcd(b, r); + if (d.deg() > 0) { + a = Polynomial.divrem(a, d)[0]; + b = Polynomial.divrem(b, d)[0]; + } + obj = Object.create(RationalFunction.prototype); + obj.num = a; + obj.den = b; + return obj; + } + + add_props(RationalFunction.prototype, { + [Symbol.operatorPlus]() { + return this; + }, + [Symbol.operatorNeg]() { + return RationalFunction(-this.num, this.den); + }, + inverse() { + return RationalFunction(this.den, this.num); + }, + conj() { + return RationalFunction(this.num.conj(), this.den.conj()); + }, + toString() { + var str; + if (this.num.deg() <= 0 && + !number_need_paren(this.num[0])) + str = this.num.toString(); + else + str = "(" + this.num.toString() + ")"; + str += "/(" + this.den.toString() + ")" + return str; + }, + apply(b) { + return this.num.apply(b) / this.den.apply(b); + }, + deriv() { + var n = this.num, d = this.den; + return RationalFunction(n.deriv() * d - n * d.deriv(), d * d); + }, + }); + + add_props(RationalFunction, { + [Symbol.operatorOrder]: OT_RFUNC, + /* This function always return a RationalFunction object even + if it could simplified to a polynomial, so it is not + equivalent to RationalFunction(a) */ + toRationalFunction(a) { + var obj; + if (a instanceof RationalFunction) { + return a; + } else { + obj = Object.create(RationalFunction.prototype); + obj.num = Polynomial(a); + obj.den = Polynomial(1); + return obj; + } + }, + [Symbol.operatorAdd](a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den + a.den * b.num, a.den * b.den); + }, + [Symbol.operatorSub](a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den - a.den * b.num, a.den * b.den); + }, + [Symbol.operatorMul](a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.num, a.den * b.den); + }, + [Symbol.operatorDiv](a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den, a.den * b.num); + }, + [Symbol.operatorPow]: generic_pow, + [Symbol.operatorCmpEQ](a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + /* we assume the fractions are normalized */ + return (a.num == b.num && a.den == b.den); + }, + }); + + /* Power series */ + + /* 'a' is an array */ + function get_emin(a) { + var i, n; + n = a.length; + for(i = 0; i < n; i++) { + if (a[i] != 0) + return i; + } + return n; + }; + + /* n is the maximum number of terms if 'a' is not a serie */ + Series = function Series(a, n) { + var emin, r, i; + + if (a instanceof Series) { + return a; + } else if (a.constructor[Symbol.operatorOrder] <= OT_POLY) { + if (n <= 0) { + /* XXX: should still use the polynomial degree */ + return Series.zero(0, 0); + } else { + a = Polynomial(a); + emin = get_emin(a); + r = Series.zero(n, emin); + n = Math.min(a.length - emin, n); + for(i = 0; i < n; i++) + r[i] = a[i + emin]; + return r; + } + } else if (a instanceof RationalFunction) { + return Series(a.num, n) / a.den; + } else { + throw TypeError("invalid type"); + } + }; + + add_props(Series.prototype, { + [Symbol.operatorPlus]() { + return this; + }, + [Symbol.operatorNeg]() { + var obj, n, i; + n = this.length; + obj = Series.zero(this.length, this.emin); + for(i = 0; i < n; i++) { + obj[i] = -this[i]; + } + return obj; + }, + conj() { + var obj, n, i; + n = this.length; + obj = Series.zero(this.length, this.emin); + for(i = 0; i < n; i++) { + obj[i] = this[i].conj(); + } + return obj; + }, + inverse() { + var r, n, i, j, sum, v1 = this; + n = v1.length; + if (n == 0) + throw RangeError("division by zero"); + r = Series.zero(n, -v1.emin); + r[0] = 1 / v1[0]; + for(i = 1; i < n; i++) { + sum = 0; + for(j = 1; j <= i; j++) { + sum += v1[j] * r[i - j]; + } + r[i] = -sum * r[0]; + } + return r; + }, + /* remove leading zero terms */ + trim() { + var i, j, n, r, v1 = this; + n = v1.length; + i = 0; + while (i < n && v1[i] == 0) + i++; + if (i == 0) + return v1; + for(j = i; j < n; j++) + v1[j - i] = v1[j]; + v1.length = n - i; + v1.__proto__.emin += i; + return v1; + }, + toString() { + var i, j, str, str1, c, a = this, emin, n; + str=""; + emin = this.emin; + n = this.length; + for(j = 0; j < n; j++) { + i = j + emin; + c = a[j]; + if (c != 0) { + str1 = monomial_toString(c, i); + if (str1[0] != "-") { + if (str != "") + str += "+"; + } + str += str1; + } + } + if (str != "") + str += "+"; + str += "O(" + monomial_toString(1, n + emin) + ")"; + return str; + }, + apply(b) { + var i, n, r, a = this; + n = a.length; + if (n == 0) + return 0; + r = a[--n]; + while (n > 0) { + n--; + r = r * b + a[n]; + } + if (a.emin != 0) + r *= b ^ a.emin; + return r; + }, + deriv() { + var a = this, n = a.length, emin = a.emin, r, i, j; + if (n == 0 && emin == 0) { + return Series.zero(0, 0); + } else { + r = Series.zero(n, emin - 1); + for(i = 0; i < n; i++) { + j = emin + i; + if (j == 0) + r[i] = 0; + else + r[i] = j * a[i]; + } + return r.trim(); + } + }, + integ() { + var a = this, n = a.length, emin = a.emin, i, j, r; + r = Series.zero(n, emin + 1); + for(i = 0; i < n; i++) { + j = emin + i; + if (j == -1) { + if (a[i] != 0) + throw RangError("cannot represent integ(1/X)"); + } else { + r[i] = a[i] / (j + 1); + } + } + return r.trim(); + }, + exp() { + var c, i, r, n, a = this; + if (a.emin < 0) + throw RangeError("negative exponent in exp"); + n = a.emin + a.length; + if (a.emin > 0 || a[0] == 0) { + c = 1; + } else { + c = global.exp(a[0]); + a -= a[0]; + } + r = Series.zero(n, 0); + for(i = 0; i < n; i++) { + r[i] = c / fact(i); + } + return r.apply(a); + }, + log() { + var a = this, r; + if (a.emin != 0) + throw Range("log argument must have a non zero constant term"); + r = integ(deriv(a) / a); + /* add the constant term */ + r += global.log(a[0]); + return r; + }, + }); + + add_props(Series, { + [Symbol.operatorOrder]: OT_SERIES, + /* new series of length n and first exponent emin */ + zero(n, emin) { + var r, i, obj; + + r = []; + for(i = 0; i < n; i++) + r[i] = 0; + /* we return an array and store emin in its prototype */ + obj = Object.create(Series.prototype); + obj.emin = emin; + Object.setPrototypeOf(r, obj); + return r; + }, + [Symbol.operatorAdd](v1, v2) { + var tmp, d, emin, n, r, i, j, v2_emin, c1, c2; + if (!(v1 instanceof Series)) { + tmp = v1; + v1 = v2; + v2 = tmp; + } + d = v1.emin + v1.length; + if (v2.constructor[Symbol.operatorOrder] <= OT_POLY) { + v2 = Polynomial(v2); + if (d <= 0) + return v1; + v2_emin = 0; + } else if (v2 instanceof RationalFunction) { + /* compute the emin of the rational fonction */ + i = get_emin(v2.num) - get_emin(v2.den); + if (d <= i) + return v1; + /* compute the serie with the required terms */ + v2 = Series(v2, d - i); + v2_emin = v2.emin; + } else { + v2_emin = v2.emin; + d = Math.min(d, v2_emin + v2.length); + } + emin = Math.min(v1.emin, v2_emin); + n = d - emin; + r = Series.zero(n, emin); + /* XXX: slow */ + for(i = emin; i < d; i++) { + j = i - v1.emin; + if (j >= 0 && j < v1.length) + c1 = v1[j]; + else + c1 = 0; + j = i - v2_emin; + if (j >= 0 && j < v2.length) + c2 = v2[j]; + else + c2 = 0; + r[i - emin] = c1 + c2; + } + return r.trim(); + }, + [Symbol.operatorSub](a, b) { + return Series[Symbol.operatorAdd](a, -b); + }, + [Symbol.operatorMul](v1, v2) { + var n, i, j, r, n, emin, n1, n2, k; + if (!(v1 instanceof Series)) + v1 = Series(v1, v2.length); + else if (!(v2 instanceof Series)) + v2 = Series(v2, v1.length); + emin = v1.emin + v2.emin; + n = Math.min(v1.length, v2.length); + n1 = v1.length; + n2 = v2.length; + r = Series.zero(n, emin); + for(i = 0; i < n1; i++) { + k = Math.min(n2, n - i); + for(j = 0; j < k; j++) { + r[i + j] += v1[i] * v2[j]; + } + } + return r.trim(); + }, + [Symbol.operatorDiv](v1, v2) { + if (!(v2 instanceof Series)) + v2 = Series(v2, v1.length); + return Series[Symbol.operatorMul](v1, v2.inverse()); + }, + [Symbol.operatorPow](a, b) { + if (Integer.isInteger(b)) { + return generic_pow(a, b); + } else { + if (!(a instanceof Series)) + a = Series(a, b.length); + return exp(log(a) * b); + } + }, + [Symbol.operatorCmpEQ](a, b) { + var n, i; + if (!(a instanceof Series) || + !(b instanceof Series)) + return false; + if (a.emin != b.emin) + return false; + n = a.length; + if (n != b.length) + return false; + for(i = 0; i < n; i++) { + if (a[i] != b[i]) + return false; + } + return true; + }, + O(a) { + function ErrorO() { + return TypeError("invalid O() argument"); + } + var n; + if (a.constructor[Symbol.operatorOrder] <= OT_POLY) { + a = Polynomial(a); + n = a.deg(); + if (n < 0) + throw ErrorO(); + } else if (a instanceof RationalFunction) { + if (a.num.deg() != 0) + throw ErrorO(); + n = a.den.deg(); + if (n < 0) + throw ErrorO(); + n = -n; + } else + throw ErrorO(); + return Series.zero(0, n); + }, + }); + + /* Array (Matrix) */ + + Matrix = function Matrix(h, w) { + var i, j, r, rl; + if (typeof w === "undefined") + w = h; + r = []; + for(i = 0; i < h; i++) { + rl = []; + for(j = 0; j < w; j++) + rl[j] = 0; + r[i] = rl; + } + return r; + }; + + add_props(Matrix, { + idn(n) { + var r, i; + r = Matrix(n, n); + for(i = 0; i < n; i++) + r[i][i] = 1; + return r; + }, + diag(a) { + var r, i, n; + n = a.length; + r = Matrix(n, n); + for(i = 0; i < n; i++) + r[i][i] = a[i]; + return r; + }, + hilbert(n) { + var i, j, r; + r = Matrix(n); + for(i = 0; i < n; i++) { + for(j = 0; j < n; j++) { + r[i][j] = 1 / (1 + i + j); + } + } + return r; + }, + trans(a) { + var h, w, r, i, j; + if (!Array.isArray(a)) + throw TypeError("matrix expected"); + h = a.length; + if (!Array.isArray(a[0])) { + w = 1; + r = Matrix(w, h); + for(i = 0; i < h; i++) { + r[0][i] = a[i]; + } + } else { + w = a[0].length; + r = Matrix(w, h); + for(i = 0; i < h; i++) { + for(j = 0; j < w; j++) { + r[j][i] = a[i][j]; + } + } + } + return r; + }, + check_square(a) { + var a, n; + if (!Array.isArray(a)) + throw TypeError("array expected"); + n = a.length; + if (!Array.isArray(a[0]) || n != a[0].length) + throw TypeError("square matrix expected"); + return n; + }, + trace(a) { + var n, r, i; + n = Matrix.check_square(a); + r = a[0][0]; + for(i = 1; i < n; i++) { + r += a[i][i]; + } + return r; + }, + charpoly(a) { + var n, p, c, i, j, coef; + n = Matrix.check_square(a); + p = []; + for(i = 0; i < n + 1; i++) + p[i] = 0; + p[n] = 1; + c = Matrix.idn(n); + for(i = 0; i < n; i++) { + c = c * a; + coef = -trace(c) / (i + 1); + p[n - i - 1] = coef; + for(j = 0; j < n; j++) + c[j][j] += coef; + } + return Polynomial(p); + }, + eigenvals(a) { + return Polynomial.roots(Matrix.charpoly(a)); + }, + det(a) { + var n, i, j, k, s, src, v, c; + + n = Matrix.check_square(a); + s = 1; + src = a.dup(); + for(i=0;i") +DEF(_ret_, "") +DEF(_var_, "") +DEF(_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, "") +DEF(computed_field, "") +DEF(static_computed_field, "") /* must come after computed_fields */ +DEF(class_fields_init, "") +DEF(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(status, "status") +DEF(reason, "reason") +DEF(globalThis, "globalThis") +#ifdef CONFIG_BIGNUM +DEF(bigint, "bigint") +DEF(bigfloat, "bigfloat") +DEF(bigdecimal, "bigdecimal") +DEF(roundingMode, "roundingMode") +DEF(maximumSignificantDigits, "maximumSignificantDigits") +DEF(maximumFractionDigits, "maximumFractionDigits") +#endif +#ifdef CONFIG_ATOMICS +DEF(not_equal, "not-equal") +DEF(timed_out, "timed-out") +DEF(ok, "ok") +#endif +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") +#ifdef CONFIG_BIGNUM +DEF(BigInt64Array, "BigInt64Array") +DEF(BigUint64Array, "BigUint64Array") +#endif +DEF(Float32Array, "Float32Array") +DEF(Float64Array, "Float64Array") +DEF(DataView, "DataView") +#ifdef CONFIG_BIGNUM +DEF(BigInt, "BigInt") +DEF(BigFloat, "BigFloat") +DEF(BigFloatEnv, "BigFloatEnv") +DEF(BigDecimal, "BigDecimal") +#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, "") +/* 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_operatorOrder, "Symbol.operatorOrder") +DEF(Symbol_operatorAdd, "Symbol.operatorAdd") +DEF(Symbol_operatorSub, "Symbol.operatorSub") +DEF(Symbol_operatorMul, "Symbol.operatorMul") +DEF(Symbol_operatorDiv, "Symbol.operatorDiv") +DEF(Symbol_operatorMod, "Symbol.operatorMod") +DEF(Symbol_operatorPow, "Symbol.operatorPow") +DEF(Symbol_operatorShl, "Symbol.operatorShl") +DEF(Symbol_operatorShr, "Symbol.operatorShr") +DEF(Symbol_operatorAnd, "Symbol.operatorAnd") +DEF(Symbol_operatorOr, "Symbol.operatorOr") +DEF(Symbol_operatorXor, "Symbol.operatorXor") +DEF(Symbol_operatorCmpLT, "Symbol.operatorCmpLT") +DEF(Symbol_operatorCmpLE, "Symbol.operatorCmpLE") +DEF(Symbol_operatorCmpEQ, "Symbol.operatorCmpEQ") +DEF(Symbol_operatorPlus, "Symbol.operatorPlus") +DEF(Symbol_operatorNeg, "Symbol.operatorNeg") +DEF(Symbol_operatorNot, "Symbol.operatorNot") +DEF(Symbol_operatorInc, "Symbol.operatorInc") +DEF(Symbol_operatorDec, "Symbol.operatorDec") +DEF(Symbol_operatorMathMod, "Symbol.operatorMathMod") +#endif + +#endif /* DEF */ diff --git a/quickjs-libc.c b/quickjs-libc.c new file mode 100644 index 0000000..da53fef --- /dev/null +++ b/quickjs-libc.c @@ -0,0 +1,2804 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2020 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#include +#if defined(__APPLE__) +typedef sig_t sighandler_t; +#endif +#endif + +#include "cutils.h" +#include "list.h" +#include "quickjs-libc.h" + +static void js_std_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt); +} + +/* TODO: + - add worker + - add minimal VT100 emulation for win32 + - add socket calls +*/ + +typedef struct { + struct list_head link; + int fd; + JSValue rw_func[2]; +} JSOSRWHandler; + +typedef struct { + struct list_head link; + int sig_num; + JSValue func; +} JSOSSignalHandler; + +typedef struct { + struct list_head link; + BOOL has_object; + int64_t timeout; + JSValue func; +} JSOSTimer; + +/* initialize the lists so js_std_free_handlers() can always be called */ +static struct list_head os_rw_handlers = LIST_HEAD_INIT(os_rw_handlers); +static struct list_head os_signal_handlers = LIST_HEAD_INIT(os_signal_handlers); +static struct list_head os_timers = LIST_HEAD_INIT(os_timers); +static uint64_t os_pending_signals; +static int eval_script_recurse; +static int (*os_poll_func)(JSContext *ctx); + +static JSValue js_printf_internal(JSContext *ctx, + int argc, JSValueConst *argv, FILE *fp) +{ + char fmtbuf[32]; + uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; + JSValue res; + DynBuf dbuf; + const char *fmt_str; + const uint8_t *fmt, *fmt_end; + const uint8_t *p; + char *q; + int i, c, len; + size_t fmt_len; + int32_t int32_arg; + int64_t int64_arg; + double double_arg; + const char *string_arg; + enum { PART_FLAGS, PART_WIDTH, PART_DOT, PART_PREC, PART_MODIFIER } part; + int modsize; + /* Use indirect call to dbuf_printf to prevent gcc warning */ + int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf; + + js_std_dbuf_init(ctx, &dbuf); + + if (argc > 0) { + fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]); + if (!fmt_str) + goto fail; + + i = 1; + fmt = (const uint8_t *)fmt_str; + fmt_end = fmt + fmt_len; + while (fmt < fmt_end) { + for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++) + continue; + dbuf_put(&dbuf, p, fmt - p); + if (fmt >= fmt_end) + break; + q = fmtbuf; + *q++ = *fmt++; /* copy '%' */ + part = PART_FLAGS; + modsize = 0; + for (;;) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + + c = *fmt++; + *q++ = c; + *q = '\0'; + + switch (c) { + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if (part != PART_PREC) { + if (part <= PART_WIDTH) + part = PART_WIDTH; + else + goto invalid; + } + continue; + + case '0': case '#': case '+': case '-': case ' ': case '\'': + if (part > PART_FLAGS) + goto invalid; + continue; + + case '.': + if (part > PART_DOT) + goto invalid; + part = PART_DOT; + continue; + + case '*': + if (part < PART_WIDTH) + part = PART_DOT; + else if (part == PART_DOT) + part = PART_MODIFIER; + else + goto invalid; + + if (i >= argc) + goto missing; + + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); + continue; + + case 'h': + if (modsize != 0 && modsize != -1) + goto invalid; + modsize--; + part = PART_MODIFIER; + continue; + case 'l': + q--; + if (modsize != 0 && modsize != 1) + goto invalid; + modsize++; + part = PART_MODIFIER; + continue; + + case 'c': + if (i >= argc) + goto missing; + if (JS_IsString(argv[i])) { + string_arg = JS_ToCString(ctx, argv[i++]); + if (!string_arg) + goto fail; + int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); + JS_FreeCString(ctx, string_arg); + } else { + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + } + /* handle utf-8 encoding explicitly */ + if ((unsigned)int32_arg > 0x10FFFF) + int32_arg = 0xFFFD; + /* ignore conversion flags, width and precision */ + len = unicode_to_utf8(cbuf, int32_arg); + dbuf_put(&dbuf, cbuf, len); + break; + + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (i >= argc) + goto missing; + if (modsize > 0) { + if (JS_ToInt64(ctx, &int64_arg, argv[i++])) + goto fail; + q[1] = q[-1]; + q[-1] = q[0] = 'l'; + q[2] = '\0'; + dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg); + } else { + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + dbuf_printf_fun(&dbuf, fmtbuf, int32_arg); + } + break; + + case 's': + if (i >= argc) + goto missing; + string_arg = JS_ToCString(ctx, argv[i++]); + if (!string_arg) + goto fail; + dbuf_printf_fun(&dbuf, fmtbuf, string_arg); + JS_FreeCString(ctx, string_arg); + break; + + case 'e': + case 'f': + case 'g': + case 'a': + case 'E': + case 'F': + case 'G': + case 'A': + if (i >= argc) + goto missing; + if (JS_ToFloat64(ctx, &double_arg, argv[i++])) + goto fail; + dbuf_printf_fun(&dbuf, fmtbuf, double_arg); + break; + + case '%': + dbuf_putc(&dbuf, '%'); + break; + + default: + /* XXX: should support an extension mechanism */ + invalid: + JS_ThrowTypeError(ctx, "invalid conversion specifier in format string"); + goto fail; + missing: + JS_ThrowReferenceError(ctx, "missing argument for conversion specifier"); + goto fail; + } + break; + } + } + JS_FreeCString(ctx, fmt_str); + } + if (dbuf.error) { + res = JS_ThrowOutOfMemory(ctx); + } else { + if (fp) { + len = fwrite(dbuf.buf, 1, dbuf.size, fp); + res = JS_NewInt32(ctx, len); + } else { + res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + } + } + dbuf_free(&dbuf); + return res; + +fail: + dbuf_free(&dbuf); + return JS_EXCEPTION; +} + +uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) +{ + FILE *f; + uint8_t *buf; + size_t buf_len; + long lret; + + f = fopen(filename, "rb"); + if (!f) + return NULL; + if (fseek(f, 0, SEEK_END) < 0) + goto fail; + lret = ftell(f); + if (lret < 0) + goto fail; + /* XXX: on Linux, ftell() return LONG_MAX for directories */ + if (lret == LONG_MAX) { + errno = EISDIR; + goto fail; + } + buf_len = lret; + if (fseek(f, 0, SEEK_SET) < 0) + goto fail; + if (ctx) + buf = js_malloc(ctx, buf_len + 1); + else + buf = malloc(buf_len + 1); + if (!buf) + goto fail; + if (fread(buf, 1, buf_len, f) != buf_len) { + errno = EIO; + if (ctx) + js_free(ctx, buf); + else + free(buf); + fail: + fclose(f); + return NULL; + } + buf[buf_len] = '\0'; + fclose(f); + *pbuf_len = buf_len; + return buf; +} + +/* load and evaluate a file */ +static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint8_t *buf; + const char *filename; + JSValue ret; + size_t buf_len; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load '%s'", filename); + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + ret = JS_Eval(ctx, (char *)buf, buf_len, filename, + JS_EVAL_TYPE_GLOBAL); + js_free(ctx, buf); + JS_FreeCString(ctx, filename); + return ret; +} + +typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, + const char *module_name); + + +#if defined(_WIN32) +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); + return NULL; +} +#else +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JSModuleDef *m; + void *hd; + JSInitModuleFunc *init; + char *filename; + + if (!strchr(module_name, '/')) { + /* must add a '/' so that the DLL is not searched in the + system library paths */ + filename = js_malloc(ctx, strlen(module_name) + 2 + 1); + if (!filename) + return NULL; + strcpy(filename, "./"); + strcpy(filename + 2, module_name); + } else { + filename = (char *)module_name; + } + + /* C module */ + hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); + if (filename != module_name) + js_free(ctx, filename); + if (!hd) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library", + module_name); + goto fail; + } + + init = dlsym(hd, "js_init_module"); + if (!init) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", + module_name); + goto fail; + } + + m = init(ctx, module_name); + if (!m) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", + module_name); + fail: + if (hd) + dlclose(hd); + return NULL; + } + return m; +} +#endif /* !_WIN32 */ + +int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, + JS_BOOL use_realpath, JS_BOOL is_main) +{ + JSModuleDef *m; + char buf[PATH_MAX + 16]; + JSValue meta_obj; + JSAtom module_name_atom; + const char *module_name; + + assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); + m = JS_VALUE_GET_PTR(func_val); + + module_name_atom = JS_GetModuleName(ctx, m); + module_name = JS_AtomToCString(ctx, module_name_atom); + JS_FreeAtom(ctx, module_name_atom); + if (!module_name) + return -1; + if (!strchr(module_name, ':')) { + strcpy(buf, "file://"); +#if !defined(_WIN32) + /* realpath() cannot be used with modules compiled with qjsc + because the corresponding module source code is not + necessarily present */ + if (use_realpath) { + char *res = realpath(module_name, buf + strlen(buf)); + if (!res) { + JS_ThrowTypeError(ctx, "realpath failure"); + JS_FreeCString(ctx, module_name); + return -1; + } + } else +#endif + { + pstrcat(buf, sizeof(buf), module_name); + } + } else { + pstrcpy(buf, sizeof(buf), module_name); + } + JS_FreeCString(ctx, module_name); + + meta_obj = JS_GetImportMeta(ctx, m); + if (JS_IsException(meta_obj)) + return -1; + JS_DefinePropertyValueStr(ctx, meta_obj, "url", + JS_NewString(ctx, buf), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, meta_obj, "main", + JS_NewBool(ctx, is_main), + JS_PROP_C_W_E); + JS_FreeValue(ctx, meta_obj); + return 0; +} + +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + + if (has_suffix(module_name, ".so")) { + m = js_module_loader_so(ctx, module_name); + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + /* XXX: could propagate the exception */ + js_module_set_import_meta(ctx, func_val, TRUE, FALSE); + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int status; + if (JS_ToInt32(ctx, &status, argv[0])) + status = -1; + exit(status); + return JS_UNDEFINED; +} + +static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name, *str; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + str = getenv(name); + JS_FreeCString(ctx, name); + if (!str) + return JS_UNDEFINED; + else + return JS_NewString(ctx, str); +} + +static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +static int interrupt_handler(JSRuntime *rt, void *opaque) +{ + return (os_pending_signals >> SIGINT) & 1; +} + +static int get_bool_option(JSContext *ctx, BOOL *pbool, + JSValueConst obj, + const char *option) +{ + JSValue val; + val = JS_GetPropertyStr(ctx, obj, option); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) { + *pbool = JS_ToBool(ctx, val); + } + JS_FreeValue(ctx, val); + return 0; +} + +static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *str; + size_t len; + JSValue ret; + JSValueConst options_obj; + BOOL backtrace_barrier = FALSE; + int flags; + + if (argc >= 2) { + options_obj = argv[1]; + if (get_bool_option(ctx, &backtrace_barrier, options_obj, + "backtrace_barrier")) + return JS_EXCEPTION; + } + + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + if (++eval_script_recurse == 1) { + /* install the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); + } + flags = JS_EVAL_TYPE_GLOBAL; + if (backtrace_barrier) + flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; + ret = JS_Eval(ctx, str, len, "", flags); + JS_FreeCString(ctx, str); + if (--eval_script_recurse == 0) { + /* remove the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL); + os_pending_signals &= ~((uint64_t)1 << SIGINT); + /* convert the uncatchable "interrupted" error into a normal error + so that it can be caught by the REPL */ + if (JS_IsException(ret)) + JS_ResetUncatchableError(ctx); + } + return ret; +} + +static JSClassID js_std_file_class_id; + +typedef struct { + FILE *f; + BOOL close_in_finalizer; + BOOL is_popen; +} JSSTDFile; + +static void js_std_file_finalizer(JSRuntime *rt, JSValue val) +{ + JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id); + if (s) { + if (s->f && s->close_in_finalizer) { + if (s->is_popen) + pclose(s->f); + else + fclose(s->f); + } + js_free_rt(rt, s); + } +} + +static JSValue js_new_std_error(JSContext *ctx, int err) +{ + JSValue obj; + /* XXX: could add a specific Error prototype */ + obj = JS_NewError(ctx); + JS_DefinePropertyValueStr(ctx, obj, "message", + JS_NewString(ctx, strerror(err)), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_DefinePropertyValueStr(ctx, obj, "errno", + JS_NewInt32(ctx, err), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + return obj; +} + +static JSValue js_std_error_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + int err; + if (JS_ToInt32(ctx, &err, argv[0])) + return JS_EXCEPTION; + return js_new_std_error(ctx, err); +} + +static JSValue js_std_error_strerror(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int err; + if (JS_ToInt32(ctx, &err, argv[0])) + return JS_EXCEPTION; + return JS_NewString(ctx, strerror(err)); +} + +static JSValue js_std_throw_errno(JSContext *ctx, int err) +{ + JSValue obj; + obj = js_new_std_error(ctx, err); + if (JS_IsException(obj)) + obj = JS_NULL; + return JS_Throw(ctx, obj); +} + +static JSValue js_new_std_file(JSContext *ctx, FILE *f, + BOOL close_in_finalizer, + BOOL is_popen) +{ + JSSTDFile *s; + JSValue obj; + obj = JS_NewObjectClass(ctx, js_std_file_class_id); + if (JS_IsException(obj)) + return obj; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + s->close_in_finalizer = close_in_finalizer; + s->is_popen = is_popen; + s->f = f; + JS_SetOpaque(obj, s); + return obj; +} + +static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+b")] != '\0') { + js_std_throw_errno(ctx, EINVAL); + goto fail; + } + + f = fopen(filename, mode); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + if (!f) + return js_std_throw_errno(ctx, errno); + return js_new_std_file(ctx, f, TRUE, FALSE); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rw")] != '\0') { + js_std_throw_errno(ctx, EINVAL); + goto fail; + } + + f = popen(filename, mode); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + if (!f) + return js_std_throw_errno(ctx, errno); + return js_new_std_file(ctx, f, TRUE, TRUE); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *mode; + FILE *f; + int fd; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+")] != '\0') { + js_std_throw_errno(ctx, EINVAL); + goto fail; + } + + f = fdopen(fd, mode); + JS_FreeCString(ctx, mode); + if (!f) + return js_std_throw_errno(ctx, errno); + return js_new_std_file(ctx, f, TRUE, FALSE); + fail: + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f; + f = tmpfile(); + if (!f) + return js_std_throw_errno(ctx, errno); + return js_new_std_file(ctx, f, TRUE, FALSE); +} + +static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_printf_internal(ctx, argc, argv, NULL); +} + +static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_printf_internal(ctx, argc, argv, stdout); +} + +static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj) +{ + JSSTDFile *s = JS_GetOpaque2(ctx, obj, js_std_file_class_id); + if (!s) + return NULL; + if (!s->f) { + js_std_throw_errno(ctx, EBADF); + return NULL; + } + return s->f; +} + +static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + FILE *f; + int i; + const char *str; + + if (magic == 0) { + f = stdout; + } else { + f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + } + + for(i = 0; i < argc; i++) { + str = JS_ToCString(ctx, argv[i]); + if (!str) + return JS_EXCEPTION; + fputs(str, f); + JS_FreeCString(ctx, str); + } + return JS_UNDEFINED; +} + +static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSSTDFile *s = JS_GetOpaque2(ctx, this_val, js_std_file_class_id); + if (!s) + return JS_EXCEPTION; + if (!s->f) + return js_std_throw_errno(ctx, EBADF); + /* XXX: could return exit code */ + if (s->is_popen) + pclose(s->f); + else + fclose(s->f); + s->f = NULL; + return JS_UNDEFINED; +} + +static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return js_printf_internal(ctx, argc, argv, f); +} + +static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + fflush(f); + return JS_UNDEFINED; +} + +static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + if (!f) + return JS_EXCEPTION; +#if defined(__linux__) + pos = ftello(f); +#else + pos = ftell(f); +#endif + return JS_NewInt64(ctx, pos); +} + +static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + int whence, ret; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt64(ctx, &pos, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[1])) + return JS_EXCEPTION; +#if defined(__linux__) + ret = fseeko(f, pos, whence); +#else + ret = fseek(f, pos, whence); +#endif + if (ret < 0) + return js_std_throw_errno(ctx, EBADF); + return JS_UNDEFINED; +} + +static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewBool(ctx, feof(f)); +} + +static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fileno(f)); +} + +static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + FILE *f = js_std_file_get(ctx, this_val); + uint64_t pos, len; + size_t size, ret; + uint8_t *buf; + + if (!f) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[0]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = fwrite(buf + pos, 1, len, f); + else + ret = fread(buf + pos, 1, len, f); + return JS_NewInt64(ctx, ret); +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + + if (!f) + return JS_EXCEPTION; + + js_std_dbuf_init(ctx, &dbuf); + for(;;) { + c = fgetc(f); + if (c == EOF) { + if (dbuf.size == 0) { + /* EOF */ + dbuf_free(&dbuf); + return JS_NULL; + } else { + break; + } + } + if (c == '\n') + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_ThrowOutOfMemory(ctx); + } + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_readAsString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + uint64_t max_size64; + size_t max_size; + JSValueConst max_size_val; + + if (!f) + return JS_EXCEPTION; + + if (argc >= 1) + max_size_val = argv[0]; + else + max_size_val = JS_UNDEFINED; + max_size = (size_t)-1; + if (!JS_IsUndefined(max_size_val)) { + if (JS_ToIndex(ctx, &max_size64, max_size_val)) + return JS_EXCEPTION; + if (max_size64 < max_size) + max_size = max_size64; + } + + js_std_dbuf_init(ctx, &dbuf); + while (max_size != 0) { + c = fgetc(f); + if (c == EOF) + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_EXCEPTION; + } + max_size--; + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fgetc(f)); +} + +static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &c, argv[0])) + return JS_EXCEPTION; + c = fputc(c, f); + return JS_NewInt32(ctx, c); +} + +/* urlGet */ + +#define URL_GET_PROGRAM "curl -s -i" +#define URL_GET_BUF_SIZE 4096 + +static int http_get_header_line(FILE *f, char *buf, size_t buf_size, + DynBuf *dbuf) +{ + int c; + char *p; + + p = buf; + for(;;) { + c = fgetc(f); + if (c < 0) + return -1; + if ((p - buf) < buf_size - 1) + *p++ = c; + if (dbuf) + dbuf_putc(dbuf, c); + if (c == '\n') + break; + } + *p = '\0'; + return 0; +} + +static int http_get_status(const char *buf) +{ + const char *p = buf; + while (*p != ' ' && *p != '\0') + p++; + if (*p != ' ') + return 0; + while (*p == ' ') + p++; + return atoi(p); +} + +static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *url; + DynBuf cmd_buf; + DynBuf data_buf_s, *data_buf = &data_buf_s; + DynBuf header_buf_s, *header_buf = &header_buf_s; + char *buf; + size_t i, len; + int c, status; + JSValue response = JS_UNDEFINED, ret_obj; + JSValueConst options_obj; + FILE *f; + BOOL binary_flag, full_flag; + + url = JS_ToCString(ctx, argv[0]); + if (!url) + return JS_EXCEPTION; + + binary_flag = FALSE; + full_flag = FALSE; + + if (argc >= 2) { + options_obj = argv[1]; + + if (get_bool_option(ctx, &binary_flag, options_obj, "binary")) + goto fail_obj; + + if (get_bool_option(ctx, &full_flag, options_obj, "full")) { + fail_obj: + JS_FreeCString(ctx, url); + return JS_EXCEPTION; + } + } + + js_std_dbuf_init(ctx, &cmd_buf); + dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM); + len = strlen(url); + for(i = 0; i < len; i++) { + c = url[i]; + if (c == '\'' || c == '\\') + dbuf_putc(&cmd_buf, '\\'); + dbuf_putc(&cmd_buf, c); + } + JS_FreeCString(ctx, url); + dbuf_putstr(&cmd_buf, "''"); + dbuf_putc(&cmd_buf, '\0'); + if (dbuf_error(&cmd_buf)) { + dbuf_free(&cmd_buf); + return JS_EXCEPTION; + } + // printf("%s\n", (char *)cmd_buf.buf); + f = popen((char *)cmd_buf.buf, "r"); + dbuf_free(&cmd_buf); + if (!f) { + return js_std_throw_errno(ctx, errno); + } + + js_std_dbuf_init(ctx, data_buf); + js_std_dbuf_init(ctx, header_buf); + + buf = js_malloc(ctx, URL_GET_BUF_SIZE); + if (!buf) + goto fail; + + /* get the HTTP status */ + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) + goto bad_header; + status = http_get_status(buf); + if (!full_flag && !(status >= 200 && status <= 299)) { + js_std_throw_errno(ctx, ENOENT); + goto fail; + } + + /* wait until there is an empty line */ + for(;;) { + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { + bad_header: + js_std_throw_errno(ctx, EINVAL); + goto fail; + } + if (!strcmp(buf, "\r\n")) + break; + } + if (dbuf_error(header_buf)) + goto fail; + header_buf->size -= 2; /* remove the trailing CRLF */ + + /* download the data */ + for(;;) { + len = fread(buf, 1, URL_GET_BUF_SIZE, f); + if (len == 0) + break; + dbuf_put(data_buf, (uint8_t *)buf, len); + } + js_free(ctx, buf); + buf = NULL; + pclose(f); + f = NULL; + + if (dbuf_error(data_buf)) + goto fail; + if (binary_flag) { + response = JS_NewArrayBufferCopy(ctx, + data_buf->buf, data_buf->size); + } else { + response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size); + } + dbuf_free(data_buf); + data_buf = NULL; + if (JS_IsException(response)) + goto fail; + + if (full_flag) { + ret_obj = JS_NewObject(ctx); + if (JS_IsException(ret_obj)) + goto fail; + JS_DefinePropertyValueStr(ctx, ret_obj, "response", + response, + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders", + JS_NewStringLen(ctx, (char *)header_buf->buf, + header_buf->size), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, ret_obj, "status", + JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + } else { + ret_obj = response; + } + dbuf_free(header_buf); + return ret_obj; + fail: + if (f) + pclose(f); + js_free(ctx, buf); + if (data_buf) + dbuf_free(data_buf); + if (header_buf) + dbuf_free(header_buf); + JS_FreeValue(ctx, response); + return JS_EXCEPTION; +} + +static JSClassDef js_std_file_class = { + "FILE", + .finalizer = js_std_file_finalizer, +}; + +static const JSCFunctionListEntry js_std_funcs[] = { + JS_CFUNC_DEF("exit", 1, js_std_exit ), + JS_CFUNC_DEF("gc", 0, js_std_gc ), + JS_CFUNC_DEF("evalScript", 1, js_evalScript ), + JS_CFUNC_DEF("loadScript", 1, js_loadScript ), + JS_CFUNC_DEF("getenv", 1, js_std_getenv ), + JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), + + /* FILE I/O */ + JS_CFUNC_DEF("open", 2, js_std_open ), + JS_CFUNC_DEF("popen", 2, js_std_popen ), + JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ), + JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ), + JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ), + JS_CFUNC_DEF("printf", 1, js_std_printf ), + JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ), + JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), + + /* setenv, ... */ +}; + +static const JSCFunctionListEntry js_std_error_funcs[] = { + JS_CFUNC_DEF("strerror", 1, js_std_error_strerror ), + /* various errno values */ +#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + DEF(EINVAL), + DEF(EIO), + DEF(EACCES), + DEF(EEXIST), + DEF(ENOSPC), + DEF(ENOSYS), + DEF(EBUSY), + DEF(ENOENT), + DEF(EPERM), + DEF(EPIPE), + DEF(EBADF), +#undef DEF +}; + +static const JSCFunctionListEntry js_std_file_proto_funcs[] = { + JS_CFUNC_DEF("close", 0, js_std_file_close ), + JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ), + JS_CFUNC_DEF("printf", 1, js_std_file_printf ), + JS_CFUNC_DEF("flush", 0, js_std_file_flush ), + JS_CFUNC_DEF("tell", 0, js_std_file_tell ), + JS_CFUNC_DEF("seek", 2, js_std_file_seek ), + JS_CFUNC_DEF("eof", 0, js_std_file_eof ), + JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ), + JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ), + JS_CFUNC_DEF("getline", 0, js_std_file_getline ), + JS_CFUNC_DEF("readAsString", 0, js_std_file_readAsString ), + JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ), + JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ), + /* setvbuf, ferror, clearerr, ... */ +}; + +static int js_std_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue proto, obj; + + /* FILE class */ + /* the class ID is created once */ + JS_NewClassID(&js_std_file_class_id); + /* the class is created once per runtime */ + JS_NewClass(JS_GetRuntime(ctx), js_std_file_class_id, &js_std_file_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs, + countof(js_std_file_proto_funcs)); + JS_SetClassProto(ctx, js_std_file_class_id, proto); + + JS_SetModuleExportList(ctx, m, js_std_funcs, + countof(js_std_funcs)); + JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE)); + JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE)); + JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE)); + + obj = JS_NewCFunction2(ctx, js_std_error_constructor, + "Error", 1, JS_CFUNC_constructor, 0); + JS_SetPropertyFunctionList(ctx, obj, js_std_error_funcs, + countof(js_std_error_funcs)); + JS_SetModuleExport(ctx, m, "Error", obj); + + return 0; +} + +JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_std_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs)); + JS_AddModuleExport(ctx, m, "in"); + JS_AddModuleExport(ctx, m, "out"); + JS_AddModuleExport(ctx, m, "err"); + JS_AddModuleExport(ctx, m, "Error"); + return m; +} + +/**********************************************************/ +/* 'os' object */ + +static JSValue js_os_return(JSContext *ctx, ssize_t ret) +{ + if (ret < 0) + ret = -errno; + return JS_NewInt64(ctx, ret); +} + +static JSValue js_os_open(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + int flags, mode, ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &flags, argv[1])) + goto fail; + if (argc >= 3 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32(ctx, &mode, argv[2])) { + fail: + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + } else { + mode = 0666; + } +#if defined(_WIN32) + /* force binary mode by default */ + if (!(flags & O_TEXT)) + flags |= O_BINARY; +#endif + ret = open(filename, flags, mode); + JS_FreeCString(ctx, filename); + return js_os_return(ctx, ret); +} + +static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, ret; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = close(fd); + return js_os_return(ctx, ret); +} + +static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, whence, ret; + int64_t pos; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt64(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[2])) + return JS_EXCEPTION; + ret = lseek(fd, pos, whence); + return js_os_return(ctx, ret); +} + +static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + int fd; + uint64_t pos, len; + size_t size; + ssize_t ret; + uint8_t *buf; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[2])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[3])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[1]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = write(fd, buf + pos, len); + else + ret = read(fd, buf + pos, len); + return js_os_return(ctx, ret); +} + +static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + return JS_NewBool(ctx, isatty(fd) == 1); +} + +#if defined(_WIN32) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + + if (!GetConsoleScreenBufferInfo(handle, &info)) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E); + return obj; +} + +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + HANDLE handle; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + + SetConsoleMode(handle, ENABLE_WINDOW_INPUT); + return JS_UNDEFINED; +} +#else +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + struct winsize ws; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && + ws.ws_col >= 4 && ws.ws_row >= 4) { + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E); + return obj; + } else { + return JS_NULL; + } +} + +static struct termios oldtty; + +static void term_exit(void) +{ + tcsetattr(0, TCSANOW, &oldtty); +} + +/* XXX: should add a way to go back to normal mode */ +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + struct termios tty; + int fd; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + + memset(&tty, 0, sizeof(tty)); + tcgetattr(fd, &tty); + oldtty = tty; + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + tcsetattr(fd, TCSANOW, &tty); + + atexit(term_exit); + return JS_UNDEFINED; +} + +#endif /* !_WIN32 */ + +static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + int ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + ret = remove(filename); + JS_FreeCString(ctx, filename); + return js_os_return(ctx, ret); +} + +static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *oldpath, *newpath; + int ret; + + oldpath = JS_ToCString(ctx, argv[0]); + if (!oldpath) + return JS_EXCEPTION; + newpath = JS_ToCString(ctx, argv[1]); + if (!newpath) { + JS_FreeCString(ctx, oldpath); + return JS_EXCEPTION; + } + ret = rename(oldpath, newpath); + JS_FreeCString(ctx, oldpath); + JS_FreeCString(ctx, newpath); + return js_os_return(ctx, ret); +} + +static JSOSRWHandler *find_rh(int fd) +{ + JSOSRWHandler *rh; + struct list_head *el; + list_for_each(el, &os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == fd) + return rh; + } + return NULL; +} + +static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) +{ + int i; + list_del(&rh->link); + for(i = 0; i < 2; i++) { + JS_FreeValueRT(rt, rh->rw_func[i]); + } + js_free_rt(rt, rh); +} + +static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSOSRWHandler *rh; + int fd; + JSValueConst func; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + func = argv[1]; + if (JS_IsNull(func)) { + rh = find_rh(fd); + if (rh) { + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_NULL; + if (JS_IsNull(rh->rw_func[0]) && + JS_IsNull(rh->rw_func[1])) { + /* remove the entry */ + free_rw_handler(JS_GetRuntime(ctx), rh); + } + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + rh = find_rh(fd); + if (!rh) { + rh = js_mallocz(ctx, sizeof(*rh)); + if (!rh) + return JS_EXCEPTION; + rh->fd = fd; + rh->rw_func[0] = JS_NULL; + rh->rw_func[1] = JS_NULL; + list_add_tail(&rh->link, &os_rw_handlers); + } + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSOSSignalHandler *find_sh(int sig_num) +{ + JSOSSignalHandler *sh; + struct list_head *el; + list_for_each(el, &os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + if (sh->sig_num == sig_num) + return sh; + } + return NULL; +} + +static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh) +{ + list_del(&sh->link); + JS_FreeValueRT(rt, sh->func); + js_free_rt(rt, sh); +} + +static void os_signal_handler(int sig_num) +{ + os_pending_signals |= ((uint64_t)1 << sig_num); +} + +#if defined(_WIN32) +typedef void (*sighandler_t)(int sig_num); +#endif + +static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSOSSignalHandler *sh; + uint32_t sig_num; + JSValueConst func; + sighandler_t handler; + + if (JS_ToUint32(ctx, &sig_num, argv[0])) + return JS_EXCEPTION; + if (sig_num >= 64) + return JS_ThrowRangeError(ctx, "invalid signal number"); + func = argv[1]; + /* func = null: SIG_DFL, func = undefined, SIG_IGN */ + if (JS_IsNull(func) || JS_IsUndefined(func)) { + sh = find_sh(sig_num); + if (sh) { + free_sh(JS_GetRuntime(ctx), sh); + } + if (JS_IsNull(func)) + handler = SIG_DFL; + else + handler = SIG_IGN; + signal(sig_num, handler); + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + sh = find_sh(sig_num); + if (!sh) { + sh = js_mallocz(ctx, sizeof(*sh)); + if (!sh) + return JS_EXCEPTION; + sh->sig_num = sig_num; + list_add_tail(&sh->link, &os_signal_handlers); + } + JS_FreeValue(ctx, sh->func); + sh->func = JS_DupValue(ctx, func); + signal(sig_num, os_signal_handler); + } + return JS_UNDEFINED; +} + +#if defined(__linux__) || defined(__APPLE__) +static int64_t get_time_ms(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); +} +#else +/* more portable, but does not work if the date is updated */ +static int64_t get_time_ms(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); +} +#endif + +static void unlink_timer(JSRuntime *rt, JSOSTimer *th) +{ + if (th->link.prev) { + list_del(&th->link); + th->link.prev = th->link.next = NULL; + } +} + +static void free_timer(JSRuntime *rt, JSOSTimer *th) +{ + JS_FreeValueRT(rt, th->func); + js_free_rt(rt, th); +} + +static JSClassID js_os_timer_class_id; + +static void js_os_timer_finalizer(JSRuntime *rt, JSValue val) +{ + JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); + if (th) { + th->has_object = FALSE; + if (!th->link.prev) + free_timer(rt, th); + } +} + +static void js_os_timer_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); + if (th) { + JS_MarkValue(rt, th->func, mark_func); + } +} + +static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t delay; + JSValueConst func; + JSOSTimer *th; + JSValue obj; + + func = argv[0]; + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (JS_ToInt64(ctx, &delay, argv[1])) + return JS_EXCEPTION; + obj = JS_NewObjectClass(ctx, js_os_timer_class_id); + if (JS_IsException(obj)) + return obj; + th = js_mallocz(ctx, sizeof(*th)); + if (!th) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + th->has_object = TRUE; + th->timeout = get_time_ms() + delay; + th->func = JS_DupValue(ctx, func); + list_add_tail(&th->link, &os_timers); + JS_SetOpaque(obj, th); + return obj; +} + +static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id); + if (!th) + return JS_EXCEPTION; + unlink_timer(JS_GetRuntime(ctx), th); + return JS_UNDEFINED; +} + +static JSClassDef js_os_timer_class = { + "OSTimer", + .finalizer = js_os_timer_finalizer, + .gc_mark = js_os_timer_mark, +}; + +static void call_handler(JSContext *ctx, JSValueConst func) +{ + JSValue ret, func1; + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func1 = JS_DupValue(ctx, func); + ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); + JS_FreeValue(ctx, func1); + if (JS_IsException(ret)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, ret); +} + +#if defined(_WIN32) + +static int js_os_poll(JSContext *ctx) +{ + int min_delay, console_fd; + int64_t cur_time, delay; + JSOSRWHandler *rh; + struct list_head *el; + + /* XXX: handle signals if useful */ + + if (list_empty(&os_rw_handlers) && list_empty(&os_timers)) + return -1; /* no more events */ + + /* XXX: only timers and basic console input are supported */ + if (!list_empty(&os_timers)) { + cur_time = get_time_ms(); + min_delay = 10000; + list_for_each(el, &os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + delay = th->timeout - cur_time; + if (delay <= 0) { + JSValue func; + /* the timer expired */ + func = th->func; + th->func = JS_UNDEFINED; + unlink_timer(JS_GetRuntime(ctx), th); + if (!th->has_object) + free_timer(JS_GetRuntime(ctx), th); + call_handler(ctx, func); + JS_FreeValue(ctx, func); + return 0; + } else if (delay < min_delay) { + min_delay = delay; + } + } + } else { + min_delay = -1; + } + + console_fd = -1; + list_for_each(el, &os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + console_fd = rh->fd; + break; + } + } + + if (console_fd >= 0) { + DWORD ti, ret; + HANDLE handle; + if (min_delay == -1) + ti = INFINITE; + else + ti = min_delay; + handle = (HANDLE)_get_osfhandle(console_fd); + ret = WaitForSingleObject(handle, ti); + if (ret == WAIT_OBJECT_0) { + list_for_each(el, &os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { + call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + break; + } + } + } + } else { + Sleep(min_delay); + } + return 0; +} +#else +static int js_os_poll(JSContext *ctx) +{ + int ret, fd_max, min_delay; + int64_t cur_time, delay; + fd_set rfds, wfds; + JSOSRWHandler *rh; + struct list_head *el; + struct timeval tv, *tvp; + + if (unlikely(os_pending_signals != 0)) { + JSOSSignalHandler *sh; + uint64_t mask; + + list_for_each(el, &os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + mask = (uint64_t)1 << sh->sig_num; + if (os_pending_signals & mask) { + os_pending_signals &= ~mask; + call_handler(ctx, sh->func); + return 0; + } + } + } + + if (list_empty(&os_rw_handlers) && list_empty(&os_timers)) + return -1; /* no more events */ + + if (!list_empty(&os_timers)) { + cur_time = get_time_ms(); + min_delay = 10000; + list_for_each(el, &os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + delay = th->timeout - cur_time; + if (delay <= 0) { + JSValue func; + /* the timer expired */ + func = th->func; + th->func = JS_UNDEFINED; + unlink_timer(JS_GetRuntime(ctx), th); + if (!th->has_object) + free_timer(JS_GetRuntime(ctx), th); + call_handler(ctx, func); + JS_FreeValue(ctx, func); + return 0; + } else if (delay < min_delay) { + min_delay = delay; + } + } + tv.tv_sec = min_delay / 1000; + tv.tv_usec = (min_delay % 1000) * 1000; + tvp = &tv; + } else { + tvp = NULL; + } + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + fd_max = -1; + list_for_each(el, &os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + fd_max = max_int(fd_max, rh->fd); + if (!JS_IsNull(rh->rw_func[0])) + FD_SET(rh->fd, &rfds); + if (!JS_IsNull(rh->rw_func[1])) + FD_SET(rh->fd, &wfds); + } + + ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp); + if (ret > 0) { + list_for_each(el, &os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (!JS_IsNull(rh->rw_func[0]) && + FD_ISSET(rh->fd, &rfds)) { + call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + break; + } + if (!JS_IsNull(rh->rw_func[1])) { + FD_SET(rh->fd, &wfds); + call_handler(ctx, rh->rw_func[1]); + /* must stop because the list may have been modified */ + break; + } + } + } + return 0; +} +#endif /* !_WIN32 */ + +static JSValue make_obj_error(JSContext *ctx, + JSValue obj, + int err) +{ + JSValue arr; + if (JS_IsException(obj)) + return obj; + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return JS_EXCEPTION; + JS_DefinePropertyValueUint32(ctx, arr, 0, obj, + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err), + JS_PROP_C_W_E); + return arr; +} + +static JSValue make_string_error(JSContext *ctx, + const char *buf, + int err) +{ + return make_obj_error(ctx, JS_NewString(ctx, buf), err); +} + +/* return [cwd, errorcode] */ +static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char buf[PATH_MAX]; + int err; + + if (!getcwd(buf, sizeof(buf))) { + buf[0] = '\0'; + err = -errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} + +#if !defined(_WIN32) + +/* return [path, errorcode] */ +static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + char buf[PATH_MAX], *res; + int err; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = realpath(path, buf); + JS_FreeCString(ctx, path); + if (!res) { + buf[0] = '\0'; + err = -errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} + +static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int mode, ret; + const char *path; + + if (argc >= 2) { + if (JS_ToInt32(ctx, &mode, argv[1])) + return JS_EXCEPTION; + } else { + mode = 0777; + } + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + ret = mkdir(path, mode); + JS_FreeCString(ctx, path); + return js_os_return(ctx, ret); +} + +static int64_t timespec_to_ms(const struct timespec *tv) +{ + return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000); +} + +/* return [obj, errcode] */ +static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_lstat) +{ + const char *path; + int err, res; + struct stat st; + JSValue obj; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + if (is_lstat) + res = lstat(path, &st); + else + res = stat(path, &st); + JS_FreeCString(ctx, path); + if (res < 0) { + err = -errno; + obj = JS_NULL; + } else { + err = 0; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JS_DefinePropertyValueStr(ctx, obj, "dev", + JS_NewInt64(ctx, st.st_dev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ino", + JS_NewInt64(ctx, st.st_ino), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mode", + JS_NewInt32(ctx, st.st_mode), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "nlink", + JS_NewInt64(ctx, st.st_nlink), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "uid", + JS_NewInt64(ctx, st.st_uid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "gid", + JS_NewInt64(ctx, st.st_gid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "rdev", + JS_NewInt64(ctx, st.st_rdev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "size", + JS_NewInt64(ctx, st.st_size), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "blocks", + JS_NewInt64(ctx, st.st_blocks), + JS_PROP_C_W_E); +#if defined(__APPLE__) + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)), + JS_PROP_C_W_E); +#else + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)), + JS_PROP_C_W_E); +#endif + } + return make_obj_error(ctx, obj, err); +} + +static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *target, *linkpath; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + linkpath = JS_ToCString(ctx, argv[1]); + if (!linkpath) { + JS_FreeCString(ctx, target); + return JS_EXCEPTION; + } + err = symlink(target, linkpath); + JS_FreeCString(ctx, target); + JS_FreeCString(ctx, linkpath); + return js_os_return(ctx, err); +} + +/* return [path, errorcode] */ +static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + char buf[PATH_MAX]; + int err; + ssize_t res; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = readlink(path, buf, sizeof(buf) - 1); + JS_FreeCString(ctx, path); + if (res < 0) { + buf[0] = '\0'; + err = -errno; + } else { + buf[res] = '\0'; + err = 0; + } + return make_string_error(ctx, buf, err); +} + +/* return [array, errorcode] */ +static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + DIR *f; + struct dirent *d; + JSValue obj; + int err; + uint32_t len; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) { + JS_FreeCString(ctx, path); + return JS_EXCEPTION; + } + f = opendir(path); + JS_FreeCString(ctx, path); + err = 0; + if (!f) { + err = -errno; + goto done; + } + len = 0; + for(;;) { + errno = 0; + d = readdir(f); + if (!d) { + err = -errno; + break; + } + JS_DefinePropertyValueUint32(ctx, obj, len++, + JS_NewString(ctx, d->d_name), + JS_PROP_C_W_E); + } + closedir(f); + done: + return make_obj_error(ctx, obj, err); +} + +static void ms_to_timeval(struct timeval *tv, uint64_t v) +{ + tv->tv_sec = v / 1000; + tv->tv_usec = (v % 1000) * 1000; +} + +static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + int64_t atime, mtime; + int ret; + struct timeval times[2]; + + if (JS_ToInt64(ctx, &atime, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt64(ctx, &mtime, argv[2])) + return JS_EXCEPTION; + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + ms_to_timeval(×[0], atime); + ms_to_timeval(×[1], mtime); + ret = utimes(path, times); + JS_FreeCString(ctx, path); + return js_os_return(ctx, ret); +} + +/* exec(args[, options]) -> exitcode */ +static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst options, args = argv[0]; + JSValue val, ret_val; + const char **exec_argv, *file = NULL, *str, *cwd = NULL; + uint32_t exec_argc, i; + int ret, pid, status; + BOOL block_flag = TRUE, use_path = TRUE; + static const char *std_name[3] = { "stdin", "stdout", "stderr" }; + int std_fds[3]; + + val = JS_GetPropertyStr(ctx, args, "length"); + if (JS_IsException(val)) + return JS_EXCEPTION; + ret = JS_ToUint32(ctx, &exec_argc, val); + JS_FreeValue(ctx, val); + if (ret) + return JS_EXCEPTION; + /* arbitrary limit to avoid overflow */ + if (exec_argc < 1 || exec_argc > 65535) { + return JS_ThrowTypeError(ctx, "invalid number of arguments"); + } + exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1)); + if (!exec_argv) + return JS_EXCEPTION; + for(i = 0; i < exec_argc; i++) { + val = JS_GetPropertyUint32(ctx, args, i); + if (JS_IsException(val)) + goto exception; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto exception; + exec_argv[i] = str; + } + exec_argv[exec_argc] = NULL; + + for(i = 0; i < 3; i++) + std_fds[i] = i; + + /* get the options, if any */ + if (argc >= 2) { + options = argv[1]; + + if (get_bool_option(ctx, &block_flag, options, "block")) + goto exception; + if (get_bool_option(ctx, &use_path, options, "usePath")) + goto exception; + + val = JS_GetPropertyStr(ctx, options, "file"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + file = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!file) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "cwd"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + cwd = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!cwd) + goto exception; + } + + /* stdin/stdout/stderr handles */ + for(i = 0; i < 3; i++) { + val = JS_GetPropertyStr(ctx, options, std_name[i]); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + int fd; + ret = JS_ToInt32(ctx, &fd, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + std_fds[i] = fd; + } + } + } + + pid = fork(); + if (pid < 0) { + JS_ThrowTypeError(ctx, "fork error"); + goto exception; + } + if (pid == 0) { + /* child */ + int fd_max = sysconf(_SC_OPEN_MAX); + + /* remap the stdin/stdout/stderr handles if necessary */ + for(i = 0; i < 3; i++) { + if (std_fds[i] != i) { + if (dup2(std_fds[i], i) < 0) + _exit(127); + } + } + + for(i = 3; i < fd_max; i++) + close(i); + if (cwd) { + if (chdir(cwd) < 0) + _exit(127); + } + if (!file) + file = exec_argv[0]; + if (use_path) + ret = execvp(file, (char **)exec_argv); + else + ret = execv(file, (char **)exec_argv); + _exit(127); + } + /* parent */ + if (block_flag) { + for(;;) { + ret = waitpid(pid, &status, 0); + if (ret == pid) { + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + break; + } else if (WIFSIGNALED(status)) { + ret = -WTERMSIG(status); + break; + } + } + } + } else { + ret = pid; + } + ret_val = JS_NewInt32(ctx, ret); + done: + JS_FreeCString(ctx, file); + JS_FreeCString(ctx, cwd); + for(i = 0; i < exec_argc; i++) + JS_FreeCString(ctx, exec_argv[i]); + js_free(ctx, exec_argv); + return ret_val; + exception: + ret_val = JS_EXCEPTION; + goto done; +} + +/* waitpid(pid, block) -> [pid, status] */ +static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid, status, options, ret; + JSValue obj; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &options, argv[1])) + return JS_EXCEPTION; + + ret = waitpid(pid, &status, options); + if (ret < 0) { + ret = -errno; + status = 0; + } + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + return obj; +} + +/* pipe() -> [read_fd, write_fd] or null if error */ +static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pipe_fds[2], ret; + JSValue obj; + + ret = pipe(pipe_fds); + if (ret < 0) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), + JS_PROP_C_W_E); + return obj; +} + +/* kill(pid, sig) */ +static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid, sig, ret; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &sig, argv[1])) + return JS_EXCEPTION; + ret = kill(pid, sig); + return js_os_return(ctx, ret); +} + +/* sleep(delay_ms) */ +static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t delay; + struct timespec ts; + int ret; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + ts.tv_sec = delay / 1000; + ts.tv_nsec = (delay % 1000) * 1000000; + ret = nanosleep(&ts, NULL); + return js_os_return(ctx, ret); +} + +/* dup(fd) */ +static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = dup(fd); + return js_os_return(ctx, ret); +} + +/* dup2(fd) */ +static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, fd2, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &fd2, argv[1])) + return JS_EXCEPTION; + ret = dup2(fd, fd2); + return js_os_return(ctx, ret); +} + +#endif /* !_WIN32 */ + +#if defined(_WIN32) +#define OS_PLATFORM "win32" +#elif defined(__APPLE__) +#define OS_PLATFORM "darwin" +#elif defined(EMSCRIPTEN) +#define OS_PLATFORM "js" +#else +#define OS_PLATFORM "linux" +#endif + +#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + +static const JSCFunctionListEntry js_os_funcs[] = { + JS_CFUNC_DEF("open", 2, js_os_open ), + OS_FLAG(O_RDONLY), + OS_FLAG(O_WRONLY), + OS_FLAG(O_RDWR), + OS_FLAG(O_APPEND), + OS_FLAG(O_CREAT), + OS_FLAG(O_EXCL), + OS_FLAG(O_TRUNC), +#if defined(_WIN32) + OS_FLAG(O_BINARY), + OS_FLAG(O_TEXT), +#endif + JS_CFUNC_DEF("close", 1, js_os_close ), + JS_CFUNC_DEF("seek", 3, js_os_seek ), + JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ), + JS_CFUNC_DEF("isatty", 1, js_os_isatty ), + JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ), + JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), + JS_CFUNC_DEF("remove", 1, js_os_remove ), + JS_CFUNC_DEF("rename", 2, js_os_rename ), + JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), + JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), + JS_CFUNC_DEF("signal", 2, js_os_signal ), + OS_FLAG(SIGINT), + OS_FLAG(SIGABRT), + OS_FLAG(SIGFPE), + OS_FLAG(SIGILL), + OS_FLAG(SIGSEGV), + OS_FLAG(SIGTERM), +#if !defined(_WIN32) + OS_FLAG(SIGQUIT), + OS_FLAG(SIGPIPE), + OS_FLAG(SIGALRM), + OS_FLAG(SIGUSR1), + OS_FLAG(SIGUSR2), + OS_FLAG(SIGCHLD), + OS_FLAG(SIGCONT), + OS_FLAG(SIGSTOP), + OS_FLAG(SIGTSTP), + OS_FLAG(SIGTTIN), + OS_FLAG(SIGTTOU), +#endif + JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ), + JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), + JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), + JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), +#if !defined(_WIN32) + JS_CFUNC_DEF("realpath", 1, js_os_realpath ), + JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), + JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), + JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), + /* st_mode constants */ + OS_FLAG(S_IFMT), + OS_FLAG(S_IFIFO), + OS_FLAG(S_IFCHR), + OS_FLAG(S_IFDIR), + OS_FLAG(S_IFBLK), + OS_FLAG(S_IFREG), + OS_FLAG(S_IFSOCK), + OS_FLAG(S_IFLNK), + OS_FLAG(S_ISGID), + OS_FLAG(S_ISUID), + JS_CFUNC_DEF("symlink", 2, js_os_symlink ), + JS_CFUNC_DEF("readlink", 1, js_os_readlink ), + JS_CFUNC_DEF("readdir", 1, js_os_readdir ), + JS_CFUNC_DEF("utimes", 3, js_os_utimes ), + JS_CFUNC_DEF("exec", 1, js_os_exec ), + JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), + OS_FLAG(WNOHANG), + JS_CFUNC_DEF("pipe", 0, js_os_pipe ), + JS_CFUNC_DEF("kill", 2, js_os_kill ), + JS_CFUNC_DEF("sleep", 1, js_os_sleep ), + JS_CFUNC_DEF("dup", 1, js_os_dup ), + JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), +#endif +}; + +static int js_os_init(JSContext *ctx, JSModuleDef *m) +{ + os_poll_func = js_os_poll; + + /* OSTimer class */ + JS_NewClassID(&js_os_timer_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class); + + return JS_SetModuleExportList(ctx, m, js_os_funcs, + countof(js_os_funcs)); +} + +JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_os_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); + return m; +} + +/**********************************************************/ + +static JSValue js_print(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int i; + const char *str; + + for(i = 0; i < argc; i++) { + if (i != 0) + putchar(' '); + str = JS_ToCString(ctx, argv[i]); + if (!str) + return JS_EXCEPTION; + fputs(str, stdout); + JS_FreeCString(ctx, str); + } + putchar('\n'); + return JS_UNDEFINED; +} + +void js_std_add_helpers(JSContext *ctx, int argc, char **argv) +{ + JSValue global_obj, console, args; + int i; + + /* XXX: should these global definitions be enumerable? */ + global_obj = JS_GetGlobalObject(ctx); + + console = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, console, "log", + JS_NewCFunction(ctx, js_print, "log", 1)); + JS_SetPropertyStr(ctx, global_obj, "console", console); + + /* same methods as the mozilla JS shell */ + args = JS_NewArray(ctx); + for(i = 0; i < argc; i++) { + JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i])); + } + JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args); + + JS_SetPropertyStr(ctx, global_obj, "print", + JS_NewCFunction(ctx, js_print, "print", 1)); + JS_SetPropertyStr(ctx, global_obj, "__loadScript", + JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1)); + + JS_FreeValue(ctx, global_obj); + + /* XXX: not multi-context */ + init_list_head(&os_rw_handlers); + init_list_head(&os_signal_handlers); + init_list_head(&os_timers); + os_pending_signals = 0; +} + +void js_std_free_handlers(JSRuntime *rt) +{ + struct list_head *el, *el1; + + list_for_each_safe(el, el1, &os_rw_handlers) { + JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); + free_rw_handler(rt, rh); + } + + list_for_each_safe(el, el1, &os_signal_handlers) { + JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); + free_sh(rt, sh); + } + + list_for_each_safe(el, el1, &os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + unlink_timer(rt, th); + if (!th->has_object) + free_timer(rt, th); + } +} + +static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val, + BOOL is_throw) +{ + JSValue val; + const char *stack; + BOOL is_error; + + is_error = JS_IsError(ctx, exception_val); + if (is_throw && !is_error) + printf("Throw: "); + js_print(ctx, JS_NULL, 1, (JSValueConst *)&exception_val); + if (is_error) { + val = JS_GetPropertyStr(ctx, exception_val, "stack"); + if (!JS_IsUndefined(val)) { + stack = JS_ToCString(ctx, val); + printf("%s\n", stack); + JS_FreeCString(ctx, stack); + } + JS_FreeValue(ctx, val); + } +} + +void js_std_dump_error(JSContext *ctx) +{ + JSValue exception_val; + + exception_val = JS_GetException(ctx); + js_std_dump_error1(ctx, exception_val, TRUE); + JS_FreeValue(ctx, exception_val); +} + +void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, + JSValueConst reason, + BOOL is_handled, void *opaque) +{ + if (!is_handled) { + printf("Possibly unhandled promise rejection: "); + js_std_dump_error1(ctx, reason, FALSE); + } +} + +/* main loop which calls the user JS callbacks */ +void js_std_loop(JSContext *ctx) +{ + JSContext *ctx1; + int err; + + for(;;) { + /* execute the pending jobs */ + for(;;) { + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err <= 0) { + if (err < 0) { + js_std_dump_error(ctx1); + } + break; + } + } + + if (!os_poll_func || os_poll_func(ctx)) + break; + } +} + +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int load_only) +{ + JSValue obj, val; + obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + goto exception; + if (load_only) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + js_module_set_import_meta(ctx, obj, FALSE, FALSE); + } + } else { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + if (JS_ResolveModule(ctx, obj) < 0) { + JS_FreeValue(ctx, obj); + goto exception; + } + js_module_set_import_meta(ctx, obj, FALSE, TRUE); + } + val = JS_EvalFunction(ctx, obj); + if (JS_IsException(val)) { + exception: + js_std_dump_error(ctx); + exit(1); + } + JS_FreeValue(ctx, val); + } +} diff --git a/quickjs-libc.h b/quickjs-libc.h new file mode 100644 index 0000000..93a53da --- /dev/null +++ b/quickjs-libc.h @@ -0,0 +1,49 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_LIBC_H +#define QUICKJS_LIBC_H + +#include +#include + +#include "quickjs.h" + +JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); +JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); +void js_std_add_helpers(JSContext *ctx, int argc, char **argv); +void js_std_loop(JSContext *ctx); +void js_std_free_handlers(JSRuntime *rt); +void js_std_dump_error(JSContext *ctx); +uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); +int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, + JS_BOOL use_realpath, JS_BOOL is_main); +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque); +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags); +void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, + JSValueConst reason, + JS_BOOL is_handled, void *opaque); + +#endif /* QUICKJS_LIBC_H */ diff --git a/quickjs-opcode.h b/quickjs-opcode.h new file mode 100644 index 0000000..cc514e5 --- /dev/null +++ b/quickjs-opcode.h @@ -0,0 +1,371 @@ +/* + * 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_var, 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_ctor, 1, 1, 1, none) +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_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( 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(for_await_of_next, 1, 3, 4, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF(iterator_close_return, 1, 4, 4, none) +DEF(async_iterator_close, 1, 3, 2, none) +DEF(async_iterator_next, 1, 4, 4, none) +DEF(async_iterator_get, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 2, 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) +#ifdef CONFIG_BIGNUM +DEF( mul_pow10, 1, 2, 1, none) +DEF( math_div, 1, 2, 1, none) +DEF( math_mod, 1, 2, 1, none) +DEF( math_pow, 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(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */ + +def(close_var_object, 1, 0, 0, none) /* emitted in phase 1, removed in phase 2 */ +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 */ + +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_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, 1, 1, atom_u16) /* obj value ->, 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( is_function, 1, 1, 1, none) +#endif + +#undef DEF +#undef def +#endif /* DEF */ diff --git a/quickjs.c b/quickjs.c new file mode 100644 index 0000000..646af8a --- /dev/null +++ b/quickjs.c @@ -0,0 +1,51583 @@ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2020 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#elif defined(__linux__) +#include +#endif + +#include "cutils.h" +#include "list.h" +#include "quickjs.h" +#include "libregexp.h" + +/* enable bignums */ +#define CONFIG_BIGNUM + +#ifdef CONFIG_BIGNUM +#include "libbf.h" +#endif + +#define OPTIMIZE 1 +#define SHORT_OPCODES 1 +#if defined(EMSCRIPTEN) +#define DIRECT_DISPATCH 0 +#else +#define DIRECT_DISPATCH 1 +#endif + +#if defined(__APPLE__) +#define MALLOC_OVERHEAD 0 +#else +#define MALLOC_OVERHEAD 8 +#endif + +#if !defined(_WIN32) +/* define it if printf uses the RNDN rounding mode instead of RNDNA */ +#define CONFIG_PRINTF_RNDN +#endif + +/* define to include Atomics.* operations which depend on the OS + threads */ +#if !defined(EMSCRIPTEN) +#define CONFIG_ATOMICS +#endif + +/* dump object free */ +//#define DUMP_FREE +//#define DUMP_CLOSURE +/* dump the bytecode of the compiled functions: combination of bits + 1: dump pass 3 final byte code + 2: dump pass 2 code + 4: dump pass 1 code + 8: dump stdlib functions + 16: dump bytecode in hex + 32: dump line number table + */ +//#define DUMP_BYTECODE (1) +/* dump the occurence of the automatic GC */ +//#define DUMP_GC +/* dump objects freed by the garbage collector */ +//#define DUMP_GC_FREE +/* dump objects leaking when freeing the runtime */ +//#define DUMP_LEAKS 1 +/* dump memory usage before running the garbage collector */ +//#define DUMP_MEM +//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */ +//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */ +//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */ +//#define DUMP_MODULE_RESOLVE +//#define DUMP_PROMISE +//#define DUMP_READ_OBJECT + +/* test the GC by forcing it before each object allocation */ +//#define FORCE_GC_AT_MALLOC + +#ifdef CONFIG_ATOMICS +#include +#include +#include +#endif + +enum { + /* classid tag */ /* union usage | properties */ + JS_CLASS_OBJECT = 1, /* must be first */ + JS_CLASS_ARRAY, /* u.array | length */ + JS_CLASS_ERROR, + JS_CLASS_NUMBER, /* u.object_data */ + JS_CLASS_STRING, /* u.object_data */ + JS_CLASS_BOOLEAN, /* u.object_data */ + JS_CLASS_SYMBOL, /* u.object_data */ + JS_CLASS_ARGUMENTS, /* u.array | length */ + JS_CLASS_MAPPED_ARGUMENTS, /* | length */ + JS_CLASS_DATE, /* u.object_data */ + JS_CLASS_MODULE_NS, + JS_CLASS_C_FUNCTION, /* u.cfunc */ + JS_CLASS_BYTECODE_FUNCTION, /* u.func */ + JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ + JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ + JS_CLASS_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ + JS_CLASS_REGEXP, /* u.regexp */ + JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ +#ifdef CONFIG_BIGNUM + JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ +#endif + JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_DATAVIEW, /* u.typed_array */ +#ifdef CONFIG_BIGNUM + JS_CLASS_BIG_INT, /* u.object_data */ + JS_CLASS_BIG_FLOAT, /* u.object_data */ + JS_CLASS_FLOAT_ENV, /* u.float_env */ + JS_CLASS_BIG_DECIMAL, /* u.object_data */ +#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_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ +} JSErrorEnum; + +#define JS_MAX_LOCAL_VARS 65536 +#define JS_STACK_SIZE_MAX 65536 +#define JS_STRING_LEN_MAX ((1 << 30) - 1) + +#define __exception __attribute__((warn_unused_result)) + +typedef struct JSShape JSShape; +typedef struct JSString JSString; +typedef struct JSString JSAtomStruct; + +typedef enum { + JS_GC_PHASE_NONE, + JS_GC_PHASE_DECREF, + JS_GC_PHASE_REMOVE_CYCLES, +} JSGCPhaseEnum; + +typedef enum OPCodeEnum OPCodeEnum; + +#ifdef CONFIG_BIGNUM +/* function pointers are used for numeric operations so that it is + possible to remove some numeric types */ +typedef struct { + JSValue (*to_string)(JSContext *ctx, JSValueConst val); + JSValue (*from_string)(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent); + int (*unary_arith)(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1); + int (*binary_arith)(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2); + int (*compare)(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2); + /* only for bigfloat: */ + JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a, + int64_t exponent); + int (*mul_pow10)(JSContext *ctx, JSValue *sp); +} JSNumericOperations; +#endif + +struct JSRuntime { + JSMallocFunctions mf; + JSMallocState malloc_state; + const char *rt_info; + + int atom_hash_size; /* power of two */ + int atom_count; + int atom_size; + int atom_count_resize; /* resize hash table at this count */ + uint32_t *atom_hash; + JSAtomStruct **atom_array; + int atom_free_index; /* 0 = none */ + + int class_count; /* size of class_array */ + JSClass *class_array; + + struct list_head context_list; /* list of JSContext.link */ + /* list of JSGCObjectHeader.link. List of allocated GC objects (used + by the garbage collector) */ + struct list_head gc_obj_list; + /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ + struct list_head gc_zero_ref_count_list; + struct list_head tmp_obj_list; /* used during GC */ + JSGCPhaseEnum gc_phase : 8; + size_t malloc_gc_threshold; +#ifdef DUMP_LEAKS + struct list_head string_list; /* list of JSString.link */ +#endif + + JSInterruptHandler *interrupt_handler; + void *interrupt_opaque; + + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; + void *host_promise_rejection_tracker_opaque; + + struct list_head job_list; /* list of JSJobEntry.link */ + + JSModuleNormalizeFunc *module_normalize_func; + JSModuleLoaderFunc *module_loader_func; + void *module_loader_opaque; + + BOOL can_block : 8; /* TRUE if Atomics.wait can block */ + + /* Shape hash table */ + int shape_hash_bits; + int shape_hash_size; + int shape_hash_count; /* number of hashed shapes */ + JSShape **shape_hash; +#ifdef CONFIG_BIGNUM + bf_context_t bf_ctx; + JSNumericOperations bigint_ops; + JSNumericOperations bigfloat_ops; + JSNumericOperations bigdecimal_ops; +#endif +}; + +struct JSClass { + uint32_t class_id; /* 0 means free entry */ + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + JSClassCall *call; + /* pointers for exotic behavior, can be NULL if none are present */ + const JSClassExoticMethods *exotic; +}; + +#define JS_MODE_STRICT (1 << 0) +#define JS_MODE_STRIP (1 << 1) +#define JS_MODE_BIGINT (1 << 2) +#define JS_MODE_MATH (1 << 3) + +typedef struct JSStackFrame { + struct JSStackFrame *prev_frame; /* NULL if first stack frame */ + JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ + JSValue *arg_buf; /* arguments */ + JSValue *var_buf; /* variables */ + struct list_head var_ref_list; /* list of JSVarRef.link */ + const uint8_t *cur_pc; /* only used in bytecode functions : PC of the + instruction after the call */ + int arg_count; + int js_mode; /* 0 or JS_MODE_BIGINT for C functions */ + /* only used in generators. Current stack pointer value. NULL if + the function is running. */ + JSValue *cur_sp; +} JSStackFrame; + +typedef enum { + JS_GC_OBJ_TYPE_JS_OBJECT, + JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, + JS_GC_OBJ_TYPE_SHAPE, + JS_GC_OBJ_TYPE_VAR_REF, + JS_GC_OBJ_TYPE_ASYNC_FUNCTION, +} JSGCObjectTypeEnum; + +/* header for GC objects. GC objects are C data structures with a + reference count that can reference other GC objects. JS Objects are + a particular type of GC object. */ +struct JSGCObjectHeader { + int ref_count; /* must come first, 32-bit */ + JSGCObjectTypeEnum gc_obj_type : 4; + uint8_t mark : 4; /* used by the GC */ + uint8_t dummy1; /* not used by the GC */ + uint16_t dummy2; /* not used by the GC */ + struct list_head link; +}; + +typedef struct JSVarRef { + union { + JSGCObjectHeader header; /* must come first */ + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + /* 0 : the JSVarRef is on the stack. header.link is an element + of JSStackFrame.var_ref_list. + 1 : the JSVarRef is detached. header.link has the normal meanning + */ + uint8_t is_detached : 1; + uint8_t is_arg : 1; + uint16_t var_idx; /* index of the corresponding function variable on + the stack */ + }; + }; + JSValue *pvalue; /* pointer to the value, either on the stack or + to 'value' */ + JSValue value; /* used when the variable is no longer on the stack */ +} JSVarRef; + +#ifdef CONFIG_BIGNUM +typedef struct JSFloatEnv { + limb_t prec; + bf_flags_t flags; + unsigned int status; +} JSFloatEnv; + +/* the same structure is used for big integers and big floats. Big + integers are never infinite or NaNs */ +typedef struct JSBigFloat { + JSRefCountHeader header; /* must come first, 32-bit */ + bf_t num; +} JSBigFloat; + +typedef struct JSBigDecimal { + JSRefCountHeader header; /* must come first, 32-bit */ + bfdec_t num; +} JSBigDecimal; +#endif + +/* 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 { + JSRuntime *rt; + struct list_head link; + const uint8_t *stack_top; + size_t stack_size; /* in bytes */ + + JSValue current_exception; + /* true if inside an out of memory error, to avoid recursing */ + BOOL in_out_of_memory : 8; + uint16_t binary_object_count; + int binary_object_size; + + JSShape *array_shape; /* initial shape for Array objects */ + + JSStackFrame *current_stack_frame; + + JSValue *class_proto; + JSValue function_proto; + JSValue function_ctor; + JSValue regexp_ctor; + JSValue promise_ctor; + JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; + JSValue iterator_proto; + JSValue async_iterator_proto; + JSValue array_proto_values; + JSValue throw_type_error; + JSValue eval_obj; + + JSValue global_obj; /* global object */ + JSValue global_var_obj; /* contains the global let/const definitions */ + + uint64_t random_state; +#ifdef CONFIG_BIGNUM + bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ + JSFloatEnv fp_env; /* global FP environment */ + BOOL bignum_ext; /* enable bigint mode, math mode and operator + overloading */ +#endif + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ + int interrupt_counter; + BOOL is_error_property_enabled; + + struct list_head loaded_modules; /* list of JSModuleDef.link */ + + /* if NULL, RegExp compilation is not supported */ + JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); + /* if NULL, eval is not supported */ + JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx); + void *user_opaque; +}; + +typedef union JSFloat64Union { + double d; + uint64_t u64; + uint32_t u32[2]; +} JSFloat64Union; + +enum { + JS_ATOM_TYPE_STRING = 1, + JS_ATOM_TYPE_GLOBAL_SYMBOL, + JS_ATOM_TYPE_SYMBOL, + JS_ATOM_TYPE_PRIVATE, +}; + +enum { + JS_ATOM_HASH_SYMBOL, + JS_ATOM_HASH_PRIVATE, +}; + +typedef enum { + JS_ATOM_KIND_STRING, + JS_ATOM_KIND_SYMBOL, + JS_ATOM_KIND_PRIVATE, +} JSAtomKindEnum; + +#define JS_ATOM_HASH_MASK ((1 << 30) - 1) + +struct JSString { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len : 31; + uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ + /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, + for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 + XXX: could change encoding to have one more bit in hash */ + uint32_t hash : 30; + uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ + uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ +#ifdef DUMP_LEAKS + struct list_head link; /* string list */ +#endif + union { + uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ + uint16_t str16[0]; + } u; +}; + +typedef struct JSClosureVar { + uint8_t is_local : 1; + uint8_t is_arg : 1; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t var_kind : 3; /* see JSVarKindEnum */ + /* 9 bits available */ + uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the + parent function. otherwise: index to a closure + variable of the parent function */ + JSAtom var_name; +} JSClosureVar; + +typedef struct JSVarScope { + int parent; /* index into fd->scopes of the enclosing scope */ + int first; /* index into fd->vars of the last variable in this scope */ +} JSVarScope; + +typedef enum { + /* XXX: add more variable kinds here instead of using bit fields */ + JS_VAR_NORMAL, + JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ + JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator + function declaration */ + JS_VAR_CATCH, + JS_VAR_PRIVATE_FIELD, + JS_VAR_PRIVATE_METHOD, + JS_VAR_PRIVATE_GETTER, + JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ + JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ +} JSVarKindEnum; + +typedef struct JSVarDef { + JSAtom var_name; + int scope_level; /* index into fd->scopes of this variable lexical scope */ + int scope_next; /* index into fd->vars of the next variable in the + * same or enclosing lexical scope */ + uint8_t is_func_var : 1; /* used for the function self reference */ + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t is_captured : 1; + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* only used during compilation: function pool index for lexical + variables with var_kind = + JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of + the definition of the 'var' variables (they have scope_level = + 0) */ + int func_pool_or_scope_idx : 24; /* only used during compilation */ +} JSVarDef; + +/* for the encoding of the pc2line table */ +#define PC2LINE_BASE (-1) +#define PC2LINE_RANGE 5 +#define PC2LINE_OP_FIRST 1 +#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) + +typedef enum JSFunctionKindEnum { + JS_FUNC_NORMAL = 0, + JS_FUNC_GENERATOR = (1 << 0), + JS_FUNC_ASYNC = (1 << 1), + JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), +} JSFunctionKindEnum; + +typedef struct JSFunctionBytecode { + JSGCObjectHeader header; /* must come first */ + uint8_t js_mode; + uint8_t has_prototype : 1; /* true if a prototype field is necessary */ + uint8_t has_simple_parameter_list : 1; + uint8_t is_derived_class_constructor : 1; + /* true if home_object needs to be initialized */ + uint8_t need_home_object : 1; + uint8_t func_kind : 2; + uint8_t new_target_allowed : 1; + uint8_t super_call_allowed : 1; + uint8_t super_allowed : 1; + uint8_t arguments_allowed : 1; + uint8_t has_debug : 1; + uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ + uint8_t read_only_bytecode : 1; + /* XXX: 4 bits available */ + uint8_t *byte_code_buf; /* (self pointer) */ + int byte_code_len; + JSAtom func_name; + JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ + JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ + uint16_t arg_count; + uint16_t var_count; + uint16_t defined_arg_count; /* for length function property */ + uint16_t stack_size; /* maximum stack size */ + JSValue *cpool; /* constant pool (self pointer) */ + int cpool_count; + int closure_var_count; + struct { + /* debug info, move to separate structure to save memory? */ + JSAtom filename; + int line_num; + int source_len; + int pc2line_len; + uint8_t *pc2line_buf; + char *source; + } debug; +} JSFunctionBytecode; + +typedef struct JSBoundFunction { + JSValue func_obj; + JSValue this_val; + int argc; + JSValue argv[0]; +} JSBoundFunction; + +typedef enum JSIteratorKindEnum { + JS_ITERATOR_KIND_KEY, + JS_ITERATOR_KIND_VALUE, + JS_ITERATOR_KIND_KEY_AND_VALUE, +} JSIteratorKindEnum; + +typedef struct JSForInIterator { + JSValue obj; + BOOL is_array; + uint32_t array_length; + uint32_t idx; +} JSForInIterator; + +typedef struct JSRegExp { + JSString *pattern; + JSString *bytecode; /* also contains the flags */ +} JSRegExp; + +typedef struct JSProxyData { + JSValue target; + JSValue handler; + JSValue proto; + uint8_t is_func; + uint8_t is_revoked; +} JSProxyData; + +typedef struct JSArrayBuffer { + int byte_length; /* 0 if detached */ + uint8_t detached; + uint8_t shared; /* if shared, the array buffer cannot be detached */ + uint8_t *data; /* NULL if detached */ + struct list_head array_list; + void *opaque; + JSFreeArrayBufferDataFunc *free_func; +} JSArrayBuffer; + +typedef struct JSTypedArray { + struct list_head link; /* link to arraybuffer */ + JSObject *obj; /* back pointer to the TypedArray/DataView object */ + JSObject *buffer; /* based array buffer */ + uint32_t offset; /* offset in the array buffer */ + uint32_t length; /* length in the array buffer */ +} JSTypedArray; + +typedef struct JSAsyncFunctionState { + JSValue this_val; /* 'this' generator argument */ + int argc; /* number of function arguments */ + BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ + JSStackFrame frame; +} JSAsyncFunctionState; + +/* XXX: could use an object instead to avoid the + JS_TAG_ASYNC_FUNCTION tag for the GC */ +typedef struct JSAsyncFunctionData { + JSGCObjectHeader header; /* must come first */ + JSValue resolving_funcs[2]; + BOOL is_active; /* true if the async function state is valid */ + JSAsyncFunctionState func_state; +} JSAsyncFunctionData; + +typedef struct JSReqModuleEntry { + JSAtom module_name; + JSModuleDef *module; /* used using resolution */ +} JSReqModuleEntry; + +typedef enum JSExportTypeEnum { + JS_EXPORT_TYPE_LOCAL, + JS_EXPORT_TYPE_INDIRECT, +} JSExportTypeEnum; + +typedef struct JSExportEntry { + union { + struct { + int var_idx; /* closure variable index */ + JSVarRef *var_ref; /* if != NULL, reference to the variable */ + } local; /* for local export */ + int req_module_idx; /* module for indirect export */ + } u; + JSExportTypeEnum export_type; + JSAtom local_name; /* '*' if export ns from. not used for local + export after compilation */ + JSAtom export_name; /* exported variable name */ +} JSExportEntry; + +typedef struct JSStarExportEntry { + int req_module_idx; /* in req_module_entries */ +} JSStarExportEntry; + +typedef struct JSImportEntry { + int var_idx; /* closure variable index */ + JSAtom import_name; + int req_module_idx; /* in req_module_entries */ +} JSImportEntry; + +struct JSModuleDef { + JSRefCountHeader header; /* must come first, 32-bit */ + JSAtom module_name; + struct list_head link; + + JSReqModuleEntry *req_module_entries; + int req_module_entries_count; + int req_module_entries_size; + + JSExportEntry *export_entries; + int export_entries_count; + int export_entries_size; + + JSStarExportEntry *star_export_entries; + int star_export_entries_count; + int star_export_entries_size; + + JSImportEntry *import_entries; + int import_entries_count; + int import_entries_size; + + JSValue module_ns; + JSValue func_obj; /* only used for JS modules */ + JSModuleInitFunc *init_func; /* only used for C modules */ + BOOL resolved : 8; + BOOL instantiated : 8; + BOOL evaluated : 8; + BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ + /* true if evaluation yielded an exception. It is saved in + eval_exception */ + BOOL eval_has_exception : 8; + JSValue eval_exception; + JSValue meta_obj; /* for import.meta */ +}; + +typedef struct JSJobEntry { + struct list_head link; + JSContext *ctx; + JSJobFunc *job_func; + int argc; + JSValue argv[0]; +} JSJobEntry; + +typedef struct JSProperty { + union { + JSValue value; /* JS_PROP_NORMAL */ + struct { /* JS_PROP_GETSET */ + JSObject *getter; /* NULL if undefined */ + JSObject *setter; /* NULL if undefined */ + } getset; + JSVarRef *var_ref; /* JS_PROP_VARREF */ + struct { /* JS_PROP_AUTOINIT */ + int (*init_func)(JSContext *ctx, JSObject *obj, + JSAtom prop, void *opaque); + void *opaque; + } init; + } u; +} JSProperty; + +#define JS_PROP_INITIAL_SIZE 2 +#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ +#define JS_ARRAY_INITIAL_SIZE 2 + +typedef struct JSShapeProperty { + uint32_t hash_next : 26; /* 0 if last in list */ + uint32_t flags : 6; /* JS_PROP_XXX */ + JSAtom atom; /* JS_ATOM_NULL = free property entry */ +} JSShapeProperty; + +struct JSShape { + uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1 + before the start of the structure. */ + JSGCObjectHeader header; + /* true if the shape is inserted in the shape hash table. If not, + JSShape.hash is not valid */ + uint8_t is_hashed; + /* If true, the shape may have small array index properties 'n' with 0 + <= n <= 2^31-1. If false, the shape is guaranteed not to have + small array index properties */ + uint8_t has_small_array_index; + uint32_t hash; /* current hash value */ + uint32_t prop_hash_mask; + int prop_size; /* allocated properties */ + int prop_count; + JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ + JSObject *proto; + JSShapeProperty prop[0]; /* prop_size elements */ +}; +struct JSObject { + union { + JSGCObjectHeader header; + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ + uint8_t fast_array : 1; /* TRUE if u.array is used for get/put */ + uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ + uint8_t is_class : 1; /* TRUE if object is a class constructor */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint16_t class_id; /* see JS_CLASS_x */ + }; + }; + /* byte offsets: 16/24 */ + JSShape *shape; /* prototype and property names + flag */ + JSProperty *prop; /* array of properties */ + /* byte offsets: 24/40 */ + struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */ + /* byte offsets: 28/48 */ + union { + void *opaque; + struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ + struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ + struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ + struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ +#ifdef CONFIG_BIGNUM + struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ +#endif + struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ + struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ + struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ + struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ + struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ + struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ + struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ + struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ + struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ + struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ + struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ + /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + struct JSFunctionBytecode *function_bytecode; + JSVarRef **var_refs; + JSObject *home_object; /* for 'super' access */ + } func; + struct { /* JS_CLASS_C_FUNCTION: 8/12 bytes */ + JSCFunctionType c_function; + uint8_t length; + uint8_t cproto; + int16_t magic; + } cfunc; + /* array part for fast arrays and typed arrays */ + struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + union { + uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + } u1; + union { + JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ + uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ + int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ + uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ + int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ + uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ + int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ + uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ + double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ + } u; + uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ + } array; /* 12/20 bytes */ + JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ + JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ + } u; + /* byte sizes: 40/48/72 */ +}; +enum { + JS_ATOM_NULL, +#define DEF(name, str) JS_ATOM_ ## name, +#include "quickjs-atom.h" +#undef DEF + JS_ATOM_END, +}; +#define JS_ATOM_LAST_KEYWORD JS_ATOM_super +#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield + +static const char js_atom_init[] = +#define DEF(name, str) str "\0" +#include "quickjs-atom.h" +#undef DEF +; + +typedef enum OPCodeFormat { +#define FMT(f) OP_FMT_ ## f, +#define DEF(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" +#undef DEF +#undef FMT +} OPCodeFormat; + +enum OPCodeEnum { +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_COUNT, /* excluding temporary opcodes */ + /* temporary opcodes : overlap with the short opcodes */ + OP_TEMP_START = OP_nop + 1, + OP___dummy = OP_TEMP_START - 1, +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) +#define def(id, size, n_pop, n_push, f) OP_ ## id, +#include "quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_TEMP_END, +}; + +static int JS_InitAtoms(JSRuntime *rt); +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type); +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); +static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); +static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValue *argv, int flags); +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValue *argv, int flags); +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv); +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValueConst *argv); +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val); +static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, + JSValueConst val, int flags, int scope_idx); +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); +static __maybe_unused void JS_DumpString(JSRuntime *rt, + const JSString *p); +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); +static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, + JSValueConst val); +static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val); +static __maybe_unused void JS_PrintValue(JSContext *ctx, + const char *str, + JSValueConst val); +static __maybe_unused void JS_DumpShapes(JSRuntime *rt); +static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +static void js_array_finalizer(JSRuntime *rt, JSValue val); +static void js_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_object_data_finalizer(JSRuntime *rt, JSValue val); +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static void js_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); +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); +static int JS_ToBoolFree(JSContext *ctx, JSValue val); +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); +static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); +static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, + JSValue pattern, JSValue bc); +static void gc_decref(JSRuntime *rt); +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name); + +typedef enum JSStrictEqModeEnum { + JS_EQ_STRICT, + JS_EQ_SAME_VALUE, + JS_EQ_SAME_VALUE_ZERO, +} JSStrictEqModeEnum; + +static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode); +static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); +static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); +static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); +static JSValue JS_ToObject(JSContext *ctx, JSValueConst val); +static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags); +#ifdef CONFIG_BIGNUM +static void js_float_env_finalizer(JSRuntime *rt, JSValue val); +static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a); +static JSValue JS_NewBigDecimal(JSContext *ctx, bfdec_t *a); +static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint); +static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a); +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); +static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); +static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); +static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val); +static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, + BOOL allow_null_or_undefined); +static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); +#endif +JSValue JS_ThrowOutOfMemory(JSContext *ctx); +static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); +static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, BOOL throw_flag); +static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); +static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); +static int js_proxy_isArray(JSContext *ctx, JSValueConst obj); +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); +static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); +static void reset_weak_ref(JSRuntime *rt, JSObject *p); +static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p); +static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); +static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, + BOOL is_arg); +static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags); +static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx); +static void js_free_module_def(JSContext *ctx, JSModuleDef *m); +static JSValue js_import_meta(JSContext *ctx); +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); +static JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst ctor); +static __exception int perform_promise_then(JSContext *ctx, + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs); +static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +static int js_string_compare(JSContext *ctx, + const JSString *p1, const JSString *p2); +static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); +static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop, JSValue val, int flags); +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); +static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop); +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func); +static void JS_AddIntrinsicBasicObjects(JSContext *ctx); +static void js_free_shape(JSRuntime *rt, JSShape *sh); +static void js_free_shape_null(JSRuntime *rt, JSShape *sh); +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs); +static int init_shape_hash(JSRuntime *rt); +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, + JSValueConst obj); +static __exception int js_get_length64(JSContext *ctx, int64_t *pres, + JSValueConst obj); +static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); +static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, + JSValueConst array_arg); +static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, + JSValue **arrpp, uint32_t *countp); +static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, + JSValueConst sync_iter); +static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); +static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, int flags); +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type); +static void remove_gc_object(JSGCObjectHeader *h); +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); + +static 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; + +JS_BOOL JS_IsNumber(JSValueConst v) +{ +#ifdef CONFIG_BIGNUM + /* XXX: check the bignum case. Should use a different function */ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || + tag == JS_TAG_BIG_FLOAT || JS_TAG_IS_FLOAT64(tag); +#else + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); +#endif +} + +static void js_trigger_gc(JSRuntime *rt, size_t size) +{ + BOOL force_gc; +#ifdef FORCE_GC_AT_MALLOC + force_gc = TRUE; +#else + force_gc = ((rt->malloc_state.malloc_size + size) > + rt->malloc_gc_threshold); +#endif + if (force_gc) { +#ifdef DUMP_GC + printf("GC: size=%" PRIu64 "\n", + (uint64_t)rt->malloc_state.malloc_size); +#endif + JS_RunGC(rt); + rt->malloc_gc_threshold = rt->malloc_state.malloc_size + + (rt->malloc_state.malloc_size >> 1); + } +} + +static size_t js_malloc_usable_size_unknown(const void *ptr) +{ + return 0; +} + +void *js_malloc_rt(JSRuntime *rt, size_t size) +{ + return rt->mf.js_malloc(&rt->malloc_state, size); +} + +void js_free_rt(JSRuntime *rt, void *ptr) +{ + rt->mf.js_free(&rt->malloc_state, ptr); +} + +void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) +{ + return rt->mf.js_realloc(&rt->malloc_state, ptr, size); +} + +size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) +{ + return rt->mf.js_malloc_usable_size(ptr); +} + +void *js_mallocz_rt(JSRuntime *rt, size_t size) +{ + void *ptr; + ptr = js_malloc_rt(rt, size); + if (!ptr) + return NULL; + return memset(ptr, 0, size); +} + +#ifdef CONFIG_BIGNUM +/* called by libbf */ +static void *js_bf_realloc(void *opaque, void *ptr, size_t size) +{ + JSRuntime *rt = opaque; + return js_realloc_rt(rt, ptr, size); +} +#endif /* CONFIG_BIGNUM */ + +/* Throw out of memory in case of error */ +void *js_malloc(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_malloc_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void *js_mallocz(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_mallocz_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +void js_free(JSContext *ctx, void *ptr) +{ + js_free_rt(ctx->rt, ptr); +} + +/* Throw out of memory in case of error */ +void *js_realloc(JSContext *ctx, void *ptr, size_t size) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ret; +} + +/* store extra allocated size in *pslack if successful */ +void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + if (pslack) { + size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); + *pslack = (new_size > size) ? new_size - size : 0; + } + return ret; +} + +size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) +{ + return js_malloc_usable_size_rt(ctx->rt, ptr); +} + +/* Throw out of memory exception in case of error */ +char *js_strndup(JSContext *ctx, const char *s, size_t n) +{ + char *ptr; + ptr = js_malloc(ctx, n + 1); + if (ptr) { + memcpy(ptr, s, n); + ptr[n] = '\0'; + } + return ptr; +} + +char *js_strdup(JSContext *ctx, const char *str) +{ + return js_strndup(ctx, str, strlen(str)); +} + +static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); +} + +static inline int is_digit(int c) { + return c >= '0' && c <= '9'; +} + +typedef struct JSClassShortDef { + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; +} JSClassShortDef; + +static JSClassShortDef const js_std_class_def[] = { + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ + { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ + { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ + { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ + { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ + { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ + { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ + { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ + { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */ + { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ + { JS_ATOM_Function, NULL, NULL }, /* JS_CLASS_C_FUNCTION */ + { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ + { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ + { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ + { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ + { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ + { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ + { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ + { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ + { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ + { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ + { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ + { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ + { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ + { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ + { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ +#ifdef CONFIG_BIGNUM + { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ + { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ +#endif + { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ + { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ + { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ +#ifdef CONFIG_BIGNUM + { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ + { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */ + { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */ + { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */ +#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_STRING_ITERATOR */ + { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ +}; + +static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, + int start, int count) +{ + JSClassDef cm_s, *cm = &cm_s; + int i, class_id; + + for(i = 0; i < count; i++) { + class_id = i + start; + memset(cm, 0, sizeof(*cm)); + cm->finalizer = tab[i].finalizer; + cm->gc_mark = tab[i].gc_mark; + if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) + return -1; + } + return 0; +} + +#ifdef CONFIG_BIGNUM +static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "unsupported operation"); +} + +static JSValue invalid_to_string(JSContext *ctx, JSValueConst val) +{ + return JS_ThrowUnsupportedOperation(ctx); +} + +static JSValue invalid_from_string(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent) +{ + return JS_NAN; +} + +static int invalid_unary_arith(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) +{ + JS_FreeValue(ctx, op1); + JS_ThrowUnsupportedOperation(ctx); + return -1; +} + +static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2) +{ + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + JS_ThrowUnsupportedOperation(ctx); + return -1; +} + +static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, + int64_t exponent) +{ + return JS_ThrowUnsupportedOperation(ctx); +} + +static int invalid_mul_pow10(JSContext *ctx, JSValue *sp) +{ + JS_ThrowUnsupportedOperation(ctx); + return -1; +} + +static void set_dummy_numeric_ops(JSNumericOperations *ops) +{ + ops->to_string = invalid_to_string; + ops->from_string = invalid_from_string; + ops->unary_arith = invalid_unary_arith; + ops->binary_arith = invalid_binary_arith; + ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64; + ops->mul_pow10 = invalid_mul_pow10; +} + +#endif /* CONFIG_BIGNUM */ + +JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) +{ + JSRuntime *rt; + JSMallocState ms; + + memset(&ms, 0, sizeof(ms)); + ms.opaque = opaque; + ms.malloc_limit = -1; + + rt = mf->js_malloc(&ms, sizeof(JSRuntime)); + if (!rt) + return NULL; + memset(rt, 0, sizeof(*rt)); + rt->mf = *mf; + if (!rt->mf.js_malloc_usable_size) { + /* use dummy function if none provided */ + rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown; + } + rt->malloc_state = ms; + rt->malloc_gc_threshold = 256 * 1024; + +#ifdef CONFIG_BIGNUM + bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); + set_dummy_numeric_ops(&rt->bigint_ops); + set_dummy_numeric_ops(&rt->bigfloat_ops); + set_dummy_numeric_ops(&rt->bigdecimal_ops); +#endif + + init_list_head(&rt->context_list); + init_list_head(&rt->gc_obj_list); + init_list_head(&rt->gc_zero_ref_count_list); + rt->gc_phase = JS_GC_PHASE_NONE; + +#ifdef DUMP_LEAKS + init_list_head(&rt->string_list); +#endif + init_list_head(&rt->job_list); + + if (JS_InitAtoms(rt)) + goto fail; + + /* create the object, array and function classes */ + if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, + countof(js_std_class_def)) < 0) + goto fail; + rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; + rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; + rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; + + rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; + rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; + rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; + rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; + if (init_shape_hash(rt)) + goto fail; + return rt; + fail: + JS_FreeRuntime(rt); + return NULL; +} + +/* default memory allocation functions with memory limitation */ +static inline size_t js_def_malloc_usable_size(void *ptr) +{ +#if defined(__APPLE__) + return malloc_size(ptr); +#elif defined(_WIN32) + return _msize(ptr); +#elif defined(EMSCRIPTEN) + return 0; +#elif defined(__linux__) + return malloc_usable_size(ptr); +#else + /* change this to `return 0;` if compilation fails */ + return malloc_usable_size(ptr); +#endif +} + +static void *js_def_malloc(JSMallocState *s, size_t size) +{ + void *ptr; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(size != 0); + + if (unlikely(s->malloc_size + size > s->malloc_limit)) + return NULL; + + ptr = malloc(size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +static void js_def_free(JSMallocState *s, void *ptr) +{ + if (!ptr) + return; + + s->malloc_count--; + s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + free(ptr); +} + +static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) +{ + size_t old_size; + + if (!ptr) { + if (size == 0) + return NULL; + return js_def_malloc(s, size); + } + old_size = js_def_malloc_usable_size(ptr); + if (size == 0) { + s->malloc_count--; + s->malloc_size -= old_size + MALLOC_OVERHEAD; + free(ptr); + return NULL; + } + if (s->malloc_size + size - old_size > s->malloc_limit) + return NULL; + + ptr = realloc(ptr, size); + if (!ptr) + return NULL; + + s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; + return ptr; +} + +static const JSMallocFunctions def_malloc_funcs = { + js_def_malloc, + js_def_free, + js_def_realloc, +#if defined(__APPLE__) + malloc_size, +#elif defined(_WIN32) + (size_t (*)(const void *))_msize, +#elif defined(EMSCRIPTEN) + NULL, +#elif defined(__linux__) + (size_t (*)(const void *))malloc_usable_size, +#else + /* change this to `NULL,` if compilation fails */ + malloc_usable_size, +#endif +}; + +JSRuntime *JS_NewRuntime(void) +{ + return JS_NewRuntime2(&def_malloc_funcs, NULL); +} + +void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) +{ + rt->malloc_state.malloc_limit = limit; +} + +/* use -1 to disable automatic GC */ +void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) +{ + rt->malloc_gc_threshold = gc_threshold; +} + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p,s) realloc_is_forbidden(p,s) + +void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) +{ + rt->interrupt_handler = cb; + rt->interrupt_opaque = opaque; +} + +void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) +{ + rt->can_block = can_block; +} + +/* 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; + + list_for_each_safe(el, el1, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JS_FreeContext(ctx); + } + + list_for_each_safe(el, el1, &rt->job_list) { + JSJobEntry *e = list_entry(el, JSJobEntry, link); + for(i = 0; i < e->argc; i++) + JS_FreeValueRT(rt, e->argv[i]); + js_free_rt(rt, e); + } + init_list_head(&rt->job_list); + + JS_RunGC(rt); + +#ifdef DUMP_LEAKS + /* leaking objects */ + { + BOOL header_done; + JSGCObjectHeader *p; + int count; + + /* remove the internal refcounts to display only the object + referenced externally */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + p->mark = 0; + } + gc_decref(rt); + + header_done = FALSE; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count != 0) { + if (!header_done) { + printf("Object leaks:\n"); + JS_DumpObjectHeader(rt); + header_done = TRUE; + } + JS_DumpGCObject(rt, p); + } + } + + count = 0; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count == 0) { + count++; + } + } + if (count != 0) + printf("Secondary object leaks: %d\n", count); + } +#endif + assert(list_empty(&rt->gc_obj_list)); + + /* free the classes */ + for(i = 0; i < rt->class_count; i++) { + JSClass *cl = &rt->class_array[i]; + if (cl->class_id != 0) { + JS_FreeAtomRT(rt, cl->class_name); + } + } + js_free_rt(rt, rt->class_array); + +#ifdef CONFIG_BIGNUM + bf_context_end(&rt->bf_ctx); +#endif + +#ifdef DUMP_LEAKS + /* only the atoms defined in JS_InitAtoms() should be left */ + { + BOOL header_done = FALSE; + + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p) /* && p->str*/) { + if (i >= JS_ATOM_END || p->header.ref_count != 1) { + if (!header_done) { + header_done = TRUE; + if (rt->rt_info) { + printf("%s:1: atom leakage:", rt->rt_info); + } else { + printf("Atom leaks:\n" + " %6s %6s %s\n", + "ID", "REFCNT", "NAME"); + } + } + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u %6u ", i, p->header.ref_count); + } + switch (p->atom_type) { + case JS_ATOM_TYPE_STRING: + JS_DumpString(rt, p); + break; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + printf("Symbol.for("); + JS_DumpString(rt, p); + printf(")"); + break; + case JS_ATOM_TYPE_SYMBOL: + if (p->hash == JS_ATOM_HASH_SYMBOL) { + printf("Symbol("); + JS_DumpString(rt, p); + printf(")"); + } else { + printf("Private("); + JS_DumpString(rt, p); + printf(")"); + } + break; + } + if (rt->rt_info) { + printf(":%u", p->header.ref_count); + } else { + printf("\n"); + } + } + } + } + if (rt->rt_info && header_done) + printf("\n"); + } +#endif + + /* free the atoms */ + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } + js_free_rt(rt, rt->atom_array); + js_free_rt(rt, rt->atom_hash); + js_free_rt(rt, rt->shape_hash); +#ifdef DUMP_LEAKS + if (!list_empty(&rt->string_list)) { + if (rt->rt_info) { + printf("%s:1: string leakage:", rt->rt_info); + } else { + printf("String leaks:\n" + " %6s %s\n", + "REFCNT", "VALUE"); + } + list_for_each_safe(el, el1, &rt->string_list) { + JSString *str = list_entry(el, JSString, link); + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u ", str->header.ref_count); + } + JS_DumpString(rt, str); + if (rt->rt_info) { + printf(":%u", str->header.ref_count); + } else { + printf("\n"); + } + list_del(&str->link); + js_free_rt(rt, str); + } + if (rt->rt_info) + printf("\n"); + } + { + JSMallocState *s = &rt->malloc_state; + if (s->malloc_count > 1) { + if (rt->rt_info) + printf("%s:1: ", rt->rt_info); + printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n", + (uint64_t)(s->malloc_size - sizeof(JSRuntime)), + (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]); + } + } +#endif + + { + JSMallocState ms = rt->malloc_state; + rt->mf.js_free(&ms, rt); + } +} + +#if defined(EMSCRIPTEN) +/* currently no stack limitation */ +static inline uint8_t *js_get_stack_pointer(void) +{ + return NULL; +} + +static inline BOOL js_check_stack_overflow(JSContext *ctx, size_t alloca_size) +{ + return FALSE; +} +#else +/* Note: OS and CPU dependent */ +static inline uint8_t *js_get_stack_pointer(void) +{ + return __builtin_frame_address(0); +} + +static inline BOOL js_check_stack_overflow(JSContext *ctx, size_t alloca_size) +{ + size_t size; + size = ctx->stack_top - js_get_stack_pointer(); + return unlikely((size + alloca_size) > ctx->stack_size); +} +#endif + +JSContext *JS_NewContextRaw(JSRuntime *rt) +{ + JSContext *ctx; + int i; + + ctx = js_mallocz_rt(rt, sizeof(JSContext)); + if (!ctx) + return NULL; + 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->stack_top = js_get_stack_pointer(); + ctx->stack_size = JS_DEFAULT_STACK_SIZE; + ctx->current_exception = JS_NULL; +#ifdef CONFIG_BIGNUM + ctx->bf_ctx = &rt->bf_ctx; + ctx->fp_env.prec = 113; + ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL; +#endif + for(i = 0; i < rt->class_count; i++) + ctx->class_proto[i] = JS_NULL; + ctx->regexp_ctor = JS_NULL; + ctx->promise_ctor = JS_NULL; + init_list_head(&ctx->loaded_modules); + + JS_AddIntrinsicBasicObjects(ctx); + return ctx; +} + +JSContext *JS_NewContext(JSRuntime *rt) +{ + JSContext *ctx; + + ctx = JS_NewContextRaw(rt); + if (!ctx) + return NULL; + + JS_AddIntrinsicBaseObjects(ctx); + JS_AddIntrinsicDate(ctx); + JS_AddIntrinsicEval(ctx); + JS_AddIntrinsicStringNormalize(ctx); + JS_AddIntrinsicRegExp(ctx); + JS_AddIntrinsicJSON(ctx); + JS_AddIntrinsicProxy(ctx); + JS_AddIntrinsicMapSet(ctx); + JS_AddIntrinsicTypedArrays(ctx); + JS_AddIntrinsicPromise(ctx); +#ifdef CONFIG_BIGNUM + JS_AddIntrinsicBigInt(ctx); +#endif + return ctx; +} + +void *JS_GetContextOpaque(JSContext *ctx) +{ + return ctx->user_opaque; +} + +void JS_SetContextOpaque(JSContext *ctx, void *opaque) +{ + ctx->user_opaque = opaque; +} + +/* set the new value and free the old value after (freeing the value + can reallocate the object data) */ +static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) +{ + JSValue old_val; + old_val = *pval; + *pval = new_val; + JS_FreeValue(ctx, old_val); +} + +void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) +{ + JSRuntime *rt = ctx->rt; + assert(class_id < rt->class_count); + set_value(ctx, &ctx->class_proto[class_id], obj); +} + +JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) +{ + JSRuntime *rt = ctx->rt; + assert(class_id < rt->class_count); + return JS_DupValue(ctx, ctx->class_proto[class_id]); +} + +typedef enum JSFreeModuleEnum { + JS_FREE_MODULE_ALL, + JS_FREE_MODULE_NOT_RESOLVED, + JS_FREE_MODULE_NOT_EVALUATED, +} JSFreeModuleEnum; + +/* XXX: would be more efficient with separate module lists */ +static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) +{ + struct list_head *el, *el1; + list_for_each_safe(el, el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + if (flag == JS_FREE_MODULE_ALL || + (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) || + (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) { + js_free_module_def(ctx, m); + } + } +} + +void JS_FreeContext(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + int i; + +#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->current_exception); + + 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->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); + js_free_rt(ctx->rt, ctx); +} + +JSRuntime *JS_GetRuntime(JSContext *ctx) +{ + return ctx->rt; +} + +void JS_SetMaxStackSize(JSContext *ctx, size_t stack_size) +{ + ctx->stack_size = stack_size; +} + +static inline BOOL is_strict_mode(JSContext *ctx) +{ + JSStackFrame *sf = ctx->current_stack_frame; + return (sf && (sf->js_mode & JS_MODE_STRICT)); +} + +#ifdef CONFIG_BIGNUM +static inline BOOL is_bigint_mode(JSContext *ctx) +{ + JSStackFrame *sf = ctx->current_stack_frame; + return (sf && (sf->js_mode & JS_MODE_BIGINT)); +} +#endif + +JSValue JS_NewInt64(JSContext *ctx, int64_t v) +{ + if (v == (int32_t)v) { + return JS_NewInt32(ctx, v); + } else { +#ifdef CONFIG_BIGNUM + if (is_bigint_mode(ctx)) { + bf_t a_s, *a = &a_s; + bf_init(ctx->bf_ctx, a); + bf_set_si(a, v); + return JS_NewBigInt(ctx, a); + } else +#endif + { + return __JS_NewFloat64(ctx, (double)v); + } + } +} + +static force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) +{ +#ifdef CONFIG_BIGNUM + return JS_NewInt64(ctx, val); +#else + JSValue v; + if (val <= 0x7fffffff) { + v = JS_MKVAL(JS_TAG_INT, val); + } else { + v = __JS_NewFloat64(ctx, val); + } + return v; +#endif +} + +/* JSAtom support */ + +#define JS_ATOM_TAG_INT (1U << 31) +#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) +#define JS_ATOM_MAX ((1U << 30) - 1) + +/* return the max count from the hash size */ +#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) + +static inline BOOL __JS_AtomIsConst(JSAtom v) +{ +#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 + return (int32_t)v <= 0; +#else + return (int32_t)v < JS_ATOM_END; +#endif +} + +static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) +{ + return (v & JS_ATOM_TAG_INT) != 0; +} + +static inline JSAtom __JS_AtomFromUInt32(uint32_t v) +{ + return v | JS_ATOM_TAG_INT; +} + +static inline uint32_t __JS_AtomToUInt32(JSAtom atom) +{ + return atom & ~JS_ATOM_TAG_INT; +} + +static inline int is_num(int c) +{ + return c >= '0' && c <= '9'; +} + +/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ +static inline BOOL is_num_string(uint32_t *pval, const JSString *p) +{ + uint32_t n; + uint64_t n64; + int c, i, len; + + len = p->len; + if (len == 0 || len > 10) + return FALSE; + if (p->is_wide_char) + c = p->u.str16[0]; + else + c = p->u.str8[0]; + if (is_num(c)) { + if (c == '0') { + if (len != 1) + return FALSE; + n = 0; + } else { + n = c - '0'; + for(i = 1; i < len; i++) { + if (p->is_wide_char) + c = p->u.str16[i]; + else + c = p->u.str8[i]; + if (!is_num(c)) + return FALSE; + n64 = (uint64_t)n * 10 + (c - '0'); + if ((n64 >> 32) != 0) + return FALSE; + n = n64; + } + } + *pval = n; + return TRUE; + } else { + return FALSE; + } +} + +/* XXX: could use faster version ? */ +static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static inline uint32_t hash_string16(const uint16_t *str, + size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static uint32_t hash_string(const JSString *str, uint32_t h) +{ + if (str->is_wide_char) + h = hash_string16(str->u.str16, str->len, h); + else + h = hash_string8(str->u.str8, str->len, h); + return h; +} + +static __maybe_unused void JS_DumpString(JSRuntime *rt, + const JSString *p) +{ + int i, c, sep; + + if (p == NULL) { + printf(""); + return; + } + printf("%d", p->header.ref_count); + sep = (p->header.ref_count == 1) ? '\"' : '\''; + putchar(sep); + for(i = 0; i < p->len; i++) { + if (p->is_wide_char) + c = p->u.str16[i]; + else + c = p->u.str8[i]; + if (c == sep || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar(sep); +} + +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) +{ + JSAtomStruct *p; + int h, i; + /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ + printf("JSAtom count=%d size=%d hash_size=%d:\n", + rt->atom_count, rt->atom_size, rt->atom_hash_size); + printf("JSAtom hash table: {\n"); + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + if (h) { + printf(" %d:", i); + while (h) { + p = rt->atom_array[h]; + printf(" "); + JS_DumpString(rt, p); + h = p->hash_next; + } + printf("\n"); + } + } + printf("}\n"); + printf("JSAtom table: {\n"); + for(i = 0; i < rt->atom_size; i++) { + p = rt->atom_array[i]; + if (!atom_is_free(p)) { + printf(" %d: { %d %08x ", i, p->atom_type, p->hash); + if (!(p->len == 0 && p->is_wide_char != 0)) + JS_DumpString(rt, p); + printf(" %d }\n", p->hash_next); + } + } + printf("}\n"); +} + +static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) +{ + JSAtomStruct *p; + uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; + + assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ + new_hash_mask = new_hash_size - 1; + new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); + if (!new_hash) + return -1; + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + while (h != 0) { + p = rt->atom_array[h]; + hash_next1 = p->hash_next; + /* add in new hash table */ + j = p->hash & new_hash_mask; + p->hash_next = new_hash[j]; + new_hash[j] = h; + h = hash_next1; + } + } + js_free_rt(rt, rt->atom_hash); + rt->atom_hash = new_hash; + rt->atom_hash_size = new_hash_size; + rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); + // JS_DumpAtoms(rt); + return 0; +} + +static int JS_InitAtoms(JSRuntime *rt) +{ + int i, len, atom_type; + const char *p; + + rt->atom_hash_size = 0; + rt->atom_hash = NULL; + rt->atom_count = 0; + rt->atom_size = 0; + rt->atom_free_index = 0; + if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ + return -1; + + p = js_atom_init; + for(i = 1; i < JS_ATOM_END; i++) { + if (i == JS_ATOM_Private_brand) + atom_type = JS_ATOM_TYPE_PRIVATE; + else if (i >= JS_ATOM_Symbol_toPrimitive) + atom_type = JS_ATOM_TYPE_SYMBOL; + else + atom_type = JS_ATOM_TYPE_STRING; + len = strlen(p); + if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) + return -1; + p = p + len + 1; + } + return 0; +} + +static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) +{ + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + rt = ctx->rt; + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return JS_ATOM_KIND_STRING; + p = rt->atom_array[v]; + switch(p->atom_type) { + case JS_ATOM_TYPE_STRING: + return JS_ATOM_KIND_STRING; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_TYPE_SYMBOL: + switch(p->hash) { + case JS_ATOM_HASH_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_HASH_PRIVATE: + return JS_ATOM_KIND_PRIVATE; + default: + abort(); + } + default: + abort(); + } +} + +static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) +{ + return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; +} + +static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p1; + + i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; + p1 = rt->atom_array[i]; + while (p1 != p) { + assert(i != 0); + i = p1->hash_next; + p1 = rt->atom_array[i]; + } + } + return i; +} + +/* string case (internal). Return JS_ATOM_NULL if error. 'str' is + freed. */ +static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + int len; + +#if 0 + printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n"); +#endif + if (atom_type < JS_ATOM_TYPE_SYMBOL) { + /* str is not NULL */ + if (str->atom_type == atom_type) { + /* str is the atom, return its index */ + i = js_get_atom_index(rt, str); + /* reduce string refcount and increase atom's unless constant */ + if (__JS_AtomIsConst(i)) + str->header.ref_count--; + return i; + } + /* try and locate an already registered atom */ + len = str->len; + h = hash_string(str, atom_type); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == atom_type && + p->len == len && + js_string_memcmp(p, str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + goto done; + } + i = p->hash_next; + } + } else { + h1 = 0; /* avoid warning */ + if (atom_type == JS_ATOM_TYPE_SYMBOL) { + h = JS_ATOM_HASH_SYMBOL; + } else { + h = JS_ATOM_HASH_PRIVATE; + atom_type = JS_ATOM_TYPE_SYMBOL; + } + } + + if (rt->atom_free_index == 0) { + /* allow new atom entries */ + uint32_t new_size, start; + JSAtomStruct **new_array; + + /* alloc new with size progression 3/2: + 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 + preallocating space for predefined atoms (at least 195). + */ + new_size = max_int(211, rt->atom_size * 3 / 2); + if (new_size > JS_ATOM_MAX) + goto fail; + /* XXX: should use realloc2 to use slack space */ + new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); + if (!new_array) + goto fail; + /* Note: the atom 0 is not used */ + start = rt->atom_size; + if (start == 0) { + /* JS_ATOM_NULL entry */ + p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); + if (!p) { + js_free_rt(rt, new_array); + goto fail; + } + p->header.ref_count = 1; /* not refcounted */ + p->atom_type = JS_ATOM_TYPE_SYMBOL; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + new_array[0] = p; + rt->atom_count++; + start = 1; + } + rt->atom_size = new_size; + rt->atom_array = new_array; + rt->atom_free_index = start; + for(i = start; i < new_size; i++) { + uint32_t next; + if (i == (new_size - 1)) + next = 0; + else + next = i + 1; + rt->atom_array[i] = atom_set_free(next); + } + } + + if (str) { + if (str->atom_type == 0) { + p = str; + p->atom_type = atom_type; + } else { + p = js_malloc_rt(rt, sizeof(JSString) + + (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + if (unlikely(!p)) + goto fail; + p->header.ref_count = 1; + p->is_wide_char = str->is_wide_char; + p->len = str->len; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + js_free_string(rt, str); + } + } else { + p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ + if (!p) + return JS_ATOM_NULL; + p->header.ref_count = 1; + p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ + p->len = 0; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + } + + /* use an already free entry */ + i = rt->atom_free_index; + rt->atom_free_index = atom_get_free(rt->atom_array[i]); + rt->atom_array[i] = p; + + p->hash = h; + p->hash_next = i; /* atom_index */ + p->atom_type = atom_type; + + rt->atom_count++; + + if (atom_type != JS_ATOM_TYPE_SYMBOL) { + p->hash_next = rt->atom_hash[h1]; + rt->atom_hash[h1] = i; + if (unlikely(rt->atom_count >= rt->atom_count_resize)) + JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); + } + + // JS_DumpAtoms(rt); + return i; + + fail: + i = JS_ATOM_NULL; + done: + if (str) + js_free_string(rt, str); + return i; +} + +/* only works with zero terminated 8 bit strings */ +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type) +{ + JSString *p; + p = js_alloc_string_rt(rt, len, 0); + if (!p) + return JS_ATOM_NULL; + memcpy(p->u.str8, str, len); + p->u.str8[len] = '\0'; + return __JS_NewAtom(rt, p, atom_type); +} + +static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, + int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + + h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == JS_ATOM_TYPE_STRING && + p->len == len && + p->is_wide_char == 0 && + memcmp(p->u.str8, str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + return i; + } + i = p->hash_next; + } + return JS_ATOM_NULL; +} + +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) +{ +#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */ + if (unlikely(i == JS_ATOM_NULL)) { + p->header.ref_count = INT32_MAX / 2; + return; + } +#endif + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p0, *p1; + uint32_t h0; + + h0 = p->hash & (rt->atom_hash_size - 1); + i = rt->atom_hash[h0]; + p1 = rt->atom_array[i]; + if (p1 == p) { + rt->atom_hash[h0] = p1->hash_next; + } else { + for(;;) { + assert(i != 0); + p0 = p1; + i = p1->hash_next; + p1 = rt->atom_array[i]; + if (p1 == p) { + p0->hash_next = p1->hash_next; + break; + } + } + } + } + /* insert in free atom list */ + rt->atom_array[i] = atom_set_free(rt->atom_free_index); + rt->atom_free_index = i; + /* free the string structure */ +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + rt->atom_count--; + assert(rt->atom_count >= 0); +} + +static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) +{ + JSAtomStruct *p; + + p = rt->atom_array[i]; + if (--p->header.ref_count > 0) + return; + JS_FreeAtomStruct(rt, p); +} + +/* Warning: 'p' is freed */ +static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) +{ + JSRuntime *rt = ctx->rt; + uint32_t n; + if (is_num_string(&n, p)) { + if (n <= JS_ATOM_MAX_INT) { + js_free_string(rt, p); + return __JS_AtomFromUInt32(n); + } + } + /* XXX: should generate an exception */ + return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); +} + +JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) +{ + JSValue val; + + if (len == 0 || !is_digit(*str)) { + JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); + if (atom) + return atom; + } + val = JS_NewStringLen(ctx, str, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); +} + +JSAtom JS_NewAtom(JSContext *ctx, const char *str) +{ + return JS_NewAtomLen(ctx, str, strlen(str)); +} + +JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) +{ + if (n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32(n); + } else { + char buf[11]; + JSValue val; + snprintf(buf, sizeof(buf), "%u", n); + val = JS_NewString(ctx, buf); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) +{ + if ((uint64_t)n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32((uint32_t)n); + } else { + char buf[24]; + JSValue val; + snprintf(buf, sizeof(buf), "%" PRId64 , n); + val = JS_NewString(ctx, buf); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +/* 'p' is freed */ +static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSAtom atom; + atom = __JS_NewAtom(rt, p, atom_type); + if (atom == JS_ATOM_NULL) + return JS_ThrowOutOfMemory(ctx); + return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); +} + +/* descr must be a non-numeric string atom */ +static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, + int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSString *p; + + assert(!__JS_AtomIsTaggedInt(descr)); + assert(descr < rt->atom_size); + p = rt->atom_array[descr]; + JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); + return JS_NewSymbol(ctx, p, atom_type); +} + +#define ATOM_GET_STR_BUF_SIZE 64 + +/* Should only be used for debug. */ +static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, + JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); + } else { + JSAtomStruct *p; + assert(atom < rt->atom_size); + if (atom == JS_ATOM_NULL) { + snprintf(buf, buf_size, ""); + } else { + int i, c; + char *q; + JSString *str; + + q = buf; + p = rt->atom_array[atom]; + assert(!atom_is_free(p)); + str = p; + if (str) { + if (!str->is_wide_char) { + /* special case ASCII strings */ + c = 0; + for(i = 0; i < str->len; i++) { + c |= str->u.str8[i]; + } + if (c < 0x80) + return (const char *)str->u.str8; + } + for(i = 0; i < str->len; i++) { + if (str->is_wide_char) + c = str->u.str16[i]; + else + c = str->u.str8[i]; + if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) + break; + if (c < 128) { + *q++ = c; + } else { + q += unicode_to_utf8((uint8_t *)q, c); + } + } + } + *q = '\0'; + } + } + return buf; +} + +static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) +{ + return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); +} + +static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (__JS_AtomIsTaggedInt(atom)) { + snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom)); + return JS_NewString(ctx, buf); + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING) { + goto ret_string; + } else if (force_string) { + if (p->len == 0 && p->is_wide_char != 0) { + /* no description string */ + p = rt->atom_array[JS_ATOM_empty_string]; + } + ret_string: + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); + } else { + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p)); + } + } +} + +JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, FALSE); +} + +JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, TRUE); +} + +/* return TRUE if the atom is an array index (i.e. 0 <= index <= + 2^32-2 and return its value */ +static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + *pval = __JS_AtomToUInt32(atom); + return TRUE; + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + uint32_t val; + + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING && + is_num_string(&val, p) && val != -1) { + *pval = val; + return TRUE; + } else { + *pval = 0; + return FALSE; + } + } +} + +/* This test must be fast if atom is not a numeric index (e.g. a + method name). Return JS_UNDEFINED if not a numeric + index. JS_EXCEPTION can also be returned. */ +static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) +{ + JSRuntime *rt = ctx->rt; + JSAtomStruct *p1; + JSString *p; + int c, len, ret; + JSValue num, str; + + if (__JS_AtomIsTaggedInt(atom)) + return JS_NewInt32(ctx, __JS_AtomToUInt32(atom)); + assert(atom < rt->atom_size); + p1 = rt->atom_array[atom]; + if (p1->atom_type != JS_ATOM_TYPE_STRING) + return JS_UNDEFINED; + p = p1; + len = p->len; + if (p->is_wide_char) { + const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) + goto minus_zero; + } + /* XXX: should test NaN, but the tests do not check it */ + if (!is_num(c)) { + /* XXX: String should be normalized, therefore 8-bit only */ + const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) + return JS_UNDEFINED; + } + } else { + const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) { + minus_zero: + return __JS_NewFloat64(ctx, -0.0); + } + } + if (!is_num(c)) { + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, "nfinity", 7))) + return JS_UNDEFINED; + } + } + /* XXX: bignum: would be better to only accept integer to avoid + relying on current floating point precision */ + /* this is ECMA CanonicalNumericIndexString primitive */ + num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); + if (JS_IsException(num)) + return num; + str = JS_ToString(ctx, num); + if (JS_IsException(str)) { + JS_FreeValue(ctx, num); + return str; + } + ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str)); + JS_FreeValue(ctx, str); + if (ret == 0) { + return num; + } else { + JS_FreeValue(ctx, num); + return JS_UNDEFINED; + } +} + +/* return -1 if exception or TRUE/FALSE */ +static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) +{ + JSValue num; + num = JS_AtomIsNumericIndex1(ctx, atom); + if (likely(JS_IsUndefined(num))) + return FALSE; + if (JS_IsException(num)) + return -1; + JS_FreeValue(ctx, num); + return TRUE; +} + +void JS_FreeAtom(JSContext *ctx, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(ctx->rt, v); +} + +void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(rt, v); +} + +/* return TRUE if 'v' is a symbol with a string description */ +static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return FALSE; + p = rt->atom_array[v]; + return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && + p->hash == JS_ATOM_HASH_SYMBOL) || + p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && + !(p->len == 0 && p->is_wide_char != 0)); +} + +static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + const char *p; + int i; + + /* XXX: should handle embedded null characters */ + /* XXX: should move encoding code to JS_AtomGetStr */ + p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + for (i = 0; p[i]; i++) { + int c = (unsigned char)p[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) + break; + } + if (i > 0 && p[i] == '\0') { + printf("%s", p); + } else { + putchar('"'); + printf("%.*s", i, p); + for (; p[i]; i++) { + int c = (unsigned char)p[i]; + if (c == '\"' || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar('\"'); + } +} + +/* free with JS_FreeCString() */ +const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) +{ + JSValue str; + const char *cstr; + + str = JS_AtomToString(ctx, atom); + if (JS_IsException(str)) + return NULL; + cstr = JS_ToCString(ctx, str); + JS_FreeValue(ctx, str); + return cstr; +} + +/* return a string atom containing name concatenated with str1 */ +static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) +{ + JSValue str; + JSAtom atom; + const char *cstr; + char *cstr2; + size_t len, len1; + + str = JS_AtomToString(ctx, name); + if (JS_IsException(str)) + return JS_ATOM_NULL; + cstr = JS_ToCStringLen(ctx, &len, str); + if (!cstr) + goto fail; + len1 = strlen(str1); + cstr2 = js_malloc(ctx, len + len1 + 1); + if (!cstr2) + goto fail; + memcpy(cstr2, cstr, len); + memcpy(cstr2 + len, str1, len1); + cstr2[len + len1] = '\0'; + atom = JS_NewAtomLen(ctx, cstr2, len + len1); + js_free(ctx, cstr2); + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return atom; + fail: + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return JS_ATOM_NULL; +} + +static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%u", n); + return js_atom_concat_str(ctx, name, buf); +} + +static inline BOOL JS_IsEmptyString(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; +} + +/* JSClass support */ + +/* a new class ID is allocated if *pclass_id != 0 */ +JSClassID JS_NewClassID(JSClassID *pclass_id) +{ + JSClassID class_id; + /* XXX: make it thread safe */ + class_id = *pclass_id; + if (class_id == 0) { + class_id = js_class_id_alloc++; + *pclass_id = class_id; + } + return class_id; +} + +BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) +{ + return (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0); +} + +/* create a new object internal class. Return -1 if error, 0 if + OK. The finalizer can be NULL if none is needed. */ +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name) +{ + int new_size, i; + JSClass *cl, *new_class_array; + struct list_head *el; + + if (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0) + return -1; + + if (class_id >= rt->class_count) { + new_size = max_int(JS_CLASS_INIT_COUNT, + max_int(class_id + 1, rt->class_count * 3 / 2)); + + /* reallocate the context class prototype array, if any */ + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSValue *new_tab; + new_tab = js_realloc_rt(rt, ctx->class_proto, + sizeof(ctx->class_proto[0]) * new_size); + if (!new_tab) + return -1; + for(i = rt->class_count; i < new_size; i++) + new_tab[i] = JS_NULL; + ctx->class_proto = new_tab; + } + /* reallocate the class array */ + new_class_array = js_realloc_rt(rt, rt->class_array, + sizeof(JSClass) * new_size); + if (!new_class_array) + return -1; + memset(new_class_array + rt->class_count, 0, + (new_size - rt->class_count) * sizeof(JSClass)); + rt->class_array = new_class_array; + rt->class_count = new_size; + } + cl = &rt->class_array[class_id]; + cl->class_id = class_id; + cl->class_name = JS_DupAtomRT(rt, name); + cl->finalizer = class_def->finalizer; + cl->gc_mark = class_def->gc_mark; + cl->call = class_def->call; + cl->exotic = class_def->exotic; + return 0; +} + +int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) +{ + int ret, len; + JSAtom name; + + len = strlen(class_def->class_name); + name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) { + name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) + return -1; + } + ret = JS_NewClass1(rt, class_id, class_def, name); + JS_FreeAtomRT(rt, name); + return ret; +} + +static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len) +{ + JSString *str; + + if (len <= 0) { + return JS_AtomToString(ctx, JS_ATOM_empty_string); + } + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str->u.str8, buf, len); + str->u.str8[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str->u.str16, buf, len * 2); + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue js_new_string_char(JSContext *ctx, uint16_t c) +{ + if (c < 0x100) { + uint8_t ch8 = c; + return js_new_string8(ctx, &ch8, 1); + } else { + uint16_t ch16 = c; + return js_new_string16(ctx, &ch16, 1); + } +} + +static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) +{ + int len = end - start; + if (start == 0 && end == p->len) { + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); + } + if (p->is_wide_char && len > 0) { + JSString *str; + int i; + uint16_t c = 0; + for (i = start; i < end; i++) { + c |= p->u.str16[i]; + } + if (c > 0xFF) + return js_new_string16(ctx, p->u.str16 + start, len); + + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + for (i = 0; i < len; i++) { + str->u.str8[i] = p->u.str16[start + i]; + } + str->u.str8[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); + } else { + return js_new_string8(ctx, p->u.str8 + start, len); + } +} + +typedef struct StringBuffer { + JSContext *ctx; + JSString *str; + int len; + int size; + int is_wide_char; + int error_status; +} StringBuffer; + +/* It is valid to call string_buffer_end() and all string_buffer functions even + if string_buffer_init() or another string_buffer function returns an error. + If the error_status is set, string_buffer_end() returns JS_EXCEPTION. + */ +static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, + int is_wide) +{ + s->ctx = ctx; + s->size = size; + s->len = 0; + s->is_wide_char = is_wide; + s->error_status = 0; + s->str = js_alloc_string(ctx, size, is_wide); + if (unlikely(!s->str)) { + s->size = 0; + return s->error_status = -1; + } +#ifdef DUMP_LEAKS + /* the StringBuffer may reallocate the JSString, only link it at the end */ + list_del(&s->str->link); +#endif + return 0; +} + +static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) +{ + return string_buffer_init2(ctx, s, size, 0); +} + +static void string_buffer_free(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; +} + +static int string_buffer_set_error(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; + s->size = 0; + s->len = 0; + return s->error_status = -1; +} + +static no_inline int string_buffer_widen(StringBuffer *s, int size) +{ + JSString *str; + size_t slack; + int i; + + if (s->error_status) + return -1; + + str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); + if (!str) + return string_buffer_set_error(s); + size += slack >> 1; + for(i = s->len; i-- > 0;) { + str->u.str16[i] = str->u.str8[i]; + } + s->is_wide_char = 1; + s->size = size; + s->str = str; + return 0; +} + +static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) +{ + JSString *new_str; + int new_size; + size_t new_size_bytes, slack; + + if (s->error_status) + return -1; + + if (new_len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(s->ctx, "string too long"); + return string_buffer_set_error(s); + } + new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); + if (!s->is_wide_char && c >= 0x100) { + return string_buffer_widen(s, new_size); + } + new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; + new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); + if (!new_str) + return string_buffer_set_error(s); + new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); + s->size = new_size; + s->str = new_str; + return 0; +} + +static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + } else if (c < 0x100) { + s->str->u.str8[s->len++] = c; + } else { + if (string_buffer_widen(s, s->size)) + return -1; + s->str->u.str16[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xff */ +static int string_buffer_putc8(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + } else { + s->str->u.str8[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xffff */ +static int string_buffer_putc16(StringBuffer *s, uint32_t c) +{ + if (likely(s->len < s->size)) { + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + return 0; + } else if (c < 0x100) { + s->str->u.str8[s->len++] = c; + return 0; + } + } + return string_buffer_putc_slow(s, c); +} + +/* 0 <= c <= 0x10ffff */ +static int string_buffer_putc(StringBuffer *s, uint32_t c) +{ + if (unlikely(c >= 0x10000)) { + /* surrogate pair */ + c -= 0x10000; + if (string_buffer_putc16(s, (c >> 10) + 0xd800)) + return -1; + c = (c & 0x3ff) + 0xdc00; + } + return string_buffer_putc16(s, c); +} + +static int string_get(const JSString *p, int idx) { + return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; +} + +static int string_getc(const JSString *p, int *pidx) +{ + int idx, c, c1; + idx = *pidx; + if (p->is_wide_char) { + c = p->u.str16[idx++]; + if (c >= 0xd800 && c < 0xdc00 && idx < p->len) { + c1 = p->u.str16[idx]; + if (c1 >= 0xdc00 && c1 < 0xe000) { + c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + idx++; + } + } + } else { + c = p->u.str8[idx++]; + } + *pidx = idx; + return c; +} + +static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) +{ + int i; + + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, 0)) + return -1; + } + if (s->is_wide_char) { + for (i = 0; i < len; i++) { + s->str->u.str16[s->len + i] = p[i]; + } + s->len += len; + } else { + memcpy(&s->str->u.str8[s->len], p, len); + s->len += len; + } + return 0; +} + +static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) +{ + int c = 0, i; + + for (i = 0; i < len; i++) { + c |= p[i]; + } + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, c)) + return -1; + } else if (!s->is_wide_char && c >= 0x100) { + if (string_buffer_widen(s, s->size)) + return -1; + } + if (s->is_wide_char) { + memcpy(&s->str->u.str16[s->len], p, len << 1); + s->len += len; + } else { + for (i = 0; i < len; i++) { + s->str->u.str8[s->len + i] = p[i]; + } + s->len += len; + } + return 0; +} + +/* appending an ASCII string */ +static int string_buffer_puts8(StringBuffer *s, const char *str) +{ + return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); +} + +static int string_buffer_concat(StringBuffer *s, const JSString *p, + uint32_t from, uint32_t to) +{ + if (to <= from) + return 0; + if (p->is_wide_char) + return string_buffer_write16(s, p->u.str16 + from, to - from); + else + return string_buffer_write8(s, p->u.str8 + from, to - from); +} + +static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) +{ + JSString *p; + JSValue v1; + int res; + + if (s->error_status) { + /* prevent exception overload */ + return -1; + } + if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { + v1 = JS_ToString(s->ctx, v); + if (JS_IsException(v1)) + return string_buffer_set_error(s); + p = JS_VALUE_GET_STRING(v1); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v1); + return res; + } + p = JS_VALUE_GET_STRING(v); + return string_buffer_concat(s, p, 0, p->len); +} + +static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) +{ + JSString *p; + int res; + + if (s->error_status) { + /* prevent exception overload */ + JS_FreeValue(s->ctx, v); + return -1; + } + if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { + v = JS_ToStringFree(s->ctx, v); + if (JS_IsException(v)) + return string_buffer_set_error(s); + } + p = JS_VALUE_GET_STRING(v); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v); + return res; +} + +static int string_buffer_fill(StringBuffer *s, int c, int count) +{ + /* XXX: optimize */ + if (s->len + count > s->size) { + if (string_buffer_realloc(s, s->len + count, c)) + return -1; + } + while (count-- > 0) { + if (string_buffer_putc16(s, c)) + return -1; + } + return 0; +} + +static JSValue string_buffer_end(StringBuffer *s) +{ + JSString *str; + str = s->str; + if (s->error_status) + return JS_EXCEPTION; + if (s->len == 0) { + js_free(s->ctx, str); + s->str = NULL; + return JS_AtomToString(s->ctx, JS_ATOM_empty_string); + } + if (s->len < s->size) { + /* smaller size so js_realloc should not fail, but OK if it does */ + /* XXX: should add some slack to avoid unnecessary calls */ + /* XXX: might need to use malloc+free to ensure smaller size */ + str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + + (s->len << s->is_wide_char) + 1 - s->is_wide_char); + if (str == NULL) + str = s->str; + s->str = str; + } + if (!s->is_wide_char) + str->u.str8[s->len] = 0; +#ifdef DUMP_LEAKS + list_add_tail(&str->link, &s->ctx->rt->string_list); +#endif + str->is_wide_char = s->is_wide_char; + str->len = s->len; + s->str = NULL; + return JS_MKPTR(JS_TAG_STRING, str); +} + +/* create a string from a UTF-8 buffer */ +JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) +{ + const uint8_t *p, *p_end, *p_start, *p_next; + uint32_t c; + StringBuffer b_s, *b = &b_s; + size_t len1; + + p_start = (const uint8_t *)buf; + p_end = p_start + buf_len; + p = p_start; + while (p < p_end && *p < 128) + p++; + len1 = p - p_start; + if (len1 > JS_STRING_LEN_MAX) + return JS_ThrowInternalError(ctx, "string too long"); + if (p == p_end) { + /* ASCII string */ + return js_new_string8(ctx, (const uint8_t *)buf, buf_len); + } else { + if (string_buffer_init(ctx, b, buf_len)) + goto fail; + string_buffer_write8(b, p_start, len1); + while (p < p_end) { + if (*p < 128) { + string_buffer_putc8(b, *p++); + } else { + /* parse utf-8 sequence, return 0xFFFFFFFF for error */ + c = unicode_from_utf8(p, p_end - p, &p_next); + if (c < 0x10000) { + p = p_next; + } else if (c <= 0x10FFFF) { + p = p_next; + /* surrogate pair */ + c -= 0x10000; + string_buffer_putc16(b, (c >> 10) + 0xd800); + c = (c & 0x3ff) + 0xdc00; + } else { + /* invalid char */ + c = 0xfffd; + /* skip the invalid chars */ + /* XXX: seems incorrect. Why not just use c = *p++; ? */ + while (p < p_end && (*p >= 0x80 && *p < 0xc0)) + p++; + if (p < p_end) { + p++; + while (p < p_end && (*p >= 0x80 && *p < 0xc0)) + p++; + } + } + string_buffer_putc16(b, c); + } + } + } + return string_buffer_end(b); + + fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, + JSValue str2, const char *str3) +{ + StringBuffer b_s, *b = &b_s; + int len1, len3; + JSString *p; + + if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { + str2 = JS_ToStringFree(ctx, str2); + if (JS_IsException(str2)) + goto fail; + } + p = JS_VALUE_GET_STRING(str2); + len1 = strlen(str1); + len3 = strlen(str3); + + if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) + goto fail; + + string_buffer_write8(b, (const uint8_t *)str1, len1); + string_buffer_concat(b, p, 0, p->len); + string_buffer_write8(b, (const uint8_t *)str3, len3); + + JS_FreeValue(ctx, str2); + return string_buffer_end(b); + + fail: + JS_FreeValue(ctx, str2); + return JS_EXCEPTION; +} + +JSValue JS_NewString(JSContext *ctx, const char *str) +{ + return JS_NewStringLen(ctx, str, strlen(str)); +} + +JSValue JS_NewAtomString(JSContext *ctx, const char *str) +{ + JSAtom atom = JS_NewAtom(ctx, str); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + JSValue val = JS_AtomToString(ctx, atom); + JS_FreeAtom(ctx, atom); + return val; +} + +/* return (NULL, 0) if exception. */ +/* return pointer into a JSString with a live ref_count */ +/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ +const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8) +{ + JSValue val; + JSString *str, *str_new; + int pos, len, c, c1; + uint8_t *q; + + if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) { + val = JS_ToString(ctx, val1); + if (JS_IsException(val)) + goto fail; + } else { + val = JS_DupValue(ctx, val1); + } + + str = JS_VALUE_GET_STRING(val); + len = str->len; + if (!str->is_wide_char) { + const uint8_t *src = str->u.str8; + int count; + + /* count the number of non-ASCII characters */ + /* Scanning the whole string is required for ASCII strings, + and computing the number of non-ASCII bytes is less expensive + than testing each byte, hence this method is faster for ASCII + strings, which is the most common case. + */ + count = 0; + for (pos = 0; pos < len; pos++) { + count += src[pos] >> 7; + } + if (count == 0) { + if (plen) + *plen = len; + return (const char *)src; + } + str_new = js_alloc_string(ctx, len + count, 0); + if (!str_new) + goto fail; + q = str_new->u.str8; + for (pos = 0; pos < len; pos++) { + c = src[pos]; + if (c < 0x80) { + *q++ = c; + } else { + *q++ = (c >> 6) | 0xc0; + *q++ = (c & 0x3f) | 0x80; + } + } + } else { + const uint16_t *src = str->u.str16; + /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may + produce 4 bytes but use 2 code points. + */ + str_new = js_alloc_string(ctx, len * 3, 0); + if (!str_new) + goto fail; + q = str_new->u.str8; + pos = 0; + while (pos < len) { + c = src[pos++]; + if (c < 0x80) { + *q++ = c; + } else { + if (c >= 0xd800 && c < 0xdc00) { + if (pos < len && !cesu8) { + c1 = src[pos]; + if (c1 >= 0xdc00 && c1 < 0xe000) { + pos++; + /* surrogate pair */ + c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } + q += unicode_to_utf8(q, c); + } + } + } + + *q = '\0'; + str_new->len = q - str_new->u.str8; + JS_FreeValue(ctx, val); + if (plen) + *plen = str_new->len; + return (const char *)str_new->u.str8; + fail: + if (plen) + *plen = 0; + return NULL; +} + +void JS_FreeCString(JSContext *ctx, const char *ptr) +{ + JSString *p; + if (!ptr) + return; + /* purposely removing constness */ + p = (JSString *)(void *)(ptr - offsetof(JSString, u)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); +} + +static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) +{ + int res; + + if (likely(!p1->is_wide_char)) { + if (likely(!p2->is_wide_char)) + res = memcmp(p1->u.str8, p2->u.str8, len); + else + res = -memcmp16_8(p2->u.str16, p1->u.str8, len); + } else { + if (!p2->is_wide_char) + res = memcmp16_8(p1->u.str16, p2->u.str8, len); + else + res = memcmp16(p1->u.str16, p2->u.str16, len); + } + return res; +} + +/* return < 0, 0 or > 0 */ +static int js_string_compare(JSContext *ctx, + const JSString *p1, const JSString *p2) +{ + int res, len; + len = min_int(p1->len, p2->len); + res = js_string_memcmp(p1, p2, len); + if (res == 0) { + if (p1->len == p2->len) + res = 0; + else if (p1->len < p2->len) + res = -1; + else + res = 1; + } + return res; +} + +static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) +{ + if (p->is_wide_char) { + memcpy(dst, p->u.str16 + offset, len * 2); + } else { + const uint8_t *src1 = p->u.str8 + offset; + int i; + + for(i = 0; i < len; i++) + dst[i] = src1[i]; + } +} + +static JSValue JS_ConcatString1(JSContext *ctx, + const JSString *p1, const JSString *p2) +{ + JSString *p; + uint32_t len; + int is_wide_char; + + len = p1->len + p2->len; + if (len > JS_STRING_LEN_MAX) + return JS_ThrowInternalError(ctx, "string too long"); + is_wide_char = p1->is_wide_char | p2->is_wide_char; + p = js_alloc_string(ctx, len, is_wide_char); + if (!p) + return JS_EXCEPTION; + if (!is_wide_char) { + memcpy(p->u.str8, p1->u.str8, p1->len); + memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); + p->u.str8[len] = '\0'; + } else { + copy_str16(p->u.str16, p1, 0, p1->len); + copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); + } + return JS_MKPTR(JS_TAG_STRING, p); +} + +/* op1 and op2 are converted to strings. For convience, op1 or op2 = + JS_EXCEPTION are accepted and return JS_EXCEPTION. */ +static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) +{ + JSValue ret; + JSString *p1, *p2; + + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { + op1 = JS_ToStringFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + } + if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { + op2 = JS_ToStringFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + } + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + + /* XXX: could also check if p1 is empty */ + if (p2->len == 0) { + goto ret_op1; + } + if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char + && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { + /* Concatenate in place in available space at the end of p1 */ + if (p1->is_wide_char) { + memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); + p1->len += p2->len; + } else { + memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); + p1->len += p2->len; + p1->u.str8[p1->len] = '\0'; + } + ret_op1: + JS_FreeValue(ctx, op2); + return op1; + } + ret = JS_ConcatString1(ctx, p1, p2); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return ret; +} + +/* Shape support */ + +static inline size_t get_shape_size(size_t hash_size, size_t prop_size) +{ + return hash_size * sizeof(uint32_t) + sizeof(JSShape) + + prop_size * sizeof(JSShapeProperty); +} + +static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) +{ + return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); +} + +static inline void *get_alloc_from_shape(JSShape *sh) +{ + return sh->prop_hash_end - ((intptr_t)sh->prop_hash_mask + 1); +} + +static inline JSShapeProperty *get_shape_prop(JSShape *sh) +{ + return sh->prop; +} + +static int init_shape_hash(JSRuntime *rt) +{ + rt->shape_hash_bits = 4; /* 16 shapes */ + rt->shape_hash_size = 1 << rt->shape_hash_bits; + rt->shape_hash_count = 0; + rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + rt->shape_hash_size); + if (!rt->shape_hash) + return -1; + return 0; +} + +/* same magic hash multiplier as the Linux kernel */ +static uint32_t shape_hash(uint32_t h, uint32_t val) +{ + return (h + val) * 0x9e370001; +} + +/* truncate the shape hash to 'hash_bits' bits */ +static uint32_t get_shape_hash(uint32_t h, int hash_bits) +{ + return h >> (32 - hash_bits); +} + +static uint32_t shape_initial_hash(JSObject *proto) +{ + uint32_t h; + h = shape_hash(1, (uintptr_t)proto); + if (sizeof(proto) > 4) + h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); + return h; +} + +static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) +{ + int new_shape_hash_size, i; + uint32_t h; + JSShape **new_shape_hash, *sh, *sh_next; + + new_shape_hash_size = 1 << new_shape_hash_bits; + new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + new_shape_hash_size); + if (!new_shape_hash) + return -1; + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { + sh_next = sh->shape_hash_next; + h = get_shape_hash(sh->hash, new_shape_hash_bits); + sh->shape_hash_next = new_shape_hash[h]; + new_shape_hash[h] = sh; + } + } + js_free_rt(rt, rt->shape_hash); + rt->shape_hash_bits = new_shape_hash_bits; + rt->shape_hash_size = new_shape_hash_size; + rt->shape_hash = new_shape_hash; + return 0; +} + +static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + sh->shape_hash_next = rt->shape_hash[h]; + rt->shape_hash[h] = sh; + rt->shape_hash_count++; +} + +static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + JSShape **psh; + + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + psh = &rt->shape_hash[h]; + while (*psh != sh) + psh = &(*psh)->shape_hash_next; + *psh = sh->shape_hash_next; + rt->shape_hash_count--; +} + +/* create a new empty shape with prototype 'proto' */ +static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, + int hash_size, int prop_size) +{ + JSRuntime *rt = ctx->rt; + void *sh_alloc; + JSShape *sh; + + /* resize the shape hash table if necessary */ + if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { + resize_shape_hash(rt, rt->shape_hash_bits + 1); + } + + sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size)); + if (!sh_alloc) + return NULL; + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + if (proto) + JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto)); + sh->proto = proto; + memset(sh->prop_hash_end - hash_size, 0, sizeof(sh->prop_hash_end[0]) * + hash_size); + sh->prop_hash_mask = hash_size - 1; + sh->prop_count = 0; + sh->prop_size = prop_size; + + /* insert in the hash table */ + sh->hash = shape_initial_hash(proto); + sh->is_hashed = TRUE; + sh->has_small_array_index = FALSE; + js_shape_hash_link(ctx->rt, sh); + return sh; +} + +static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) +{ + return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, + JS_PROP_INITIAL_SIZE); +} + +/* The shape is cloned. The new shape is not inserted in the shape + hash table */ +static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) +{ + JSShape *sh; + void *sh_alloc, *sh_alloc1; + size_t size; + JSShapeProperty *pr; + uint32_t i, hash_size; + + hash_size = sh1->prop_hash_mask + 1; + size = get_shape_size(hash_size, sh1->prop_size); + sh_alloc = js_malloc(ctx, size); + if (!sh_alloc) + return NULL; + sh_alloc1 = get_alloc_from_shape(sh1); + memcpy(sh_alloc, sh_alloc1, size); + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + sh->is_hashed = FALSE; + if (sh->proto) { + JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + JS_DupAtom(ctx, pr->atom); + } + return sh; +} + +static JSShape *js_dup_shape(JSShape *sh) +{ + sh->header.ref_count++; + return sh; +} + +static void js_free_shape0(JSRuntime *rt, JSShape *sh) +{ + uint32_t i; + JSShapeProperty *pr; + + assert(sh->header.ref_count == 0); + if (sh->is_hashed) + js_shape_hash_unlink(rt, sh); + if (sh->proto != NULL) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JS_FreeAtomRT(rt, pr->atom); + pr++; + } + remove_gc_object(&sh->header); + js_free_rt(rt, get_alloc_from_shape(sh)); +} + +static void js_free_shape(JSRuntime *rt, JSShape *sh) +{ + if (unlikely(--sh->header.ref_count <= 0)) { + js_free_shape0(rt, sh); + } +} + +static void js_free_shape_null(JSRuntime *rt, JSShape *sh) +{ + if (sh) + js_free_shape(rt, sh); +} + +/* make space to hold at least 'count' properties */ +static no_inline int resize_properties(JSContext *ctx, JSShape **psh, + JSObject *p, uint32_t count) +{ + JSShape *sh; + uint32_t new_size, new_hash_size, new_hash_mask, i; + JSShapeProperty *pr; + void *sh_alloc; + intptr_t h; + + sh = *psh; + new_size = max_int(count, sh->prop_size * 3 / 2); + /* Reallocate prop array first to avoid crash or size inconsistency + in case of memory allocation failure */ + if (p) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (unlikely(!new_prop)) + return -1; + p->prop = new_prop; + } + new_hash_size = sh->prop_hash_mask + 1; + while (new_hash_size < new_size) + new_hash_size = 2 * new_hash_size; + if (new_hash_size != (sh->prop_hash_mask + 1)) { + JSShape *old_sh; + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + /* copy all the fields and the properties */ + memcpy(sh, old_sh, + sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + new_hash_mask = new_hash_size - 1; + sh->prop_hash_mask = new_hash_mask; + memset(sh->prop_hash_end - new_hash_size, 0, + sizeof(sh->prop_hash_end[0]) * new_hash_size); + for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { + if (pr->atom != JS_ATOM_NULL) { + h = ((uintptr_t)pr->atom & new_hash_mask); + pr->hash_next = sh->prop_hash_end[-h - 1]; + sh->prop_hash_end[-h - 1] = i + 1; + } + } + js_free(ctx, get_alloc_from_shape(old_sh)); + } else { + /* only resize the properties */ + list_del(&sh->header.link); + sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), + get_shape_size(new_hash_size, new_size)); + if (unlikely(!sh_alloc)) { + /* insert again in the GC list */ + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + return -1; + } + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + } + *psh = sh; + sh->prop_size = new_size; + return 0; +} + +static int add_shape_property(JSContext *ctx, JSShape **psh, + JSObject *p, JSAtom atom, int prop_flags) +{ + JSRuntime *rt = ctx->rt; + JSShape *sh = *psh; + JSShapeProperty *pr, *prop; + uint32_t hash_mask, new_shape_hash = 0; + intptr_t h; + + /* update the shape hash */ + if (sh->is_hashed) { + js_shape_hash_unlink(rt, sh); + new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); + } + + if (unlikely(sh->prop_count >= sh->prop_size)) { + if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { + /* in case of error, reinsert in the hash table. + sh is still valid if resize_properties() failed */ + if (sh->is_hashed) + js_shape_hash_link(rt, sh); + return -1; + } + sh = *psh; + } + if (sh->is_hashed) { + sh->hash = new_shape_hash; + js_shape_hash_link(rt, sh); + } + /* Initialize the new shape property. + The object property at p->prop[sh->prop_count] is uninitialized */ + prop = get_shape_prop(sh); + pr = &prop[sh->prop_count++]; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = prop_flags; + sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); + /* add in hash table */ + hash_mask = sh->prop_hash_mask; + h = atom & hash_mask; + pr->hash_next = sh->prop_hash_end[-h - 1]; + sh->prop_hash_end[-h - 1] = sh->prop_count; + return 0; +} + +/* find a hashed empty shape matching the prototype. Return NULL if + not found */ +static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) +{ + JSShape *sh1; + uint32_t h, h1; + + h = shape_initial_hash(proto); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + if (sh1->hash == h && + sh1->proto == proto && + sh1->prop_count == 0) { + return sh1; + } + } + return NULL; +} + +/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if + not found */ +static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, + JSAtom atom, int prop_flags) +{ + JSShape *sh1; + uint32_t h, h1, i, n; + + h = sh->hash; + h = shape_hash(h, atom); + h = shape_hash(h, prop_flags); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + /* we test the hash first so that the rest is done only if the + shapes really match */ + if (sh1->hash == h && + sh1->proto == sh->proto && + sh1->prop_count == ((n = sh->prop_count) + 1)) { + for(i = 0; i < n; i++) { + if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || + unlikely(sh1->prop[i].flags != sh->prop[i].flags)) + goto next; + } + if (unlikely(sh1->prop[n].atom != atom) || + unlikely(sh1->prop[n].flags != prop_flags)) + goto next; + return sh1; + } + next: ; + } + return NULL; +} + +static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) +{ + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + int j; + + /* XXX: should output readable class prototype */ + printf("%5d %3d%c %14p %5d %5d", i, + sh->header.ref_count, " *"[sh->is_hashed], + (void *)sh->proto, sh->prop_size, sh->prop_count); + for(j = 0; j < sh->prop_count; j++) { + printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), + sh->prop[j].atom)); + } + printf("\n"); +} + +static __maybe_unused void JS_DumpShapes(JSRuntime *rt) +{ + int i; + JSShape *sh; + struct list_head *el; + JSObject *p; + JSGCObjectHeader *gp; + + printf("JSShapes: {\n"); + printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + JS_DumpShape(rt, i, sh); + assert(sh->is_hashed); + } + } + /* dump non-hashed shapes */ + list_for_each(el, &rt->gc_obj_list) { + gp = list_entry(el, JSGCObjectHeader, link); + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + if (!p->shape->is_hashed) { + JS_DumpShape(rt, -1, p->shape); + } + } + } + printf("}\n"); +} + +static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id) +{ + JSObject *p; + + js_trigger_gc(ctx->rt, sizeof(JSObject)); + p = js_malloc(ctx, sizeof(JSObject)); + if (unlikely(!p)) + goto fail; + p->class_id = class_id; + p->extensible = TRUE; + p->free_mark = 0; + p->is_exotic = 0; + p->fast_array = 0; + p->is_constructor = 0; + p->is_uncatchable_error = 0; + p->is_class = 0; + p->tmp_mark = 0; + p->first_weak_ref = NULL; + p->u.opaque = NULL; + p->shape = sh; + p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); + if (unlikely(!p->prop)) { + js_free(ctx, p); + fail: + js_free_shape(ctx->rt, sh); + return JS_EXCEPTION; + } + + switch(class_id) { + case JS_CLASS_OBJECT: + break; + case JS_CLASS_ARRAY: + { + JSProperty *pr; + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.values = NULL; + p->u.array.count = 0; + p->u.array.u1.size = 0; + /* the length property is always the first one */ + if (likely(sh == ctx->array_shape)) { + pr = &p->prop[0]; + } else { + /* only used for the first array */ + /* cannot fail */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_LENGTH); + } + pr->u.value = JS_NewInt32(ctx, 0); + } + break; + case JS_CLASS_C_FUNCTION: + p->prop[0].u.value = JS_UNDEFINED; + break; + case JS_CLASS_ARGUMENTS: + case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY: + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_DATAVIEW: + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: + case JS_CLASS_BIG_FLOAT: + case JS_CLASS_BIG_DECIMAL: +#endif + p->u.object_data = JS_UNDEFINED; + goto set_exotic; + case JS_CLASS_REGEXP: + p->u.regexp.pattern = NULL; + p->u.regexp.bytecode = NULL; + goto set_exotic; + default: + set_exotic: + if (ctx->rt->class_array[class_id].exotic) { + p->is_exotic = 1; + } + break; + } + p->header.ref_count = 1; + add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); + return JS_MKPTR(JS_TAG_OBJECT, p); +} + +static JSObject *get_proto_obj(JSValueConst proto_val) +{ + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) + return NULL; + else + return JS_VALUE_GET_OBJ(proto_val); +} + +/* WARNING: proto must be an object or JS_NULL */ +JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, + JSClassID class_id) +{ + JSShape *sh; + JSObject *proto; + + proto = get_proto_obj(proto_val); + sh = find_hashed_shape_proto(ctx->rt, proto); + if (likely(sh)) { + sh = js_dup_shape(sh); + } else { + sh = js_new_shape(ctx, proto); + if (!sh) + return JS_EXCEPTION; + } + return JS_NewObjectFromShape(ctx, sh, class_id); +} + +#if 0 +static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: + case JS_CLASS_BIG_FLOAT: + case JS_CLASS_BIG_DECIMAL: +#endif + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_UNDEFINED; +} +#endif + +static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: + case JS_CLASS_BIG_FLOAT: + case JS_CLASS_BIG_DECIMAL: +#endif + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = val; + return 0; + } + } + JS_FreeValue(ctx, val); + if (!JS_IsException(obj)) + JS_ThrowTypeError(ctx, "invalid object type"); + return -1; +} + +JSValue JS_NewObjectClass(JSContext *ctx, int class_id) +{ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); +} + +JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) +{ + return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); +} + +JSValue JS_NewArray(JSContext *ctx) +{ + return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), + JS_CLASS_ARRAY); +} + +JSValue JS_NewObject(JSContext *ctx) +{ + /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); +} + +static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, + JSAtom name, int len) +{ + /* ES6 feature non compatible with ES5.1: length is configurable */ + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len), + JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, + JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); +} + +static BOOL js_class_has_bytecode(JSClassID class_id) +{ + return (class_id == JS_CLASS_BYTECODE_FUNCTION || + class_id == JS_CLASS_GENERATOR_FUNCTION || + class_id == JS_CLASS_ASYNC_FUNCTION || + class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); +} + +/* return NULL without exception if not a function or no bytecode */ +static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(val); + if (!js_class_has_bytecode(p->class_id)) + return NULL; + return p->u.func.function_bytecode; +} + +static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, + JSValueConst home_obj) +{ + JSObject *p, *p1; + JSFunctionBytecode *b; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + return; + b = p->u.func.function_bytecode; + if (b->need_home_object) { + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) + p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj)); + else + p1 = NULL; + p->u.func.home_object = p1; + } +} + +static JSValue js_get_function_name(JSContext *ctx, JSAtom name) +{ + JSValue name_str; + + name_str = JS_AtomToString(ctx, name); + if (JS_AtomSymbolHasDescription(ctx, name)) { + name_str = JS_ConcatString3(ctx, "[", name_str, "]"); + } + return name_str; +} + +/* Modify the name of a method according to the atom and + 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and + JS_PROP_HAS_SET. Also set the home object of the method. + Return < 0 if exception. */ +static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, + JSAtom name, int flags, JSValueConst home_obj) +{ + JSValue name_str; + + name_str = js_get_function_name(ctx, name); + if (flags & JS_PROP_HAS_GET) { + name_str = JS_ConcatString3(ctx, "get ", name_str, ""); + } else if (flags & JS_PROP_HAS_SET) { + name_str = JS_ConcatString3(ctx, "set ", name_str, ""); + } + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, + JS_PROP_CONFIGURABLE) < 0) + return -1; + js_method_set_home_object(ctx, func_obj, home_obj); + return 0; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValueConst proto_val) +{ + JSValue func_obj; + JSObject *p; + JSAtom name_atom; + + func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); + if (JS_IsException(func_obj)) + return func_obj; + p = JS_VALUE_GET_OBJ(func_obj); + p->u.cfunc.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 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) { + /* nothing to do */ + } + } else { + JS_FreeValueRT(rt, pr->u.value); + } +} + +static force_inline JSShapeProperty *find_own_property1(JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = sh->prop_hash_end[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + return pr; + } + h = pr->hash_next; + } + return NULL; +} + +static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, + JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = sh->prop_hash_end[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + *ppr = &p->prop[h - 1]; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +} + +/* indicate that the object may be part of a function prototype cycle */ +static void set_cycle_flag(JSContext *ctx, JSValueConst obj) +{ +} + +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) +{ + if (var_ref) { + assert(var_ref->header.ref_count > 0); + if (--var_ref->header.ref_count == 0) { + if (var_ref->is_detached) { + JS_FreeValueRT(rt, var_ref->value); + remove_gc_object(&var_ref->header); + } else { + list_del(&var_ref->header.link); /* still on the stack */ + } + js_free_rt(rt, var_ref); + } + } +} + +static void js_array_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_FreeValueRT(rt, p->u.array.u.values[i]); + } + js_free_rt(rt, p->u.array.u.values); +} + +static void js_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_MarkValue(rt, p->u.array.u.values[i], mark_func); + } +} + +static void js_object_data_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_FreeValueRT(rt, p->u.object_data); + p->u.object_data = JS_UNDEFINED; +} + +static void js_object_data_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_MarkValue(rt, p->u.object_data, mark_func); +} + +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p1, *p = JS_VALUE_GET_OBJ(val); + JSFunctionBytecode *b; + JSVarRef **var_refs; + int i; + + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + b = p->u.func.function_bytecode; + if (b) { + var_refs = p->u.func.var_refs; + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) + free_var_ref(rt, var_refs[i]); + js_free_rt(rt, var_refs); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); + } +} + +static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSVarRef **var_refs = p->u.func.var_refs; + JSFunctionBytecode *b = p->u.func.function_bytecode; + int i; + + if (p->u.func.home_object) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), + mark_func); + } + if (b) { + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) { + JSVarRef *var_ref = var_refs[i]; + if (var_ref && var_ref->is_detached) { + mark_func(rt, &var_ref->header); + } + } + } + /* must mark the function bytecode because template objects may be + part of a cycle */ + JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); + } +} + +static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_FreeValueRT(rt, bf->func_obj); + JS_FreeValueRT(rt, bf->this_val); + for(i = 0; i < bf->argc; i++) { + JS_FreeValueRT(rt, bf->argv[i]); + } + js_free_rt(rt, bf); +} + +static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_MarkValue(rt, bf->func_obj, mark_func); + JS_MarkValue(rt, bf->this_val, mark_func); + for(i = 0; i < bf->argc; i++) + JS_MarkValue(rt, bf->argv[i], mark_func); +} + +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); +} + +static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_MarkValue(rt, it->obj, mark_func); +} + +static void free_object(JSRuntime *rt, JSObject *p) +{ + int i; + JSClassFinalizer *finalizer; + JSShape *sh; + JSShapeProperty *pr; + + p->free_mark = 1; /* used to tell the object is invalid when + freeing cycles */ + /* free all the fields */ + sh = p->shape; + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + free_property(rt, &p->prop[i], pr->flags); + pr++; + } + js_free_rt(rt, p->prop); + /* as an optimization we destroy the shape immediately without + putting it in gc_zero_ref_count_list */ + js_free_shape(rt, sh); + + /* fail safe */ + p->shape = NULL; + p->prop = NULL; + + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, p); + } + + finalizer = rt->class_array[p->class_id].finalizer; + if (finalizer) + (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); + + /* fail safe */ + p->class_id = 0; + p->u.opaque = NULL; + p->u.func.var_refs = NULL; + p->u.func.home_object = NULL; + + remove_gc_object(&p->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { + list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, p); + } +} + +static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + free_object(rt, (JSObject *)gp); + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + free_function_bytecode(rt, (JSFunctionBytecode *)gp); + break; + default: + abort(); + } +} + +static void free_zero_refcount(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + rt->gc_phase = JS_GC_PHASE_DECREF; + for(;;) { + el = rt->gc_zero_ref_count_list.next; + if (el == &rt->gc_zero_ref_count_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count == 0); + free_gc_object(rt, p); + } + rt->gc_phase = JS_GC_PHASE_NONE; +} + +/* called with the ref_count of 'v' reaches zero. */ +void __JS_FreeValueRT(JSRuntime *rt, JSValue v) +{ + uint32_t tag = JS_VALUE_GET_TAG(v); + +#ifdef DUMP_FREE + { + printf("Freeing "); + if (tag == JS_TAG_OBJECT) { + JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); + } else { + JS_DumpValueShort(rt, v); + printf("\n"); + } + } +#endif + + switch(tag) { + case JS_TAG_STRING: + { + JSString *p = JS_VALUE_GET_STRING(v); + if (p->atom_type) { + JS_FreeAtomStruct(rt, p); + } else { +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } + break; + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + { + JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); + if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { + list_del(&p->link); + list_add(&p->link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_NONE) { + free_zero_refcount(rt); + } + } + } + break; + case JS_TAG_MODULE: + abort(); /* never freed here */ + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *bf = JS_VALUE_GET_PTR(v); + bf_delete(&bf->num); + js_free_rt(rt, bf); + } + break; + case JS_TAG_BIG_DECIMAL: + { + JSBigDecimal *bf = JS_VALUE_GET_PTR(v); + bfdec_delete(&bf->num); + js_free_rt(rt, bf); + } + break; +#endif + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(v); + JS_FreeAtomStruct(rt, p); + } + break; + default: + printf("__JS_FreeValue: unknown tag=%d\n", tag); + abort(); + } +} + +void __JS_FreeValue(JSContext *ctx, JSValue v) +{ + __JS_FreeValueRT(ctx->rt, v); +} + +/* garbage collection */ + +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type) +{ + h->mark = 0; + h->gc_obj_type = type; + list_add_tail(&h->link, &rt->gc_obj_list); +} + +static void remove_gc_object(JSGCObjectHeader *h) +{ + list_del(&h->link); +} + +void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + if (JS_VALUE_HAS_REF_COUNT(val)) { + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + mark_func(rt, JS_VALUE_GET_PTR(val)); + break; + default: + break; + } + } +} + +static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, + JS_MarkFunc *mark_func) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + { + JSObject *p = (JSObject *)gp; + JSShapeProperty *prs; + JSShape *sh; + int i; + sh = p->shape; + mark_func(rt, &sh->header); + /* mark all the fields */ + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL) { + if (prs->flags & JS_PROP_TMASK) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + mark_func(rt, &pr->u.getset.getter->header); + if (pr->u.getset.setter) + mark_func(rt, &pr->u.getset.setter->header); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (pr->u.var_ref->is_detached) { + /* Note: the tag does not matter + provided it is a GC object */ + mark_func(rt, &pr->u.var_ref->header); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* nothing to do */ + } + } 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); + } + } + break; + case JS_GC_OBJ_TYPE_VAR_REF: + { + JSVarRef *var_ref = (JSVarRef *)gp; + /* only detached variable referenced are taken into account */ + assert(var_ref->is_detached); + JS_MarkValue(rt, *var_ref->pvalue, mark_func); + } + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + { + JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp; + if (s->is_active) + async_func_mark(rt, &s->func_state, mark_func); + JS_MarkValue(rt, s->resolving_funcs[0], mark_func); + JS_MarkValue(rt, s->resolving_funcs[1], mark_func); + } + break; + case JS_GC_OBJ_TYPE_SHAPE: + { + JSShape *sh = (JSShape *)gp; + if (sh->proto != NULL) { + mark_func(rt, &sh->proto->header); + } + } + break; + default: + abort(); + } +} + +#if 0 +/* not useful until realms are supported */ +static void mark_context(JSRuntime *rt, JSContext *ctx) +{ + int i; + struct list_head *el; + + list_for_each(el, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + JS_MarkValue(rt, m->module_ns); + JS_MarkValue(rt, m->func_obj); + } + + JS_MarkValue(rt, ctx->current_exception); + + for(i = 0; i < rt->class_count; i++) + JS_MarkValue(rt, ctx->class_proto[i]); + JS_MarkValue(rt, ctx->regexp_ctor); + JS_MarkValue(rt, ctx->function_ctor); + JS_MarkValue(rt, ctx->function_proto); + JS_MarkValue(rt, ctx->iterator_proto); + JS_MarkValue(rt, ctx->async_iterator_proto); + JS_MarkValue(rt, ctx->array_proto_values); + + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) + JS_MarkValue(rt, ctx->native_error_proto[i]); + + JS_MarkValue(rt, ctx->throw_type_error); + JS_MarkValue(rt, ctx->eval_obj); + JS_MarkValue(rt, ctx->global_obj); + JS_MarkValue(rt, ctx->global_var_obj); +} +#endif + +static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + assert(p->ref_count > 0); + p->ref_count--; + if (p->ref_count == 0 && p->mark == 1) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } +} + +static void gc_decref(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; + + init_list_head(&rt->tmp_obj_list); + + /* decrement the refcount of all the children of all the GC + objects and move the GC objects with zero refcount to + tmp_obj_list */ + list_for_each_safe(el, el1, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->mark == 0); + mark_children(rt, p, gc_decref_child); + p->mark = 1; + if (p->ref_count == 0) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } + } +} + +static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; + if (p->ref_count == 1) { + /* ref_count was 0: remove from tmp_obj_list and add at the + end of gc_obj_list */ + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_obj_list); + p->mark = 0; /* reset the mark for the next GC call */ + } +} + +static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; +} + +static void gc_scan(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + /* keep the objects with a refcount > 0 and their children. */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count > 0); + p->mark = 0; /* reset the mark for the next GC call */ + mark_children(rt, p, gc_scan_incref_child); + } + + /* restore the refcount of the objects to be deleted. */ + list_for_each(el, &rt->tmp_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + mark_children(rt, p, gc_scan_incref_child2); + } +} + +static void gc_free_cycles(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; +#ifdef DUMP_GC_FREE + BOOL header_done = FALSE; +#endif + + rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; + + for(;;) { + el = rt->tmp_obj_list.next; + if (el == &rt->tmp_obj_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + /* Only need to free the GC object associated with JS + values. The rest will be automatically removed because they + must be referenced by them. */ + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: +#ifdef DUMP_GC_FREE + if (!header_done) { + printf("Freeing cycles:\n"); + JS_DumpObjectHeader(rt); + header_done = TRUE; + } + JS_DumpGCObject(rt, p); +#endif + free_gc_object(rt, p); + break; + default: + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_zero_ref_count_list); + break; + } + } + rt->gc_phase = JS_GC_PHASE_NONE; + + list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + js_free_rt(rt, p); + } + + init_list_head(&rt->gc_zero_ref_count_list); +} + +void JS_RunGC(JSRuntime *rt) +{ + /* decrement the reference of the children of each object. mark = + 1 after this pass. */ + gc_decref(rt); + + /* keep the GC objects with a non zero refcount and their childs */ + gc_scan(rt); + + /* free the GC objects in a cycle */ + gc_free_cycles(rt); +} + +/* Return false if not an object or if the object has already been + freed (zombie objects are visible in finalizers when freeing + cycles). */ +BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj) +{ + JSObject *p; + if (!JS_IsObject(obj)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + return !p->free_mark; +} + +/* Compute memory used by various object types */ +/* XXX: poor man's approach to handling multiply referenced objects */ +typedef struct JSMemoryUsage_helper { + double memory_used_count; + double str_count; + double str_size; + int64_t js_func_count; + double js_func_size; + int64_t js_func_code_size; + int64_t js_func_pc2line_count; + int64_t js_func_pc2line_size; +} JSMemoryUsage_helper; + +static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp); + +static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) +{ + if (!str->atom_type) { /* atoms are handled separately */ + double s_ref_count = str->header.ref_count; + hp->str_count += 1 / s_ref_count; + hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + + 1 - str->is_wide_char) / s_ref_count); + } +} + +static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) +{ + int memory_used_count, js_func_size, i; + + memory_used_count = 0; + js_func_size = offsetof(JSFunctionBytecode, debug); + if (b->vardefs) { + js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); + } + if (b->cpool) { + js_func_size += b->cpool_count * sizeof(*b->cpool); + for (i = 0; i < b->cpool_count; i++) { + JSValueConst val = b->cpool[i]; + compute_value_size(val, hp); + } + } + if (b->closure_var) { + js_func_size += b->closure_var_count * sizeof(*b->closure_var); + } + if (!b->read_only_bytecode && b->byte_code_buf) { + hp->js_func_code_size += b->byte_code_len; + } + if (b->has_debug) { + js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug); + if (b->debug.source) { + memory_used_count++; + js_func_size += b->debug.source_len + 1; + } + if (b->debug.pc2line_len) { + memory_used_count++; + hp->js_func_pc2line_count += 1; + hp->js_func_pc2line_size += b->debug.pc2line_len; + } + } + hp->js_func_size += js_func_size; + hp->js_func_count += 1; + hp->memory_used_count += memory_used_count; +} + +static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) +{ + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_STRING: + compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + case JS_TAG_BIG_DECIMAL: + /* should track JSBigFloat usage */ + break; +#endif + } +} + +void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) +{ + struct list_head *el, *el1; + int i; + JSMemoryUsage_helper mem = { 0 }, *hp = &mem; + + memset(s, 0, sizeof(*s)); + s->malloc_count = rt->malloc_state.malloc_count; + s->malloc_size = rt->malloc_state.malloc_size; + s->malloc_limit = rt->malloc_state.malloc_limit; + + s->memory_used_count = 2; /* rt + rt->class_array */ + s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count; + + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSShape *sh = ctx->array_shape; + s->memory_used_count += 2; /* ctx + ctx->class_proto */ + s->memory_used_size += sizeof(JSContext) + + sizeof(JSValue) * rt->class_count; + s->binary_object_count += ctx->binary_object_count; + s->binary_object_size += ctx->binary_object_size; + + /* the hashed shapes are counted separately */ + if (sh && !sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + list_for_each(el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el1, JSModuleDef, link); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*m); + if (m->req_module_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); + } + if (m->export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); + for (i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { + /* potential multiple count */ + s->memory_used_count += 1; + compute_value_size(me->u.local.var_ref->value, hp); + } + } + } + if (m->star_export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); + } + if (m->import_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); + } + compute_value_size(m->module_ns, hp); + compute_value_size(m->func_obj, hp); + } + } + + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + JSShape *sh; + JSShapeProperty *prs; + + /* XXX: could count the other GC object types too */ + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { + compute_bytecode_size((JSFunctionBytecode *)gp, hp); + continue; + } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { + continue; + } + p = (JSObject *)gp; + sh = p->shape; + s->obj_count++; + if (p->prop) { + s->memory_used_count++; + s->prop_size += sh->prop_size * sizeof(*p->prop); + s->prop_count += sh->prop_count; + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { + compute_value_size(pr->u.value, hp); + } + prs++; + } + } + /* the hashed shapes are counted separately */ + if (!sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + + switch(p->class_id) { + case JS_CLASS_ARRAY: /* u.array | length */ + case JS_CLASS_ARGUMENTS: /* u.array | length */ + s->array_count++; + if (p->fast_array) { + s->fast_array_count++; + if (p->u.array.u.values) { + s->memory_used_count++; + s->memory_used_size += p->u.array.count * + sizeof(*p->u.array.u.values); + s->fast_array_elements += p->u.array.count; + for (i = 0; i < p->u.array.count; i++) { + compute_value_size(p->u.array.u.values[i], hp); + } + } + } + break; + case JS_CLASS_NUMBER: /* u.object_data */ + case JS_CLASS_STRING: /* u.object_data */ + case JS_CLASS_BOOLEAN: /* u.object_data */ + case JS_CLASS_SYMBOL: /* u.object_data */ + case JS_CLASS_DATE: /* u.object_data */ +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: /* u.object_data */ + case JS_CLASS_BIG_FLOAT: /* u.object_data */ +#endif + compute_value_size(p->u.object_data, hp); + break; + case JS_CLASS_C_FUNCTION: /* u.cfunc */ + s->c_func_count++; + break; + case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ + { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs = p->u.func.var_refs; + /* home_object: object will be accounted for in list scan */ + if (var_refs) { + s->memory_used_count++; + s->js_func_size += b->closure_var_count * sizeof(*var_refs); + for (i = 0; i < b->closure_var_count; i++) { + if (var_refs[i]) { + double ref_count = var_refs[i]->header.ref_count; + s->memory_used_count += 1 / ref_count; + s->js_func_size += sizeof(*var_refs[i]) / ref_count; + /* handle non object closed values */ + if (var_refs[i]->pvalue == &var_refs[i]->value) { + /* potential multiple count */ + compute_value_size(var_refs[i]->value, hp); + } + } + } + } + } + break; + case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ + { + JSBoundFunction *bf = p->u.bound_function; + /* func_obj and this_val are objects */ + for (i = 0; i < bf->argc; i++) { + compute_value_size(bf->argv[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); + } + break; + case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ + { + JSCFunctionDataRecord *fd = p->u.c_function_data_record; + if (fd) { + for (i = 0; i < fd->data_len; i++) { + compute_value_size(fd->data[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); + } + } + break; + case JS_CLASS_REGEXP: /* u.regexp */ + compute_jsstring_size(p->u.regexp.pattern, hp); + compute_jsstring_size(p->u.regexp.bytecode, hp); + break; + + case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ + { + JSForInIterator *it = p->u.for_in_iterator; + if (it) { + compute_value_size(it->obj, hp); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*it); + } + } + break; + case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ + case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ + { + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf) { + s->memory_used_count += 1; + s->memory_used_size += sizeof(*abuf); + if (abuf->data) { + s->memory_used_count += 1; + s->memory_used_size += abuf->byte_length; + } + } + } + break; + case JS_CLASS_GENERATOR: /* u.generator_data */ + case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ +#endif + case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_DATAVIEW: /* u.typed_array */ +#ifdef CONFIG_BIGNUM + case JS_CLASS_FLOAT_ENV: /* u.float_env */ +#endif + case JS_CLASS_MAP: /* u.map_state */ + case JS_CLASS_SET: /* u.map_state */ + case JS_CLASS_WEAKMAP: /* u.map_state */ + case JS_CLASS_WEAKSET: /* u.map_state */ + case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_PROXY: /* u.proxy_data */ + case JS_CLASS_PROMISE: /* u.promise_data */ + case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ + case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ + case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ + case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ + /* TODO */ + default: + /* XXX: class definition should have an opaque block size */ + if (p->u.opaque) { + s->memory_used_count += 1; + } + break; + } + } + s->obj_size += s->obj_count * sizeof(JSObject); + + /* hashed shapes */ + s->memory_used_count++; /* rt->shape_hash */ + s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; + for(i = 0; i < rt->shape_hash_size; i++) { + JSShape *sh; + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + } + + /* atoms */ + s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ + s->atom_count = rt->atom_count; + s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + + sizeof(rt->atom_hash[0]) * rt->atom_hash_size; + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { + s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + + 1 - p->is_wide_char); + } + } + s->str_count = round(mem.str_count); + s->str_size = round(mem.str_size); + s->js_func_count = mem.js_func_count; + s->js_func_size = round(mem.js_func_size); + s->js_func_code_size = mem.js_func_code_size; + s->js_func_pc2line_count = mem.js_func_pc2line_count; + s->js_func_pc2line_size = mem.js_func_pc2line_size; + s->memory_used_count += round(mem.memory_used_count) + + s->atom_count + s->str_count + + s->obj_count + s->shape_count + + s->js_func_count + s->js_func_pc2line_count; + s->memory_used_size += s->atom_size + s->str_size + + s->obj_size + s->prop_size + s->shape_size + + s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; +} + +void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) +{ + fprintf(fp, "QuickJS memory usage -- " +#ifdef CONFIG_BIGNUM + "BigNum " +#endif + CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", + (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit); +#if 1 + if (rt) { + static const struct { + const char *name; + size_t size; + } object_types[] = { + { "JSRuntime", sizeof(JSRuntime) }, + { "JSContext", sizeof(JSContext) }, + { "JSObject", sizeof(JSObject) }, + { "JSString", sizeof(JSString) }, + { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, + }; + int i, usage_size_ok = 0; + for(i = 0; i < countof(object_types); i++) { + unsigned int size = object_types[i].size; + void *p = js_malloc_rt(rt, size); + if (p) { + unsigned int size1 = js_malloc_usable_size_rt(rt, p); + if (size1 >= size) { + usage_size_ok = 1; + fprintf(fp, " %3u + %-2u %s\n", + size, size1 - size, object_types[i].name); + } + js_free_rt(rt, p); + } + } + if (!usage_size_ok) { + fprintf(fp, " malloc_usable_size unavailable\n"); + } + { + int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; + int class_id; + struct list_head *el; + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; + } + } + fprintf(fp, "\n" "JSObject classes\n"); + if (obj_classes[0]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); + for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { + if (obj_classes[class_id]) { + char buf[ATOM_GET_STR_BUF_SIZE]; + fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, + JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name)); + } + } + if (obj_classes[JS_CLASS_INIT_COUNT]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); + } + fprintf(fp, "\n"); + } +#endif + fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); + + if (s->malloc_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", + "memory allocated", s->malloc_count, s->malloc_size, + (double)s->malloc_size / s->malloc_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", + "memory used", s->memory_used_count, s->memory_used_size, + MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / + s->memory_used_count)); + } + if (s->atom_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", + "atoms", s->atom_count, s->atom_size, + (double)s->atom_size / s->atom_count); + } + if (s->str_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", + "strings", s->str_count, s->str_size, + (double)s->str_size / s->str_count); + } + if (s->obj_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + "objects", s->obj_count, s->obj_size, + (double)s->obj_size / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + " properties", s->prop_count, s->prop_size, + (double)s->prop_count / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", + " shapes", s->shape_count, s->shape_size, + (double)s->shape_size / s->shape_count); + } + if (s->js_func_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "bytecode functions", s->js_func_count, s->js_func_size); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " bytecode", s->js_func_count, s->js_func_code_size, + (double)s->js_func_code_size / s->js_func_count); + if (s->js_func_pc2line_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " pc2line", s->js_func_pc2line_count, + s->js_func_pc2line_size, + (double)s->js_func_pc2line_size / s->js_func_pc2line_count); + } + } + if (s->c_func_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); + } + if (s->array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); + if (s->fast_array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", + " elements", s->fast_array_elements, + s->fast_array_elements * (int)sizeof(JSValue), + (double)s->fast_array_elements / s->fast_array_count); + } + } + if (s->binary_object_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "binary objects", s->binary_object_count, s->binary_object_size); + } +} + +JSValue JS_GetGlobalObject(JSContext *ctx) +{ + return JS_DupValue(ctx, ctx->global_obj); +} + +/* WARNING: obj is freed */ +JSValue JS_Throw(JSContext *ctx, JSValue obj) +{ + JS_FreeValue(ctx, ctx->current_exception); + ctx->current_exception = obj; + return JS_EXCEPTION; +} + +/* return the pending exception (cannot be called twice). */ +JSValue JS_GetException(JSContext *ctx) +{ + JSValue val; + val = ctx->current_exception; + ctx->current_exception = JS_NULL; + return val; +} + +static void dbuf_put_leb128(DynBuf *s, uint32_t v) +{ + uint32_t a; + for(;;) { + a = v & 0x7f; + v >>= 7; + if (v != 0) { + dbuf_putc(s, a | 0x80); + } else { + dbuf_putc(s, a); + break; + } + } +} + +static void dbuf_put_sleb128(DynBuf *s, int32_t v1) +{ + uint32_t v = v1; + dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); +} + +static int get_leb128(uint32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + const uint8_t *ptr = buf; + uint32_t v, a, i; + v = 0; + for(i = 0; i < 5; i++) { + if (unlikely(ptr >= buf_end)) + break; + a = *ptr++; + v |= (a & 0x7f) << (i * 7); + if (!(a & 0x80)) { + *pval = v; + return ptr - buf; + } + } + *pval = 0; + return -1; +} + +static int get_sleb128(int32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + int ret; + uint32_t val; + ret = get_leb128(&val, buf, buf_end); + if (ret < 0) { + *pval = 0; + return -1; + } + *pval = (val >> 1) ^ -(val & 1); + return ret; +} + +static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, + uint32_t pc_value) +{ + const uint8_t *p_end, *p; + int new_line_num, line_num, pc, v, ret; + unsigned int op; + + if (!b->has_debug || !b->debug.pc2line_buf) { + /* function was stripped */ + return -1; + } + + p = b->debug.pc2line_buf; + p_end = p + b->debug.pc2line_len; + pc = 0; + line_num = b->debug.line_num; + while (p < p_end) { + op = *p++; + if (op == 0) { + uint32_t val; + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + pc += val; + p += ret; + ret = get_sleb128(&v, p, p_end); + if (ret < 0) { + fail: + /* should never happen */ + return b->debug.line_num; + } + p += ret; + new_line_num = line_num + v; + } else { + op -= PC2LINE_OP_FIRST; + pc += (op / PC2LINE_RANGE); + new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; + } + if (pc_value < pc) + return line_num; + line_num = new_line_num; + } + return line_num; +} + +/* in order to avoid executing arbitrary code during the stack trace + generation, we only look at simple 'name' properties containing a + string. */ +static const char *get_func_name(JSContext *ctx, JSValueConst func) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValueConst val; + + if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT) + return NULL; + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name); + if (!prs) + return NULL; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return NULL; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return NULL; + return JS_ToCString(ctx, val); +} + +#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) +/* only taken into account if filename is provided */ +#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) + +/* if filename != NULL, an additional level is added with the filename + and line number information (used for parse error). */ +static void build_backtrace(JSContext *ctx, JSValueConst error_obj, + const char *filename, int line_num, + int backtrace_flags) +{ + JSStackFrame *sf; + JSValue str; + DynBuf dbuf; + const char *func_name_str; + const char *str1; + JSObject *p; + BOOL backtrace_barrier; + + js_dbuf_init(ctx, &dbuf); + if (filename) { + dbuf_printf(&dbuf, " at %s", filename); + if (line_num != -1) + dbuf_printf(&dbuf, ":%d", line_num); + dbuf_putc(&dbuf, '\n'); + str = JS_NewString(ctx, filename); + JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL) + goto done; + } + for(sf = ctx->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 = ""; + 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; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + 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); + dbuf_printf(&dbuf, " (%s", + JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), + b->debug.filename)); + 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'); + 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) +{ + JSStackFrame *sf; + BOOL add_backtrace; + + /* the backtrace is added later if called from a bytecode function */ + sf = ctx->current_stack_frame; + add_backtrace = !ctx->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; + } +} + +static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) +{ + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + char buf[ATOM_GET_STR_BUF_SIZE]; + JS_ThrowTypeError(ctx, "%s is read-only", + JS_AtomGetStr(ctx, buf, sizeof(buf), 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) +{ + if (!ctx->in_out_of_memory) { + ctx->in_out_of_memory = TRUE; + JS_ThrowInternalError(ctx, "out of memory"); + ctx->in_out_of_memory = FALSE; + } + return JS_EXCEPTION; +} + +static JSValue JS_ThrowStackOverflow(JSContext *ctx) +{ + return JS_ThrowInternalError(ctx, "stack overflow"); +} + +static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not an object"); +} + +static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not defined", + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not initialized", + name == JS_ATOM_NULL ? "lexical variable" : + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) +{ + JSRuntime *rt = ctx->rt; + char buf[ATOM_GET_STR_BUF_SIZE]; + JSAtom name; + name = rt->class_array[class_id].class_name; + return JS_ThrowTypeError(ctx, "%s object expected", + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +/* 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); +} + +/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ +JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + + switch(JS_VALUE_GET_NORM_TAG(val)) { +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + val = ctx->class_proto[JS_CLASS_BIG_INT]; + break; + case JS_TAG_INT: + if (is_bigint_mode(ctx)) { + val = ctx->class_proto[JS_CLASS_BIG_INT]; + } else { + val = ctx->class_proto[JS_CLASS_NUMBER]; + } + break; + case JS_TAG_FLOAT64: + val = ctx->class_proto[JS_CLASS_NUMBER]; + break; + case JS_TAG_BIG_FLOAT: + val = ctx->class_proto[JS_CLASS_BIG_FLOAT]; + break; + case JS_TAG_BIG_DECIMAL: + val = ctx->class_proto[JS_CLASS_BIG_DECIMAL]; + break; +#else + case JS_TAG_INT: + case JS_TAG_FLOAT64: + val = ctx->class_proto[JS_CLASS_NUMBER]; + break; +#endif + 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: + p = JS_VALUE_GET_OBJ(val); + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + val = js_proxy_getPrototypeOf(ctx, val); + } else { + p = p->shape->proto; + if (!p) + val = JS_NULL; + else + val = JS_MKPTR(JS_TAG_OBJECT, p); + } + break; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + val = JS_NULL; + break; + } + return val; +} + +/* 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; + ret = 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) { + if (p->class_id == JS_CLASS_PROXY) { + JSValueConst proto_val; + proto_val = JS_GetPrototype(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); + if (JS_IsException(proto_val)) { + ret = -1; + goto done; + } + proto1 = JS_VALUE_GET_OBJ(proto_val); + if (!proto1) + break; + } else { + 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); +} + +static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSProperty *pr) +{ + return (*pr->u.init.init_func)(ctx, p, prop, pr->u.init.opaque); +} + +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: + case JS_TAG_UNDEFINED: + return JS_ThrowTypeError(ctx, "value has no property"); + 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_GetPrototype(ctx, obj)); + if (!p) + return JS_UNDEFINED; + } else { + p = JS_VALUE_GET_OBJ(obj); + } + + for(;;) { + prs = find_own_property(&pr, p, prop); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (unlikely(!pr->u.getset.getter)) { + return JS_UNDEFINED; + } else { + JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); + /* Note: the field could be removed in the getter */ + func = JS_DupValue(ctx, func); + return JS_CallFree(ctx, func, this_obj, 0, NULL); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return JS_DupValue(ctx, val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr)) + return JS_EXCEPTION; + continue; + } + } else { + return JS_DupValue(ctx, pr->u.value); + } + } + if (unlikely(p->is_exotic)) { + /* exotic behaviors */ + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + /* we avoid duplicating the code */ + return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + goto typed_array_oob; + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + int ret; + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return JS_EXCEPTION; + typed_array_oob: + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return JS_UNDEFINED; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->get_property) { + /* XXX: should pass throw_ref_error */ + return em->get_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), + prop, this_obj); + } + if (em->get_own_property) { + JSPropertyDescriptor desc; + int ret; + + ret = em->get_own_property(ctx, &desc, JS_MKPTR(JS_TAG_OBJECT, p), prop); + 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) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowTypeError(ctx, "private class field %s does not exist", + JS_AtomGetStr(ctx, buf, sizeof(buf), 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) { + char buf[ATOM_GET_STR_BUF_SIZE]; + JS_ThrowTypeError(ctx, "private class field %s already exists", + JS_AtomGetStr(ctx, buf, sizeof(buf), prop)); + goto fail; + } + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (unlikely(!pr)) { + fail: + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return JS_ThrowTypeErrorNotAnObject(ctx); + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) + return JS_ThrowTypeErrorNotASymbol(ctx); + prop = js_symbol_to_atom(ctx, (JSValue)name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + return JS_EXCEPTION; + } + return JS_DupValue(ctx, pr->u.value); +} + +static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, (JSValue)name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + fail: + JS_FreeValue(ctx, val); + return -1; + } + set_value(ctx, &pr->u.value, val); + return 0; +} + +static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + JSAtom brand_atom; + + if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(home_obj); + prs = find_own_property(&pr, p, JS_ATOM_Private_brand); + if (!prs) { + brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(brand)) + return -1; + /* if the brand is not present, add it */ + pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); + if (!pr) { + JS_FreeValue(ctx, brand); + return -1; + } + pr->u.value = JS_DupValue(ctx, brand); + } else { + brand = JS_DupValue(ctx, pr->u.value); + } + brand_atom = js_symbol_to_atom(ctx, brand); + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + JS_FreeAtom(ctx, brand_atom); + return -1; + } + p1 = JS_VALUE_GET_OBJ(obj); + pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); + JS_FreeAtom(ctx, brand_atom); + if (!pr) + return -1; + pr->u.value = JS_UNDEFINED; + return 0; +} + +static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) +{ + JSObject *p, *p1, *home_obj; + JSShapeProperty *prs; + JSProperty *pr; + JSValueConst brand; + + /* get the home object of 'func' */ + if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p1 = JS_VALUE_GET_OBJ(func); + if (!js_class_has_bytecode(p1->class_id)) + goto not_obj; + home_obj = p1->u.func.home_object; + if (!home_obj) + goto not_obj; + prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); + if (!prs) { + JS_ThrowTypeError(ctx, "expecting private field"); + return -1; + } + brand = pr->u.value; + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) + goto not_obj; + + /* get the brand array of 'obj' */ + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + goto not_obj; + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); + if (!prs) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + return -1; + } + return 0; +} + +static int num_keys_cmp(const void *p1, const void *p2, void *opaque) +{ + JSContext *ctx = opaque; + JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; + JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; + uint32_t v1, v2; + BOOL atom1_is_integer, atom2_is_integer; + + atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); + atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); + assert(atom1_is_integer && atom2_is_integer); + if (v1 < v2) + return -1; + else if (v1 == v2) + return 0; + else + return 1; +} + +static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) +{ + uint32_t i; + if (tab) { + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + } +} + +/* return < 0 in case if exception, 0 if OK. ptab and its atoms must + be freed by the user. */ +static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSObject *p, int flags) +{ + int i, j; + JSShape *sh; + JSShapeProperty *prs; + JSPropertyEnum *tab_atom, *tab_exotic; + JSAtom atom; + uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; + uint32_t num_index, str_index, sym_index, exotic_count; + BOOL is_enumerable, num_sorted; + uint32_t num_key; + JSAtomKindEnum kind; + + /* clear pointer for consistency in case of failure */ + *ptab = NULL; + *plen = 0; + + /* compute the number of returned properties */ + num_keys_count = 0; + str_keys_count = 0; + sym_keys_count = 0; + exotic_count = 0; + tab_exotic = NULL; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + /* need to raise an exception in case of the module + name space (implicit GetOwnProperty) */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && + (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { + JSVarRef *var_ref = p->prop[i].u.var_ref; + if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + num_keys_count++; + } else if (kind == JS_ATOM_KIND_STRING) { + str_keys_count++; + } else { + sym_keys_count++; + } + } + } + } + + if (p->is_exotic) { + if (p->fast_array) { + /* the implicit GetOwnProperty raises an exception if the + typed array is detached */ + if ((flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) && + (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) && + typed_array_is_detached(ctx, p) && + typed_array_get_length(ctx, p) != 0) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return -1; + } + num_keys_count += p->u.array.count; + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property_names) { + if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, + JS_MKPTR(JS_TAG_OBJECT, p))) + return -1; + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + kind = JS_AtomGetKind(ctx, atom); + if (((flags >> kind) & 1) != 0) { + is_enumerable = FALSE; + if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { + JSPropertyDescriptor desc; + int res; + /* set the "is_enumerable" field if necessary */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + if (res) { + is_enumerable = + ((desc.flags & JS_PROP_ENUMERABLE) != 0); + js_free_desc(ctx, &desc); + } + tab_exotic[i].is_enumerable = is_enumerable; + } + if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + num_keys_count++; + } else if (kind == JS_ATOM_KIND_STRING) { + str_keys_count++; + } else { + sym_keys_count++; + } + } + } + } + } + } + } + + /* fill them */ + + atom_count = num_keys_count + str_keys_count + sym_keys_count; + /* avoid allocating 0 bytes */ + tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); + if (!tab_atom) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + + num_index = 0; + str_index = num_keys_count; + sym_index = str_index + str_keys_count; + + num_sorted = TRUE; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + j = num_index++; + num_sorted = FALSE; + } else if (kind == JS_ATOM_KIND_STRING) { + j = str_index++; + } else { + j = sym_index++; + } + tab_atom[j].atom = JS_DupAtom(ctx, atom); + tab_atom[j].is_enumerable = is_enumerable; + } + } + } + + if (p->is_exotic) { + if (p->fast_array) { + for(i = 0; i < p->u.array.count; i++) { + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); + if (tab_atom[num_index].atom == JS_ATOM_NULL) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + js_free_prop_enum(ctx, tab_atom, num_index); + return -1; + } + tab_atom[num_index].is_enumerable = TRUE; + num_index++; + } + } + if (exotic_count > 0) { + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + is_enumerable = tab_exotic[i].is_enumerable; + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + j = num_index++; + num_sorted = FALSE; + } else if (kind == JS_ATOM_KIND_STRING) { + j = str_index++; + } else { + j = sym_index++; + } + tab_atom[j].atom = atom; + tab_atom[j].is_enumerable = is_enumerable; + } else { + JS_FreeAtom(ctx, atom); + } + } + } + js_free(ctx, tab_exotic); + } + + assert(num_index == num_keys_count); + assert(str_index == num_keys_count + str_keys_count); + assert(sym_index == atom_count); + + if (num_keys_count != 0 && !num_sorted) { + rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, + ctx); + } + *ptab = tab_atom; + *plen = atom_count; + return 0; +} + +int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValueConst obj, int flags) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, + JS_VALUE_GET_OBJ(obj), flags); +} + +/* Return -1 if exception, + FALSE if the property does not exist, TRUE if it exists. If TRUE is + returned, the property descriptor 'desc' is filled present. */ +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop) +{ + JSShapeProperty *prs; + JSProperty *pr; + +retry: + prs = find_own_property(&pr, p, prop); + if (prs) { + if (desc) { + desc->flags = prs->flags & JS_PROP_C_W_E; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_UNDEFINED; + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + desc->flags |= JS_PROP_GETSET; + if (pr->u.getset.getter) + desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + desc->value = JS_DupValue(ctx, val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr)) + return -1; + goto retry; + } + } else { + desc->value = JS_DupValue(ctx, pr->u.value); + } + } else { + /* for consistency, send the exception even if desc is NULL */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { + if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* nothing to do: delay instantiation until actual value and/or attributes are read */ + } + } + return TRUE; + } + if (p->is_exotic) { + if (p->fast_array) { + /* specific case for fast arrays */ + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx; + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + if (desc) { + desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE; + if (p->class_id == JS_CLASS_ARRAY || + p->class_id == JS_CLASS_ARGUMENTS) + desc->flags |= JS_PROP_CONFIGURABLE; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } + return TRUE; + } + } + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + int ret; + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return -1; + } + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property) { + return em->get_own_property(ctx, desc, + JS_MKPTR(JS_TAG_OBJECT, p), prop); + } + } + } + return FALSE; +} + +int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); +} + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_IsExtensible(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isExtensible(ctx, obj); + else + return p->extensible; +} + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_preventExtensions(ctx, obj); + p->extensible = FALSE; + return TRUE; +} + +/* return -1 if exception otherwise TRUE or FALSE */ +int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) +{ + JSObject *p; + int ret; + + 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) + return em->has_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), prop); + } + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); + if (ret != 0) + return ret; + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + /* the detached array test was done in + JS_GetOwnPropertyInternal() */ + return FALSE; + } + } + p = p->shape->proto; + if (!p) + break; + } + return FALSE; +} + +/* val must be a symbol */ +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val) +{ + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + return js_get_atom_index(ctx->rt, p); +} + +/* return JS_ATOM_NULL in case of exception */ +JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) +{ + JSAtom atom; + uint32_t tag; + tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_INT && + (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { + /* fast path for integer values */ + atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); + } else if (tag == JS_TAG_SYMBOL) { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); + } else { + JSValue str; + str = JS_ToPropertyKey(ctx, val); + if (JS_IsException(str)) + return JS_ATOM_NULL; + if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { + atom = js_symbol_to_atom(ctx, str); + } else { + atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str)); + } + } + return atom; +} + +static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop) +{ + JSAtom atom; + JSValue ret; + + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject *p; + uint32_t idx, len; + /* fast path for array access */ + p = JS_VALUE_GET_OBJ(this_obj); + idx = JS_VALUE_GET_INT(prop); + len = (uint32_t)p->u.array.count; + if (unlikely(idx >= len)) + goto slow_path; + switch(p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + return JS_DupValue(ctx, p->u.array.u.values[idx]); + case JS_CLASS_INT8_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]); + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]); + case JS_CLASS_INT16_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]); + case JS_CLASS_UINT16_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]); + case JS_CLASS_INT32_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]); + case JS_CLASS_UINT32_ARRAY: + return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]); +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); + case JS_CLASS_BIG_UINT64_ARRAY: + return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); +#endif + case JS_CLASS_FLOAT32_ARRAY: + return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); + case JS_CLASS_FLOAT64_ARRAY: + return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); + default: + goto slow_path; + } + } else { + slow_path: + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; + } +} + +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx) +{ + return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx)); +} + +/* Check if an object has a generalized numeric property. Return value: + -1 for exception, + TRUE if property exists, stored into *pval, + FALSE if proprty does not exist. + */ +static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) +{ + JSValue val = JS_UNDEFINED; + JSAtom prop; + int present; + + if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { + /* fast path */ + present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); + if (present > 0) { + val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); + if (unlikely(JS_IsException(val))) + present = -1; + } + } else { + prop = JS_NewAtomInt64(ctx, idx); + present = -1; + if (likely(prop != JS_ATOM_NULL)) { + present = JS_HasProperty(ctx, obj, prop); + if (present > 0) { + val = JS_GetProperty(ctx, obj, prop); + if (unlikely(JS_IsException(val))) + present = -1; + } + JS_FreeAtom(ctx, prop); + } + } + *pval = val; + return present; +} + +static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) +{ + JSAtom prop; + JSValue val; + + if ((uint64_t)idx <= INT32_MAX) { + /* fast path for fast arrays */ + return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return JS_EXCEPTION; + + val = JS_GetProperty(ctx, obj, prop); + JS_FreeAtom(ctx, prop); + return val; +} + +JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop) +{ + JSAtom atom; + JSValue ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* Note: the property value is not initialized. Return NULL if memory + error. */ +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags) +{ + JSShape *sh, *new_sh; + + sh = p->shape; + if (sh->is_hashed) { + /* try to find an existing shape */ + new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); + if (new_sh) { + /* matching shape found: use it */ + /* the property array may need to be resized */ + if (new_sh->prop_size != sh->prop_size) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * + new_sh->prop_size); + if (!new_prop) + return NULL; + p->prop = new_prop; + } + p->shape = js_dup_shape(new_sh); + js_free_shape(ctx->rt, sh); + return &p->prop[new_sh->prop_count - 1]; + } else if (sh->header.ref_count != 1) { + /* if the shape is shared, clone it */ + new_sh = js_clone_shape(ctx, sh); + if (!new_sh) + return NULL; + /* hash the cloned shape */ + new_sh->is_hashed = TRUE; + js_shape_hash_link(ctx->rt, new_sh); + js_free_shape(ctx->rt, p->shape); + p->shape = new_sh; + } + } + assert(p->shape->header.ref_count == 1); + if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) + return NULL; + return &p->prop[p->shape->prop_count - 1]; +} + +/* can be called on Array or Arguments objects. return < 0 if + memory alloc error. */ +static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, + JSObject *p) +{ + JSProperty *pr; + JSShape *sh; + JSValue *tab; + uint32_t i, len, new_count; + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + len = p->u.array.count; + /* resize the properties once to simplify the error handling */ + sh = p->shape; + new_count = sh->prop_count + len; + if (new_count > sh->prop_size) { + if (resize_properties(ctx, &p->shape, p, new_count)) + return -1; + } + + tab = p->u.array.u.values; + for(i = 0; i < len; i++) { + /* add_property cannot fail here but + __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); + pr->u.value = *tab++; + } + js_free(ctx, p->u.array.u.values); + p->u.array.count = 0; + p->u.array.u.values = NULL; /* fail safe */ + p->u.array.u1.size = 0; + p->fast_array = 0; + return 0; +} + +static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *lpr, *prop; + JSProperty *pr1; + uint32_t lpr_idx; + intptr_t h, h1; + + redo: + sh = p->shape; + h1 = atom & sh->prop_hash_mask; + h = sh->prop_hash_end[-h1 - 1]; + prop = get_shape_prop(sh); + lpr = NULL; + lpr_idx = 0; /* prevent warning */ + while (h != 0) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + /* found ! */ + if (!(pr->flags & JS_PROP_CONFIGURABLE)) + return FALSE; + /* realloc the shape if needed */ + if (lpr) + lpr_idx = lpr - get_shape_prop(sh); + if (js_shape_prepare_update(ctx, p, &pr)) + return -1; + sh = p->shape; + /* remove property */ + if (lpr) { + lpr = get_shape_prop(sh) + lpr_idx; + lpr->hash_next = pr->hash_next; + } else { + sh->prop_hash_end[-h1 - 1] = pr->hash_next; + } + /* 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; + return TRUE; + } + lpr = pr; + h = pr->hash_next; + } + + if (p->is_exotic) { + if (p->fast_array) { + uint32_t idx; + if (JS_AtomIsArrayIndex(ctx, &idx, atom) && + idx < p->u.array.count) { + if (p->class_id == JS_CLASS_ARRAY || + p->class_id == JS_CLASS_ARGUMENTS) { + /* Special case deleting the last element of a fast Array */ + if (idx == p->u.array.count - 1) { + JS_FreeValue(ctx, p->u.array.u.values[idx]); + p->u.array.count = idx; + return TRUE; + } + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto redo; + } else { + return FALSE; /* not configurable */ + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->delete_property) { + return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); + } + } + } + /* not found */ + return TRUE; +} + +static int call_setter(JSContext *ctx, JSObject *setter, + JSValueConst this_obj, JSValue val, int flags) +{ + JSValue ret, func; + if (likely(setter)) { + func = JS_MKPTR(JS_TAG_OBJECT, setter); + /* Note: the field could be removed in the setter */ + func = JS_DupValue(ctx, func); + ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val); + JS_FreeValue(ctx, val); + if (JS_IsException(ret)) + return -1; + JS_FreeValue(ctx, ret); + } 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; + } + /* XXX: should return FALSE? */ + } + return TRUE; +} + +/* set the array length and remove the array elements if necessary. */ +static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop, + JSValue val, int flags) +{ + uint32_t len, idx, cur_len; + int i, ret; + + ret = JS_ToArrayLengthFree(ctx, &len, val); + if (ret) + return -1; + if (likely(p->fast_array)) { + uint32_t old_len = p->u.array.count; + if (len < old_len) { + for(i = len; i < old_len; i++) { + JS_FreeValue(ctx, p->u.array.u.values[i]); + } + p->u.array.count = len; + } +#ifdef CONFIG_BIGNUM + set_value(ctx, &prop->u.value, JS_NewUint32(ctx, len)); +#else + prop->u.value = JS_NewUint32(ctx, len); +#endif + } else { + /* Note: length is always a uint32 because the object is an + array */ + JS_ToUint32(ctx, &cur_len, prop->u.value); + if (len < cur_len) { + uint32_t d; + JSShape *sh; + JSShapeProperty *pr; + + d = cur_len - len; + sh = p->shape; + if (d <= sh->prop_count) { + JSAtom atom; + + /* faster to iterate */ + while (cur_len > len) { + atom = JS_NewAtomUInt32(ctx, cur_len - 1); + ret = delete_property(ctx, p, atom); + JS_FreeAtom(ctx, atom); + if (unlikely(!ret)) { + /* unlikely case: property is not + configurable */ + break; + } + cur_len--; + } + } else { + /* faster to iterate thru all the properties. Need two + passes in case one of the property is not + configurable */ + cur_len = len; + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len && + !(pr->flags & JS_PROP_CONFIGURABLE)) { + cur_len = idx + 1; + } + } + } + + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len) { + /* remove the property */ + delete_property(ctx, p, pr->atom); + /* WARNING: the shape may have been modified */ + sh = p->shape; + pr = get_shape_prop(sh) + i; + } + } + } + } + } else { + cur_len = len; + } + set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len)); + if (unlikely(cur_len > len)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); + } + } + return TRUE; +} + +/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = + TRUE and p->extensible = TRUE */ +static int add_fast_array_element(JSContext *ctx, JSObject *p, + JSValue val, int flags) +{ + uint32_t new_len, array_len; + /* extend the array by one */ + /* XXX: convert to slow array if new_len > 2^31-1 elements */ + new_len = p->u.array.count + 1; + /* update the length if necessary. We assume that if the length is + not an integer, then if it >= 2^31. */ + if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { + array_len = JS_VALUE_GET_INT(p->prop[0].u.value); + if (new_len > array_len) { + if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + } + p->prop[0].u.value = JS_NewInt32(ctx, new_len); + } + } + if (unlikely(new_len > p->u.array.u1.size)) { + uint32_t new_size; + size_t slack; + JSValue *new_array_prop; + /* XXX: potential arithmetic overflow */ + new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); + new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); + if (!new_array_prop) { + JS_FreeValue(ctx, val); + return -1; + } + new_size += slack / sizeof(*new_array_prop); + p->u.array.u.values = new_array_prop; + p->u.array.u1.size = new_size; + } + p->u.array.u.values[new_len - 1] = val; + p->u.array.count = new_len; + return TRUE; +} + +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) +{ + JS_FreeValue(ctx, desc->getter); + JS_FreeValue(ctx, desc->setter); + JS_FreeValue(ctx, desc->value); +} + +/* generic (and slower) version of JS_SetProperty() for Reflect.set() */ +static int JS_SetPropertyGeneric(JSContext *ctx, + JSObject *p, JSAtom prop, + JSValue val, JSValueConst this_obj, + int flags) +{ + int ret; + JSPropertyDescriptor desc; + + while (p != NULL) { + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->set_property) { + ret = em->set_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), prop, + val, this_obj, flags); + JS_FreeValue(ctx, val); + return ret; + } + } + + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (ret < 0) + return ret; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JSObject *setter; + if (JS_IsUndefined(desc.setter)) + setter = NULL; + else + setter = JS_VALUE_GET_OBJ(desc.setter); + ret = call_setter(ctx, setter, this_obj, val, flags); + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + return ret; + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE)) { + goto read_only_error; + } + } + break; + } + p = p->shape->proto; + } + + if (!JS_IsObject(this_obj)) + return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object"); + + p = JS_VALUE_GET_OBJ(this_obj); + + /* modify the property in this_obj if it already exists */ + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (ret < 0) + return ret; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE) || + p->class_id == JS_CLASS_MODULE_NS) { + read_only_error: + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + } + } + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + JS_FreeValue(ctx, val); + return ret; + } + + ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | + JS_PROP_HAS_VALUE | + JS_PROP_HAS_ENUMERABLE | + JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_CONFIGURABLE | + JS_PROP_C_W_E); + JS_FreeValue(ctx, val); + return ret; +} + +/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is + freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, + JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, + the new property is not added and an error is raised. */ +int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue val, int flags) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + uint32_t tag; + JSPropertyDescriptor desc; + int ret; +#if 0 + printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n"); +#endif + tag = JS_VALUE_GET_TAG(this_obj); + if (unlikely(tag != JS_TAG_OBJECT)) { + switch(tag) { + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "value has no property"); + return -1; + default: + /* even on a primitive type we can have setters on the prototype */ + p = NULL; + p1 = JS_VALUE_GET_OBJ(JS_GetPrototype(ctx, this_obj)); + goto prototype_lookup; + } + } + p = JS_VALUE_GET_OBJ(this_obj); +retry: + prs = find_own_property(&pr, p, prop); + if (prs) { + if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | + JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { + /* fast case */ + set_value(ctx, &pr->u.value, val); + return TRUE; + } else if ((prs->flags & (JS_PROP_LENGTH | JS_PROP_WRITABLE)) == + (JS_PROP_LENGTH | JS_PROP_WRITABLE)) { + assert(p->class_id == JS_CLASS_ARRAY); + assert(prop == JS_ATOM_length); + return set_array_length(ctx, p, pr, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (p->class_id == JS_CLASS_MODULE_NS) + goto read_only_prop; + set_value(ctx, pr->u.var_ref->pvalue, val); + return TRUE; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p, prop, pr)) { + JS_FreeValue(ctx, val); + return -1; + } + goto retry; + } else { + goto read_only_prop; + } + } + + p1 = p; + for(;;) { + if (p1->is_exotic) { + if (p1->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p1->u.array.count) { + if (unlikely(p == p1)) + return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); + else + break; + } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && + p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + goto typed_array_oob; + } + } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && + p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) { + JS_FreeValue(ctx, val); + return -1; + } + typed_array_oob: + val = JS_ToNumberFree(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(val)) + return -1; + if (typed_array_is_detached(ctx, p1)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return -1; + } + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; + if (em) { + if (em->set_property) { + ret = em->set_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p1), prop, + val, this_obj, flags); + JS_FreeValue(ctx, val); + return ret; + } + if (em->get_own_property) { + ret = em->get_own_property(ctx, &desc, + JS_MKPTR(JS_TAG_OBJECT, p1), prop); + if (ret < 0) { + JS_FreeValue(ctx, val); + return ret; + } + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JSObject *setter; + if (JS_IsUndefined(desc.setter)) + setter = NULL; + else + setter = JS_VALUE_GET_OBJ(desc.setter); + ret = call_setter(ctx, setter, this_obj, val, flags); + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + return ret; + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE)) + goto read_only_prop; + if (likely(p == p1)) { + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + JS_FreeValue(ctx, val); + return ret; + } else { + break; + } + } + } + } + } + } + } + p1 = p1->shape->proto; + prototype_lookup: + if (!p1) + break; + + retry2: + prs = find_own_property(&pr, p1, prop); + if (prs) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p1, prop, pr)) + return -1; + goto retry2; + } else if (!(prs->flags & JS_PROP_WRITABLE)) { + read_only_prop: + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + } + } + } + + if (unlikely(flags & JS_PROP_NO_ADD)) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorNotDefined(ctx, prop); + return -1; + } + + if (unlikely(!p)) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); + } + + if (unlikely(!p->extensible)) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + } + + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY && p->fast_array && + __JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + /* fast case */ + return add_fast_array_element(ctx, p, val, flags); + } else { + goto generic_create_prop; + } + } else { + generic_create_prop: + ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | + JS_PROP_HAS_VALUE | + JS_PROP_HAS_ENUMERABLE | + JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_CONFIGURABLE | + JS_PROP_C_W_E); + JS_FreeValue(ctx, val); + return ret; + } + } + + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (unlikely(!pr)) { + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return TRUE; +} + +/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ +static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, + JSValue prop, JSValue val, int flags) +{ + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject *p; + uint32_t idx; + double d; + int32_t v; + + /* fast path for array access */ + p = JS_VALUE_GET_OBJ(this_obj); + idx = JS_VALUE_GET_INT(prop); + switch(p->class_id) { + case JS_CLASS_ARRAY: + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + JSObject *p1; + JSShape *sh1; + + /* fast path to add an element to the array */ + if (idx != (uint32_t)p->u.array.count || + !p->fast_array || !p->extensible) + goto slow_path; + /* check if prototype chain has a numeric property */ + p1 = p->shape->proto; + while (p1 != NULL) { + sh1 = p1->shape; + if (p1->class_id == JS_CLASS_ARRAY) { + if (unlikely(!p1->fast_array)) + goto slow_path; + } else if (p1->class_id == JS_CLASS_OBJECT) { + if (unlikely(sh1->has_small_array_index)) + goto slow_path; + } else { + goto slow_path; + } + p1 = sh1->proto; + } + /* add element */ + return add_fast_array_element(ctx, p, val, flags); + } + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto slow_path; + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_UINT8C_ARRAY: + if (JS_ToUint8ClampFree(ctx, &v, val)) + return -1; + /* Note: the conversion can detach the typed array, so the + array bound check must be done after */ + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint16_ptr[idx] = v; + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint32_ptr[idx] = v; + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + /* XXX: need specific conversion function */ + { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint64_ptr[idx] = v; + } + break; +#endif + case JS_CLASS_FLOAT32_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.float_ptr[idx] = d; + break; + case JS_CLASS_FLOAT64_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + ta_out_of_bound: + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return -1; + } else { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); + } + } + p->u.array.u.double_ptr[idx] = d; + break; + default: + goto slow_path; + } + return TRUE; + } else { + JSAtom atom; + int ret; + slow_path: + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; + } +} + +int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val) +{ + return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val, + JS_PROP_THROW); +} + +int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= INT32_MAX) { + /* fast path for fast arrays */ + return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, + JS_PROP_THROW); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + res = JS_SetProperty(ctx, this_obj, prop, val); + JS_FreeAtom(ctx, prop); + return res; +} + +int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* compute the property flags. For each flag: (JS_PROP_HAS_x forces + it, otherwise def_flags is used) + Note: makes assumption about the bit pattern of the flags +*/ +static int get_prop_flags(int flags, int def_flags) +{ + int mask; + mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E; + return (flags & mask) | (def_flags & ~mask); +} + +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags) +{ + JSProperty *pr; + int ret, prop_flags; + + /* add a new property or modify an existing exotic one */ + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY) { + uint32_t idx, len; + + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + if (!p->extensible) + goto not_extensible; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) + goto convert_to_array; + prop_flags = get_prop_flags(flags, 0); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_array; + return add_fast_array_element(ctx, p, + JS_DupValue(ctx, val), flags); + } else { + goto convert_to_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + /* convert the fast array to normal array */ + convert_to_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto generic_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + JSProperty *plen; + JSShapeProperty *pslen; + generic_array: + /* update the length field */ + plen = &p->prop[0]; + JS_ToUint32(ctx, &len, plen->u.value); + if ((idx + 1) > len) { + pslen = get_shape_prop(p->shape); + if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + /* XXX: should update the length after defining + the property */ + len = idx + 1; + set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len)); + } + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array"); + } + } else if (!(flags & JS_PROP_NO_EXOTIC)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->define_own_property) { + return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), + prop, val, getter, setter, flags); + } + ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret < 0) + return -1; + if (!ret) + goto not_extensible; + } + } + } + + if (!p->extensible) { + not_extensible: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + } + + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + } else { + prop_flags = flags & JS_PROP_C_W_E; + } + pr = add_property(ctx, p, prop, prop_flags); + if (unlikely(!pr)) + return -1; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + pr->u.getset.getter = NULL; + if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) { + pr->u.getset.getter = + JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter)); + } + pr->u.getset.setter = NULL; + if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) { + pr->u.getset.setter = + JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter)); + } + } else { + if (flags & JS_PROP_HAS_VALUE) { + pr->u.value = JS_DupValue(ctx, val); + } else { + pr->u.value = JS_UNDEFINED; + } + } + return TRUE; +} + +/* return FALSE if not OK */ +static BOOL check_define_prop_flags(int prop_flags, int flags) +{ + BOOL has_accessor, is_getset; + + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == + (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { + return FALSE; + } + if ((flags & JS_PROP_HAS_ENUMERABLE) && + (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) + return FALSE; + } + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); + is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); + if (has_accessor != is_getset) + return FALSE; + if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { + /* not writable: cannot set the writable bit */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) + return FALSE; + } + } + } + return TRUE; +} + +/* ensure that the shape can be safely modified */ +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs) +{ + JSShape *sh; + uint32_t idx = 0; /* prevent warning */ + + sh = p->shape; + if (sh->is_hashed) { + if (sh->header.ref_count != 1) { + if (pprs) + idx = *pprs - get_shape_prop(sh); + /* clone the shape (the resulting one is no longer hashed) */ + sh = js_clone_shape(ctx, sh); + if (!sh) + return -1; + js_free_shape(ctx->rt, p->shape); + p->shape = sh; + if (pprs) + *pprs = get_shape_prop(sh) + idx; + } else { + js_shape_hash_unlink(ctx->rt, sh); + sh->is_hashed = FALSE; + } + } + return 0; +} + +static int js_update_property_flags(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs, int flags) +{ + if (flags != (*pprs)->flags) { + if (js_shape_prepare_update(ctx, p, pprs)) + return -1; + (*pprs)->flags = flags; + } + return 0; +} + +/* allowed flags: + JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE + JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, + JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, + JS_PROP_THROW, JS_PROP_NO_EXOTIC. + If JS_PROP_THROW is set, return an exception instead of FALSE. + if JS_PROP_NO_EXOTIC is set, do not call the exotic + define_own_property callback. + return -1 (exception), FALSE or TRUE. +*/ +int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int mask, res; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(this_obj); + + redo_prop_update: + prs = find_own_property(&pr, p, prop); + if (prs) { + /* property already exists */ + if (!check_define_prop_flags(prs->flags, flags)) { + not_configurable: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + + retry: + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + JSObject *new_getter, *new_setter; + + if (JS_IsFunction(ctx, getter)) { + new_getter = JS_VALUE_GET_OBJ(getter); + } else { + new_getter = NULL; + } + if (JS_IsFunction(ctx, setter)) { + new_setter = JS_VALUE_GET_OBJ(setter); + } else { + new_setter = NULL; + } + + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + /* convert to getset */ + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(ctx->rt, pr->u.var_ref); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* clear property and update */ + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + goto retry; + } else { + JS_FreeValue(ctx, pr->u.value); + } + prs->flags = (prs->flags & + (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + pr->u.getset.getter = NULL; + pr->u.getset.setter = NULL; + } else { + if (!(prs->flags & JS_PROP_CONFIGURABLE)) { + if ((flags & JS_PROP_HAS_GET) && + new_getter != pr->u.getset.getter) { + goto not_configurable; + } + if ((flags & JS_PROP_HAS_SET) && + new_setter != pr->u.getset.setter) { + goto not_configurable; + } + } + } + if (flags & JS_PROP_HAS_GET) { + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (new_getter) + JS_DupValue(ctx, getter); + pr->u.getset.getter = new_getter; + } + if (flags & JS_PROP_HAS_SET) { + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + if (new_setter) + JS_DupValue(ctx, setter); + pr->u.getset.setter = new_setter; + } + } else { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + /* convert to data descriptor */ + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + pr->u.value = JS_UNDEFINED; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* Note: JS_PROP_VARREF is always writable */ + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* clear property and update */ + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + } else { + if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && + (flags & JS_PROP_HAS_VALUE) && + !js_same_value(ctx, val, pr->u.value)) { + goto not_configurable; + } + } + if (prs->flags & JS_PROP_LENGTH) { + if (flags & JS_PROP_HAS_VALUE) { + res = set_array_length(ctx, p, pr, JS_DupValue(ctx, val), + flags); + } else { + res = TRUE; + } + /* still need to reset the writable flag if needed. + The JS_PROP_LENGTH is reset to have the correct + read-only behavior in JS_SetProperty(). */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + JS_PROP_HAS_WRITABLE) { + prs = get_shape_prop(p->shape); + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~(JS_PROP_WRITABLE | JS_PROP_LENGTH))) + return -1; + } + return res; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (flags & JS_PROP_HAS_VALUE) { + if (p->class_id == JS_CLASS_MODULE_NS) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) + goto not_configurable; + } + /* update the reference */ + set_value(ctx, pr->u.var_ref->pvalue, + JS_DupValue(ctx, val)); + } + /* if writable is set to false, no longer a + reference (for mapped arguments) */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { + JSValue val1; + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue); + free_var_ref(ctx->rt, pr->u.var_ref); + pr->u.value = val1; + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* XXX: should never happen, type was reset above */ + abort(); + } else { + if (flags & JS_PROP_HAS_VALUE) { + JS_FreeValue(ctx, pr->u.value); + pr->u.value = JS_DupValue(ctx, val); + } + if (flags & JS_PROP_HAS_WRITABLE) { + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~JS_PROP_WRITABLE) | + (flags & JS_PROP_WRITABLE))) + return -1; + } + } + } + } + mask = 0; + if (flags & JS_PROP_HAS_CONFIGURABLE) + mask |= JS_PROP_CONFIGURABLE; + if (flags & JS_PROP_HAS_ENUMERABLE) + mask |= JS_PROP_ENUMERABLE; + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~mask) | (flags & mask))) + return -1; + return TRUE; + } + + /* handle modification of fast array elements */ + if (p->fast_array) { + uint32_t idx; + uint32_t prop_flags; + if (p->class_id == JS_CLASS_ARRAY) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_slow_array; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + convert_to_slow_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + else + goto redo_prop_update; + } + if (flags & JS_PROP_HAS_VALUE) { + set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val)); + } + return TRUE; + } + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + JSValue num; + int ret; + + if (!__JS_AtomIsTaggedInt(prop)) { + /* slow path with to handle all numeric indexes */ + num = JS_AtomIsNumericIndex1(ctx, prop); + if (JS_IsUndefined(num)) + goto typed_array_done; + if (JS_IsException(num)) + return -1; + ret = JS_NumberIsInteger(ctx, num); + if (ret < 0) { + JS_FreeValue(ctx, num); + return -1; + } + if (!ret) { + JS_FreeValue(ctx, num); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); + } + ret = JS_NumberIsNegativeOrMinusZero(ctx, num); + JS_FreeValue(ctx, num); + if (ret) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); + } + if (!__JS_AtomIsTaggedInt(prop)) + goto typed_array_oob; + } + idx = __JS_AtomToUInt32(prop); + /* if the typed array is detached, p->u.array.count = 0 */ + if (idx >= typed_array_get_length(ctx, p)) { + typed_array_oob: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); + } + prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || + prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); + } + if (flags & JS_PROP_HAS_VALUE) { + return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags); + } + return TRUE; + typed_array_done: ; + } + } + + return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); +} + +static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, int (*init_func)(JSContext *ctx, JSObject *obj, + JSAtom prop, void *opaque), + 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.init_func = init_func; + 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) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowSyntaxError(ctx, "redeclaration of %s", + JS_AtomGetStr(ctx, buf, sizeof(buf), 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; + char buf[ATOM_GET_STR_BUF_SIZE]; + + 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_ThrowTypeError(ctx, "cannot define variable %s", + JS_AtomGetStr(ctx, buf, sizeof(buf), prop)); + return -1; + } + } + } + } + /* check if there already is a lexical declaration */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + fail_redeclaration: + JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); + return -1; + } + return 0; +} + +/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSValue val; + int flags; + + if (def_flags & DEFINE_GLOBAL_LEX_VAR) { + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | + JS_PROP_CONFIGURABLE; + val = JS_UNINITIALIZED; + } else { + p = JS_VALUE_GET_OBJ(ctx->global_obj); + flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | + (def_flags & JS_PROP_CONFIGURABLE); + val = JS_UNDEFINED; + } + prs = find_own_property1(p, prop); + if (prs) + return 0; + if (!p->extensible) + return 0; + pr = add_property(ctx, p, prop, flags); + if (unlikely(!pr)) + return -1; + pr->u.value = val; + return 0; +} + +/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, + JSValueConst func, int def_flags) +{ + + JSObject *p; + JSShapeProperty *prs; + int flags; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; + if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { + flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; + } + if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, + JS_UNDEFINED, JS_UNDEFINED, flags) < 0) + return -1; + return 0; +} + +static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, + BOOL throw_ref_error) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_TMASK properties */ + if (unlikely(JS_IsUninitialized(pr->u.value))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return JS_DupValue(ctx, pr->u.value); + } + return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, + ctx->global_obj, throw_ref_error); +} + +/* construct a reference to a global variable */ +static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + /* XXX: conformance: do these tests in + OP_put_var_ref/OP_get_var_ref ? */ + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + sp[0] = JS_DupValue(ctx, ctx->global_var_obj); + } else { + int ret; + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + sp[0] = JS_DupValue(ctx, ctx->global_obj); + } else { + sp[0] = JS_UNDEFINED; + } + } + sp[1] = JS_AtomToValue(ctx, prop); + return 0; +} + +/* use for strict variable access: test if the variable exists */ +static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + int ret; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + ret = TRUE; + } else { + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + } + return ret; +} + +/* flag = 0: normal variable write + flag = 1: initialize lexical variable + flag = 2: normal variable write, strict check was done before +*/ +static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, + int flag) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int flags; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + if (flag != 1) { + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + } + set_value(ctx, &pr->u.value, val); + return 0; + } + + flags = JS_PROP_THROW_STRICT; + if (flag != 2 && is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); +} + +/* return -1, FALSE or TRUE. return FALSE if not configurable or + invalid object. return -1 in case of exception. + flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ +int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) +{ + JSValue obj1; + JSObject *p; + int res; + + obj1 = JS_ToObject(ctx, obj); + if (JS_IsException(obj1)) + return -1; + p = JS_VALUE_GET_OBJ(obj1); + res = delete_property(ctx, p, prop); + JS_FreeValue(ctx, obj1); + if (res != FALSE) + return res; + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "could not delete property"); + return -1; + } + return FALSE; +} + +int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= JS_ATOM_MAX_INT) { + /* fast path for fast arrays */ + return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return -1; + res = JS_DeleteProperty(ctx, obj, prop, flags); + JS_FreeAtom(ctx, prop); + return res; +} + +BOOL JS_IsFunction(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + switch(p->class_id) { + case JS_CLASS_BYTECODE_FUNCTION: + return TRUE; + case JS_CLASS_PROXY: + return p->u.proxy_data->is_func; + default: + return (ctx->rt->class_array[p->class_id].call != NULL); + } +} + +BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_C_FUNCTION) + return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); + else + return FALSE; +} + +BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->is_constructor; +} + +BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(func_obj); + p->is_constructor = val; + return TRUE; +} + +BOOL JS_IsError(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return (p->class_id == JS_CLASS_ERROR); +} + +/* used to avoid catching interrupt exceptions */ +BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; +} + +void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_ERROR) + p->is_uncatchable_error = flag; +} + +void JS_ResetUncatchableError(JSContext *ctx) +{ + JS_SetUncatchableError(ctx, ctx->current_exception, FALSE); +} + +void JS_SetOpaque(JSValue obj, void *opaque) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + p->u.opaque = opaque; + } +} + +/* return NULL if not an object of class class_id */ +void *JS_GetOpaque(JSValueConst obj, JSClassID class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != class_id) + return NULL; + return p->u.opaque; +} + +void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) +{ + void *p = JS_GetOpaque(obj, class_id); + if (unlikely(!p)) { + JS_ThrowTypeErrorInvalidClass(ctx, class_id); + } + return p; +} + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#ifdef CONFIG_BIGNUM +#define HINT_INTEGER 3 +#endif +/* don't try Symbol.toPrimitive */ +#define HINT_FORCE_ORDINARY (1 << 4) + +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) +{ + int i; + BOOL force_ordinary; + + JSAtom method_name; + JSValue method, ret; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return val; + force_ordinary = hint & HINT_FORCE_ORDINARY; + hint &= ~HINT_FORCE_ORDINARY; + if (!force_ordinary) { + method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); + if (JS_IsException(method)) + goto exception; + /* ECMA says *If exoticToPrim is not undefined* but tests in + test262 use null as a non callable converter */ + if (!JS_IsUndefined(method) && !JS_IsNull(method)) { + JSAtom atom; + JSValue arg; + switch(hint) { + case HINT_STRING: + atom = JS_ATOM_string; + break; + case HINT_NUMBER: + atom = JS_ATOM_number; + break; + default: + case HINT_NONE: + atom = JS_ATOM_default; + break; +#ifdef CONFIG_BIGNUM + case HINT_INTEGER: + atom = JS_ATOM_integer; + break; +#endif + } + arg = JS_AtomToString(ctx, atom); + ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg); + JS_FreeValue(ctx, arg); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) + return ret; + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "toPrimitive"); + } + } + if (hint != HINT_STRING) + hint = HINT_NUMBER; + for(i = 0; i < 2; i++) { + if ((i ^ hint) == 0) { + method_name = JS_ATOM_toString; + } else { + method_name = JS_ATOM_valueOf; + } + method = JS_GetProperty(ctx, val, method_name); + if (JS_IsException(method)) + goto exception; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, val, 0, NULL); + if (JS_IsException(ret)) + goto exception; + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, val); + return ret; + } + JS_FreeValue(ctx, ret); + } else { + JS_FreeValue(ctx, method); + } + } + JS_ThrowTypeError(ctx, "toPrimitive"); +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) +{ + return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); +} + +static int JS_ToBoolFree(JSContext *ctx, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + return JS_VALUE_GET_INT(val) != 0; + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_VALUE_GET_INT(val); + case JS_TAG_EXCEPTION: + return -1; + case JS_TAG_STRING: + { + BOOL ret = JS_VALUE_GET_STRING(val)->len != 0; + JS_FreeValue(ctx, val); + return ret; + } +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + BOOL ret; + ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_BIG_DECIMAL: + { + JSBigDecimal *p = JS_VALUE_GET_PTR(val); + BOOL ret; + ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + JS_FreeValue(ctx, val); + return ret; + } +#endif + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d = JS_VALUE_GET_FLOAT64(val); + return !isnan(d) && d != 0; + } else { + JS_FreeValue(ctx, val); + return TRUE; + } + } +} + +int JS_ToBool(JSContext *ctx, JSValueConst val) +{ + return JS_ToBoolFree(ctx, JS_DupValue(ctx, val)); +} + +static int skip_spaces(const char *pc) +{ + const uint8_t *p, *p_next, *p_start; + uint32_t c; + + p = p_start = (const uint8_t *)pc; + for (;;) { + c = *p; + if (c < 128) { + if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) + break; + p++; + } else { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); + if (!lre_is_space(c)) + break; + p = p_next; + } + } + return p - p_start; +} + +static inline int to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* XXX: remove */ +static double js_strtod(const char *p, int radix, BOOL is_float) +{ + double d; + int c; + + if (!is_float || radix != 10) { + uint64_t n_max, n; + int int_exp, is_neg; + + is_neg = 0; + if (*p == '-') { + is_neg = 1; + p++; + } + + /* skip leading zeros */ + while (*p == '0') + p++; + n = 0; + if (radix == 10) + n_max = ((uint64_t)-1 - 9) / 10; /* most common case */ + else + n_max = ((uint64_t)-1 - (radix - 1)) / radix; + /* XXX: could be more precise */ + int_exp = 0; + while (*p != '\0') { + c = to_digit((uint8_t)*p); + if (c >= radix) + break; + if (n <= n_max) { + n = n * radix + c; + } else { + int_exp++; + } + p++; + } + d = n; + if (int_exp != 0) { + d *= pow(radix, int_exp); + } + if (is_neg) + d = -d; + } else { + d = strtod(p, NULL); + } + return d; +} + +#define ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define ATOD_ACCEPT_BIN_OCT (1 << 2) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) +/* accept _ between digits as a digit separator */ +#define ATOD_ACCEPT_UNDERSCORES (1 << 5) +/* allow a suffix to override the type */ +#define ATOD_ACCEPT_SUFFIX (1 << 6) +/* default type */ +#define ATOD_TYPE_MASK (3 << 7) +#define ATOD_TYPE_FLOAT64 (0 << 7) +#define ATOD_TYPE_BIG_INT (1 << 7) +#define ATOD_TYPE_BIG_FLOAT (2 << 7) +#define ATOD_TYPE_BIG_DECIMAL (3 << 7) +/* assume bigint mode: floats are parsed as integers if no decimal + point nor exponent */ +#define ATOD_MODE_BIGINT (1 << 9) +/* accept -0x1 */ +#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) + +#ifdef CONFIG_BIGNUM +static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent) +{ + bf_t a_s, *a = &a_s; + int ret; + bf_init(ctx->bf_ctx, a); + ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); + if (ret & BF_ST_MEM_ERROR) { + bf_delete(a); + return JS_ThrowOutOfMemory(ctx); + } + return JS_NewBigInt2(ctx, a, !(flags & ATOD_MODE_BIGINT)); +} + +static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent) +{ + bf_t a_s, *a = &a_s; + int ret; + + bf_init(ctx->bf_ctx, a); + 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) { + bf_delete(a); + return JS_ThrowOutOfMemory(ctx); + } + return JS_NewBigFloat(ctx, a); +} + +static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent) +{ + bfdec_t a_s, *a = &a_s; + int ret; + bfdec_init(ctx->bf_ctx, a); + ret = bfdec_atof(a, buf, NULL, BF_PREC_INF, + BF_RNDZ | BF_ATOF_NO_NAN_INF); + if (ret & BF_ST_MEM_ERROR) { + bfdec_delete(a); + return JS_ThrowOutOfMemory(ctx); + } + return JS_NewBigDecimal(ctx, a); +} + +#endif + +/* return an exception in case of memory error. Return JS_NAN if + invalid syntax */ +#ifdef CONFIG_BIGNUM +static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp, + int radix, int flags, slimb_t *pexponent) +#else +static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, + int radix, int flags) +#endif +{ + const char *p, *p_start; + int sep, is_neg; + BOOL is_float, has_legacy_octal; + int atod_type = flags & ATOD_TYPE_MASK; + char buf1[64], *buf; + int i, j, len; + BOOL buf_allocated = FALSE; + JSValue val; + + /* optional separator between digits */ + sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + has_legacy_octal = FALSE; + + p = str; + p_start = p; + is_neg = 0; + if (p[0] == '+') { + p++; + p_start++; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } else if (p[0] == '-') { + p++; + p_start++; + is_neg = 1; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16)) { + p += 2; + radix = 16; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 2; + } else if ((p[1] >= '0' && p[1] <= '9') && + radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { + int i; + has_legacy_octal = TRUE; + sep = 256; + for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) + continue; + if (p[i] == '8' || p[i] == '9') + goto no_prefix; + p += 1; + radix = 8; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (to_digit((uint8_t)*p) >= radix) + goto fail; + no_prefix: ; + } else { + no_radix_prefix: + if (!(flags & ATOD_INT_ONLY) && + (atod_type == ATOD_TYPE_FLOAT64 || + atod_type == ATOD_TYPE_BIG_FLOAT) && + strstart(p, "Infinity", &p)) { +#ifdef CONFIG_BIGNUM + if (atod_type == ATOD_TYPE_BIG_FLOAT) { + bf_t a_s, *a = &a_s; + bf_init(ctx->bf_ctx, a); + bf_set_inf(a, is_neg); + val = JS_NewBigFloat(ctx, a); + } else +#endif + { + double d = 1.0 / 0.0; + if (is_neg) + d = -d; + val = JS_NewFloat64(ctx, d); + } + goto done; + } + } + if (radix == 0) + radix = 10; + is_float = FALSE; + p_start = p; + while (to_digit((uint8_t)*p) < radix + || (*p == sep && (radix != 10 || + p != p_start + 1 || p[-1] != '0') && + to_digit((uint8_t)p[1]) < radix)) { + p++; + } + if (!(flags & ATOD_INT_ONLY)) { + if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) { + is_float = TRUE; + p++; + while (to_digit((uint8_t)*p) < radix || + (*p == sep && to_digit((uint8_t)p[1]) < radix)) + p++; + } + if (p > p_start && + (((*p == 'e' || *p == 'E') && radix == 10) || + ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { + const char *p1 = p + 1; + is_float = TRUE; + if (*p1 == '+') { + p1++; + } else if (*p1 == '-') { + p1++; + } + if (is_digit((uint8_t)*p1)) { + p = p1 + 1; + while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) + p++; + } + } + } + if (p == p_start) + goto fail; + + buf = buf1; + buf_allocated = FALSE; + len = p - p_start; + if (unlikely((len + 2) > sizeof(buf1))) { + buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ + if (!buf) + goto mem_error; + buf_allocated = TRUE; + } + /* remove the separators and the radix prefixes */ + j = 0; + if (is_neg) + buf[j++] = '-'; + for (i = 0; i < len; i++) { + if (p_start[i] != '_') + buf[j++] = p_start[i]; + } + buf[j] = '\0'; + +#ifdef CONFIG_BIGNUM + if (flags & ATOD_ACCEPT_SUFFIX) { + if (*p == 'n') { + p++; + atod_type = ATOD_TYPE_BIG_INT; + } else if (*p == 'l') { + p++; + atod_type = ATOD_TYPE_BIG_FLOAT; + } else if (*p == 'd') { + p++; + atod_type = ATOD_TYPE_BIG_DECIMAL; + } else { + if (flags & ATOD_MODE_BIGINT) { + if (!is_float) + atod_type = ATOD_TYPE_BIG_INT; + if (has_legacy_octal) + goto fail; + } else { + if (is_float && radix != 10) + goto fail; + } + } + } else { + if (atod_type == ATOD_TYPE_FLOAT64) { + if (flags & ATOD_MODE_BIGINT) { + if (!is_float) + atod_type = ATOD_TYPE_BIG_INT; + if (has_legacy_octal) + goto fail; + } else { + if (is_float && radix != 10) + goto fail; + } + } + } + + switch(atod_type) { + case ATOD_TYPE_FLOAT64: + { + double d; + d = js_strtod(buf, radix, is_float); + /* return int or float64 */ + val = JS_NewFloat64(ctx, d); + } + break; + case ATOD_TYPE_BIG_INT: + if (has_legacy_octal || is_float) + goto fail; + val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL); + break; + case ATOD_TYPE_BIG_FLOAT: + if (has_legacy_octal) + goto fail; + val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags, + pexponent); + break; + case ATOD_TYPE_BIG_DECIMAL: + if (radix != 10) + goto fail; + val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL); + break; + default: + abort(); + } +#else + { + double d; + (void)has_legacy_octal; + if (is_float && radix != 10) + goto fail; + d = js_strtod(buf, radix, is_float); + val = JS_NewFloat64(ctx, d); + } +#endif + +done: + if (buf_allocated) + js_free_rt(ctx->rt, buf); + if (pp) + *pp = p; + return val; + fail: + val = JS_NAN; + goto done; + mem_error: + val = JS_ThrowOutOfMemory(ctx); + goto done; +} + +#ifdef CONFIG_BIGNUM +static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, + int radix, int flags) +{ + return js_atof2(ctx, str, pp, radix, flags, NULL); +} +#endif + +typedef enum JSToNumberHintEnum { + TON_FLAG_NUMBER, + TON_FLAG_INTEGER, + TON_FLAG_NUMERIC, +} JSToNumberHintEnum; + +static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, + JSToNumberHintEnum flag) +{ + uint32_t tag; + JSValue ret; + int hint; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_DECIMAL: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number"); + } + ret = val; + break; + case JS_TAG_BIG_INT: + if (flag == TON_FLAG_NUMBER && !is_bigint_mode(ctx)) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); + } + /* fall thru */ + case JS_TAG_BIG_FLOAT: +#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: +#ifdef CONFIG_BIGNUM + hint = flag == TON_FLAG_INTEGER ? HINT_INTEGER : HINT_NUMBER; +#else + hint = HINT_NUMBER; +#endif + val = JS_ToPrimitiveFree(ctx, val, hint); + 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; +#ifdef CONFIG_BIGNUM + if (is_bigint_mode(ctx)) + flags |= ATOD_MODE_BIGINT; +#endif + 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); +} + +#ifdef CONFIG_BIGNUM +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)); +} +#endif + +static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, + JSValue val) +{ + double d; + uint32_t tag; + + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = JS_FLOAT64_NAN; + return -1; + } + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + d = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(val); + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + /* XXX: there can be a double rounding issue with some + primitives (such as JS_ToUint8ClampFree()), but it is + not critical to fix it. */ + bf_get_float64(&p->num, &d, BF_RNDN); + JS_FreeValue(ctx, val); + } + break; +#endif + default: + abort(); + } + *pres = d; + return 0; +} + +static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_TAG(val); + if (tag <= JS_TAG_NULL) { + *pres = JS_VALUE_GET_INT(val); + return 0; + } else if (JS_TAG_IS_FLOAT64(tag)) { + *pres = JS_VALUE_GET_FLOAT64(val); + return 0; + } else { + return __JS_ToFloat64Free(ctx, pres, val); + } +} + +int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) +{ + return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); +} + +static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) +{ + return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); +} + +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 */ + /* XXX: should not be done here ? */ + d = trunc(d) + 0.0; + ret = JS_NewFloat64(ctx, d); + } + } + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + if (!is_bigint_mode(ctx)) + goto to_number; + ret = val; + break; + case JS_TAG_BIG_FLOAT: + { + bf_t a_s, *a, r_s, *r = &r_s; + BOOL is_nan; + + a = JS_ToBigFloat(ctx, &a_s, val); + if (!bf_is_finite(a)) { + is_nan = bf_is_nan(a); + if (is_nan) + ret = JS_NewInt32(ctx, 0); + else + ret = JS_DupValue(ctx, val); + } else { + bf_init(ctx->bf_ctx, r); + bf_set(r, a); + bf_rint(r, BF_RNDZ); + ret = JS_NewBigInt(ctx, r); + } + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, val); + } + break; +#endif + default: +#ifdef CONFIG_BIGNUM + to_number: +#endif + 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: + to_bf: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + bf_get_int32(&ret, &p->num, 0); + JS_FreeValue(ctx, val); + } + break; + case JS_TAG_BIG_INT: + if (is_bigint_mode(ctx)) + goto to_bf; + /* fall thru */ +#endif + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) +{ + return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); +} + +int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, + int min, int max, int min_offset) +{ + int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); + if (res == 0) { + if (*pres < min) { + *pres += min_offset; + if (*pres < min) + *pres = min; + } else { + if (*pres > max) + *pres = max; + } + } + return res; +} + +static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + *pres = JS_VALUE_GET_INT(val); + return 0; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + *pres = 0; + } else { + if (d < INT64_MIN) + *pres = INT64_MIN; + else if (d > INT64_MAX) + *pres = INT64_MAX; + else + *pres = (int64_t)d; + } + } + return 0; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: + to_bf: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + bf_get_int64(pres, &p->num, 0); + JS_FreeValue(ctx, val); + } + return 0; + case JS_TAG_BIG_INT: + if (is_bigint_mode(ctx)) + goto to_bf; + /* fall thru */ +#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: + to_bf: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + bf_get_int64(&ret, &p->num, BF_GET_INT_MOD); + JS_FreeValue(ctx, val); + } + break; + case JS_TAG_BIG_INT: + if (is_bigint_mode(ctx)) + goto to_bf; + /* fall thru */ +#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)); +} + +/* 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: + to_bf: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + bf_get_int32(&ret, &p->num, BF_GET_INT_MOD); + JS_FreeValue(ctx, val); + } + break; + case JS_TAG_BIG_INT: + if (is_bigint_mode(ctx)) + goto to_bf; + /* fall thru */ +#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_INT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + if (!is_bigint_mode(ctx)) + goto to_number; + bf_get_int32(&res, &p->num, 0); + JS_FreeValue(ctx, val); + } + goto int_clamp; + 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: +#ifdef CONFIG_BIGNUM + to_number: +#endif + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = res; + return 0; +} + +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val) +{ + uint32_t tag, len; + + redo: + tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + { + int v; + v = JS_VALUE_GET_INT(val); + if (v < 0) + goto fail; + len = v; + } + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + bf_t a; + BOOL res; + bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); + bf_init(ctx->bf_ctx, &a); + bf_set_ui(&a, len); + res = bf_cmp_eq(&a, &p->num); + bf_delete(&a); + JS_FreeValue(ctx, val); + if (!res) + goto fail; + } + break; +#endif + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d; + d = JS_VALUE_GET_FLOAT64(val); + len = (uint32_t)d; + if (len != d) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } else { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + goto redo; + } + break; + } + *plen = len; + return 0; +} + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) + +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 */ +/* XXX: bignum case */ +static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) +{ + double d; + if (!JS_IsNumber(val)) + return FALSE; + if (unlikely(JS_ToFloat64(ctx, &d, val))) + return -1; + return isfinite(d) && floor(d) == d; +} + +static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + { + int v; + v = JS_VALUE_GET_INT(val); + return (v < 0); + } + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + u.d = JS_VALUE_GET_FLOAT64(val); + return (u.u64 >> 63); + } +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + /* Note: integer zeros are not necessarily positive */ + return p->num.sign && !bf_is_zero(&p->num); + } + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return p->num.sign; + } + break; + case JS_TAG_BIG_DECIMAL: + { + JSBigDecimal *p = JS_VALUE_GET_PTR(val); + return p->num.sign; + } + break; +#endif + default: + return FALSE; + } +} + +#ifdef CONFIG_BIGNUM + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) +{ + JSValue ret; + bf_t a_s, *a; + char *str; + int saved_sign; + + a = JS_ToBigInt(ctx, &a_s, val); + if (!a) + return JS_EXCEPTION; + saved_sign = a->sign; + if (a->expn == BF_EXP_ZERO) + a->sign = 0; + str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | + BF_FTOA_JS_QUIRKS); + a->sign = saved_sign; + JS_FreeBigInt(ctx, a, &a_s); + if (!str) + return JS_ThrowOutOfMemory(ctx); + ret = JS_NewString(ctx, str); + bf_free(ctx->bf_ctx, str); + return ret; +} + +static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) +{ + return js_bigint_to_string1(ctx, val, 10); +} + +static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, + limb_t prec, bf_flags_t flags) +{ + JSValue val, ret; + bf_t a_s, *a; + char *str; + int saved_sign; + + val = JS_ToNumeric(ctx, val1); + if (JS_IsException(val)) + return val; + a = JS_ToBigFloat(ctx, &a_s, val); + saved_sign = a->sign; + if (a->expn == BF_EXP_ZERO) + a->sign = 0; + flags |= BF_FTOA_JS_QUIRKS; + if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) { + /* Note: for floating point numbers with a radix which is not + a power of two, the current precision is used to compute + the number of digits. */ + if ((radix & (radix - 1)) != 0) { + bf_t r_s, *r = &r_s; + int prec, flags1; + /* must round first */ + if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { + prec = ctx->fp_env.prec; + flags1 = ctx->fp_env.flags & + (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)); + } else { + prec = 53; + flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL; + } + bf_init(ctx->bf_ctx, r); + bf_set(r, a); + bf_round(r, prec, flags1 | BF_RNDN); + str = bf_ftoa(NULL, r, radix, prec, flags1 | flags); + bf_delete(r); + } else { + str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags); + } + } else { + str = bf_ftoa(NULL, a, radix, prec, flags); + } + a->sign = saved_sign; + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, val); + if (!str) + return JS_ThrowOutOfMemory(ctx); + ret = JS_NewString(ctx, str); + bf_free(ctx->bf_ctx, str); + return ret; +} + +static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val) +{ + return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); +} + +static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) +{ + JSValue ret; + bfdec_t *a; + char *str; + int saved_sign; + + a = JS_ToBigDecimal(ctx, val); + saved_sign = a->sign; + if (a->expn == BF_EXP_ZERO) + a->sign = 0; + str = bfdec_ftoa(NULL, a, 0, BF_RNDZ | BF_FTOA_FORMAT_FREE | + 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; +} + +#endif /* CONFIG_BIGNUM */ + +/* 2 <= base <= 36 */ +static char *i64toa(char *buf_end, int64_t n, unsigned int base) +{ + char *q = buf_end; + int digit, is_neg; + + is_neg = 0; + if (n < 0) { + is_neg = 1; + n = -n; + } + *--q = '\0'; + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + *--q = digit; + } while (n != 0); + if (is_neg) + *--q = '-'; + return q; +} + +/* buf1 contains the printf result */ +static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf, + int rounding_mode, char *buf1, int buf1_size) +{ + if (rounding_mode != FE_TONEAREST) + fesetround(rounding_mode); + snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d); + if (rounding_mode != FE_TONEAREST) + fesetround(FE_TONEAREST); + *sign = (buf1[0] == '-'); + /* mantissa */ + buf[0] = buf1[1]; + if (n_digits > 1) + memcpy(buf + 1, buf1 + 3, n_digits - 1); + buf[n_digits] = '\0'; + /* exponent */ + *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1; +} + +/* maximum buffer size for js_dtoa */ +#define JS_DTOA_BUF_SIZE 128 + +/* needed because ecvt usually limits the number of digits to + 17. Return the number of digits. */ +static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf, + BOOL is_fixed) +{ + int rounding_mode; + char buf_tmp[JS_DTOA_BUF_SIZE]; + + if (!is_fixed) { + unsigned int n_digits_min, n_digits_max; + /* find the minimum amount of digits (XXX: inefficient but simple) */ + n_digits_min = 1; + n_digits_max = 17; + while (n_digits_min < n_digits_max) { + n_digits = (n_digits_min + n_digits_max) / 2; + js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST, + buf_tmp, sizeof(buf_tmp)); + if (strtod(buf_tmp, NULL) == d) { + /* no need to keep the trailing zeros */ + while (n_digits >= 2 && buf[n_digits - 1] == '0') + n_digits--; + n_digits_max = n_digits; + } else { + n_digits_min = n_digits + 1; + } + } + n_digits = n_digits_max; + rounding_mode = FE_TONEAREST; + } else { + rounding_mode = FE_TONEAREST; +#ifdef CONFIG_PRINTF_RNDN + { + char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE]; + int decpt1, sign1, decpt2, sign2; + /* The JS rounding is specified as round to nearest ties away + from zero (RNDNA), but in printf the "ties" case is not + specified (for example it is RNDN for glibc, RNDNA for + Windows), so we must round manually. */ + js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST, + buf_tmp, sizeof(buf_tmp)); + /* XXX: could use 2 digits to reduce the average running time */ + if (buf1[n_digits] == '5') { + js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD, + buf_tmp, sizeof(buf_tmp)); + js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD, + buf_tmp, sizeof(buf_tmp)); + if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) { + /* exact result: round away from zero */ + if (sign1) + rounding_mode = FE_DOWNWARD; + else + rounding_mode = FE_UPWARD; + } + } + } +#endif /* CONFIG_PRINTF_RNDN */ + } + js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode, + buf_tmp, sizeof(buf_tmp)); + return n_digits; +} + +static int js_fcvt1(char *buf, int buf_size, double d, int n_digits, + int rounding_mode) +{ + int n; + if (rounding_mode != FE_TONEAREST) + fesetround(rounding_mode); + n = snprintf(buf, buf_size, "%.*f", n_digits, d); + if (rounding_mode != FE_TONEAREST) + fesetround(FE_TONEAREST); + assert(n < buf_size); + return n; +} + +static void js_fcvt(char *buf, int buf_size, double d, int n_digits) +{ + int rounding_mode; + rounding_mode = FE_TONEAREST; +#ifdef CONFIG_PRINTF_RNDN + { + int n1, n2; + char buf1[JS_DTOA_BUF_SIZE]; + char buf2[JS_DTOA_BUF_SIZE]; + + /* The JS rounding is specified as round to nearest ties away from + zero (RNDNA), but in printf the "ties" case is not specified + (for example it is RNDN for glibc, RNDNA for Windows), so we + must round manually. */ + n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST); + rounding_mode = FE_TONEAREST; + /* XXX: could use 2 digits to reduce the average running time */ + if (buf1[n1 - 1] == '5') { + n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD); + n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD); + if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) { + /* exact result: round away from zero */ + if (buf1[0] == '-') + rounding_mode = FE_DOWNWARD; + else + rounding_mode = FE_UPWARD; + } + } + } +#endif /* CONFIG_PRINTF_RNDN */ + js_fcvt1(buf, buf_size, d, n_digits, rounding_mode); +} + +/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ +/* use as many digits as necessary */ +#define JS_DTOA_VAR_FORMAT (0 << 0) +/* use n_digits significant digits (1 <= n_digits <= 101) */ +#define JS_DTOA_FIXED_FORMAT (1 << 0) +/* force fractional format: [-]dd.dd with n_digits fractional digits */ +#define JS_DTOA_FRAC_FORMAT (2 << 0) +/* force exponential notation either in fixed or variable format */ +#define JS_DTOA_FORCE_EXP (1 << 2) + +/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. + XXX: radix != 10 is only supported for small integers +*/ +static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags) +{ + char *q; + + if (!isfinite(d)) { + if (isnan(d)) { + strcpy(buf, "NaN"); + } else { + q = buf; + if (d < 0) + *q++ = '-'; + strcpy(q, "Infinity"); + } + } else if (flags == JS_DTOA_VAR_FORMAT) { + int64_t i64; + char buf1[70], *ptr; + i64 = (int64_t)d; + if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER) + goto generic_conv; + /* fast path for integers */ + ptr = i64toa(buf1 + sizeof(buf1), i64, radix); + strcpy(buf, ptr); + } else { + if (d == 0.0) + d = 0.0; /* convert -0 to 0 */ + if (flags == JS_DTOA_FRAC_FORMAT) { + js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits); + } else { + char buf1[JS_DTOA_BUF_SIZE]; + int sign, decpt, k, n, i, p, n_max; + BOOL is_fixed; + generic_conv: + is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT); + if (is_fixed) { + n_max = n_digits; + } else { + n_max = 21; + } + /* the number has k digits (k >= 1) */ + k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed); + n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ + q = buf; + if (sign) + *q++ = '-'; + if (flags & JS_DTOA_FORCE_EXP) + goto force_exp; + if (n >= 1 && n <= n_max) { + if (k <= n) { + memcpy(q, buf1, k); + q += k; + for(i = 0; i < (n - k); i++) + *q++ = '0'; + *q = '\0'; + } else { + /* k > n */ + memcpy(q, buf1, n); + q += n; + *q++ = '.'; + for(i = 0; i < (k - n); i++) + *q++ = buf1[n + i]; + *q = '\0'; + } + } else if (n >= -5 && n <= 0) { + *q++ = '0'; + *q++ = '.'; + for(i = 0; i < -n; i++) + *q++ = '0'; + memcpy(q, buf1, k); + q += k; + *q = '\0'; + } else { + force_exp: + /* exponential notation */ + *q++ = buf1[0]; + if (k > 1) { + *q++ = '.'; + for(i = 1; i < k; i++) + *q++ = buf1[i]; + } + *q++ = 'e'; + p = n - 1; + if (p >= 0) + *q++ = '+'; + sprintf(q, "%d", p); + } + } + } +} + +static JSValue js_dtoa(JSContext *ctx, + double d, int radix, int n_digits, int flags) +{ + char buf[JS_DTOA_BUF_SIZE]; + js_dtoa1(buf, d, radix, n_digits, flags); + return JS_NewString(ctx, buf); +} + +JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) +{ + uint32_t tag; + const char *str; + char buf[32]; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_STRING: + return JS_DupValue(ctx, val); + case JS_TAG_INT: + snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val)); + str = buf; + goto new_string; + case JS_TAG_BOOL: + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? + JS_ATOM_true : JS_ATOM_false); + case JS_TAG_NULL: + return JS_AtomToString(ctx, JS_ATOM_null); + case JS_TAG_UNDEFINED: + return JS_AtomToString(ctx, JS_ATOM_undefined); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_OBJECT: + { + JSValue val1, ret; + val1 = JS_ToPrimitive(ctx, val, HINT_STRING); + if (JS_IsException(val1)) + return val1; + ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); + JS_FreeValue(ctx, val1); + return ret; + } + break; + case JS_TAG_FUNCTION_BYTECODE: + str = "[function bytecode]"; + goto new_string; + case JS_TAG_SYMBOL: + if (is_ToPropertyKey) { + return JS_DupValue(ctx, val); + } else { + return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); + } + case JS_TAG_FLOAT64: + return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, + JS_DTOA_VAR_FORMAT); +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + return ctx->rt->bigint_ops.to_string(ctx, val); + case JS_TAG_BIG_FLOAT: + return ctx->rt->bigfloat_ops.to_string(ctx, val); + case JS_TAG_BIG_DECIMAL: + return ctx->rt->bigdecimal_ops.to_string(ctx, val); +#endif + default: + str = "[unsupported type]"; + new_string: + return JS_NewString(ctx, str); + } +} + +JSValue JS_ToString(JSContext *ctx, JSValueConst val) +{ + return JS_ToStringInternal(ctx, val, FALSE); +} + +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) +{ + JSValue ret; + ret = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) +{ + if (JS_IsUndefined(val) || JS_IsNull(val)) + return JS_ToStringFree(ctx, val); + return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); +} + +JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) +{ + return JS_ToStringInternal(ctx, val, TRUE); +} + +static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); + return JS_ToString(ctx, val); +} + +static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) +{ + JSValue val; + JSString *p; + int i; + uint32_t c; + StringBuffer b_s, *b = &b_s; + char buf[16]; + + val = JS_ToStringCheckObject(ctx, val1); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + + if (string_buffer_init(ctx, b, p->len + 2)) + goto fail; + + if (string_buffer_putc8(b, '\"')) + goto fail; + for(i = 0; i < p->len; ) { + c = string_getc(p, &i); + switch(c) { + case '\t': + c = 't'; + goto quote; + case '\r': + c = 'r'; + goto quote; + case '\n': + c = 'n'; + goto quote; + case '\b': + c = 'b'; + goto quote; + case '\f': + c = 'f'; + goto quote; + case '\"': + case '\\': + quote: + if (string_buffer_putc8(b, '\\')) + goto fail; + if (string_buffer_putc8(b, c)) + goto fail; + break; + default: + if (c < 32 || (c >= 0xd800 && c < 0xe000)) { + snprintf(buf, sizeof(buf), "\\u%04x", c); + if (string_buffer_puts8(b, buf)) + goto fail; + } else { + if (string_buffer_putc(b, c)) + goto fail; + } + break; + } + } + if (string_buffer_putc8(b, '\"')) + goto fail; + JS_FreeValue(ctx, val); + return string_buffer_end(b); + fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) +{ + printf("%14s %4s %4s %14s %10s %s\n", + "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); +} + +/* for debug only: dump an object without side effect */ +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) +{ + uint32_t i; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + JSShape *sh; + JSShapeProperty *prs; + JSProperty *pr; + BOOL is_first = TRUE; + + /* XXX: should encode atoms with special characters */ + sh = p->shape; /* the shape can be NULL while freeing an object */ + printf("%14p %4d ", + (void *)p, + p->header.ref_count); + if (sh) { + printf("%3d%c %14p ", + sh->header.ref_count, + " *"[sh->is_hashed], + (void *)sh->proto); + } else { + printf("%3s %14s ", "-", "-"); + } + printf("%10s ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); + if (p->is_exotic && p->fast_array) { + printf("[ "); + for(i = 0; i < p->u.array.count; i++) { + if (i != 0) + printf(", "); + switch (p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + JS_DumpValueShort(rt, p->u.array.u.values[i]); + break; + case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY: + { + int size = 1 << typed_array_size_log2(p->class_id); + const uint8_t *b = p->u.array.u.uint8_ptr + i * size; + while (size-- > 0) + printf("%02X", *b++); + } + break; + } + } + printf(" ] "); + } + + if (sh) { + printf("{ "); + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->atom != JS_ATOM_NULL) { + pr = &p->prop[i]; + if (!is_first) + printf(", "); + printf("%s: ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + printf("[getset %p %p]", (void *)pr->u.getset.getter, + (void *)pr->u.getset.setter); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + printf("[varref %p]", (void *)pr->u.var_ref); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + printf("[autoinit %p %p]", (void *)pr->u.init.init_func, + (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; + default: + printf("[unknown %d]", p->gc_obj_type); + break; + } + printf("\n"); + } +} + +static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, + JSValueConst val) +{ + uint32_t tag = JS_VALUE_GET_NORM_TAG(val); + const char *str; + + switch(tag) { + case JS_TAG_INT: + printf("%d", JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BOOL: + if (JS_VALUE_GET_BOOL(val)) + str = "true"; + else + str = "false"; + goto print_str; + case JS_TAG_NULL: + str = "null"; + goto print_str; + case JS_TAG_EXCEPTION: + str = "exception"; + goto print_str; + case JS_TAG_UNINITIALIZED: + str = "uninitialized"; + goto print_str; + case JS_TAG_UNDEFINED: + str = "undefined"; + print_str: + printf("%s", str); + break; + case JS_TAG_FLOAT64: + printf("%.14g", JS_VALUE_GET_FLOAT64(val)); + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + char *str; + str = bf_ftoa(NULL, &p->num, 10, 0, + BF_RNDZ | BF_FTOA_FORMAT_FRAC); + printf("%sn", str); + bf_realloc(&rt->bf_ctx, str, 0); + } + break; + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + char *str; + str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF, + BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX); + printf("%sl", str); + bf_free(&rt->bf_ctx, str); + } + break; + case JS_TAG_BIG_DECIMAL: + { + JSBigDecimal *p = JS_VALUE_GET_PTR(val); + char *str; + str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF, + BF_RNDZ | BF_FTOA_FORMAT_FREE); + printf("%sm", str); + bf_free(&rt->bf_ctx, str); + } + break; +#endif + case JS_TAG_STRING: + { + JSString *p; + p = JS_VALUE_GET_STRING(val); + JS_DumpString(rt, p); + } + break; + case JS_TAG_FUNCTION_BYTECODE: + { + JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + } + break; + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAtom atom = rt->class_array[p->class_id].class_name; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("[%s %p]", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("Symbol(%s)", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); + } + break; + case JS_TAG_MODULE: + printf("[module]"); + break; + default: + printf("[unknown tag %d]", tag); + break; + } +} + +static __maybe_unused void JS_DumpValue(JSContext *ctx, + JSValueConst val) +{ + JS_DumpValueShort(ctx->rt, val); +} + +static __maybe_unused void JS_PrintValue(JSContext *ctx, + const char *str, + JSValueConst val) +{ + printf("%s=", str); + JS_DumpValueShort(ctx->rt, val); + printf("\n"); +} + +/* return -1 if exception (proxy case) or TRUE/FALSE */ +int JS_IsArray(JSContext *ctx, JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(val); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isArray(ctx, val); + else + return p->class_id == JS_CLASS_ARRAY; + } else { + return FALSE; + } +} + +static double js_pow(double a, double b) +{ + if (unlikely(!isfinite(b)) && fabs(a) == 1) { + /* not compatible with IEEE 754 */ + return JS_FLOAT64_NAN; + } else { + return pow(a, b); + } +} + +#ifdef CONFIG_BIGNUM + +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) +{ + BOOL is_bignum = is_bigint_mode(ctx); + if (is_bignum && v == (int32_t)v) { + return JS_NewInt32(ctx, v); + } else { + bf_t a_s, *a = &a_s; + bf_init(ctx->bf_ctx, a); + bf_set_si(a, v); + return JS_NewBigInt2(ctx, a, TRUE); + } +} + +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) +{ + BOOL is_bignum = is_bigint_mode(ctx); + if (is_bignum && v == (int32_t)v) { + return JS_NewInt32(ctx, v); + } else { + bf_t a_s, *a = &a_s; + bf_init(ctx->bf_ctx, a); + bf_set_ui(a, v); + return JS_NewBigInt2(ctx, a, TRUE); + } +} + +/* if the returned bigfloat is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return + NULL in case of error. */ +static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val) +{ + uint32_t tag; + bf_t *r; + JSBigFloat *p; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_si(r, JS_VALUE_GET_INT(val))) + goto fail; + break; + case JS_TAG_FLOAT64: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { + fail: + bf_delete(r); + return NULL; + } + break; + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_UNDEFINED: + default: + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set_nan(r); + break; + } + return r; +} + +/* return NULL if invalid type */ +static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val) +{ + uint32_t tag; + JSBigDecimal *p; + bfdec_t *r; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_BIG_DECIMAL: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + default: + JS_ThrowTypeError(ctx, "bigdecimal expected"); + r = NULL; + break; + } + return r; +} + +/* return NaN if bad bigint literal */ +static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) +{ + const char *str, *p; + size_t len; + int flags; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + bf_t a_s, *a = &a_s; + bf_init(ctx->bf_ctx, a); + bf_set_si(a, 0); + val = JS_NewBigInt(ctx, a); + } else { + flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; + if (is_bigint_mode(ctx)) + flags |= ATOD_MODE_BIGINT; + val = js_atof(ctx, p, &p, 0, flags); + p += skip_spaces(p); + if (!JS_IsException(val)) { + if ((p - str) != len) { + JS_FreeValue(ctx, val); + val = JS_NAN; + } + } + } + JS_FreeCString(ctx, str); + return val; +} + +static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) +{ + val = JS_StringToBigInt(ctx, val); + if (JS_VALUE_IS_NAN(val)) + return JS_ThrowSyntaxError(ctx, "invalid bigint literal"); + return val; +} + +/* if the returned bigfloat is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */ +static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) +{ + uint32_t tag; + bf_t *r; + JSBigFloat *p; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + if (!is_bigint_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_bigint_mode(ctx)) + goto fail; + if (!isfinite(d)) + goto fail; + r = buf; + bf_init(ctx->bf_ctx, r); + d = trunc(d); + bf_set_float64(r, d); + } + break; + case JS_TAG_BIG_INT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_BIG_FLOAT: + if (!is_bigint_mode(ctx)) + goto fail; + p = JS_VALUE_GET_PTR(val); + if (!bf_is_finite(&p->num)) + goto fail; + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set(r, &p->num); + bf_rint(r, BF_RNDZ); + JS_FreeValue(ctx, val); + break; + case JS_TAG_STRING: + val = JS_StringToBigIntErr(ctx, val); + if (JS_IsException(val)) + return NULL; + goto redo; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, + is_bigint_mode(ctx) ? HINT_INTEGER : 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, b_s, *b; + int ret; + + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) + return JS_EXCEPTION; + b = &b_s; + bf_init(ctx->bf_ctx, b); + ret = bf_set(b, a); + JS_FreeBigInt(ctx, a, &a_s); + if (ret) { + bf_delete(b); + return JS_ThrowOutOfMemory(ctx); + } + return JS_NewBigInt2(ctx, b, TRUE); + } +} + +/* free the bf_t allocated by JS_ToBigInt */ +static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) +{ + if (a == buf) { + bf_delete(a); + } else { + JSBigFloat *p = (JSBigFloat *)((uint8_t *)a - + offsetof(JSBigFloat, num)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p)); + } +} + +/* XXX: merge with JS_ToInt64Free with a specific flag */ +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + bf_t a_s, *a; + + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) { + *pres = 0; + return -1; + } + bf_get_int64(pres, a, BF_GET_INT_MOD); + JS_FreeBigInt(ctx, a, &a_s); + return 0; +} + +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val)); +} + +static JSBigFloat *js_new_bf(JSContext *ctx) +{ + JSBigFloat *p; + p = js_mallocz(ctx, sizeof(*p)); + if (!p) + return NULL; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return p; +} + +/* WARNING: 'a' is freed */ +static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a) +{ + JSValue ret; + JSBigFloat *p; + + p = js_new_bf(ctx); + bf_memcpy(&p->num, a); + ret = JS_MKPTR(JS_TAG_BIG_FLOAT, p); + return ret; +} + +/* WARNING: 'a' is freed */ +static JSValue JS_NewBigDecimal(JSContext *ctx, bfdec_t *a) +{ + JSValue ret; + JSBigDecimal *p; + + p = js_mallocz(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bfdec_init(ctx->bf_ctx, &p->num); + bfdec_memcpy(&p->num, a); + ret = JS_MKPTR(JS_TAG_BIG_DECIMAL, p); + return ret; +} + +/* WARNING: 'a' is freed */ +static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint) +{ + JSValue ret; + JSBigFloat *p; + int32_t v; + + if (!force_bigint && bf_get_int32(&v, a, 0) == 0) { + /* can fit in an int32 */ + ret = JS_NewInt32(ctx, v); + bf_delete(a); + } else { + p = js_new_bf(ctx); + bf_memcpy(&p->num, a); + /* normalize the zero representation */ + if (bf_is_zero(&p->num)) + p->num.sign = 0; + ret = JS_MKPTR(JS_TAG_BIG_INT, p); + } + return ret; +} + +static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a) +{ + return JS_NewBigInt2(ctx, a, !is_bigint_mode(ctx)); +} + +/* return < 0 if exception, 0 if overloading method, 1 if overloading + operator called */ +static __exception int js_call_binary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op) +{ + JSAtom op_name; + JSValue method, ret, c1, c2; + BOOL bool_result, swap_op; + JSValueConst args[2]; + + bool_result = FALSE; + swap_op = FALSE; + c1 = JS_UNDEFINED; + c2 = JS_UNDEFINED; + switch(op) { + case OP_add: + op_name = JS_ATOM_Symbol_operatorAdd; + break; + case OP_sub: + op_name = JS_ATOM_Symbol_operatorSub; + break; + case OP_mul: + op_name = JS_ATOM_Symbol_operatorMul; + break; + case OP_div: + case OP_math_div: + op_name = JS_ATOM_Symbol_operatorDiv; + break; + case OP_mod: + op_name = JS_ATOM_Symbol_operatorMod; + break; + case OP_pow: + case OP_math_pow: + op_name = JS_ATOM_Symbol_operatorPow; + break; + case OP_math_mod: + op_name = JS_ATOM_Symbol_operatorMathMod; + break; + case OP_shl: + op_name = JS_ATOM_Symbol_operatorShl; + break; + case OP_sar: + op_name = JS_ATOM_Symbol_operatorShr; + break; + case OP_and: + op_name = JS_ATOM_Symbol_operatorAnd; + break; + case OP_or: + op_name = JS_ATOM_Symbol_operatorOr; + break; + case OP_xor: + op_name = JS_ATOM_Symbol_operatorXor; + break; + case OP_lt: + op_name = JS_ATOM_Symbol_operatorCmpLT; + bool_result = TRUE; + break; + case OP_lte: + op_name = JS_ATOM_Symbol_operatorCmpLE; + bool_result = TRUE; + break; + case OP_gt: + op_name = JS_ATOM_Symbol_operatorCmpLT; + bool_result = TRUE; + swap_op = TRUE; + break; + case OP_gte: + op_name = JS_ATOM_Symbol_operatorCmpLE; + bool_result = TRUE; + swap_op = TRUE; + break; + case OP_eq: + case OP_neq: + op_name = JS_ATOM_Symbol_operatorCmpEQ; + bool_result = TRUE; + break; + default: + goto fail; + } + c1 = JS_GetProperty(ctx, op1, JS_ATOM_constructor); + if (JS_IsException(c1)) + goto exception; + c2 = JS_GetProperty(ctx, op2, JS_ATOM_constructor); + if (JS_IsException(c2)) + goto exception; + if (JS_VALUE_GET_TAG(c1) != JS_TAG_OBJECT || + JS_VALUE_GET_TAG(c2) != JS_TAG_OBJECT) + goto fail; + if (JS_VALUE_GET_OBJ(c1) == JS_VALUE_GET_OBJ(c2)) { + /* if same constructor, there is no ambiguity */ + method = JS_GetProperty(ctx, c1, op_name); + } else { + JSValue val; + int order1, order2; + + /* different constructors: we use a user-defined ordering */ + val = JS_GetProperty(ctx, c1, JS_ATOM_Symbol_operatorOrder); + if (JS_IsException(val)) + goto exception; + if (JS_IsUndefined(val)) + goto undef_order; + if (JS_ToInt32Free(ctx, &order1, val)) + goto exception; + val = JS_GetProperty(ctx, c2, JS_ATOM_Symbol_operatorOrder); + if (JS_IsException(val)) + goto exception; + if (JS_IsUndefined(val)) { + undef_order: + JS_FreeValue(ctx, c1); + JS_FreeValue(ctx, c2); + *pret = JS_UNDEFINED; + return 0; + } + if (JS_ToInt32Free(ctx, &order2, val)) + goto exception; + /* ambiguous priority: error */ + if (order1 == order2) { + JS_ThrowTypeError(ctx, "operator_order is identical in both constructors"); + goto exception; + } + if (order1 > order2) { + val = c1; + } else { + val = c2; + } + method = JS_GetProperty(ctx, val, op_name); + } + JS_FreeValue(ctx, c1); + JS_FreeValue(ctx, c2); + c1 = JS_UNDEFINED; + c2 = JS_UNDEFINED; + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + *pret = JS_UNDEFINED; + return 0; + } + if (swap_op) { + args[0] = op2; + args[1] = op1; + } else { + args[0] = op1; + args[1] = op2; + } + ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); + if (JS_IsException(ret)) + goto exception; + if (bool_result) { + BOOL res = JS_ToBoolFree(ctx, ret); + if (op == OP_neq) + res ^= 1; + ret = JS_NewBool(ctx, res); + } + *pret = ret; + return 1; + fail: + JS_ThrowTypeError(ctx, "invalid types for binary operator"); + exception: + JS_FreeValue(ctx, c1); + JS_FreeValue(ctx, c2); + *pret = JS_UNDEFINED; + return -1; +} + +static JSValue throw_bf_exception(JSContext *ctx, int status) +{ + const char *str; + if (status & BF_ST_MEM_ERROR) + return JS_ThrowOutOfMemory(ctx); + if (status & BF_ST_DIVIDE_ZERO) { + str = "division by zero"; + } else if (status & BF_ST_INVALID_OP) { + str = "invalid operation"; + } else { + str = "integer overflow"; + } + return JS_ThrowRangeError(ctx, "%s", str); +} + +static int js_unary_arith_bigint(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) +{ + bf_t a_s, r_s, *r = &r_s, *a; + int ret, v; + + if (op == OP_plus && !is_bigint_mode(ctx)) { + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + a = JS_ToBigInt(ctx, &a_s, op1); + bf_init(ctx->bf_ctx, r); + 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)) { + bf_delete(r); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_NewBigInt(ctx, r); + return 0; +} + +static int js_unary_arith_bigfloat(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) +{ + bf_t a_s, r_s, *r = &r_s, *a; + int ret, v; + + if (op == OP_plus && !is_bigint_mode(ctx)) { + JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + + a = JS_ToBigFloat(ctx, &a_s, op1); + bf_init(ctx->bf_ctx, r); + 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)) { + bf_delete(r); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_NewBigFloat(ctx, r); + return 0; +} + +static int js_unary_arith_bigdecimal(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) +{ + bfdec_t r_s, *r = &r_s, *a; + int ret, v; + + if (op == OP_plus && !is_bigint_mode(ctx)) { + JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + + a = JS_ToBigDecimal(ctx, op1); + bfdec_init(ctx->bf_ctx, r); + 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)) { + bfdec_delete(r); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_NewBigDecimal(ctx, r); + return 0; +} + +static no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, val, method; + BOOL is_legacy; + JSAtom op_name; + 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; + if (JS_IsObject(op1) && ctx->bignum_ext) { + switch(op) { + case OP_plus: + op_name = JS_ATOM_Symbol_operatorPlus; + break; + case OP_neg: + op_name = JS_ATOM_Symbol_operatorNeg; + break; + case OP_inc: + op_name = JS_ATOM_Symbol_operatorInc; + break; + case OP_dec: + op_name = JS_ATOM_Symbol_operatorDec; + break; + default: + abort(); + } + method = JS_GetProperty(ctx, op1, op_name); + if (JS_IsException(method)) + return -1; + if (JS_IsUndefined(method) || JS_IsNull(method)) + goto to_number; + val = JS_CallFree(ctx, method, op1, 0, NULL); + if (JS_IsException(val)) + return -1; + JS_FreeValue(ctx, op1); + sp[-1] = val; + } else { + to_number: + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + is_legacy = is_bigint_mode(ctx) ^ 1; + 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 && is_legacy) { + 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: + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + case JS_TAG_BIG_FLOAT: + if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + case JS_TAG_BIG_DECIMAL: + if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + default: + handle_float64: + { + double d; + 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, method, val; + + op1 = sp[-1]; + if (JS_IsObject(op1)) { + if (!ctx->bignum_ext) + goto to_number; + method = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorNot); + if (JS_IsException(method)) + return -1; + if (JS_IsUndefined(method) || JS_IsNull(method)) + goto to_number; + val = JS_CallFree(ctx, method, op1, 0, NULL); + if (JS_IsException(val)) + return -1; + JS_FreeValue(ctx, op1); + sp[-1] = val; + } else { + if (JS_IsString(op1)) { + to_number: + op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER); + if (JS_IsException(op1)) + goto exception; + } + if (is_bigint_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) + goto exception; + } else { + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = JS_NewInt32(ctx, ~v1); + } + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, r_s, *r, *a, *b; + int ret, rnd_mode; + + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + r = &r_s; + 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_math_div: + case OP_div: + ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_math_mod: + /* Euclidian remainder */ + rnd_mode = BF_DIVREM_EUCLIDIAN; + goto do_mod; + case OP_mod: + rnd_mode = BF_RNDZ; + do_mod: + { + bf_t q_s, *q = &q_s; + bf_init(ctx->bf_ctx, q); + ret = bf_divrem(q, r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + rnd_mode); + bf_delete(q); + } + break; + case OP_pow: + case OP_math_pow: + ret = bf_pow(r, a, b, ctx->fp_env.prec, + ctx->fp_env.flags | BF_POW_JS_QUICKS); + 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)) { + bf_delete(r); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_NewBigFloat(ctx, r); + return 0; +} + +static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, r_s, *r, *a, *b; + int ret, rnd_mode; + JSValue res; + + 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 = &r_s; + bf_init(ctx->bf_ctx, r); + 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_math_div: + goto op_fallback; + case OP_div: + if (!is_bigint_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 { + bf_div(r, a, b, 53, bf_set_exp_bits(11) | + BF_RNDN | BF_FLAG_SUBNORMAL); + goto float64_result; + } + break; + case OP_math_mod: + /* Euclidian remainder */ + rnd_mode = BF_DIVREM_EUCLIDIAN; + goto do_int_mod; + case OP_mod: + rnd_mode = BF_RNDZ; + do_int_mod: + { + bf_t q_s, *q = &q_s; + bf_init(ctx->bf_ctx, q); + ret = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, + rnd_mode) & BF_ST_INVALID_OP; + bf_delete(q); + } + break; + case OP_pow: + case OP_math_pow: + if (b->sign) { + if (!is_bigint_mode(ctx)) { + ret = BF_ST_INVALID_OP; + } else if (op == OP_math_pow) { + op_fallback: + bf_delete(r); + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + return -1; + } else if (ret == 0) { + JS_ThrowTypeError(ctx, "operator must be defined for exact division or power"); + return -1; + } + *pres = res; + return 0; + } else { + double dr; + bf_pow(r, a, b, 53, bf_set_exp_bits(11) | + BF_RNDN | BF_FLAG_SUBNORMAL); + float64_result: + bf_get_float64(r, &dr, BF_RNDN); + bf_delete(r); + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + *pres = __JS_NewFloat64(ctx, dr); + return 0; + } + } else { + ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUICKS); + } + 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)) { + bf_delete(r); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_NewBigInt(ctx, r); + 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_s, *r, *a, *b; + int ret, rnd_mode; + + /* big decimal result */ + a = JS_ToBigDecimal(ctx, op1); + if (!a) + goto fail; + b = JS_ToBigDecimal(ctx, op2); + if (!b) + goto fail; + r = &r_s; + bfdec_init(ctx->bf_ctx, r); + 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_math_div: + case OP_div: + ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_math_mod: + /* Euclidian remainder */ + rnd_mode = BF_DIVREM_EUCLIDIAN; + goto do_mod_dec; + case OP_mod: + rnd_mode = BF_RNDZ; + do_mod_dec: + { + bfdec_t q_s, *q = &q_s; + bfdec_init(ctx->bf_ctx, q); + ret = bfdec_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, rnd_mode); + bfdec_delete(q); + } + break; + case OP_pow: + case OP_math_pow: + ret = js_bfdec_pow(r, a, b); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (unlikely(ret)) { + bfdec_delete(r); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_NewBigDecimal(ctx, r); + return 0; + fail: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return -1; +} + +static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2, res; + BOOL is_legacy; + uint32_t tag1, tag2; + int ret; + double d1, d2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float operations */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + goto handle_float64; + } + + /* try to call an overloaded operator */ + if (((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && + ctx->bignum_ext) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + if (ret != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + goto exception; + } else { + sp[-2] = res; + return 0; + } + } + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + is_legacy = is_bigint_mode(ctx) ^ 1; + 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_legacy && v == 0 && (v1 | v2) < 0) { + sp[-2] = __JS_NewFloat64(ctx, -0.0); + return 0; + } + break; + case OP_math_div: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + return 0; + case OP_div: + sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); + return 0; + case OP_math_mod: + if (unlikely(v2 == 0)) { + throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); + goto exception; + } + v = (int64_t)v1 % (int64_t)v2; + if (v < 0) { + if (v2 < 0) + v -= v2; + else + v += v2; + } + break; + case OP_mod: + if (is_legacy && (v1 < 0 || v2 <= 0)) { + sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); + return 0; + } else { + if (unlikely(v2 == 0)) { + throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); + goto exception; + } + v = (int64_t)v1 % (int64_t)v2; + } + break; + case OP_pow: + case OP_math_pow: + if (is_legacy) { + sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); + return 0; + } else { + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + return 0; + } + break; + default: + abort(); + } + sp[-2] = JS_NewInt64(ctx, v); + } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { + if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } else if (tag1 == JS_TAG_FLOAT64 || tag2 == JS_TAG_FLOAT64) { + double dr; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + handle_float64: + switch(op) { + case OP_sub: + dr = d1 - d2; + break; + case OP_mul: + dr = d1 * d2; + break; + case OP_div: + case OP_math_div: + dr = d1 / d2; + break; + case OP_mod: + dr = fmod(d1, d2); + break; + case OP_math_mod: + d2 = fabs(d2); + dr = fmod(d1, d2); + /* XXX: loss of accuracy if dr < 0 */ + if (dr < 0) + dr += d2; + break; + case OP_pow: + case OP_math_pow: + dr = js_pow(d1, d2); + break; + default: + abort(); + } + sp[-2] = __JS_NewFloat64(ctx, dr); + } else { + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2, res; + uint32_t tag1, tag2; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float64 */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + double d1, d2; + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + sp[-2] = __JS_NewFloat64(ctx, d1 + d2); + return 0; + } + + if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { + /* try to call an overloaded operator */ + if (((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && + ctx->bignum_ext) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add); + if (ret != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + goto exception; + } else { + sp[-2] = res; + return 0; + } + } + } + + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + } + + if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + if (JS_IsException(sp[-2])) + goto exception; + return 0; + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + v = (int64_t)v1 + (int64_t)v2; + sp[-2] = JS_NewInt64(ctx, v); + } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { + if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } else if (tag1 == JS_TAG_FLOAT64 || tag2 == JS_TAG_FLOAT64) { + 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; + sp[-2] = __JS_NewFloat64(ctx, d1 + d2); + } else { + if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2, res; + int ret; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + /* try to call an overloaded operator */ + if (((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && + ctx->bignum_ext) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + if (ret != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + goto exception; + } else { + sp[-2] = res; + return 0; + } + } + } + + op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumberHintFree(ctx, op2, TON_FLAG_INTEGER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + if (!is_bigint_mode(ctx)) { + uint32_t v1, v2, r; + + 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 { + goto bigint_op; + } + } 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); + } + } else { + bigint_op: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +/* Note: also used for bigint */ +static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, *a, *b; + int res; + + a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, op2); + return -1; + } + b = JS_ToBigFloat(ctx, &b_s, op2); + if (!b) { + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return -1; + } + switch(op) { + case OP_lt: + res = bf_cmp_lt(a, b); /* if NaN return false */ + break; + case OP_lte: + res = bf_cmp_le(a, b); /* if NaN return false */ + break; + case OP_gt: + res = bf_cmp_lt(b, a); /* if NaN return false */ + break; + case OP_gte: + res = bf_cmp_le(b, a); /* if NaN return false */ + break; + case OP_eq: + res = bf_cmp_eq(a, b); /* if NaN return false */ + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; +} + +static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2) +{ + bfdec_t *a, *b; + int res; + + /* Note: binary floats are converted to bigdecimal with + toString(). It is not mathematically correct but is consistent + with the BigDecimal() constructor behavior */ + op1 = JS_ToBigDecimalFree(ctx, op1, TRUE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + return -1; + } + op2 = JS_ToBigDecimalFree(ctx, op2, TRUE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return -1; + } + a = JS_ToBigDecimal(ctx, op1); + b = JS_ToBigDecimal(ctx, op2); + + switch(op) { + case OP_lt: + res = bfdec_cmp_lt(a, b); /* if NaN return false */ + break; + case OP_lte: + res = bfdec_cmp_le(a, b); /* if NaN return false */ + break; + case OP_gt: + res = bfdec_cmp_lt(b, a); /* if NaN return false */ + break; + case OP_gte: + res = bfdec_cmp_le(b, a); /* if NaN return false */ + break; + case OP_eq: + res = bfdec_cmp_eq(a, b); /* if NaN return false */ + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; +} + +static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2, ret; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* try to call an overloaded operator */ + if (((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) && + ctx->bignum_ext) { + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op); + if (res != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (res < 0) { + goto exception; + } else { + sp[-2] = ret; + return 0; + } + } + } + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + JSString *p1, *p2; + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = js_string_compare(ctx, p1, p2); + switch(op) { + case OP_lt: + res = (res < 0); + break; + case OP_lte: + res = (res <= 0); + break; + case OP_gt: + res = (res > 0); + break; + default: + case OP_gte: + res = (res >= 0); + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && + (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { + /* can use floating point comparison */ + double d1, d2; + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } + } else { + if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || + (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && + !is_bigint_mode(ctx)) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + + if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_DECIMAL || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_DECIMAL) { + res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2); + if (res < 0) + goto exception; + } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_FLOAT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_FLOAT) { + res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2); + if (res < 0) + goto exception; + } else { + res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2); + if (res < 0) + goto exception; + } + } + done: + sp[-2] = JS_NewBool(ctx, res); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static BOOL tag_is_number(uint32_t tag) +{ + return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || + tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT || + tag == JS_TAG_BIG_DECIMAL); +} + +static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + JSValue op1, op2, ret; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + redo: + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag_is_number(tag1) && tag_is_number(tag2)) { + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + } else if ((tag1 == JS_TAG_FLOAT64 && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || + (tag2 == JS_TAG_FLOAT64 && + (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { + double d1, d2; + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + res = (d1 == d2); + } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { + res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } else { + res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } + } else if (tag1 == tag2) { + if (tag1 == JS_TAG_OBJECT && ctx->bignum_ext) { + /* try the fallback operator */ + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, + is_neq ? OP_neq : OP_eq); + if (res != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (res < 0) { + goto exception; + } else { + sp[-2] = ret; + return 0; + } + } + } + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || + (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { + res = TRUE; + } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || + (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { + + if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && + !is_bigint_mode(ctx)) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + res = js_strict_eq(ctx, op1, op2); + } else if (tag1 == JS_TAG_BOOL) { + op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); + goto redo; + } else if ((tag1 == JS_TAG_OBJECT && + (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || + (tag2 == JS_TAG_OBJECT && + (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { + + /* try the fallback operator */ + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, + is_neq ? OP_neq : OP_eq); + if (res != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (res < 0) { + goto exception; + } else { + sp[-2] = ret; + return 0; + } + } + + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + goto redo; + } else { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + } + done: + sp[-2] = JS_NewBool(ctx, res ^ is_neq); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + /* XXX: could forbid >>> in bignum mode */ + if (!is_bigint_mode(ctx) && + (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { + JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + goto exception; + } + /* cannot give an exception */ + JS_ToUint32Free(ctx, &v1, op1); + JS_ToUint32Free(ctx, &v2, op2); + r = v1 >> (v2 & 0x1f); + sp[-2] = JS_NewUint32(ctx, r); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, + int64_t exponent) +{ + bf_t r_s, *r = &r_s; + double d; + int ret; + + /* always convert to Float64 */ + bf_init(ctx->bf_ctx, r); + ret = bf_mul_pow_radix(r, a, 10, exponent, + 53, bf_set_exp_bits(11) | BF_RNDN | + BF_FLAG_SUBNORMAL); + bf_get_float64(r, &d, BF_RNDN); + bf_delete(r); + if (ret & BF_ST_MEM_ERROR) + return JS_ThrowOutOfMemory(ctx); + else + return __JS_NewFloat64(ctx, d); +} + +static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) +{ + bf_t a_s, *a, r_s, *r = &r_s; + JSValue op1, op2; + slimb_t e; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) + return -1; +#if LIMB_BITS == 32 + ret = JS_ToInt32(ctx, &e, op2); +#else + ret = JS_ToInt64(ctx, &e, op2); +#endif + if (ret) { + if (a == &a_s) + bf_delete(a); + return -1; + } + + bf_init(ctx->bf_ctx, r); + 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] = JS_NewBigFloat(ctx, r); + return 0; +} + +#else /* !CONFIG_BIGNUM */ + +static no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1; + double d; + + op1 = sp[-1]; + if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) { + sp[-1] = JS_UNDEFINED; + return -1; + } + switch(op) { + case OP_inc: + d++; + break; + case OP_dec: + d--; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); + } + sp[-1] = JS_NewFloat64(ctx, d); + return 0; +} + +/* specific case necessary for correct return value semantics */ +static __exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op) +{ + JSValue op1; + double d, r; + + op1 = sp[-1]; + if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) { + sp[-1] = JS_UNDEFINED; + return -1; + } + r = d + 2 * (op - OP_post_dec) - 1; + sp[0] = JS_NewFloat64(ctx, r); + sp[-1] = JS_NewFloat64(ctx, d); + return 0; +} + +static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + double d1, d2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) { + goto exception; + } + switch(op) { + case OP_sub: + r = d1 - d2; + break; + case OP_mul: + r = d1 * d2; + break; + case OP_div: + r = d1 / d2; + break; + case OP_mod: + r = fmod(d1, d2); + break; + case OP_pow: + r = js_pow(d1, d2); + break; + default: + abort(); + } + sp[-2] = JS_NewFloat64(ctx, r); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) && + (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) { + goto add_numbers; + } else { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + if (JS_IsException(sp[-2])) + goto exception; + } else { + double d1, d2; + add_numbers: + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + sp[-2] = JS_NewFloat64(ctx, d1 + d2); + } + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = JS_NewInt32(ctx, r); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +{ + int32_t v1; + + if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) { + sp[-1] = JS_UNDEFINED; + return -1; + } + sp[-1] = JS_NewInt32(ctx, ~v1); + return 0; +} + +static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + int res; + + op1 = sp[-2]; + op2 = sp[-1]; + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING && + JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { + JSString *p1, *p2; + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = js_string_compare(ctx, p1, p2); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + switch(op) { + case OP_lt: + res = (res < 0); + break; + case OP_lte: + res = (res <= 0); + break; + case OP_gt: + res = (res > 0); + break; + default: + case OP_gte: + res = (res >= 0); + break; + } + } else { + double d1, d2; + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } + } + sp[-2] = JS_NewBool(ctx, res); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + JSValue op1, op2; + int tag1, tag2; + BOOL res; + + op1 = sp[-2]; + op2 = sp[-1]; + redo: + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag1 == tag2 || + (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) || + (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) { + res = js_strict_eq(ctx, op1, op2); + } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || + (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { + res = TRUE; + } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT || + tag2 == JS_TAG_FLOAT64)) || + (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT || + tag1 == JS_TAG_FLOAT64))) { + double d1; + double d2; + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + res = (d1 == d2); + } else if (tag1 == JS_TAG_BOOL) { + op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); + goto redo; + } else if (tag1 == JS_TAG_OBJECT && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + goto redo; + } else if (tag2 == JS_TAG_OBJECT && + (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) { + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + goto redo; + } else { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + } + sp[-2] = JS_NewBool(ctx, res ^ is_neq); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToUint32Free(ctx, &v2, op2))) + goto exception; + r = v1 >> (v2 & 0x1f); + sp[-2] = JS_NewUint32(ctx, r); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +#endif /* !CONFIG_BIGNUM */ + +/* XXX: Should take JSValueConst arguments */ +static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode) +{ + BOOL res; + int tag1, tag2; + double d1, d2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + switch(tag1) { + case JS_TAG_BOOL: + if (tag1 != tag2) { + res = FALSE; + } else { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + goto done_no_free; + } + break; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = (tag1 == tag2); + break; + case JS_TAG_STRING: + { + JSString *p1, *p2; + if (tag1 != tag2) { + res = FALSE; + } else { + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = (js_string_compare(ctx, p1, p2) == 0); + } + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p1, *p2; + if (tag1 != tag2) { + res = FALSE; + } else { + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + res = (p1 == p2); + } + } + break; + case JS_TAG_OBJECT: + if (tag1 != tag2) + res = FALSE; + else + res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); + break; + case JS_TAG_INT: + d1 = JS_VALUE_GET_INT(op1); + if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + goto number_test; + } else if (tag2 == JS_TAG_FLOAT64) { +#ifdef CONFIG_BIGNUM + if (is_bigint_mode(ctx)) { + res = FALSE; + } else +#endif + { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } + } else +#ifdef CONFIG_BIGNUM + if (tag2 == JS_TAG_BIG_INT && is_bigint_mode(ctx)) { + goto bigint_test; + } else +#endif + { + 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 +#ifdef CONFIG_BIGNUM + && !is_bigint_mode(ctx) +#endif + ) { + d2 = JS_VALUE_GET_INT(op2); + } else { + res = FALSE; + break; + } + number_test: + if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { + JSFloat64Union u1, u2; + /* NaN is not always normalized, so this test is necessary */ + if (isnan(d1) || isnan(d2)) { + res = isnan(d1) == isnan(d2); + } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { + res = (d1 == d2); /* +0 == -0 */ + } else { + u1.d = d1; + u2.d = d2; + res = (u1.u64 == u2.u64); /* +0 != -0 */ + } + } else { + res = (d1 == d2); /* if NaN return false and +0 == -0 */ + } + goto done_no_free; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + { + bf_t a_s, *a, b_s, *b; + if (tag1 == tag2) { + /* OK */ + } else if (tag2 == JS_TAG_INT && is_bigint_mode(ctx)) { + /* OK */ + } else { + res = FALSE; + break; + } + bigint_test: + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + res = bf_cmp_eq(a, b); + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + } + break; + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p1, *p2; + const bf_t *a, *b; + if (tag1 != tag2) { + res = FALSE; + break; + } + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + a = &p1->num; + b = &p2->num; + if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { + if (eq_mode == JS_EQ_SAME_VALUE_ZERO && + a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) { + res = TRUE; + } else { + res = (bf_cmp_full(a, b) == 0); + } + } else { + res = bf_cmp_eq(a, b); + } + } + break; + case JS_TAG_BIG_DECIMAL: + { + JSBigDecimal *p1, *p2; + const bfdec_t *a, *b; + if (tag1 != tag2) { + res = FALSE; + break; + } + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + a = &p1->num; + b = &p2->num; + res = bfdec_cmp_eq(a, b); + } + break; +#endif + default: + res = FALSE; + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + done_no_free: + return res; +} + +static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); +} + +static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_strict_eq2(ctx, + JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), + JS_EQ_SAME_VALUE); +} + +static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_strict_eq2(ctx, + JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), + JS_EQ_SAME_VALUE_ZERO); +} + +static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + BOOL res; + res = js_strict_eq(ctx, sp[-2], sp[-1]); + sp[-2] = JS_NewBool(ctx, res ^ is_neq); + return 0; +} + +static __exception int js_operator_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + + if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + atom = JS_ValueToAtom(ctx, op1); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_HasProperty(ctx, op2, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj, + JSAtom atom) +{ + JSValue arr, val; + int ret; + + arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); + if (JS_IsException(arr)) + return -1; + ret = 0; + if (JS_IsObject(arr)) { + val = JS_GetProperty(ctx, arr, atom); + ret = JS_ToBoolFree(ctx, val); + } + JS_FreeValue(ctx, arr); + return ret; +} + +static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + BOOL ret; + + op1 = sp[-2]; + op2 = sp[-1]; + ret = JS_IsInstanceOf(ctx, op1, op2); + if (ret < 0) + return ret; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) +{ + JSAtom atom; + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(op1); + switch(tag) { +#ifdef CONFIG_BIGNUM + case JS_TAG_INT: + if (is_bigint_mode(ctx)) + atom = JS_ATOM_bigint; + else + atom = JS_ATOM_number; + break; + case JS_TAG_BIG_INT: + atom = JS_ATOM_bigint; + break; + case JS_TAG_FLOAT64: + atom = JS_ATOM_number; + break; + case JS_TAG_BIG_FLOAT: + atom = JS_ATOM_bigfloat; + break; + case JS_TAG_BIG_DECIMAL: + atom = JS_ATOM_bigdecimal; + break; +#else + case JS_TAG_INT: + case JS_TAG_FLOAT64: + atom = JS_ATOM_number; + break; +#endif + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + break; + case JS_TAG_BOOL: + atom = JS_ATOM_boolean; + break; + case JS_TAG_STRING: + atom = JS_ATOM_string; + break; + case JS_TAG_OBJECT: + if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + break; + case JS_TAG_NULL: + obj_type: + atom = JS_ATOM_object; + break; + case JS_TAG_SYMBOL: + atom = JS_ATOM_symbol; + break; + default: + atom = JS_ATOM_unknown; + break; + } + return atom; +} + +static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + if (unlikely(ret < 0)) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ThrowTypeError(ctx, "invalid property access"); +} + +/* XXX: not 100% compatible, but mozilla seems to use a similar + implementation to ensure that caller in non strict mode does not + throw (ES5 compatibility) */ +static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) { + return js_throw_type_error(ctx, this_val, 0, NULL); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_fileName(JSContext *ctx, + JSValueConst this_val) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b && b->has_debug) { + return JS_AtomToString(ctx, b->debug.filename); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_lineNumber(JSContext *ctx, + JSValueConst this_val) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b && b->has_debug) { + return JS_NewInt32(ctx, b->debug.line_num); + } + return JS_UNDEFINED; +} + +static int js_arguments_define_own_property(JSContext *ctx, + JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags) +{ + JSObject *p; + uint32_t idx; + p = JS_VALUE_GET_OBJ(this_obj); + /* convert to normal array when redefining an existing numeric field */ + if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && + idx < p->u.array.count) { + if (convert_fast_array_to_array(ctx, p)) + return -1; + } + /* run the default define own property */ + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, + flags | JS_PROP_NO_EXOTIC); +} + +static const JSClassExoticMethods js_arguments_exotic_methods = { + .define_own_property = js_arguments_define_own_property, +}; + +static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) +{ + JSValue val, *tab; + JSProperty *pr; + JSObject *p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = JS_NewInt32(ctx, argc); + + /* initialize the fast array part */ + tab = NULL; + if (argc > 0) { + tab = js_malloc(ctx, sizeof(tab[0]) * argc); + if (!tab) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + for(i = 0; i < argc; i++) { + tab[i] = JS_DupValue(ctx, argv[i]); + } + } + p->u.array.u.values = tab; + p->u.array.count = argc; + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + JS_DupValue(ctx, ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* add callee property to throw a TypeError in strict mode */ + JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, + ctx->throw_type_error, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET); + return val; +} + +#define GLOBAL_VAR_OFFSET 0x40000000 +#define ARGUMENT_VAR_OFFSET 0x20000000 + +/* legacy arguments object: add references to the function arguments */ +static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, + JSValueConst *argv, + JSStackFrame *sf, int arg_count) +{ + JSValue val; + JSProperty *pr; + JSObject *p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_MAPPED_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = JS_NewInt32(ctx, argc); + + for(i = 0; i < arg_count; i++) { + JSVarRef *var_ref; + var_ref = get_var_ref(ctx, sf, i, TRUE); + if (!var_ref) + goto fail; + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); + if (!pr) { + free_var_ref(ctx->rt, var_ref); + goto fail; + } + pr->u.var_ref = var_ref; + } + + /* the arguments not mapped to the arguments of the function can + be normal properties */ + for(i = arg_count; i < argc; i++) { + if (JS_DefinePropertyValueUint32(ctx, val, i, + JS_DupValue(ctx, argv[i]), + JS_PROP_C_W_E) < 0) + goto fail; + } + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + JS_DupValue(ctx, ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* callee returns this function in non strict mode */ + JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, + JS_DupValue(ctx, ctx->current_stack_frame->cur_func), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv) +{ + JSValue val; + int i, ret; + + val = JS_NewArray(ctx); + if (JS_IsException(val)) + return val; + for (i = first; i < argc; i++) { + ret = JS_DefinePropertyValueUint32(ctx, val, i - first, + JS_DupValue(ctx, argv[i]), + JS_PROP_C_W_E); + if (ret < 0) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + } + return val; +} + +static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) +{ + JSObject *p, *p1; + JSPropertyEnum *tab_atom; + int i; + JSValue enum_obj; + JSForInIterator *it; + uint32_t tag, tab_atom_count; + + tag = JS_VALUE_GET_TAG(obj); + if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { + obj = JS_ToObjectFree(ctx, obj); + } + + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); + if (JS_IsException(enum_obj)) { + js_free(ctx, it); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + it->is_array = FALSE; + it->obj = obj; + it->idx = 0; + p = JS_VALUE_GET_OBJ(enum_obj); + p->u.for_in_iterator = it; + + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return enum_obj; + + p = JS_VALUE_GET_OBJ(obj); + + /* fast path: assume no enumerable properties in the prototype chain */ + p1 = p->shape->proto; + while (p1 != NULL) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p1, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + goto fail; + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + if (tab_atom_count != 0) { + goto slow_path; + } + p1 = p1->shape->proto; + } + if (p->fast_array) { + JSShape *sh; + JSShapeProperty *prs; + /* check that there are no enumerable normal fields */ + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->flags & JS_PROP_ENUMERABLE) + goto normal_case; + } + /* the implicit GetOwnProperty raises an exception if the + typed array is detached */ + if ((p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) && + typed_array_is_detached(ctx, p) && + typed_array_get_length(ctx, p) != 0) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + /* for fast arrays, we only store the number of elements */ + it->is_array = TRUE; + it->array_length = p->u.array.count; + } else { + normal_case: + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + goto fail; + for(i = 0; i < tab_atom_count; i++) { + JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + } + return enum_obj; + + slow_path: + /* non enumerable properties hide the enumerables ones in the + prototype chain */ + while (p != NULL) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) + goto fail; + for(i = 0; i < tab_atom_count; i++) { + JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, + (tab_atom[i].is_enumerable ? + JS_PROP_ENUMERABLE : 0)); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + p = p->shape->proto; + } + return enum_obj; + + fail: + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; +} + +/* obj -> enum_obj */ +static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) +{ + sp[-1] = build_for_in_iterator(ctx, sp[-1]); + if (JS_IsException(sp[-1])) + return -1; + return 0; +} + +/* enum_obj -> enum_obj value done */ +static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) +{ + JSValueConst enum_obj; + JSObject *p; + JSAtom prop; + JSForInIterator *it; + int ret; + + enum_obj = sp[-1]; + /* fail safe */ + if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) + goto done; + p = JS_VALUE_GET_OBJ(enum_obj); + if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) + goto done; + it = p->u.for_in_iterator; + + for(;;) { + if (it->is_array) { + if (it->idx >= it->array_length) + goto done; + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + JSShape *sh = p->shape; + JSShapeProperty *prs; + if (it->idx >= sh->prop_count) + goto done; + prs = get_shape_prop(sh) + it->idx; + prop = prs->atom; + it->idx++; + if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) + continue; + } + /* check if the property was deleted */ + ret = JS_HasProperty(ctx, it->obj, prop); + if (ret < 0) + return ret; + if (ret) + break; + } + /* return the property */ + sp[0] = JS_AtomToValue(ctx, prop); + sp[1] = JS_FALSE; + return 0; + done: + /* return the end */ + sp[0] = JS_UNDEFINED; + sp[1] = JS_TRUE; + return 0; +} + +static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, + JSValueConst method) +{ + JSValue enum_obj; + + enum_obj = JS_Call(ctx, method, obj, 0, NULL); + if (JS_IsException(enum_obj)) + return enum_obj; + if (!JS_IsObject(enum_obj)) { + JS_FreeValue(ctx, enum_obj); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return enum_obj; +} + +static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) +{ + JSValue method, ret, sync_iter; + + if (is_async) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); + if (JS_IsException(method)) + return method; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + sync_iter = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + if (JS_IsException(sync_iter)) + return sync_iter; + ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); + JS_FreeValue(ctx, sync_iter); + return ret; + } + } else { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + } + if (!JS_IsFunction(ctx, method)) { + JS_FreeValue(ctx, method); + return JS_ThrowTypeError(ctx, "value is not iterable"); + } + ret = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + return ret; +} + +/* return *pdone = 2 if the iterator object is not parsed */ +static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, int *pdone) +{ + JSValue obj; + + /* fast path for the built-in iterators (avoid creating the + intermediate result object) */ + if (JS_IsObject(method)) { + JSObject *p = JS_VALUE_GET_OBJ(method); + if (p->class_id == JS_CLASS_C_FUNCTION && + p->u.cfunc.cproto == JS_CFUNC_iterator_next) { + JSCFunctionType func; + JSValueConst args[1]; + + /* in case the function expects one argument */ + if (argc == 0) { + args[0] = JS_UNDEFINED; + argv = args; + } + func = p->u.cfunc.c_function; + return func.iterator_next(ctx, enum_obj, argc, argv, + pdone, p->u.cfunc.magic); + } + } + obj = JS_Call(ctx, method, enum_obj, argc, argv); + if (JS_IsException(obj)) + goto fail; + if (!JS_IsObject(obj)) { + JS_FreeValue(ctx, obj); + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto fail; + } + *pdone = 2; + return obj; + fail: + *pdone = FALSE; + return JS_EXCEPTION; +} + +static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, + JSValueConst method, + int argc, JSValueConst *argv, BOOL *pdone) +{ + JSValue obj, value, done_val; + int done; + + obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); + if (JS_IsException(obj)) + goto fail; + if (done != 2) { + *pdone = done; + return obj; + } else { + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + *pdone = JS_ToBoolFree(ctx, done_val); + value = JS_UNDEFINED; + if (!*pdone) { + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + } + JS_FreeValue(ctx, obj); + return value; + } + fail: + JS_FreeValue(ctx, obj); + *pdone = FALSE; + return JS_EXCEPTION; +} + +/* return < 0 in case of exception */ +static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, + BOOL is_exception_pending) +{ + JSValue method, ret, ex_obj; + int res; + + if (is_exception_pending) { + ex_obj = ctx->current_exception; + ctx->current_exception = JS_NULL; + res = -1; + } else { + ex_obj = JS_UNDEFINED; + res = 0; + } + method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); + if (JS_IsException(method)) { + res = -1; + goto done; + } + if (JS_IsUndefined(method) || JS_IsNull(method)) { + goto done; + } + ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); + if (!is_exception_pending) { + if (JS_IsException(ret)) { + res = -1; + } else if (!JS_IsObject(ret)) { + JS_ThrowTypeErrorNotAnObject(ctx); + res = -1; + } + } + JS_FreeValue(ctx, ret); + done: + if (is_exception_pending) { + JS_Throw(ctx, ex_obj); + } + return res; +} + +/* obj -> enum_rec (3 slots) */ +static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, + BOOL is_async) +{ + JSValue op1, obj, method; + op1 = sp[-1]; + obj = JS_GetIterator(ctx, op1, is_async); + if (JS_IsException(obj)) + return -1; + JS_FreeValue(ctx, op1); + sp[-1] = obj; + method = JS_GetProperty(ctx, obj, JS_ATOM_next); + if (JS_IsException(method)) + return -1; + sp[0] = method; + return 0; +} + +/* enum_rec -> enum_rec value done */ +static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) +{ + JSValue value = JS_UNDEFINED; + int done = 1; + + if (likely(!JS_IsUndefined(sp[offset]))) { + value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); + if (JS_IsException(value)) + done = -1; + if (done) { + /* value is JS_UNDEFINED or JS_EXCEPTION */ + /* replace the iteration object with undefined */ + JS_FreeValue(ctx, sp[offset]); + sp[offset] = JS_UNDEFINED; + if (done < 0) + return -1; + } + } + sp[0] = value; + sp[1] = JS_NewBool(ctx, done); + return 0; +} + +static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp) +{ + JSValue result; + result = JS_Call(ctx, sp[-2], sp[-3], 0, NULL); + if (JS_IsException(result)) + return -1; + sp[0] = result; + return 0; +} + +static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, + BOOL *pdone) +{ + JSValue done_val, value; + BOOL done; + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + done = JS_ToBoolFree(ctx, done_val); + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + if (JS_IsException(value)) + goto fail; + *pdone = done; + return value; + fail: + *pdone = FALSE; + return JS_EXCEPTION; +} + +static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) +{ + JSValue obj, value; + BOOL done; + obj = sp[-1]; + if (!JS_IsObject(obj)) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + return -1; + } + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + if (JS_IsException(value)) + return -1; + JS_FreeValue(ctx, obj); + sp[-1] = value; + sp[0] = JS_NewBool(ctx, done); + return 0; +} + +static JSValue js_create_iterator_result(JSContext *ctx, + JSValue val, + BOOL done) +{ + JSValue obj; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, val); + return obj; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, + val, JS_PROP_C_W_E) < 0) { + goto fail; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, + JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic); + +static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); + +static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + return TRUE; + } + } + return FALSE; +} + +/* Access an Array's internal JSValue array if available */ +static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, + JSValue **arrpp, uint32_t *countp) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + *countp = p->u.array.count; + *arrpp = p->u.array.u.values; + return TRUE; + } + } + return FALSE; +} + +static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) +{ + JSValue iterator, enumobj, method, value; + int pos, is_array_iterator; + JSValue *arrp; + uint32_t i, count32; + + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { + JS_ThrowInternalError(ctx, "invalid index for append"); + return -1; + } + + pos = JS_VALUE_GET_INT(sp[-2]); + + /* XXX: further optimisations: + - use ctx->array_proto_values? + - check if array_iterator_prototype next method is built-in and + avoid constructing actual iterator object? + - build this into js_for_of_start and use in all `for (x of o)` loops + */ + iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); + if (JS_IsException(iterator)) + return -1; + is_array_iterator = JS_IsCFunction(ctx, iterator, + (JSCFunction *)js_create_array_iterator, + JS_ITERATOR_KIND_VALUE); + JS_FreeValue(ctx, iterator); + + enumobj = JS_GetIterator(ctx, sp[-1], FALSE); + if (JS_IsException(enumobj)) + return -1; + method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); + if (JS_IsException(method)) { + JS_FreeValue(ctx, enumobj); + return -1; + } + if (is_array_iterator + && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0) + && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { + int64_t len; + /* Handle fast arrays explicitly */ + if (js_get_length64(ctx, &len, sp[-1])) + goto exception; + for (i = 0; i < count32; i++) { + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, + JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) + goto exception; + } + if (len > count32) { + /* This is not strictly correct because the trailing elements are + empty instead of undefined. Append undefined entries instead. + */ + pos += len - count32; + if (JS_SetProperty(ctx, sp[-3], JS_ATOM_length, JS_NewUint32(ctx, pos)) < 0) + goto exception; + } + } else { + for (;;) { + BOOL done; + value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); + if (JS_IsException(value)) + goto exception; + if (done) { + /* value is JS_UNDEFINED */ + break; + } + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) + goto exception; + } + } + sp[-2] = JS_NewInt32(ctx, pos); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return 0; + +exception: + JS_IteratorClose(ctx, enumobj, TRUE); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return -1; +} + +static __exception int JS_CopyDataProperties(JSContext *ctx, + JSValueConst target, + JSValueConst source, + JSValueConst excluded, + BOOL setprop) +{ + JSPropertyEnum *tab_atom; + JSValue val; + uint32_t i, tab_atom_count; + JSObject *p; + JSObject *pexcl = NULL; + int ret = 0, flags; + + if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) + return 0; + + if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) + pexcl = JS_VALUE_GET_OBJ(excluded); + + p = JS_VALUE_GET_OBJ(source); + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | + JS_GPN_ENUM_ONLY)) + return -1; + + flags = JS_PROP_C_W_E; + + for (i = 0; i < tab_atom_count; i++) { + if (pexcl) { + ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); + if (ret) { + if (ret < 0) + break; + ret = 0; + continue; + } + } + ret = -1; + val = JS_GetProperty(ctx, source, tab_atom[i].atom); + if (JS_IsException(val)) + break; + if (setprop) + ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); + else + ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, flags); + if (ret < 0) + break; + ret = 0; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return ret; +} + +/* only valid inside C functions */ +static JSValueConst JS_GetActiveFunction(JSContext *ctx) +{ + return ctx->current_stack_frame->cur_func; +} + +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, + int var_idx, BOOL is_arg) +{ + JSVarRef *var_ref; + struct list_head *el; + + list_for_each(el, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { + var_ref->header.ref_count++; + return var_ref; + } + } + /* create a new one */ + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + var_ref->is_detached = FALSE; + var_ref->is_arg = is_arg; + var_ref->var_idx = var_idx; + list_add_tail(&var_ref->header.link, &sf->var_ref_list); + if (is_arg) + var_ref->pvalue = &sf->arg_buf[var_idx]; + else + var_ref->pvalue = &sf->var_buf[var_idx]; + var_ref->value = JS_UNDEFINED; + return var_ref; +} + +static JSValue js_closure2(JSContext *ctx, JSValue func_obj, + JSFunctionBytecode *b, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSObject *p; + JSVarRef **var_refs; + int i; + + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + if (cv->is_local) { + /* reuse the existing variable reference if it already exists */ + var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); + if (!var_ref) + goto fail; + } else { + var_ref = cur_var_refs[cv->var_idx]; + var_ref->header.ref_count++; + } + var_refs[i] = var_ref; + } + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) +{ + JSValue obj, this_val; + int ret; + + this_val = JS_MKPTR(JS_TAG_OBJECT, p); + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return -1; + set_cycle_flag(ctx, obj); + set_cycle_flag(ctx, this_val); + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, + JS_DupValue(ctx, this_val), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (JS_DefinePropertyValue(ctx, this_val, atom, obj, JS_PROP_WRITABLE) < 0 || ret < 0) + return -1; + return 0; +} + +static JSValue js_closure(JSContext *ctx, JSValue bfunc, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSFunctionBytecode *b; + JSValue func_obj; + JSAtom name_atom; + 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, + }; + + 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_instantiate_prototype, NULL, + JS_PROP_WRITABLE); + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) + +static int js_op_define_class(JSContext *ctx, JSValue *sp, + JSAtom class_name, int class_flags, + JSVarRef **cur_var_refs, + JSStackFrame *sf, BOOL is_computed_name) +{ + JSValue bfunc, parent_class, proto = JS_UNDEFINED; + JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; + JSFunctionBytecode *b; + + parent_class = sp[-2]; + bfunc = sp[-1]; + + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { + if (JS_IsNull(parent_class)) { + parent_proto = JS_NULL; + parent_class = JS_DupValue(ctx, ctx->function_proto); + } else { + if (!JS_IsConstructor(ctx, parent_class)) { + JS_ThrowTypeError(ctx, "parent class must be constructor"); + goto fail; + } + parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); + if (JS_IsException(parent_proto)) + goto fail; + if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { + JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); + goto fail; + } + } + } else { + /* parent_class is JS_UNDEFINED in this case */ + parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); + parent_class = JS_DupValue(ctx, ctx->function_proto); + } + proto = JS_NewObjectProto(ctx, parent_proto); + if (JS_IsException(proto)) + goto fail; + + b = JS_VALUE_GET_PTR(bfunc); + assert(b->func_kind == JS_FUNC_NORMAL); + ctor = JS_NewObjectProtoClass(ctx, parent_class, + JS_CLASS_BYTECODE_FUNCTION); + if (JS_IsException(ctor)) + goto fail; + ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); + bfunc = JS_UNDEFINED; + if (JS_IsException(ctor)) + goto fail; + js_method_set_home_object(ctx, ctor, proto); + JS_SetConstructorBit(ctx, ctor, TRUE); + + JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, + JS_NewInt32(ctx, b->defined_arg_count), + JS_PROP_CONFIGURABLE); + + if (is_computed_name) { + if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], + JS_PROP_CONFIGURABLE) < 0) + goto fail; + } else { + if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) + goto fail; + } + + /* the constructor property must be first. It can be overriden by + computed property names */ + if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, + JS_DupValue(ctx, ctor), + JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE | JS_PROP_THROW) < 0) + goto fail; + /* set the prototype property */ + if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, + JS_DupValue(ctx, proto), JS_PROP_THROW) < 0) + goto fail; + set_cycle_flag(ctx, ctor); + set_cycle_flag(ctx, proto); + + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, parent_class); + + sp[-2] = ctor; + sp[-1] = proto; + return 0; + fail: + JS_FreeValue(ctx, parent_class); + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, bfunc); + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, ctor); + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + var_idx = var_ref->var_idx; + if (var_ref->is_arg) + var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); + else + var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } +} + +static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx = idx; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { + var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + list_del(&var_ref->header.link); + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } + } +} + +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) + +static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + 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(ctx, sizeof(arg_buf[0]) * arg_count)) + return JS_ThrowStackOverflow(ctx); + + prev_sf = ctx->current_stack_frame; + sf->prev_frame = prev_sf; + ctx->current_stack_frame = sf; +#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_BIGINT; + 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(); + } + + ctx->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, 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); + } +} + +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->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; + } +} + +/* argument of OP_special_object */ +typedef enum { + OP_SPECIAL_OBJECT_ARGUMENTS, + OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, + OP_SPECIAL_OBJECT_THIS_FUNC, + OP_SPECIAL_OBJECT_NEW_TARGET, + OP_SPECIAL_OBJECT_HOME_OBJECT, + OP_SPECIAL_OBJECT_VAR_OBJECT, + OP_SPECIAL_OBJECT_IMPORT_META, +} OPSpecialObjectEnum; + +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, JSValueConst new_target, + int argc, JSValue *argv, int flags) +{ + 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(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; + 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 = ctx->current_stack_frame; + ctx->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 = 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, 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(ctx, alloca_size)) + return JS_ThrowStackOverflow(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(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 = ctx->current_stack_frame; + ctx->current_stack_frame = sf; + 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(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): + if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + BREAK; + CASE(OP_add_brand): + if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + BREAK; + + CASE(OP_throw): + JS_Throw(ctx, *--sp); + goto exception; + + CASE(OP_throw_var): +#define JS_THROW_VAR_RO 0 +#define JS_THROW_VAR_REDECL 1 +#define JS_THROW_VAR_UNINITIALIZED 2 +#define JS_THROW_VAR_DELETE_SUPER 3 + { + JSAtom atom; + int type; + atom = get_u32(pc); + type = pc[4]; + pc += 5; + if (type == JS_THROW_VAR_RO) + JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); + else + if (type == JS_THROW_VAR_REDECL) + JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); + else + if (type == JS_THROW_VAR_UNINITIALIZED) + JS_ThrowReferenceErrorUninitialized(ctx, atom); + else + if (type == JS_THROW_VAR_DELETE_SUPER) + JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); + else + JS_ThrowInternalError(ctx, "invalid throw var type %d", type); + } + goto exception; + + CASE(OP_eval): + { + JSValueConst obj; + int scope_idx; + call_argc = get_u16(pc); + scope_idx = get_u16(pc + 2) - 1; + pc += 4; + call_argv = sp - call_argc; + sf->cur_pc = pc; + if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { + if (call_argc >= 1) + obj = call_argv[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, call_argv, 0); + } + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + /* could merge with OP_apply */ + CASE(OP_apply_eval): + { + int scope_idx; + uint32_t len; + JSValue *tab; + JSValueConst obj; + + scope_idx = get_u16(pc) - 1; + pc += 2; + tab = build_arg_list(ctx, &len, sp[-1]); + if (!tab) + goto exception; + if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { + if (len >= 1) + obj = tab[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, + (JSValueConst *)tab); + } + free_arg_list(ctx, tab, len); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_regexp): + { + sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, + sp[-2], sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_super_ctor): + { + JSValue proto; + proto = JS_DupValue(ctx, JS_GetPrototype(ctx, sp[-1])); + if (JS_IsException(proto)) + goto exception; + if (!JS_IsConstructor(ctx, proto)) { + JS_FreeValue(ctx, proto); + JS_ThrowTypeError(ctx, "not a constructor"); + goto exception; + } + JS_FreeValue(ctx, sp[-1]); + sp[-1] = proto; + } + BREAK; + + CASE(OP_get_super): + { + JSValue proto; + proto = JS_DupValue(ctx, JS_GetPrototype(ctx, sp[-1])); + if (JS_IsException(proto)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = proto; + } + BREAK; + + CASE(OP_import): + { + JSValue val; + val = js_dynamic_import(ctx, sp[-1]); + if (JS_IsException(val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_check_var): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_CheckGlobalVar(ctx, atom); + if (ret < 0) + goto exception; + *sp++ = JS_NewBool(ctx, ret); + } + BREAK; + + CASE(OP_get_var_undef): + CASE(OP_get_var): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_var): + CASE(OP_put_var_init): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_var_strict): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + /* sp[-2] is JS_TRUE or JS_FALSE */ + if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + goto exception; + } + ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_check_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_CheckDefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_func): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = JS_DupValue(ctx, var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); + } + BREAK; + CASE(OP_get_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = JS_DupValue(ctx, arg_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1])); + } + BREAK; + +#if SHORT_OPCODES + CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK; + CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; + CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK; + + CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK; + CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK; + CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK; + CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK; + CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; + CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; + CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; + CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; + CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK; + CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK; + CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK; + CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK; + CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; + CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; + CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; + CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; + CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK; + CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK; + CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK; + CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK; + CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; + CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; + CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; +#endif + + CASE(OP_get_var_ref): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + sp[0] = JS_DupValue(ctx, val); + sp++; + } + BREAK; + CASE(OP_put_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1])); + } + BREAK; + CASE(OP_get_var_ref_check): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + goto exception; + } + sp[0] = JS_DupValue(ctx, val); + sp++; + } + BREAK; + CASE(OP_put_var_ref_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_var_ref_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc_uninitialized): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); + } + BREAK; + CASE(OP_get_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + goto exception; + } + sp[0] = JS_DupValue(ctx, var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_loc_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceError(ctx, "'this' can be initialized only once"); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_close_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + close_lexical_var(ctx, sf, idx, FALSE); + } + BREAK; + + CASE(OP_make_loc_ref): + CASE(OP_make_arg_ref): + CASE(OP_make_var_ref_ref): + { + JSVarRef *var_ref; + JSProperty *pr; + JSAtom atom; + int idx; + atom = get_u32(pc); + idx = get_u16(pc + 4); + pc += 6; + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + if (opcode == OP_make_var_ref_ref) { + var_ref = var_refs[idx]; + var_ref->header.ref_count++; + } else { + var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); + if (!var_ref) + goto exception; + } + pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, + JS_PROP_WRITABLE | JS_PROP_VARREF); + if (!pr) { + free_var_ref(ctx->rt, var_ref); + goto exception; + } + pr->u.var_ref = var_ref; + *sp++ = JS_AtomToValue(ctx, atom); + } + BREAK; + CASE(OP_make_var_ref): + { + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + if (JS_GetGlobalVarRef(ctx, atom, sp)) + goto exception; + sp += 2; + } + BREAK; + + CASE(OP_goto): + pc += (int32_t)get_u32(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; +#if SHORT_OPCODES + CASE(OP_goto16): + pc += (int16_t)get_u16(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto8): + pc += (int8_t)pc[0]; + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; +#endif + CASE(OP_if_true): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; +#if SHORT_OPCODES + CASE(OP_if_true8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; +#endif + CASE(OP_catch): + { + int32_t diff; + diff = get_u32(pc); + sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); + sp++; + pc += 4; + } + BREAK; + CASE(OP_gosub): + { + int32_t diff; + diff = get_u32(pc); + /* XXX: should have a different tag to avoid security flaw */ + sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf); + sp++; + pc += diff; + } + BREAK; + CASE(OP_ret): + { + JSValue op1; + uint32_t pos; + op1 = sp[-1]; + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) + goto ret_fail; + pos = JS_VALUE_GET_INT(op1); + if (unlikely(pos >= b->byte_code_len)) { + ret_fail: + JS_ThrowInternalError(ctx, "invalid ret value"); + goto exception; + } + sp--; + pc = b->byte_code_buf + pos; + } + BREAK; + + CASE(OP_for_in_start): + if (js_for_in_start(ctx, sp)) + goto exception; + BREAK; + CASE(OP_for_in_next): + if (js_for_in_next(ctx, sp)) + goto exception; + sp += 2; + BREAK; + CASE(OP_for_of_start): + if (js_for_of_start(ctx, sp, FALSE)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_for_of_next): + { + int offset = -3 - pc[0]; + pc += 1; + if (js_for_of_next(ctx, sp, offset)) + goto exception; + sp += 2; + } + BREAK; + CASE(OP_for_await_of_start): + if (js_for_of_start(ctx, sp, TRUE)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_for_await_of_next): + if (js_for_await_of_next(ctx, sp)) + goto exception; + sp += 1; + BREAK; + CASE(OP_iterator_get_value_done): + if (js_iterator_get_value_done(ctx, sp)) + goto exception; + sp += 1; + BREAK; + + CASE(OP_iterator_close): + sp--; /* drop the catch offset to avoid getting caught by exception */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + if (!JS_IsUndefined(sp[-1])) { + if (JS_IteratorClose(ctx, sp[-1], FALSE)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + } + sp--; + BREAK; + CASE(OP_iterator_close_return): + { + JSValue ret_val; + /* iter_obj next catch_offset ... ret_val -> + ret_eval iter_obj next catch_offset */ + ret_val = *--sp; + while (sp > stack_buf && + JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { + JS_FreeValue(ctx, *--sp); + } + if (unlikely(sp < stack_buf + 3)) { + JS_ThrowInternalError(ctx, "iterator_close_return"); + JS_FreeValue(ctx, ret_val); + goto exception; + } + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = ret_val; + sp++; + } + BREAK; + + CASE(OP_async_iterator_close): + /* iter_obj next catch_offset -> value flag */ + { + JSValue ret, method; + int ret_flag; + method = JS_GetProperty(ctx, sp[-3], JS_ATOM_return); + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + ret = JS_UNDEFINED; + ret_flag = TRUE; + } else { + ret = JS_CallFree(ctx, method, sp[-3], 0, NULL); + if (JS_IsException(ret)) + goto exception; + ret_flag = FALSE; + } + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp[-3] = ret; + sp[-2] = JS_NewBool(ctx, ret_flag); + sp -= 1; + } + BREAK; + + CASE(OP_async_iterator_next): + /* stack: iter_obj next catch_offset val */ + { + JSValue ret; + ret = JS_Call(ctx, sp[-3], sp[-4], + 1, (JSValueConst *)(sp - 1)); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + } + BREAK; + + CASE(OP_async_iterator_get): + /* stack: iter_obj next catch_offset val */ + { + JSValue method, ret; + BOOL ret_flag; + int flags; + flags = *pc++; + if (flags == 2) { + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + goto exception; + } + method = JS_GetProperty(ctx, sp[-4], flags ? JS_ATOM_throw : JS_ATOM_return); + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + ret_flag = TRUE; + } else { + ret = JS_CallFree(ctx, method, sp[-4], + 1, (JSValueConst *)(sp - 1)); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + ret_flag = FALSE; + } + sp[0] = JS_NewBool(ctx, ret_flag); + sp += 1; + } + BREAK; + + CASE(OP_lnot): + { + int res; + JSValue op1; + + op1 = sp[-1]; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1) != 0; + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp[-1] = JS_NewBool(ctx, !res); + } + BREAK; + + CASE(OP_get_field): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + val = JS_GetProperty(ctx, sp[-1], atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_get_field2): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + val = JS_GetProperty(ctx, sp[-1], atom); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_private_symbol): + { + JSAtom atom; + JSValue val; + + atom = get_u32(pc); + pc += 4; + val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(val)) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_get_private_field): + { + JSValue val; + + val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_put_private_field): + { + int ret; + ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_private_field): + { + int ret; + ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_set_name): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_name_computed): + { + int ret; + ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_proto): + { + JSValue proto; + proto = sp[-1]; + if (JS_IsObject(proto) || JS_IsNull(proto)) { + if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) + goto exception; + } + JS_FreeValue(ctx, proto); + sp--; + } + BREAK; + CASE(OP_set_home_object): + js_method_set_home_object(ctx, sp[-1], sp[-2]); + BREAK; + CASE(OP_define_method): + CASE(OP_define_method_computed): + { + JSValue getter, setter, value; + JSValueConst obj; + JSAtom atom; + int flags, ret, op_flags; + BOOL is_computed; +#define OP_DEFINE_METHOD_METHOD 0 +#define OP_DEFINE_METHOD_GETTER 1 +#define OP_DEFINE_METHOD_SETTER 2 +#define OP_DEFINE_METHOD_ENUMERABLE 4 + + is_computed = (opcode == OP_define_method_computed); + if (is_computed) { + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + opcode += OP_define_method - OP_define_method_computed; + } else { + atom = get_u32(pc); + pc += 4; + } + op_flags = *pc++; + + obj = sp[-2 - is_computed]; + flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | + JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; + if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) + flags |= JS_PROP_ENUMERABLE; + op_flags &= 3; + value = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (op_flags == OP_DEFINE_METHOD_METHOD) { + value = sp[-1]; + flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; + } else if (op_flags == OP_DEFINE_METHOD_GETTER) { + getter = sp[-1]; + flags |= JS_PROP_HAS_GET; + } else { + setter = sp[-1]; + flags |= JS_PROP_HAS_SET; + } + ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); + if (ret >= 0) { + ret = JS_DefineProperty(ctx, obj, atom, value, + getter, setter, flags); + } + JS_FreeValue(ctx, sp[-1]); + if (is_computed) { + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-2]); + } + sp -= 1 + is_computed; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_class): + CASE(OP_define_class_computed): + { + int class_flags; + JSAtom atom; + + atom = get_u32(pc); + class_flags = pc[4]; + pc += 5; + if (js_op_define_class(ctx, sp, atom, class_flags, + var_refs, sf, + (opcode == OP_define_class_computed)) < 0) + goto exception; + } + BREAK; + + CASE(OP_get_array_el): + { + JSValue val; + + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_array_el2): + { + JSValue val; + + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + sp[-1] = val; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_ref_value): + { + JSValue val; + if (unlikely(JS_IsUndefined(sp[-2]))) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } + val = JS_GetPropertyValue(ctx, sp[-2], + JS_DupValue(ctx, sp[-1])); + if (unlikely(JS_IsException(val))) + goto exception; + sp[0] = val; + sp++; + } + BREAK; + + CASE(OP_get_super_value): + { + JSValue val; + JSAtom atom; + atom = JS_ValueToAtom(ctx, sp[-1]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE); + JS_FreeAtom(ctx, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-3]); + sp[-3] = val; + sp -= 2; + } + BREAK; + + CASE(OP_put_array_el): + { + int ret; + + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_ref_value): + { + int ret; + if (unlikely(JS_IsUndefined(sp[-3]))) { + if (is_strict_mode(ctx)) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } else { + sp[-3] = JS_DupValue(ctx, ctx->global_obj); + } + } + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_super_value): + { + int ret; + JSAtom atom; + if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto exception; + } + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(sp[-3]), + atom, sp[-1], sp[-4], + JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-4]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + sp -= 4; + if (ret < 0) + goto exception; + } + BREAK; + + CASE(OP_define_array_el): + { + int ret; + ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp -= 1; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_append): /* array pos enumobj -- array pos */ + { + if (js_append_enumerate(ctx, sp)) + goto exception; + JS_FreeValue(ctx, *--sp); + } + BREAK; + + CASE(OP_copy_data_properties): /* target source excludeList */ + { + /* stack offsets (-1 based): + 2 bits for target, + 3 bits for source, + 2 bits for exclusionList */ + int mask; + + mask = *pc++; + if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], + sp[-1 - ((mask >> 2) & 7)], + sp[-1 - ((mask >> 5) & 7)], 0)) + goto exception; + } + BREAK; + + CASE(OP_add): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto add_slow; + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + add_slow: + if (js_add_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_add_loc): + { + JSValue ops[2]; + int idx; + idx = *pc; + pc += 1; + + ops[0] = var_buf[idx]; + ops[1] = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(ops[0], ops[1]))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(ops[0]) + JS_VALUE_GET_INT(ops[1]); + if (unlikely((int)r != r)) + goto add_loc_slow; + var_buf[idx] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_GET_TAG(ops[0]) == JS_TAG_STRING) { + sp--; + ops[1] = JS_ToPrimitiveFree(ctx, ops[1], HINT_NONE); + if (JS_IsException(ops[1])) { + goto exception; + } + /* XXX: should not modify the variable in case of + exception */ + ops[0] = JS_ConcatString(ctx, ops[0], ops[1]); + if (JS_IsException(ops[0])) { + var_buf[idx] = JS_UNDEFINED; + goto exception; + } + var_buf[idx] = ops[0]; + } else { + add_loc_slow: + /* XXX: should not modify the variable in case of + exception */ + sp--; + /* In case of exception, js_add_slow frees ops[0] + and ops[1]. */ + /* XXX: change API */ + if (js_add_slow(ctx, ops + 2)) { + var_buf[idx] = JS_UNDEFINED; + goto exception; + } + var_buf[idx] = ops[0]; + } + } + BREAK; + CASE(OP_sub): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto binary_arith_slow; + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mul): + { + JSValue op1, op2; + double d; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int32_t v1, v2; + int64_t r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + r = (int64_t)v1 * v2; +#ifdef CONFIG_BIGNUM + if (unlikely((int)r != r) || (r == 0 && !is_bigint_mode(ctx))) + goto binary_arith_slow; +#else + if (unlikely((int)r != r)) { + 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; + } +#endif + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); +#ifndef CONFIG_BIGNUM + mul_fp_res: +#endif + sp[-2] = __JS_NewFloat64(ctx, d); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; +#ifdef CONFIG_BIGNUM + CASE(OP_math_div): + CASE(OP_div): + goto binary_arith_slow; +#else + CASE(OP_div): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + 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; +#endif + 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): +#ifdef CONFIG_BIGNUM + CASE(OP_math_pow): +#endif + 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); +#ifdef CONFIG_BIGNUM + if (unlikely(val == INT32_MIN) || + (val == 0 && !is_bigint_mode(ctx))) + goto slow_neg; +#else + /* 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; + } +#endif + sp[-1] = JS_NewInt32(ctx, -val); + } else if (JS_TAG_IS_FLOAT64(tag)) { + d = -JS_VALUE_GET_FLOAT64(op1); +#ifndef CONFIG_BIGNUM + neg_fp_res: +#endif + sp[-1] = __JS_NewFloat64(ctx, d); + } else { +#ifdef CONFIG_BIGNUM + slow_neg: +#endif + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_inc): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_slow; + sp[-1] = JS_NewInt32(ctx, val + 1); + } else { + inc_slow: + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_dec): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_slow; + sp[-1] = JS_NewInt32(ctx, val - 1); + } else { + dec_slow: + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_post_inc): + CASE(OP_post_dec): + if (js_post_inc_slow(ctx, sp, opcode)) + goto exception; + sp++; + BREAK; + CASE(OP_inc_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_loc_slow; + var_buf[idx] = JS_NewInt32(ctx, val + 1); + } else { + inc_loc_slow: + if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_inc)) + goto exception; + } + } + BREAK; + CASE(OP_dec_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_loc_slow; + var_buf[idx] = JS_NewInt32(ctx, val - 1); + } else { + dec_loc_slow: + if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_dec)) + goto exception; + } + } + BREAK; + CASE(OP_not): + { + JSValue op1; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); + } else { + if (js_not_slow(ctx, sp)) + goto exception; + } + } + BREAK; + + CASE(OP_shl): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); +#ifdef CONFIG_BIGNUM + { + int64_t r; + if (is_bigint_mode(ctx)) { + 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 (is_bigint_mode(ctx)) + 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 (ctx->rt->bigfloat_ops.mul_pow10(ctx, sp)) + goto exception; + sp--; + BREAK; +#endif + CASE(OP_in): + if (js_operator_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_instanceof): + if (js_operator_instanceof(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_typeof): + { + JSValue op1; + JSAtom atom; + + op1 = sp[-1]; + atom = js_operator_typeof(ctx, op1); + JS_FreeValue(ctx, op1); + sp[-1] = JS_AtomToString(ctx, atom); + } + BREAK; + CASE(OP_delete): + if (js_operator_delete(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_delete_var): + { + JSAtom atom; + int ret; + + atom = get_u32(pc); + pc += 4; + + ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + *sp++ = JS_NewBool(ctx, ret); + } + BREAK; + + CASE(OP_to_object): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { + ret_val = JS_ToObject(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_to_propkey): + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + + CASE(OP_to_propkey2): + /* must be tested first */ + if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { + JS_ThrowTypeError(ctx, "value has no property"); + goto exception; + } + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; +#if 0 + CASE(OP_to_string): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { + ret_val = JS_ToString(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; +#endif + CASE(OP_with_get_var): + CASE(OP_with_put_var): + CASE(OP_with_delete_var): + CASE(OP_with_make_ref): + CASE(OP_with_get_ref): + CASE(OP_with_get_ref_undef): + { + JSAtom atom; + int32_t diff; + JSValue obj, val; + int ret, is_with; + atom = get_u32(pc); + diff = get_u32(pc + 4); + is_with = pc[8]; + pc += 9; + + obj = sp[-1]; + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) { + if (is_with) { + ret = js_has_unscopable(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) + goto no_with; + } + switch (opcode) { + case OP_with_get_var: + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + set_value(ctx, &sp[-1], val); + break; + case OP_with_put_var: + ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + break; + case OP_with_delete_var: + ret = JS_DeleteProperty(ctx, obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_NewBool(ctx, ret); + break; + case OP_with_make_ref: + /* produce a pair object/propname on the stack */ + *sp++ = JS_AtomToValue(ctx, atom); + break; + case OP_with_get_ref: + /* produce a pair object/method on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + break; + case OP_with_get_ref_undef: + /* produce a pair undefined/function on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_UNDEFINED; + *sp++ = val; + break; + } + pc += diff - 5; + } else { + no_with: + /* if not jumping, drop the object argument */ + JS_FreeValue(ctx, sp[-1]); + sp--; + } + } + BREAK; + + CASE(OP_await): + ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT); + goto done_generator; + CASE(OP_yield): + ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); + goto done_generator; + CASE(OP_yield_star): + CASE(OP_async_yield_star): + ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); + goto done_generator; + CASE(OP_return_async): + CASE(OP_initial_yield): + ret_val = JS_UNDEFINED; + goto done_generator; + + CASE(OP_nop): + BREAK; + CASE(OP_is_undefined_or_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } +#if SHORT_OPCODES + CASE(OP_is_undefined): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_function): + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + free_and_set_true: + JS_FreeValue(ctx, sp[-1]); +#endif + set_true: + sp[-1] = JS_TRUE; + BREAK; + free_and_set_false: + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_FALSE; + BREAK; + CASE(OP_invalid): + DEFAULT: + JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", + (int)(pc - b->byte_code_buf - 1), opcode); + goto exception; + } + } + exception: + if (is_backtrace_needed(ctx, ctx->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, ctx->current_exception, NULL, 0, 0); + } + if (!JS_IsUncatchableError(ctx, ctx->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++ = ctx->current_exception; + ctx->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(ctx->rt, sf); + } + /* free the local variables and stack */ + for(pval = local_buf; pval < sp; pval++) { + JS_FreeValue(ctx, *pval); + } + } + ctx->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; +} + +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; +} + +static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, + int class_id) +{ + JSValue proto, obj; + 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); + /* check if revoked proxy */ + { + JSProxyData *s = JS_GetOpaque(ctor, JS_CLASS_PROXY); + if (s && s->is_revoked) + return JS_ThrowTypeErrorRevokedProxy(ctx); + } + /* XXX: should use the ctor realm instead of 'ctx' */ + proto = JS_DupValue(ctx, ctx->class_proto[class_id]); + } + } + obj = JS_NewObjectProtoClass(ctx, proto, class_id); + JS_FreeValue(ctx, proto); + return obj; +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValue *argv, int flags) +{ + JSObject *p; + JSFunctionBytecode *b; + + if (js_poll_interrupts(ctx)) + return JS_EXCEPTION; + flags |= JS_CALL_FLAG_CONSTRUCTOR; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) + goto not_a_function; + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(!p->is_constructor)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = ctx->rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(ctx, "not a function"); + } + return call_func(ctx, func_obj, new_target, argc, + (JSValueConst *)argv, flags); + } + + b = p->u.func.function_bytecode; + if (b->is_derived_class_constructor) { + return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); + } else { + JSValue obj, ret; + /* legacy constructor behavior */ + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); + if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || + JS_IsException(ret)) { + JS_FreeValue(ctx, obj); + return ret; + } else { + JS_FreeValue(ctx, ret); + return obj; + } + } +} + +JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, new_target, + argc, (JSValue *)argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, + int argc, JSValueConst *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, func_obj, + argc, (JSValue *)argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, + int argc, JSValueConst *argv) +{ + JSValue func_obj; + func_obj = JS_GetProperty(ctx, this_val, atom); + if (JS_IsException(func_obj)) + return func_obj; + return JS_CallFree(ctx, func_obj, this_val, argc, argv); +} + +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValueConst *argv) +{ + JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); + JS_FreeValue(ctx, this_val); + return res; +} + +/* JSAsyncFunctionState (used by generator and async functions) */ +static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, + JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame *sf; + int local_count, i, arg_buf_len, n; + + sf = &s->frame; + init_list_head(&sf->var_ref_list); + p = JS_VALUE_GET_OBJ(func_obj); + b = p->u.func.function_bytecode; + sf->js_mode = b->js_mode; + sf->cur_pc = b->byte_code_buf; + arg_buf_len = max_int(b->arg_count, argc); + local_count = arg_buf_len + b->var_count + b->stack_size; + sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); + if (!sf->arg_buf) + return -1; + sf->cur_func = JS_DupValue(ctx, func_obj); + s->this_val = JS_DupValue(ctx, this_obj); + s->argc = argc; + sf->arg_count = arg_buf_len; + sf->var_buf = sf->arg_buf + arg_buf_len; + sf->cur_sp = sf->var_buf + b->var_count; + for(i = 0; i < argc; i++) + sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); + n = arg_buf_len + b->var_count; + for(i = argc; i < n; i++) + sf->arg_buf[i] = JS_UNDEFINED; + return 0; +} + +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + if (sf->cur_sp) { + /* if the function is running, cur_sp is not known so we + cannot mark the stack. Marking the variables is not needed + because a running function cannot be part of a removable + cycle */ + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) + JS_MarkValue(rt, *sp, mark_func); + } +} + +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + + /* close the closure variables. */ + close_var_refs(rt, sf); + + if (sf->arg_buf) { + /* cannot free the function if it is running */ + assert(sf->cur_sp != NULL); + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { + JS_FreeValueRT(rt, *sp); + } + js_free_rt(rt, sf->arg_buf); + } + JS_FreeValueRT(rt, sf->cur_func); + JS_FreeValueRT(rt, s->this_val); +} + +static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) +{ + JSValue func_obj; + + /* the tag does not matter provided it is not an object */ + func_obj = JS_MKPTR(JS_TAG_INT, s); + return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, + s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR); +} + + +/* Generators */ + +typedef enum JSGeneratorStateEnum { + JS_GENERATOR_STATE_SUSPENDED_START, + JS_GENERATOR_STATE_SUSPENDED_YIELD, + JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_GENERATOR_STATE_EXECUTING, + JS_GENERATOR_STATE_COMPLETED, +} JSGeneratorStateEnum; + +typedef struct JSGeneratorData { + JSGeneratorStateEnum state; + JSAsyncFunctionState func_state; +} JSGeneratorData; + +static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) +{ + if (s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_free(rt, &s->func_state); + s->state = JS_GENERATOR_STATE_COMPLETED; +} + +static void js_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); + + if (s) { + free_generator_stack_rt(rt, s); + js_free_rt(rt, s); + } +} + +static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) +{ + free_generator_stack_rt(ctx->rt, s); +} + +static void js_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSGeneratorData *s = p->u.generator_data; + + if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_mark(rt, &s->func_state, mark_func); +} + +/* XXX: use enum */ +#define GEN_MAGIC_NEXT 0 +#define GEN_MAGIC_RETURN 1 +#define GEN_MAGIC_THROW 2 + +static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); + JSStackFrame *sf; + JSValue ret, func_ret; + JSValueConst iter_args[1]; + + *pdone = TRUE; + if (!s) + return JS_ThrowTypeError(ctx, "not a generator"); + sf = &s->func_state.frame; + redo: + switch(s->state) { + default: + case JS_GENERATOR_STATE_SUSPENDED_START: + if (magic == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + free_generator_stack(ctx, s); + goto done; + } + break; + case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + { + int done; + JSValue method, iter_obj; + + iter_obj = sf->cur_sp[-2]; + if (magic == GEN_MAGIC_NEXT) { + method = JS_DupValue(ctx, sf->cur_sp[-1]); + } else { + method = JS_GetProperty(ctx, iter_obj, + magic == GEN_MAGIC_RETURN ? + JS_ATOM_return : JS_ATOM_throw); + if (JS_IsException(method)) + goto iter_exception; + } + if (magic != GEN_MAGIC_NEXT && + (JS_IsUndefined(method) || JS_IsNull(method))) { + /* default action */ + if (magic == GEN_MAGIC_RETURN) { + ret = JS_DupValue(ctx, argv[0]); + goto iter_done; + } else { + if (JS_IteratorClose(ctx, iter_obj, FALSE)) + goto iter_exception; + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + goto iter_exception; + } + } + ret = JS_IteratorNext2(ctx, iter_obj, method, argc, argv, &done); + JS_FreeValue(ctx, method); + if (JS_IsException(ret)) { + iter_exception: + goto exec_throw; + } + /* if not done, the iterator returns the exact object + returned by 'method' */ + if (done == 2) { + JSValue done_val, value; + done_val = JS_GetProperty(ctx, ret, JS_ATOM_done); + if (JS_IsException(done_val)) { + JS_FreeValue(ctx, ret); + goto iter_exception; + } + done = JS_ToBoolFree(ctx, done_val); + if (done) { + value = JS_GetProperty(ctx, ret, JS_ATOM_value); + JS_FreeValue(ctx, ret); + if (JS_IsException(value)) + goto iter_exception; + ret = value; + goto iter_done; + } else { + *pdone = 2; + } + } else { + if (done) { + /* 'yield *' returns the value associated to done = true */ + iter_done: + JS_FreeValue(ctx, sf->cur_sp[-2]); + JS_FreeValue(ctx, sf->cur_sp[-1]); + sf->cur_sp--; + goto exec_arg; + } else { + *pdone = FALSE; + } + } + break; + } + break; + case JS_GENERATOR_STATE_SUSPENDED_YIELD: + /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ + ret = JS_DupValue(ctx, argv[0]); + if (magic == GEN_MAGIC_THROW) { + JS_Throw(ctx, ret); + exec_throw: + s->func_state.throw_flag = TRUE; + } else { + exec_arg: + sf->cur_sp[-1] = ret; + sf->cur_sp[0] = JS_NewBool(ctx, (magic == GEN_MAGIC_RETURN)); + sf->cur_sp++; + exec_no_arg: + s->func_state.throw_flag = FALSE; + } + s->state = JS_GENERATOR_STATE_EXECUTING; + func_ret = async_func_resume(ctx, &s->func_state); + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; + if (JS_IsException(func_ret)) { + /* finalize the execution in case of exception */ + free_generator_stack(ctx, s); + return func_ret; + } + if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { + /* 'yield *' */ + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + iter_args[0] = JS_UNDEFINED; + argc = 1; + argv = iter_args; + goto redo; + } else { + /* get the return the yield value at the top of the stack */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + *pdone = FALSE; + } + } else { + /* end of iterator */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + JS_FreeValue(ctx, func_ret); + free_generator_stack(ctx, s); + } + break; + case JS_GENERATOR_STATE_COMPLETED: + done: + /* execution is finished */ + switch(magic) { + default: + case GEN_MAGIC_NEXT: + ret = JS_UNDEFINED; + break; + case GEN_MAGIC_RETURN: + ret = JS_DupValue(ctx, argv[0]); + break; + case GEN_MAGIC_THROW: + ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0])); + break; + } + break; + case JS_GENERATOR_STATE_EXECUTING: + ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); + break; + } + return ret; +} + +static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_GENERATOR_STATE_SUSPENDED_START; + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); + if (JS_IsException(obj)) + goto fail; + JS_SetOpaque(obj, s); + return obj; + fail: + free_generator_stack_rt(ctx->rt, s); + js_free(ctx, s); + return JS_EXCEPTION; +} + +/* AsyncFunction */ + +static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (s->is_active) { + async_func_free(rt, &s->func_state); + s->is_active = FALSE; + } +} + +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) +{ + js_async_function_terminate(rt, s); + JS_FreeValueRT(rt, s->resolving_funcs[0]); + JS_FreeValueRT(rt, s->resolving_funcs[1]); + remove_gc_object(&s->header); + js_free_rt(rt, s); +} + +static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (--s->header.ref_count == 0) { + js_async_function_free0(rt, s); + } +} + +static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + js_async_function_free(rt, s); + } +} + +static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + mark_func(rt, &s->header); + } +} + +static int js_async_function_resolve_create(JSContext *ctx, + JSAsyncFunctionData *s, + JSValue *resolving_funcs) +{ + int i; + JSObject *p; + + for(i = 0; i < 2; i++) { + resolving_funcs[i] = + JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); + if (JS_IsException(resolving_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + p = JS_VALUE_GET_OBJ(resolving_funcs[i]); + s->header.ref_count++; + p->u.async_function_data = s; + } + return 0; +} + +static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +{ + JSValue func_ret, ret2; + + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + JSValue error; + fail: + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + js_async_function_terminate(ctx->rt, s); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + } else { + JSValue value; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + if (JS_IsUndefined(func_ret)) { + /* function returned */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, value); + js_async_function_terminate(ctx->rt, s); + } else { + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + JS_FreeValue(ctx, value); + if (JS_IsException(promise)) + goto fail; + if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + (JSValueConst *)resolving_funcs, + (JSValueConst *)resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + } + } +} + +static JSValue js_async_function_resolve_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSAsyncFunctionData *s = p->u.async_function_data; + BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; + JSValueConst arg; + + if (argc > 0) + arg = argv[0]; + else + arg = JS_UNDEFINED; + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, JS_DupValue(ctx, arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg); + } + js_async_function_resume(ctx, s); + return JS_UNDEFINED; +} + +static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSValue promise; + JSAsyncFunctionData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + s->is_active = FALSE; + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + + promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); + if (JS_IsException(promise)) + goto fail; + + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + fail: + JS_FreeValue(ctx, promise); + js_async_function_free(ctx->rt, s); + return JS_EXCEPTION; + } + s->is_active = TRUE; + + js_async_function_resume(ctx, s); + + js_async_function_free(ctx->rt, s); + + return promise; +} + +/* AsyncGenerator */ + +typedef enum JSAsyncGeneratorStateEnum { + JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_ASYNC_GENERATOR_STATE_EXECUTING, + JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, + JS_ASYNC_GENERATOR_STATE_COMPLETED, +} JSAsyncGeneratorStateEnum; + +typedef struct JSAsyncGeneratorRequest { + struct list_head link; + /* completion */ + int completion_type; /* GEN_MAGIC_x */ + JSValue result; + /* promise capability */ + JSValue promise; + JSValue resolving_funcs[2]; +} JSAsyncGeneratorRequest; + +typedef struct JSAsyncGeneratorData { + JSObject *generator; /* back pointer to the object (const) */ + JSAsyncGeneratorStateEnum state; + JSAsyncFunctionState func_state; + struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ +} JSAsyncGeneratorData; + +static void js_async_generator_free(JSRuntime *rt, + JSAsyncGeneratorData *s) +{ + struct list_head *el, *el1; + JSAsyncGeneratorRequest *req; + + list_for_each_safe(el, el1, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_FreeValueRT(rt, req->result); + JS_FreeValueRT(rt, req->promise); + JS_FreeValueRT(rt, req->resolving_funcs[0]); + JS_FreeValueRT(rt, req->resolving_funcs[1]); + js_free_rt(rt, req); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_free(rt, &s->func_state); + } + js_free_rt(rt, s); +} + +static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); + + if (s) { + js_async_generator_free(rt, s); + } +} + +static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); + struct list_head *el; + JSAsyncGeneratorRequest *req; + if (s) { + list_for_each(el, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_MarkValue(rt, req->result, mark_func); + JS_MarkValue(rt, req->promise, mark_func); + JS_MarkValue(rt, req->resolving_funcs[0], mark_func); + JS_MarkValue(rt, req->resolving_funcs[1], mark_func); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_mark(rt, &s->func_state, mark_func); + } + } +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValue *func_data); + +static int js_async_generator_resolve_function_create(JSContext *ctx, + JSValueConst generator, + JSValue *resolving_funcs, + BOOL is_resume_next) +{ + int i; + JSValue func; + + for(i = 0; i < 2; i++) { + func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, + i + is_resume_next * 2, 1, &generator); + if (JS_IsException(func)) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + resolving_funcs[i] = func; + } + return 0; +} + +static int js_async_generator_await(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, &value, 0); + if (JS_IsException(promise)) + goto fail; + + if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs, FALSE)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + (JSValueConst *)resolving_funcs, + (JSValueConst *)resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + return 0; + fail: + return -1; +} + +static void js_async_generator_resolve_or_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst result, + int is_reject) +{ + JSAsyncGeneratorRequest *next; + JSValue ret; + + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + list_del(&next->link); + ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, + &result); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, next->result); + JS_FreeValue(ctx, next->promise); + JS_FreeValue(ctx, next->resolving_funcs[0]); + JS_FreeValue(ctx, next->resolving_funcs[1]); + js_free(ctx, next); +} + +static void js_async_generator_resolve(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value, + BOOL done) +{ + JSValue result; + result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done); + /* XXX: better exception handling ? */ + js_async_generator_resolve_or_reject(ctx, s, result, 0); + JS_FreeValue(ctx, result); + } + +static void js_async_generator_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst exception) +{ + js_async_generator_resolve_or_reject(ctx, s, exception, 1); +} + +static void js_async_generator_complete(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + async_func_free(ctx->rt, &s->func_state); + } +} + +static int js_async_generator_completed_return(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int res; + + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + if (JS_IsException(promise)) + return -1; + if (js_async_generator_resolve_function_create(ctx, + JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs1, + TRUE)) { + JS_FreeValue(ctx, promise); + return -1; + } + resolving_funcs[0] = JS_UNDEFINED; + resolving_funcs[1] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + (JSValueConst *)resolving_funcs1, + (JSValueConst *)resolving_funcs); + JS_FreeValue(ctx, resolving_funcs1[0]); + JS_FreeValue(ctx, resolving_funcs1[1]); + JS_FreeValue(ctx, promise); + return res; +} + +static void js_async_generator_resume_next(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + JSAsyncGeneratorRequest *next; + JSValue func_ret, value; + + for(;;) { + if (list_empty(&s->queue)) + break; + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + switch(s->state) { + case JS_ASYNC_GENERATOR_STATE_EXECUTING: + /* only happens when restarting execution after await() */ + goto resume_exec; + case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: + if (next->completion_type == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + js_async_generator_complete(ctx, s); + } + break; + case JS_ASYNC_GENERATOR_STATE_COMPLETED: + if (next->completion_type == GEN_MAGIC_NEXT) { + js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE); + } else if (next->completion_type == GEN_MAGIC_RETURN) { + s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; + js_async_generator_completed_return(ctx, s, next->result); + goto done; + } else { + js_async_generator_reject(ctx, s, next->result); + } + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + value = JS_DupValue(ctx, next->result); + if (next->completion_type == GEN_MAGIC_THROW && + s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, value); + s->func_state.throw_flag = TRUE; + } else { + /* 'yield' returns a value. 'yield *' also returns a value + in case the 'throw' method is called */ + s->func_state.frame.cur_sp[-1] = value; + s->func_state.frame.cur_sp[0] = + JS_NewInt32(ctx, next->completion_type); + s->func_state.frame.cur_sp++; + exec_no_arg: + s->func_state.throw_flag = FALSE; + } + s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; + resume_exec: + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + value = JS_GetException(ctx); + js_async_generator_complete(ctx, s); + js_async_generator_reject(ctx, s, value); + JS_FreeValue(ctx, value); + } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + int func_ret_code; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + func_ret_code = JS_VALUE_GET_INT(func_ret); + switch(func_ret_code) { + case FUNC_RET_YIELD: + case FUNC_RET_YIELD_STAR: + if (func_ret_code == FUNC_RET_YIELD_STAR) + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + else + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; + js_async_generator_resolve(ctx, s, value, FALSE); + JS_FreeValue(ctx, value); + break; + case FUNC_RET_AWAIT: + js_async_generator_await(ctx, s, value); + JS_FreeValue(ctx, value); + goto done; + default: + abort(); + } + } else { + assert(JS_IsUndefined(func_ret)); + /* end of function */ + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + js_async_generator_complete(ctx, s); + js_async_generator_resolve(ctx, s, value, TRUE); + JS_FreeValue(ctx, value); + } + break; + default: + abort(); + } + } + done: ; +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + BOOL is_reject = magic & 1; + JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); + JSValueConst arg = argv[0]; + + /* XXX: what if s == NULL */ + + if (magic >= 2) { + /* resume next case in AWAITING_RETURN state */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || + s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + if (is_reject) { + js_async_generator_reject(ctx, s, arg); + } else { + js_async_generator_resolve(ctx, s, arg, TRUE); + } + } else { + /* restart function execution after await() */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, JS_DupValue(ctx, arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg); + } + js_async_generator_resume_next(ctx, s); + } + return JS_UNDEFINED; +} + +/* magic = GEN_MAGIC_x */ +static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); + JSValue promise, resolving_funcs[2]; + JSAsyncGeneratorRequest *req; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + if (!s) { + JSValue err, res2; + JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); + err = JS_GetException(ctx); + res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + req = js_mallocz(ctx, sizeof(*req)); + if (!req) + goto fail; + req->completion_type = magic; + req->result = JS_DupValue(ctx, argv[0]); + req->promise = JS_DupValue(ctx, promise); + req->resolving_funcs[0] = resolving_funcs[0]; + req->resolving_funcs[1] = resolving_funcs[1]; + list_add_tail(&req->link, &s->queue); + if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { + js_async_generator_resume_next(ctx, s); + } + return promise; + fail: + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSAsyncGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; + init_list_head(&s->queue); + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' (no yield nor + await are possible) */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); + if (JS_IsException(obj)) + goto fail; + s->generator = JS_VALUE_GET_OBJ(obj); + JS_SetOpaque(obj, s); + return obj; + fail: + js_async_generator_free(ctx->rt, s); + return JS_EXCEPTION; +} + +/* JS parser */ + +enum { + TOK_NUMBER = -128, + TOK_STRING, + TOK_TEMPLATE, + TOK_IDENT, + TOK_REGEXP, + /* warning: order matters (see js_parse_assign_expr) */ + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SAR_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, +#ifdef CONFIG_BIGNUM + TOK_MATH_POW_ASSIGN, +#endif + TOK_POW_ASSIGN, + TOK_DEC, + TOK_INC, + TOK_SHL, + TOK_SAR, + TOK_SHR, + TOK_LT, + TOK_LTE, + TOK_GT, + TOK_GTE, + TOK_EQ, + TOK_STRICT_EQ, + TOK_NEQ, + TOK_STRICT_NEQ, + TOK_LAND, + TOK_LOR, +#ifdef CONFIG_BIGNUM + TOK_MATH_POW, +#endif + TOK_POW, + TOK_ARROW, + TOK_ELLIPSIS, + TOK_DOUBLE_QUESTION_MARK, + TOK_QUESTION_MARK_DOT, + TOK_ERROR, + TOK_PRIVATE_NAME, + TOK_EOF, + /* keywords: WARNING: same order as atoms */ + TOK_NULL, /* must be first */ + TOK_FALSE, + TOK_TRUE, + TOK_IF, + TOK_ELSE, + TOK_RETURN, + TOK_VAR, + TOK_THIS, + TOK_DELETE, + TOK_VOID, + TOK_TYPEOF, + TOK_NEW, + TOK_IN, + TOK_INSTANCEOF, + TOK_DO, + TOK_WHILE, + TOK_FOR, + TOK_BREAK, + TOK_CONTINUE, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_THROW, + TOK_TRY, + TOK_CATCH, + TOK_FINALLY, + TOK_FUNCTION, + TOK_DEBUGGER, + TOK_WITH, + /* FutureReservedWord */ + TOK_CLASS, + TOK_CONST, + TOK_ENUM, + TOK_EXPORT, + TOK_EXTENDS, + TOK_IMPORT, + TOK_SUPER, + /* FutureReservedWords when parsing strict mode code */ + TOK_IMPLEMENTS, + TOK_INTERFACE, + TOK_LET, + TOK_PACKAGE, + TOK_PRIVATE, + TOK_PROTECTED, + TOK_PUBLIC, + TOK_STATIC, + TOK_YIELD, + TOK_AWAIT, /* must be last */ + TOK_OF, /* only used for js_parse_skip_parens_token() */ +}; + +#define TOK_FIRST_KEYWORD TOK_NULL +#define TOK_LAST_KEYWORD TOK_AWAIT + +/* unicode code points */ +#define CP_NBSP 0x00a0 +#define CP_BOM 0xfeff + +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +typedef struct BlockEnv { + struct BlockEnv *prev; + JSAtom label_name; /* JS_ATOM_NULL if none */ + int label_break; /* -1 if none */ + int label_cont; /* -1 if none */ + int drop_count; /* number of stack elements to drop */ + int label_finally; /* -1 if none */ + int scope_level; + int has_iterator; +} BlockEnv; + +typedef struct JSHoistedDef { + int cpool_idx; /* -1 means variable global definition */ + uint8_t force_init : 1; /* initialize to undefined */ + uint8_t is_lexical : 1; /* global let/const definition */ + uint8_t is_const : 1; /* const definition */ + int var_idx; /* function object index if cpool_idx >= 0 */ + int scope_level; /* scope of definition */ + JSAtom var_name; /* variable name if cpool_idx < 0 */ +} JSHoistedDef; + +typedef struct RelocEntry { + struct RelocEntry *next; + uint32_t addr; /* address to patch */ + int size; /* address size: 1, 2 or 4 bytes */ +} RelocEntry; + +typedef struct JumpSlot { + int op; + int size; + int pos; + int label; +} JumpSlot; + +typedef struct LabelSlot { + int ref_count; + int pos; /* phase 1 address, -1 means not resolved yet */ + int pos2; /* phase 2 address, -1 means not resolved yet */ + int addr; /* phase 3 address, -1 means not resolved yet */ + RelocEntry *first_reloc; +} LabelSlot; + +typedef struct LineNumberSlot { + uint32_t pc; + int line_num; +} LineNumberSlot; + +typedef enum JSParseFunctionEnum { + JS_PARSE_FUNC_STATEMENT, + JS_PARSE_FUNC_VAR, + JS_PARSE_FUNC_EXPR, + JS_PARSE_FUNC_ARROW, + JS_PARSE_FUNC_GETTER, + JS_PARSE_FUNC_SETTER, + JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_CONSTRUCTOR, + JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, +} JSParseFunctionEnum; + +typedef enum JSParseExportEnum { + JS_PARSE_EXPORT_NONE, + JS_PARSE_EXPORT_NAMED, + JS_PARSE_EXPORT_DEFAULT, +} JSParseExportEnum; + +typedef struct JSFunctionDef { + JSContext *ctx; + struct JSFunctionDef *parent; + int parent_cpool_idx; /* index in the constant pool of the parent + or -1 if none */ + int parent_scope_level; /* scope level in parent at point of definition */ + struct list_head child_list; /* list of JSFunctionDef.link */ + struct list_head link; + + BOOL is_eval; /* TRUE if eval code */ + int eval_type; /* only valid if is_eval = TRUE */ + BOOL is_global_var; /* TRUE if variables are not defined locally: + eval global, eval module or non strict eval */ + BOOL is_func_expr; /* TRUE if function expression */ + BOOL has_home_object; /* TRUE if the home object is available */ + BOOL has_prototype; /* true if a prototype field is necessary */ + BOOL has_simple_parameter_list; + BOOL has_use_strict; /* to reject directive in special cases */ + BOOL has_eval_call; /* true if the function contains a call to eval() */ + BOOL has_arguments_binding; /* true if the 'arguments' binding is + available in the function */ + BOOL has_this_binding; /* true if the 'this' and new.target binding are + available in the function */ + BOOL new_target_allowed; /* true if the 'new.target' does not + throw a syntax error */ + BOOL super_call_allowed; /* true if super() is allowed */ + BOOL super_allowed; /* true if super. or super[] is allowed */ + BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ + BOOL is_derived_class_constructor; + BOOL in_function_body; + BOOL backtrace_barrier; + JSFunctionKindEnum func_kind : 8; + JSParseFunctionEnum func_type : 8; + uint8_t js_mode; /* bitmap of JS_MODE_x */ + JSAtom func_name; /* JS_ATOM_NULL if no name */ + + JSVarDef *vars; + int var_size; /* allocated size for vars[] */ + int var_count; + JSVarDef *args; + int arg_size; /* allocated size for args[] */ + int arg_count; /* number of arguments */ + int defined_arg_count; + int var_object_idx; /* -1 if none */ + int arguments_var_idx; /* -1 if none */ + int func_var_idx; /* variable containing the current function (-1 + if none, only used if is_func_expr is true) */ + int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ + int this_var_idx; /* variable containg the 'this' value, -1 if none */ + int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ + int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ + int home_object_var_idx; + BOOL need_home_object; + + int scope_level; /* index into fd->scopes if the current lexical scope */ + int scope_first; /* index into vd->vars of first lexically scoped variable */ + int scope_size; /* allocated size of fd->scopes array */ + int scope_count; /* number of entries used in the fd->scopes array */ + JSVarScope *scopes; + JSVarScope def_scope_array[4]; + + int hoisted_def_count; + int hoisted_def_size; + JSHoistedDef *hoisted_def; + + DynBuf byte_code; + int last_opcode_pos; /* -1 if no last opcode */ + int last_opcode_line_num; + BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ + + LabelSlot *label_slots; + int label_size; /* allocated size for label_slots[] */ + int label_count; + BlockEnv *top_break; /* break/continue label stack */ + + /* constant pool (strings, functions, numbers) */ + JSValue *cpool; + uint32_t cpool_count; + uint32_t cpool_size; + + /* list of variables in the closure */ + int closure_var_count; + int closure_var_size; + JSClosureVar *closure_var; + + JumpSlot *jump_slots; + int jump_size; + int jump_count; + + LineNumberSlot *line_number_slots; + int line_number_size; + int line_number_count; + int line_number_last; + int line_number_last_pc; + + /* pc2line table */ + JSAtom filename; + int line_num; + DynBuf pc2line; + + char *source; /* raw source, utf-8 encoded */ + int source_len; + + JSModuleDef *module; /* != NULL when parsing a module */ +} JSFunctionDef; + +typedef struct JSToken { + int val; + int line_num; /* line number of token start */ + const uint8_t *ptr; + union { + struct { + JSValue str; + int sep; + } str; + struct { + JSValue val; +#ifdef CONFIG_BIGNUM + slimb_t exponent; /* may be != 0 only if val is a float */ +#endif + } num; + struct { + JSAtom atom; + BOOL has_escape; + BOOL is_reserved; + } ident; + struct { + JSValue body; + JSValue flags; + } regexp; + } u; +} JSToken; + +typedef struct JSParseState { + JSContext *ctx; + int last_line_num; /* line number of last token */ + int line_num; /* line number of current offset */ + const char *filename; + JSToken token; + BOOL got_lf; /* true if got line feed before the current token */ + const uint8_t *last_ptr; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + + /* current function code */ + JSFunctionDef *cur_func; + BOOL is_module; /* parsing a module */ + BOOL allow_html_comments; +} JSParseState; + +typedef struct JSOpCode { +#ifdef DUMP_BYTECODE + const char *name; +#endif + uint8_t size; /* in bytes */ + /* the opcodes remove n_pop items from the top of the stack, then + pushes n_push items */ + uint8_t n_pop; + uint8_t n_push; + uint8_t fmt; +} JSOpCode; + +static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { +#define FMT(f) +#ifdef DUMP_BYTECODE +#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, +#else +#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, +#endif +#include "quickjs-opcode.h" +#undef DEF +#undef FMT +}; + +#if SHORT_OPCODES +/* After the final compilation pass, short opcodes are used. Their + opcodes overlap with the temporary opcodes which cannot appear in + the final bytecode. Their description is after the temporary + opcodes in opcode_info[]. */ +#define short_opcode_info(op) \ + opcode_info[(op) >= OP_TEMP_START ? \ + (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] +#else +#define short_opcode_info(op) opcode_info[op] +#endif + +static __exception int next_token(JSParseState *s); + +static void free_token(JSParseState *s, JSToken *token) +{ + switch(token->val) { +#ifdef CONFIG_BIGNUM + case TOK_NUMBER: + JS_FreeValue(s->ctx, token->u.num.val); + break; +#endif + case TOK_STRING: + case TOK_TEMPLATE: + JS_FreeValue(s->ctx, token->u.str.str); + break; + case TOK_REGEXP: + JS_FreeValue(s->ctx, token->u.regexp.body); + JS_FreeValue(s->ctx, token->u.regexp.flags); + break; + case TOK_IDENT: + case TOK_FIRST_KEYWORD ... TOK_LAST_KEYWORD: + case TOK_PRIVATE_NAME: + JS_FreeAtom(s->ctx, token->u.ident.atom); + break; + default: + break; + } +} + +static void __attribute((unused)) dump_token(JSParseState *s, + const JSToken *token) +{ + switch(token->val) { + case TOK_NUMBER: + { + double d; + JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ + printf("number: %.14g\n", d); + } + break; + case TOK_IDENT: + dump_atom: + { + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("ident: '%s'\n", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); + } + break; + case TOK_STRING: + { + const char *str; + /* XXX: quote the string */ + str = JS_ToCString(s->ctx, token->u.str.str); + printf("string: '%s'\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_TEMPLATE: + { + const char *str; + str = JS_ToCString(s->ctx, token->u.str.str); + printf("template: `%s`\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_REGEXP: + { + const char *str, *str2; + str = JS_ToCString(s->ctx, token->u.regexp.body); + str2 = JS_ToCString(s->ctx, token->u.regexp.flags); + printf("regexp: '%s' '%s'\n", str, str2); + JS_FreeCString(s->ctx, str); + JS_FreeCString(s->ctx, str2); + } + break; + case TOK_EOF: + printf("eof\n"); + break; + default: + if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { + goto dump_atom; + } else if (s->token.val >= 256) { + printf("token: %d\n", token->val); + } else { + printf("token: '%c'\n", token->val); + } + break; + } +} + +int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...) +{ + JSContext *ctx = s->ctx; + va_list ap; + int backtrace_flags; + + va_start(ap, fmt); + JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); + va_end(ap); + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(ctx, ctx->current_exception, s->filename, s->line_num, + backtrace_flags); + return -1; +} + +static int js_parse_expect(JSParseState *s, int tok) +{ + if (s->token.val != tok) { + /* XXX: dump token correctly in all cases */ + return js_parse_error(s, "expecting '%c'", tok); + } + return next_token(s); +} + +static int js_parse_expect_semi(JSParseState *s) +{ + if (s->token.val != ';') { + /* automatic insertion of ';' */ + if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { + return 0; + } + return js_parse_error(s, "expecting '%c'", ';'); + } + return next_token(s); +} + +static int js_parse_error_reserved_identifier(JSParseState *s) +{ + char buf1[ATOM_GET_STR_BUF_SIZE]; + return js_parse_error(s, "'%s' is a reserved identifier", + JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), + s->token.u.ident.atom)); +} + +static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) +{ + uint32_t c; + StringBuffer b_s, *b = &b_s; + + /* p points to the first byte of the template part */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + if (c == '`') { + /* template end part */ + break; + } + if (c == '$' && *p == '{') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + } + /* newline sequences are normalized as single '\n' bytes */ + if (c == '\r') { + if (*p == '\n') + p++; + c = '\n'; + } + if (c == '\n') { + s->line_num++; + } else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + s->token.val = TOK_TEMPLATE; + s->token.u.str.sep = c; + s->token.u.str.str = string_buffer_end(b); + s->buf_ptr = p; + return 0; + + unexpected_eof: + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static __exception int js_parse_string(JSParseState *s, int sep, + BOOL do_throw, const uint8_t *p, + JSToken *token, const uint8_t **pp) +{ + int ret; + uint32_t c; + StringBuffer b_s, *b = &b_s; + + /* string */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto invalid_char; + c = *p; + if (c < 0x20) { + if (!s->cur_func) { + if (do_throw) + js_parse_error(s, "invalid character in a JSON string"); + goto fail; + } + if (sep == '`') { + if (c == '\r') { + if (p[1] == '\n') + p++; + c = '\n'; + } + /* do not update s->line_num */ + } else if (c == '\n' || c == '\r') + goto invalid_char; + } + p++; + if (c == sep) + break; + if (c == '$' && *p == '{' && sep == '`') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + c = *p; + switch(c) { + case '\0': + if (p >= s->buf_end) + goto invalid_char; + p++; + break; + case '\'': + case '\"': + case '\\': + p++; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + /* ignore escaped newline sequence */ + p++; + if (sep != '`') + s->line_num++; + continue; + default: + if (c >= '0' && c <= '7') { + if (!s->cur_func) + goto invalid_octal; /* JSON case */ + if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') + goto parse_escape; + if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { + p++; + c = '\0'; + } else { + invalid_octal: + if (do_throw) + js_parse_error(s, "invalid octal syntax in strict mode"); + goto fail; + } + } else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + goto invalid_utf8; + } + p = p_next; + /* LS or PS are skipped */ + if (c == CP_LS || c == CP_PS) + continue; + } else { + parse_escape: + ret = lre_parse_escape(&p, TRUE); + if (ret == -1) { + if (do_throw) + js_parse_error(s, "malformed escape sequence in string literal"); + goto fail; + } else if (ret < 0) { + /* ignore the '\' (could output a warning) */ + p++; + } else { + c = ret; + } + } + break; + } + } else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) + goto invalid_utf8; + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + token->val = TOK_STRING; + token->u.str.sep = c; + token->u.str.str = string_buffer_end(b); + *pp = p; + return 0; + + invalid_utf8: + if (do_throw) + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + invalid_char: + if (do_throw) + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { + return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && + !s->token.u.ident.has_escape; +} + +static __exception int js_parse_regexp(JSParseState *s) +{ + const uint8_t *p; + BOOL in_class; + StringBuffer b_s, *b = &b_s; + StringBuffer b2_s, *b2 = &b2_s; + uint32_t c; + + p = s->buf_ptr; + p++; + in_class = FALSE; + if (string_buffer_init(s->ctx, b, 32)) + return -1; + if (string_buffer_init(s->ctx, b2, 1)) + goto fail; + for(;;) { + if (p >= s->buf_end) { + eof_error: + js_parse_error(s, "unexpected end of regexp"); + goto fail; + } + c = *p++; + if (c == '\n' || c == '\r') { + goto eol_error; + } else if (c == '/') { + if (!in_class) + break; + } else if (c == '[') { + in_class = TRUE; + } else if (c == ']') { + /* XXX: incorrect as the first character in a class */ + in_class = FALSE; + } else if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + c = *p++; + if (c == '\n' || c == '\r') + goto eol_error; + else if (c == '\0' && p >= s->buf_end) + goto eof_error; + else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + goto invalid_utf8; + } + p = p_next; + if (c == CP_LS || c == CP_PS) + goto eol_error; + } + } else if (c >= 0x80) { + const uint8_t *p_next; + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + invalid_utf8: + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + eol_error: + js_parse_error(s, "unexpected line terminator in regexp"); + goto fail; + } + } + if (string_buffer_putc(b, c)) + goto fail; + } + + /* flags */ + for(;;) { + const uint8_t *p_next = p; + c = *p_next++; + if (c >= 0x80) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); + if (c > 0x10FFFF) { + goto invalid_utf8; + } + } + if (!lre_js_is_ident_next(c)) + break; + if (string_buffer_putc(b2, c)) + goto fail; + p = p_next; + } + + s->token.val = TOK_REGEXP; + s->token.u.regexp.body = string_buffer_end(b); + s->token.u.regexp.flags = string_buffer_end(b2); + s->buf_ptr = p; + return 0; + fail: + string_buffer_free(b); + string_buffer_free(b2); + return -1; +} + +static __exception int next_token(JSParseState *s) +{ + const uint8_t *p; + int c; + char buf[4096], *q; + BOOL ident_has_escape; + + if (js_check_stack_overflow(s->ctx, 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: + s->token.val = TOK_EOF; + break; + case '`': + if (!s->cur_func) { + /* JSON does not accept templates */ + goto def_token; + } + if (js_parse_template_part(s, p + 1)) + goto fail; + p = s->buf_ptr; + break; + case '\'': + if (!s->cur_func) { + /* 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++; + line_terminator: + s->got_lf = TRUE; + s->line_num++; + goto redo; + case '\f': + case '\v': + if (!s->cur_func) { + /* JSONWhitespace does not match , nor */ + goto def_token; + } + /* fall through */ + 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 { + 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 { + p++; + } + } + goto redo; + } else if (p[1] == '=') { + p += 2; + s->token.val = TOK_DIV_ASSIGN; + } else { + p++; + s->token.val = c; + } + break; + case '\\': + if (p[1] == 'u') { + const uint8_t *p1 = p + 1; + int c1 = lre_parse_escape(&p1, TRUE); + if (c1 >= 0 && lre_js_is_ident_first(c1)) { + c = c1; + p = p1; + ident_has_escape = TRUE; + goto has_ident; + } else { + /* XXX: syntax error? */ + } + } + goto def_token; + case 'a' ... 'z': + case 'A' ... 'Z': + case '_': + case '$': + /* identifier */ + p++; + ident_has_escape = FALSE; + has_ident: + q = buf; + for(;;) { + const uint8_t *p1 = p; + + if (c < 128) { + *q++ = c; + } else { + q += unicode_to_utf8((uint8_t*)q, c); + } + c = *p1++; + if (c == '\\' && *p1 == 'u') { + c = lre_parse_escape(&p1, TRUE); + ident_has_escape = TRUE; + } else if (c >= 128) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); + } + /* XXX: check if c >= 0 and c <= 0x10FFFF */ + if (!lre_js_is_ident_next(c)) + break; + p = p1; + if ((q - buf) >= sizeof(buf) - UTF8_CHAR_LEN_MAX) { + js_parse_error(s, "identifier too long"); + goto fail; + } + } + *q = '\0'; + s->token.u.ident.atom = JS_NewAtomLen(s->ctx, buf, q - buf); + s->token.u.ident.has_escape = ident_has_escape; + s->token.u.ident.is_reserved = FALSE; + if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || + (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && + s->cur_func && (s->cur_func->js_mode & JS_MODE_STRICT)) || + (s->token.u.ident.atom == JS_ATOM_yield && s->cur_func && + ((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 && + ((s->cur_func->func_kind & JS_FUNC_ASYNC) || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) { + if (ident_has_escape) { + s->token.u.ident.is_reserved = TRUE; + s->token.val = TOK_IDENT; + } else { + /* The keywords atoms are pre allocated */ + s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; + } + } else { + s->token.val = TOK_IDENT; + } + break; + case '#': + /* private name */ + { + const uint8_t *p1; + p++; + q = buf; + *q++ = '#'; + 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; + for(;;) { + if (c < 128) { + *q++ = c; + } else { + q += unicode_to_utf8((uint8_t*)q, c); + } + p1 = p; + c = *p1++; + if (c == '\\' && *p1 == 'u') { + c = lre_parse_escape(&p1, TRUE); + ident_has_escape = TRUE; + } else if (c >= 128) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); + } + /* XXX: check if c >= 0 and c <= 0x10FFFF */ + if (!lre_js_is_ident_next(c)) + break; + p = p1; + if ((q - buf) >= sizeof(buf) - UTF8_CHAR_LEN_MAX) { + js_parse_error(s, "private name too long"); + goto fail; + } + } + *q = '\0'; + s->token.u.ident.atom = JS_NewAtomLen(s->ctx, buf, q - buf); + 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 or JSON parsing mode, octal literals are not accepted */ + if (is_digit(p[1]) && (!s->cur_func || + (s->cur_func->js_mode & JS_MODE_STRICT))) { + js_parse_error(s, "octal literals are deprecated in strict mode"); + goto fail; + } + goto parse_number; + case '1' ... '9': + /* number */ + parse_number: + { + JSValue ret; + const uint8_t *p1; + int flags, radix; + if (!s->cur_func) { + flags = 0; + radix = 10; + } else { + flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | + ATOD_ACCEPT_UNDERSCORES; +#ifdef CONFIG_BIGNUM + flags |= ATOD_ACCEPT_SUFFIX; + if (s->cur_func->js_mode & JS_MODE_BIGINT) { + 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 `" is not considered as an HTML comment. It is necessary + because in the spec the function body is parsed separately. */ + /* XXX: find a simpler way or be deliberately incompatible to + simplify the code ? */ + func_start_pos = b->len - 1; /* the leading '(' is not in the source */ + 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 (patch_function_constructor_source(ctx, obj, func_start_pos) < 0) + goto fail1; + if (!JS_IsUndefined(new_target)) { + /* set the prototype */ + proto = js_get_prototype_from_ctor(ctx, new_target, JS_UNDEFINED); + if (JS_IsException(proto)) + goto fail1; + if (!JS_IsUndefined(proto)) { + ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); + JS_FreeValue(ctx, proto); + if (ret < 0) + goto fail1; + } + } + return obj; + + fail: + string_buffer_free(b); + fail1: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, + JSValueConst obj) +{ + JSValue len_val; + len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); + if (JS_IsException(len_val)) { + *pres = 0; + return -1; + } + return JS_ToUint32Free(ctx, pres, len_val); +} + +static __exception int js_get_length64(JSContext *ctx, int64_t *pres, + JSValueConst obj) +{ + JSValue len_val; + len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); + if (JS_IsException(len_val)) { + *pres = 0; + return -1; + } + return JS_ToLengthFree(ctx, pres, len_val); +} + +static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len) +{ + uint32_t i; + for(i = 0; i < len; i++) { + JS_FreeValue(ctx, tab[i]); + } + js_free(ctx, tab); +} + +/* XXX: should use ValueArray */ +static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, + JSValueConst array_arg) +{ + uint32_t len, i; + JSValue *tab, ret; + JSObject *p; + + if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "not a object"); + return NULL; + } + if (js_get_length32(ctx, &len, array_arg)) + return NULL; + /* avoid allocating 0 bytes */ + tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); + if (!tab) + return NULL; + p = JS_VALUE_GET_OBJ(array_arg); + if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) && + p->fast_array && + len == p->u.array.count) { + for(i = 0; i < len; i++) { + tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]); + } + } else { + for(i = 0; i < len; i++) { + ret = JS_GetPropertyUint32(ctx, array_arg, i); + if (JS_IsException(ret)) { + free_arg_list(ctx, tab, i); + return NULL; + } + tab[i] = ret; + } + } + *plen = len; + return tab; +} + +static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValueConst this_arg, array_arg; + uint32_t len; + JSValue *tab, ret; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + this_arg = argv[0]; + array_arg = argv[1]; + if (JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) { + return JS_Call(ctx, this_val, this_arg, 0, NULL); + } + tab = build_arg_list(ctx, &len, array_arg); + if (!tab) + return JS_EXCEPTION; + if (magic) { + ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab); + } else { + ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab); + } + free_arg_list(ctx, tab, len); + return ret; +} + +static JSValue js_function_call(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + if (argc <= 0) { + return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL); + } else { + return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1); + } +} + +static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSBoundFunction *bf; + JSValue func_obj, name1; + JSObject *p; + int arg_count, i; + uint32_t len1; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_BOUND_FUNCTION); + if (JS_IsException(func_obj)) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(func_obj); + p->is_constructor = JS_IsConstructor(ctx, this_val); + arg_count = max_int(0, argc - 1); + bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue)); + if (!bf) + goto exception; + bf->func_obj = JS_DupValue(ctx, this_val); + bf->this_val = JS_DupValue(ctx, argv[0]); + bf->argc = arg_count; + for(i = 0; i < arg_count; i++) { + bf->argv[i] = JS_DupValue(ctx, argv[i + 1]); + } + p->u.bound_function = bf; + + name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); + if (JS_IsException(name1)) + goto exception; + if (!JS_IsString(name1)) { + JS_FreeValue(ctx, name1); + name1 = JS_AtomToString(ctx, JS_ATOM_empty_string); + } + name1 = JS_ConcatString3(ctx, "bound ", name1, ""); + if (JS_IsException(name1)) + goto exception; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, + JS_PROP_CONFIGURABLE); + if (js_get_length32(ctx, &len1, this_val)) + goto exception; + if (len1 <= (uint32_t)arg_count) + len1 = 0; + else + len1 -= arg_count; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, + JS_NewUint32(ctx, len1), + JS_PROP_CONFIGURABLE); + return func_obj; + exception: + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSObject *p; + JSFunctionKindEnum func_kind = JS_FUNC_NORMAL; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_OBJ(this_val); + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode *b = p->u.func.function_bytecode; + if (b->has_debug && b->debug.source) { + return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len); + } + func_kind = b->func_kind; + } + { + JSValue name; + const char *pref, *suff; + + if (p->is_class) { + pref = "class "; + suff = " {\n [native code]\n}"; + } else { + 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 js_error_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic) +{ + JSValue obj, msg, proto; + JSValueConst proto1; + + if (JS_IsUndefined(new_target)) + new_target = JS_GetActiveFunction(ctx); + if (magic < 0) { + proto1 = ctx->class_proto[JS_CLASS_ERROR]; + } else { + proto1 = ctx->native_error_proto[magic]; + } + proto = js_get_prototype_from_ctor(ctx, new_target, proto1); + if (JS_IsException(proto)) + return proto; + obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR); + JS_FreeValue(ctx, proto); + if (JS_IsException(obj)) + return obj; + if (!JS_IsUndefined(argv[0])) { + msg = JS_ToString(ctx, argv[0]); + if (unlikely(JS_IsException(msg))) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, + 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; +} + +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 ), +}; + +/* Array */ + +static int JS_CopySubArray(JSContext *ctx, + JSValueConst obj, int64_t to_pos, + int64_t from_pos, int64_t count, int dir) +{ + int64_t i, from, to; + JSValue val; + int fromPresent; + + /* XXX: should special case fast arrays */ + for (i = 0; i < count; i++) { + if (dir < 0) { + from = from_pos + count - i - 1; + to = to_pos + count - i - 1; + } else { + from = from_pos + i; + to = to_pos + i; + } + fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); + if (fromPresent < 0) + goto exception; + + if (fromPresent) { + if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) + goto exception; + } else { + if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) + goto exception; + } + } + return 0; + + exception: + return -1; +} + +static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue obj; + int i; + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY); + if (JS_IsException(obj)) + return obj; + if (argc == 1 && JS_IsNumber(argv[0])) { + uint32_t len; + if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) + goto fail; + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) + goto fail; + } else { + for(i = 0; i < argc; i++) { + if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) + goto fail; + } + } + return obj; +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // from(items, mapfn = void 0, this_arg = void 0) + JSValueConst items = argv[0], mapfn, this_arg; + JSValueConst args[2]; + JSValue stack[2]; + JSValue iter, r, v, v2, arrayLike; + int64_t k, len; + int done, mapping; + + mapping = FALSE; + mapfn = JS_UNDEFINED; + this_arg = JS_UNDEFINED; + r = JS_UNDEFINED; + arrayLike = JS_UNDEFINED; + stack[0] = JS_UNDEFINED; + stack[1] = JS_UNDEFINED; + + if (argc > 1) { + mapfn = argv[1]; + if (!JS_IsUndefined(mapfn)) { + if (check_function(ctx, mapfn)) + goto exception; + mapping = 1; + if (argc > 2) + this_arg = argv[2]; + } + } + iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto exception; + if (!JS_IsUndefined(iter)) { + JS_FreeValue(ctx, iter); + if (JS_IsConstructor(ctx, this_val)) + r = JS_CallConstructor(ctx, this_val, 0, NULL); + else + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + stack[0] = JS_DupValue(ctx, items); + if (js_for_of_start(ctx, &stack[1], FALSE)) + goto exception; + for (k = 0;; k++) { + v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); + if (JS_IsException(v)) + goto exception_close; + if (done) + break; + if (mapping) { + args[0] = v; + args[1] = JS_NewInt32(ctx, k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception_close; + } + if (JS_DefinePropertyValueInt64(ctx, r, k, v, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception_close; + } + } else { + arrayLike = JS_ToObject(ctx, items); + if (JS_IsException(arrayLike)) + goto exception; + if (js_get_length64(ctx, &len, arrayLike) < 0) + goto exception; + v = JS_NewInt64(ctx, len); + args[0] = v; + if (JS_IsConstructor(ctx, this_val)) { + r = JS_CallConstructor(ctx, this_val, 1, args); + } else { + r = js_array_constructor(ctx, JS_UNDEFINED, 1, args); + } + JS_FreeValue(ctx, v); + if (JS_IsException(r)) + goto exception; + for(k = 0; k < len; k++) { + v = JS_GetPropertyInt64(ctx, arrayLike, k); + if (JS_IsException(v)) + goto exception; + if (mapping) { + args[0] = v; + args[1] = JS_NewInt32(ctx, k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, r, k, v, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0) + goto exception; + goto done; + + exception_close: + if (!JS_IsUndefined(stack[0])) + JS_IteratorClose(ctx, stack[0], TRUE); + exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; + done: + JS_FreeValue(ctx, arrayLike); + JS_FreeValue(ctx, stack[0]); + JS_FreeValue(ctx, stack[1]); + return r; +} + +static JSValue js_array_of(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, args[1]; + int i; + + if (JS_IsConstructor(ctx, this_val)) { + args[0] = JS_NewInt32(ctx, argc); + obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args); + } else { + obj = JS_NewArray(ctx); + } + if (JS_IsException(obj)) + return JS_EXCEPTION; + for(i = 0; i < argc; i++) { + if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]), + JS_PROP_THROW) < 0) { + goto fail; + } + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int ret; + ret = JS_IsArray(ctx, argv[0]); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +static JSValue js_get_this(JSContext *ctx, + JSValueConst this_val) +{ + return JS_DupValue(ctx, this_val); +} + +static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, + JSValueConst len_val) +{ + JSValue ctor, ret; + int res; + + 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_SpeciesConstructor(ctx, obj, JS_UNDEFINED); + if (JS_IsException(ctor)) + return JS_EXCEPTION; + if (JS_IsUndefined(ctor)) + return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + ret = JS_CallConstructor(ctx, ctor, 1, &len_val); + JS_FreeValue(ctx, ctor); + return ret; +} + +static const JSCFunctionListEntry js_array_funcs[] = { + JS_CFUNC_DEF("isArray", 1, js_array_isArray ), + JS_CFUNC_DEF("from", 1, js_array_from ), + JS_CFUNC_DEF("of", 0, js_array_of ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj) +{ + JSValue val; + + if (!JS_IsObject(obj)) + return FALSE; + val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) + return JS_ToBoolFree(ctx, val); + return JS_IsArray(ctx, obj); +} + +static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, arr, val; + JSValueConst e; + int64_t len, k, n; + int i, res; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + + arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(arr)) + goto exception; + n = 0; + for (i = -1; i < argc; i++) { + if (i < 0) + e = obj; + else + e = argv[i]; + + res = JS_isConcatSpreadable(ctx, e); + if (res < 0) + goto exception; + if (res) { + if (js_get_length64(ctx, &len, e)) + goto exception; + if (n + len >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + for (k = 0; k < len; k++, n++) { + res = JS_TryGetPropertyInt64(ctx, e, k, &val); + if (res < 0) + goto exception; + if (res) { + if (JS_DefinePropertyValueInt64(ctx, arr, n, val, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + } else { + if (n >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + n++; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + goto exception; + + JS_FreeValue(ctx, obj); + return arr; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +#define special_every 0 +#define special_some 1 +#define special_forEach 2 +#define special_map 3 +#define special_filter 4 +#define special_TA 8 + +static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj); + +static JSValue js_typed_array___speciesCreate(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv); + +static JSValue js_array_every(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int special) +{ + JSValue obj, val, index_val, res, ret; + JSValueConst args[3]; + JSValueConst func, this_arg; + int64_t len, k, n; + int present; + + ret = JS_UNDEFINED; + val = JS_UNDEFINED; + if (special & special_TA) { + obj = JS_DupValue(ctx, this_val); + len = js_typed_array_get_length_internal(ctx, obj); + if (len < 0) + goto exception; + } else { + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + } + func = argv[0]; + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + if (check_function(ctx, func)) + goto exception; + + switch (special) { + case special_every: + case special_every | special_TA: + ret = JS_TRUE; + break; + case special_some: + case special_some | special_TA: + ret = JS_FALSE; + break; + case special_map: + /* XXX: JS_ArraySpeciesCreate should take int64_t */ + ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len)); + if (JS_IsException(ret)) + goto exception; + break; + case special_filter: + ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(ret)) + goto exception; + break; + case special_map | special_TA: + args[0] = obj; + args[1] = JS_NewInt32(ctx, len); + ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(ret)) + goto exception; + break; + case special_filter | special_TA: + ret = JS_NewArray(ctx); + if (JS_IsException(ret)) + goto exception; + break; + } + n = 0; + + for(k = 0; k < len; k++) { + present = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (present < 0) + goto exception; + if (present) { + index_val = JS_NewInt64(ctx, k); + if (JS_IsException(index_val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = obj; + res = JS_Call(ctx, func, this_arg, 3, args); + JS_FreeValue(ctx, index_val); + if (JS_IsException(res)) + goto exception; + switch (special) { + case special_every: + case special_every | special_TA: + if (!JS_ToBoolFree(ctx, res)) { + ret = JS_FALSE; + goto done; + } + break; + case special_some: + case special_some | special_TA: + if (JS_ToBoolFree(ctx, res)) { + ret = JS_TRUE; + goto done; + } + break; + case special_map: + if (JS_DefinePropertyValueInt64(ctx, ret, k, res, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + break; + case special_map | special_TA: + if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0) + goto exception; + break; + case special_filter: + case special_filter | special_TA: + if (JS_ToBoolFree(ctx, res)) { + if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + break; + default: + JS_FreeValue(ctx, res); + break; + } + JS_FreeValue(ctx, val); + val = JS_UNDEFINED; + } + } +done: + if (special == (special_filter | special_TA)) { + JSValue arr; + args[0] = obj; + args[1] = JS_NewInt32(ctx, n); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(arr)) + goto exception; + args[0] = ret; + res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); + if (check_exception_free(ctx, res)) + goto exception; + JS_FreeValue(ctx, ret); + ret = arr; + } + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return ret; + +exception: + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +#define special_reduce 0 +#define special_reduceRight 1 + +static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int special) +{ + JSValue obj, val, index_val, acc, acc1; + JSValueConst args[4]; + JSValueConst func; + int64_t len, k, k1; + int present; + + acc = JS_UNDEFINED; + val = JS_UNDEFINED; + if (special & special_TA) { + obj = JS_DupValue(ctx, this_val); + len = js_typed_array_get_length_internal(ctx, obj); + if (len < 0) + goto exception; + } else { + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + } + func = argv[0]; + + if (check_function(ctx, func)) + goto exception; + + k = 0; + if (argc > 1) { + acc = JS_DupValue(ctx, argv[1]); + } else { + for(;;) { + if (k >= len) { + JS_ThrowTypeError(ctx, "empty array"); + goto exception; + } + k1 = (special & special_reduceRight) ? len - k - 1 : k; + k++; + present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); + if (present < 0) + goto exception; + if (present) + break; + } + } + for (; k < len; k++) { + k1 = (special & special_reduceRight) ? len - k - 1 : k; + present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); + if (present < 0) + goto exception; + if (present) { + index_val = JS_NewInt64(ctx, k1); + if (JS_IsException(index_val)) + goto exception; + args[0] = acc; + args[1] = val; + args[2] = index_val; + args[3] = obj; + acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args); + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, val); + val = JS_UNDEFINED; + if (JS_IsException(acc1)) + goto exception; + JS_FreeValue(ctx, acc); + acc = acc1; + } + } + JS_FreeValue(ctx, obj); + return acc; + +exception: + JS_FreeValue(ctx, acc); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj; + int64_t len, start, end; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + start = 0; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len)) + goto exception; + } + + end = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len)) + goto exception; + } + + /* XXX: should special case fast arrays */ + while (start < end) { + if (JS_SetPropertyInt64(ctx, obj, start, + JS_DupValue(ctx, argv[0])) < 0) + goto exception; + start++; + } + return obj; + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, val; + int64_t len, n, res; + JSValue *arrp; + uint32_t count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + res = FALSE; + if (len > 0) { + n = 0; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count)) { + for (; n < count; n++) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), + JS_DupValue(ctx, arrp[n]), + JS_EQ_SAME_VALUE_ZERO)) { + res = TRUE; + goto done; + } + } + } + for (; n < len; n++) { + val = JS_GetPropertyInt64(ctx, obj, n); + if (JS_IsException(val)) + goto exception; + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, + JS_EQ_SAME_VALUE_ZERO)) { + res = TRUE; + break; + } + } + } + done: + JS_FreeValue(ctx, obj); + return JS_NewBool(ctx, res); + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, val; + int64_t len, n, res; + JSValue *arrp; + uint32_t count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + res = -1; + if (len > 0) { + n = 0; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count)) { + for (; n < count; n++) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), + JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) { + res = n; + goto done; + } + } + } + for (; n < len; n++) { + int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); + if (present < 0) + goto exception; + if (present) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { + res = n; + break; + } + } + } + } + done: + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, res); + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, val; + int64_t len, n, res; + int present; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + res = -1; + if (len > 0) { + n = len - 1; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len)) + goto exception; + } + /* XXX: should special case fast arrays */ + for (; n >= 0; n--) { + present = JS_TryGetPropertyInt64(ctx, obj, n, &val); + if (present < 0) + goto exception; + if (present) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { + res = n; + break; + } + } + } + } + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, res); + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int findIndex) +{ + JSValueConst func, this_arg; + JSValueConst args[3]; + JSValue obj, val, index_val, res; + int64_t len, k; + + index_val = JS_UNDEFINED; + val = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + func = argv[0]; + if (check_function(ctx, func)) + goto exception; + + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + for(k = 0; k < len; k++) { + index_val = JS_NewInt64(ctx, k); + if (JS_IsException(index_val)) + goto exception; + val = JS_GetPropertyValue(ctx, obj, index_val); + if (JS_IsException(val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = this_val; + res = JS_Call(ctx, func, this_arg, 3, args); + if (JS_IsException(res)) + goto exception; + if (JS_ToBoolFree(ctx, res)) { + if (findIndex) { + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return index_val; + } else { + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, obj); + return val; + } + } + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, index_val); + } + JS_FreeValue(ctx, obj); + if (findIndex) + return JS_NewInt32(ctx, -1); + else + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, method, ret; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + method = JS_GetProperty(ctx, obj, JS_ATOM_join); + if (JS_IsException(method)) { + ret = JS_EXCEPTION; + } else + if (!JS_IsFunction(ctx, method)) { + /* Use intrinsic Object.prototype.toString */ + JS_FreeValue(ctx, method); + ret = js_object_toString(ctx, obj, 0, NULL); + } else { + ret = JS_CallFree(ctx, method, obj, 0, NULL); + } + JS_FreeValue(ctx, obj); + return ret; +} + +static JSValue js_array_join(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int toLocaleString) +{ + JSValue obj, sep = JS_UNDEFINED, el; + StringBuffer b_s, *b = &b_s; + JSString *p = NULL; + int64_t i, n; + int c; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &n, obj)) + goto exception; + + c = ','; /* default separator */ + if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { + sep = JS_ToString(ctx, argv[0]); + if (JS_IsException(sep)) + goto exception; + p = JS_VALUE_GET_STRING(sep); + if (p->len == 1 && !p->is_wide_char) + c = p->u.str8[0]; + else + c = -1; + } + string_buffer_init(ctx, b, 0); + + for(i = 0; i < n; i++) { + if (i > 0) { + if (c >= 0) { + string_buffer_putc8(b, c); + } else { + string_buffer_concat(b, p, 0, p->len); + } + } + el = JS_GetPropertyUint32(ctx, obj, i); + if (JS_IsException(el)) + goto fail; + if (!JS_IsNull(el) && !JS_IsUndefined(el)) { + if (toLocaleString) { + el = JS_ToLocaleStringFree(ctx, el); + } + if (string_buffer_concat_value_free(b, el)) + goto fail; + } + } + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, obj); + return string_buffer_end(b); + +fail: + string_buffer_free(b); + JS_FreeValue(ctx, sep); +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int shift) +{ + JSValue obj, res = JS_UNDEFINED; + int64_t len, newLen; + JSValue *arrp; + uint32_t count32; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = 0; + if (len > 0) { + newLen = len - 1; + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (shift) { + res = arrp[0]; + memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); + p->u.array.count--; + } else { + res = arrp[count32 - 1]; + p->u.array.count--; + } + } else { + if (shift) { + res = JS_GetPropertyInt64(ctx, obj, 0); + if (JS_IsException(res)) + goto exception; + if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) + goto exception; + } else { + res = JS_GetPropertyInt64(ctx, obj, newLen); + if (JS_IsException(res)) + goto exception; + } + if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) + goto exception; + + JS_FreeValue(ctx, obj); + return res; + + exception: + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int unshift) +{ + JSValue obj; + int i; + int64_t len, from, newLen; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = len + argc; + if (newLen > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + from = len; + if (unshift && argc > 0) { + if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) + goto exception; + from = 0; + } + for(i = 0; i < argc; i++) { + if (JS_SetPropertyInt64(ctx, obj, from + i, + JS_DupValue(ctx, argv[i])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) + goto exception; + + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, newLen); + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, lval, hval; + JSValue *arrp; + int64_t len, l, h; + int l_present, h_present; + uint32_t count32; + + lval = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + uint32_t ll, hh; + + if (count32 > 1) { + for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) { + lval = arrp[ll]; + arrp[ll] = arrp[hh]; + arrp[hh] = lval; + } + } + return obj; + } + + for (l = 0, h = len - 1; l < h; l++, h--) { + l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval); + if (l_present < 0) + goto exception; + h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval); + if (h_present < 0) + goto exception; + if (h_present) { + if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0) + goto exception; + + if (l_present) { + if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { + lval = JS_UNDEFINED; + goto exception; + } + lval = JS_UNDEFINED; + } else { + if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0) + goto exception; + } + } else { + if (l_present) { + if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0) + goto exception; + if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { + lval = JS_UNDEFINED; + goto exception; + } + lval = JS_UNDEFINED; + } + } + } + return obj; + + exception: + JS_FreeValue(ctx, lval); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int splice) +{ + JSValue obj, arr, val, len_val; + int64_t len, start, k, final, n, count, del_count, new_len; + int kPresent; + JSValue *arrp; + uint32_t count32, i, item_count; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + if (splice) { + if (argc == 0) { + item_count = 0; + del_count = 0; + } else + if (argc == 1) { + item_count = 0; + del_count = len - start; + } else { + item_count = argc - 2; + if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) + goto exception; + } + if (len + item_count - del_count > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + count = del_count; + } else { + item_count = 0; /* avoid warning */ + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int64(final - start, 0); + } + len_val = JS_NewInt64(ctx, count); + arr = JS_ArraySpeciesCreate(ctx, obj, len_val); + JS_FreeValue(ctx, len_val); + if (JS_IsException(arr)) + goto exception; + + k = start; + final = start + count; + n = 0; + /* The fast array test on arr ensures that + JS_CreateDataPropertyUint32() won't modify obj in case arr is + an exotic object */ + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && + js_is_fast_array(ctx, arr)) { + /* XXX: should share code with fast array constructor */ + for (; k < final && k < count32; k++, n++) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0) + goto exception; + } + } + /* Copy the remaining elements if any (handle case of inherited properties) */ + for (; k < final; k++, n++) { + kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (kPresent < 0) + goto exception; + if (kPresent) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + goto exception; + + if (splice) { + new_len = len + item_count - del_count; + if (item_count != del_count) { + if (JS_CopySubArray(ctx, obj, start + item_count, + start + del_count, len - (start + del_count), + item_count <= del_count ? +1 : -1) < 0) + goto exception; + + for (k = len; k-- > new_len; ) { + if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0) + goto exception; + } + } + for (i = 0; i < item_count; i++) { + if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) + goto exception; + } + JS_FreeValue(ctx, obj); + return arr; + + exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj; + int64_t len, from, to, final, count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len)) + goto exception; + + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len)) + goto exception; + } + + count = min_int64(final - from, len - to); + + if (JS_CopySubArray(ctx, obj, to, from, count, + (from < to && to < from + count) ? -1 : +1)) + goto exception; + + return obj; + + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, + JSValueConst source, int64_t sourceLen, + int64_t targetIndex, int depth, + JSValueConst mapperFunction, + JSValueConst thisArg) +{ + JSValue element; + int64_t sourceIndex, elementLen; + int present, is_array; + + for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { + present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); + if (present < 0) + return -1; + if (!present) + continue; + if (!JS_IsUndefined(mapperFunction)) { + JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; + element = JS_Call(ctx, mapperFunction, thisArg, 3, args); + JS_FreeValue(ctx, (JSValue)args[0]); + JS_FreeValue(ctx, (JSValue)args[1]); + if (JS_IsException(element)) + return -1; + } + if (depth > 0) { + is_array = JS_IsArray(ctx, element); + if (is_array < 0) + goto fail; + if (is_array) { + if (js_get_length64(ctx, &elementLen, element) < 0) + goto fail; + targetIndex = JS_FlattenIntoArray(ctx, target, element, + elementLen, targetIndex, + depth - 1, + JS_UNDEFINED, JS_UNDEFINED); + if (targetIndex < 0) + goto fail; + JS_FreeValue(ctx, element); + continue; + } + } + if (targetIndex >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array too long"); + goto fail; + } + if (JS_SetPropertyInt64(ctx, target, targetIndex, element) < 0) + return -1; + targetIndex++; + } + return targetIndex; + +fail: + JS_FreeValue(ctx, element); + return -1; +} + +static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int map) +{ + JSValue obj, arr; + JSValueConst mapperFunction, thisArg; + int64_t sourceLen; + int depthNum; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &sourceLen, obj)) + goto exception; + + depthNum = 1; + mapperFunction = JS_UNDEFINED; + thisArg = JS_UNDEFINED; + if (map) { + mapperFunction = argv[0]; + if (argc > 1) { + thisArg = argv[1]; + } + if (check_function(ctx, mapperFunction)) + goto exception; + } else { + if (argc > 0 && !JS_IsUndefined(argv[0])) { + if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0) + goto exception; + } + } + arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(arr)) + goto exception; + if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, + mapperFunction, thisArg) < 0) + goto exception; + JS_FreeValue(ctx, obj); + return arr; + +exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +/* Array sort */ + +typedef struct ValueSlot { + JSValue val; + JSString *str; + int64_t pos; +} ValueSlot; + +struct array_sort_context { + JSContext *ctx; + int exception; + int has_method; + JSValueConst method; +}; + +static int js_array_cmp_generic(const void *a, const void *b, void *opaque) { + struct array_sort_context *psc = opaque; + JSContext *ctx = psc->ctx; + JSValueConst argv[2]; + JSValue res; + ValueSlot *ap = (ValueSlot *)(void *)a; + ValueSlot *bp = (ValueSlot *)(void *)b; + int cmp; + + if (psc->exception) + return 0; + + if (psc->has_method) { + /* custom sort function is specified as returning 0 for identical + * objects: avoid method call overhead. + */ + if (!memcmp(&ap->val, &bp->val, sizeof(ap->val))) + goto cmp_same; + argv[0] = ap->val; + argv[1] = bp->val; + res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv); + if (JS_IsException(res)) + goto exception; + if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { + int val = JS_VALUE_GET_INT(res); + cmp = (val > 0) - (val < 0); + } else { + double val; + if (JS_ToFloat64Free(ctx, &val, res) < 0) + goto exception; + cmp = (val > 0) - (val < 0); + } + } else { + /* Not supposed to bypass ToString even for identical objects as + * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js + */ + if (!ap->str) { + JSValue str = JS_ToString(ctx, ap->val); + if (JS_IsException(str)) + goto exception; + ap->str = JS_VALUE_GET_STRING(str); + } + if (!bp->str) { + JSValue str = JS_ToString(ctx, bp->val); + if (JS_IsException(str)) + goto exception; + bp->str = JS_VALUE_GET_STRING(str); + } + cmp = js_string_compare(ctx, ap->str, bp->str); + } + if (cmp != 0) + return cmp; +cmp_same: + /* make sort stable: compare array offsets */ + return (ap->pos > bp->pos) - (ap->pos < bp->pos); + +exception: + psc->exception = 1; + return 0; +} + +static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + struct array_sort_context asc = { ctx, 0, 0, argv[0] }; + JSValue obj = JS_UNDEFINED; + ValueSlot *array = NULL; + size_t array_size = 0, pos = 0, n = 0; + int64_t i, len, undefined_count = 0; + int present; + + if (!JS_IsUndefined(asc.method)) { + if (check_function(ctx, asc.method)) + goto exception; + asc.has_method = 1; + } + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + /* XXX: should special case fast arrays */ + for (i = 0; i < len; i++) { + if (pos >= array_size) { + size_t new_size, slack; + ValueSlot *new_array; + new_size = (array_size + (array_size >> 1) + 31) & ~15; + new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack); + if (new_array == NULL) + goto exception; + new_size += slack / sizeof(*new_array); + array = new_array; + array_size = new_size; + } + present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val); + if (present < 0) + goto exception; + if (present == 0) + continue; + if (JS_IsUndefined(array[pos].val)) { + undefined_count++; + continue; + } + array[pos].str = NULL; + array[pos].pos = i; + pos++; + } + rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc); + if (asc.exception) + goto exception; + + /* XXX: should special case fast arrays */ + while (n < pos) { + if (array[n].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); + if (array[n].pos == n) { + JS_FreeValue(ctx, array[n].val); + } else { + if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { + n++; + goto exception; + } + } + n++; + } + js_free(ctx, array); + for (i = n; undefined_count-- > 0; i++) { + if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0) + goto fail; + } + for (; i < len; i++) { + if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0) + goto fail; + } + return obj; + +exception: + for (; n < pos; n++) { + JS_FreeValue(ctx, array[n].val); + if (array[n].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); + } + js_free(ctx, array); +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +typedef struct JSArrayIteratorData { + JSValue obj; + JSIteratorKindEnum kind; + uint32_t idx; +} JSArrayIteratorData; + +static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSArrayIteratorData *it = p->u.array_iterator_data; + if (it) { + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); + } +} + +static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSArrayIteratorData *it = p->u.array_iterator_data; + if (it) { + JS_MarkValue(rt, it->obj, mark_func); + } +} + +static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab) +{ + JSValue obj; + int i; + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + for(i = 0; i < len; i++) { + if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue enum_obj, arr; + JSArrayIteratorData *it; + JSIteratorKindEnum kind; + int class_id; + + kind = magic & 3; + if (magic & 4) { + /* string iterator case */ + arr = JS_ToStringCheckObject(ctx, this_val); + class_id = JS_CLASS_STRING_ITERATOR; + } else { + arr = JS_ToObject(ctx, this_val); + class_id = JS_CLASS_ARRAY_ITERATOR; + } + if (JS_IsException(arr)) + goto fail; + enum_obj = JS_NewObjectClass(ctx, class_id); + if (JS_IsException(enum_obj)) + goto fail; + it = js_malloc(ctx, sizeof(*it)); + if (!it) + goto fail1; + it->obj = arr; + it->kind = kind; + it->idx = 0; + JS_SetOpaque(enum_obj, it); + return enum_obj; + fail1: + JS_FreeValue(ctx, enum_obj); + fail: + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSArrayIteratorData *it; + uint32_t len, idx; + JSValue val, obj; + JSObject *p; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR); + if (!it) + goto fail1; + if (JS_IsUndefined(it->obj)) + goto done; + p = JS_VALUE_GET_OBJ(it->obj); + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail1; + } + len = p->u.array.count; + } else { + if (js_get_length32(ctx, &len, it->obj)) { + fail1: + *pdone = FALSE; + return JS_EXCEPTION; + } + } + idx = it->idx; + if (idx >= len) { + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + *pdone = TRUE; + return JS_UNDEFINED; + } + it->idx = idx + 1; + *pdone = FALSE; + if (it->kind == JS_ITERATOR_KIND_KEY) { + return JS_NewUint32(ctx, idx); + } else { + val = JS_GetPropertyUint32(ctx, it->obj, idx); + if (JS_IsException(val)) + return JS_EXCEPTION; + if (it->kind == JS_ITERATOR_KIND_VALUE) { + return val; + } else { + JSValueConst args[2]; + JSValue num; + num = JS_NewUint32(ctx, idx); + args[0] = num; + args[1] = val; + obj = js_create_array(ctx, 2, args); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, num); + return obj; + } + } +} + +static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_DupValue(ctx, this_val); +} + +static const JSCFunctionListEntry js_iterator_proto_funcs[] = { + JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ), +}; + +static const JSCFunctionListEntry js_array_proto_funcs[] = { + JS_CFUNC_DEF("concat", 1, js_array_concat ), + JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ), + JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ), + JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ), + JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ), + JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ), + JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ), + JS_CFUNC_DEF("fill", 1, js_array_fill ), + JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ), + JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ), + JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ), + JS_CFUNC_DEF("includes", 1, js_array_includes ), + JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ), + JS_CFUNC_DEF("toString", 0, js_array_toString ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ), + JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ), + JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ), + JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ), + JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), + JS_CFUNC_DEF("reverse", 0, js_array_reverse ), + JS_CFUNC_DEF("sort", 1, js_array_sort ), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ), + JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ), + JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ), + JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ), + JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ), + JS_CFUNC_MAGIC_DEF("flatten", 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; +#ifdef CONFIG_BIGNUM + if (argc == 0) { + if (is_bigint_mode(ctx)) + val = __JS_NewFloat64(ctx, 0); + else + 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: + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + double d; + bf_get_float64(&p->num, &d, BF_RNDN); + JS_FreeValue(ctx, val); + val = __JS_NewFloat64(ctx, d); + } + break; + case JS_TAG_INT: + if (is_bigint_mode(ctx)) { + /* always return a number in bignum mode */ + val = __JS_NewFloat64(ctx, JS_VALUE_GET_INT(val)); + } + break; + default: + break; + } + } +#else + if (argc == 0) { + val = JS_NewInt32(ctx, 0); + } else { + val = JS_ToNumber(ctx, argv[0]); + if (JS_IsException(val)) + return val; + } +#endif + 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) +{ + /* XXX: should just check for float and big_float */ + 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, isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER); +} + +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 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 { + if (JS_ToInt32Sat(ctx, &base, argv[0])) + goto fail; + if (base < 2 || base > 36) { + JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + goto fail; + } + } + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT); + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int f; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_ToInt32Sat(ctx, &f, argv[0])) + return JS_EXCEPTION; + if (f < 0 || f > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + if (fabs(d) >= 1e21) { + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); + } else { + return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); + } +} + +static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int f, flags; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_ToInt32Sat(ctx, &f, argv[0])) + return JS_EXCEPTION; + if (!isfinite(d)) { + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); + } + if (JS_IsUndefined(argv[0])) { + flags = 0; + f = 0; + } else { + if (f < 0 || f > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + f++; + flags = JS_DTOA_FIXED_FORMAT; + } + return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP); +} + +static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int p; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_IsUndefined(argv[0])) + goto to_string; + if (JS_ToInt32Sat(ctx, &p, argv[0])) + return JS_EXCEPTION; + if (!isfinite(d)) { + to_string: + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); + } + if (p < 1 || p > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT); +} + +static const JSCFunctionListEntry js_number_proto_funcs[] = { + JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ), + JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ), + JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ), + JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ), + JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ), +}; + +static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *str, *p; + int radix, flags; + JSValue ret; + + str = JS_ToCString(ctx, argv[0]); + if (!str) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &radix, argv[1])) { + JS_FreeCString(ctx, str); + return JS_EXCEPTION; + } + if (radix != 0 && (radix < 2 || radix > 36)) { + ret = JS_NAN; + } else { + p = str; + p += skip_spaces(p); + flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; +#ifdef CONFIG_BIGNUM + if (is_bigint_mode(ctx)) + flags |= ATOD_MODE_BIGINT; +#endif + ret = js_atof(ctx, p, NULL, radix, flags); + } + JS_FreeCString(ctx, str); + return ret; +} + +static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *str, *p; + JSValue ret; + + str = JS_ToCString(ctx, argv[0]); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + ret = js_atof(ctx, p, NULL, 10, 0); + JS_FreeCString(ctx, str); + return ret; +} + +/* Boolean */ +static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue val, obj; + val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0])); + if (!JS_IsUndefined(new_target)) { + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN); + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, val); + return obj; + } else { + return val; + } +} + +static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_BOOLEAN) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL) + return p->u.object_data; + } + } + return JS_ThrowTypeError(ctx, "not a boolean"); +} + +static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val = js_thisBooleanValue(ctx, this_val); + if (JS_IsException(val)) + return val; + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? + JS_ATOM_true : JS_ATOM_false); +} + +static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisBooleanValue(ctx, this_val); +} + +static const JSCFunctionListEntry js_boolean_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_boolean_toString ), + JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ), +}; + +/* String */ + +static int js_string_get_own_property(JSContext *ctx, + JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop) +{ + JSObject *p; + JSString *p1; + uint32_t idx, ch; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + if (__JS_AtomIsTaggedInt(prop)) { + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + if (desc) { + if (p1->is_wide_char) + ch = p1->u.str16[idx]; + else + ch = p1->u.str8[idx]; + desc->flags = JS_PROP_ENUMERABLE; + desc->value = js_new_string_char(ctx, ch); + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + } + return TRUE; + } + } + } + return FALSE; +} + +static uint32_t js_string_obj_get_length(JSContext *ctx, + JSValueConst obj) +{ + JSObject *p; + JSString *p1; + uint32_t len = 0; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + len = p1->len; + } + return len; +} + +static int js_string_get_own_property_names(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSValueConst obj) +{ + JSPropertyEnum *tab; + uint32_t len, i; + + len = js_string_obj_get_length(ctx, obj); + tab = NULL; + if (len > 0) { + /* do not allocate 0 bytes */ + tab = js_malloc(ctx, sizeof(JSPropertyEnum) * len); + if (!tab) + return -1; + for(i = 0; i < len; i++) { + tab[i].atom = __JS_AtomFromUInt32(i); + } + } + *ptab = tab; + *plen = len; + return 0; +} + +static int js_string_define_own_property(JSContext *ctx, + JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, + JSValueConst setter, int flags) +{ + uint32_t idx; + + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx >= js_string_obj_get_length(ctx, this_obj)) + goto def; + if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags)) + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + /* XXX: should check if same value is configured */ + return TRUE; + } else { + def: + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, + flags | JS_PROP_NO_EXOTIC); + } +} + +static int js_string_delete_property(JSContext *ctx, + JSValueConst obj, JSAtom prop) +{ + uint32_t idx; + + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < js_string_obj_get_length(ctx, obj)) { + return FALSE; + } + } + return TRUE; +} + +static const JSClassExoticMethods js_string_exotic_methods = { + .get_own_property = js_string_get_own_property, + .get_own_property_names = js_string_get_own_property_names, + .define_own_property = js_string_define_own_property, + .delete_property = js_string_delete_property, +}; + +static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue val, obj; + if (argc == 0) { + val = JS_AtomToString(ctx, JS_ATOM_empty_string); + } else { + if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) { + JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]); + val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")"); + } else { + val = JS_ToString(ctx, argv[0]); + } + if (JS_IsException(val)) + return val; + } + if (!JS_IsUndefined(new_target)) { + JSString *p1 = JS_VALUE_GET_STRING(val); + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); + if (!JS_IsException(obj)) { + JS_SetObjectData(ctx, obj, val); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); + } + return obj; + } else { + return val; + } +} + +static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_STRING) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a string"); +} + +static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int i; + StringBuffer b_s, *b = &b_s; + + string_buffer_init(ctx, b, argc); + + for(i = 0; i < argc; i++) { + int32_t c; + if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) { + string_buffer_free(b); + return JS_EXCEPTION; + } + } + return string_buffer_end(b); +} + +static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + double d; + int i, c; + StringBuffer b_s, *b = &b_s; + + /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */ + + if (string_buffer_init(ctx, b, argc)) + goto fail; + for(i = 0; i < argc; i++) { + if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) { + c = JS_VALUE_GET_INT(argv[i]); + if (c < 0 || c > 0x10ffff) + goto range_error; + } else { + if (JS_ToFloat64(ctx, &d, argv[i])) + goto fail; + if (d < 0 || d > 0x10ffff || (c = (int)d) != d) + goto range_error; + } + if (string_buffer_putc(b, c)) + goto fail; + } + return string_buffer_end(b); + + range_error: + JS_ThrowRangeError(ctx, "invalid code point"); + fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // raw(temp,...a) + JSValue cooked, val, raw; + StringBuffer b_s, *b = &b_s; + int64_t i, n; + + string_buffer_init(ctx, b, 0); + raw = JS_UNDEFINED; + cooked = JS_ToObject(ctx, argv[0]); + if (JS_IsException(cooked)) + goto exception; + raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw)); + if (JS_IsException(raw)) + goto exception; + if (js_get_length64(ctx, &n, raw) < 0) + goto exception; + + for (i = 0; i < n; i++) { + val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i)); + if (JS_IsException(val)) + goto exception; + string_buffer_concat_value_free(b, val); + if (i < n - 1 && i + 1 < argc) { + if (string_buffer_concat_value(b, argv[i + 1])) + goto exception; + } + } + JS_FreeValue(ctx, cooked); + JS_FreeValue(ctx, raw); + return string_buffer_end(b); + +exception: + JS_FreeValue(ctx, cooked); + JS_FreeValue(ctx, raw); + string_buffer_free(b); + return JS_EXCEPTION; +} + +/* only used in test262 */ +JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint32_t start, end, i, n; + StringBuffer b_s, *b = &b_s; + + if (JS_ToUint32(ctx, &start, argv[0]) || + JS_ToUint32(ctx, &end, argv[1])) + return JS_EXCEPTION; + end = min_uint32(end, 0x10ffff + 1); + + if (start > end) { + start = end; + } + n = end - start; + if (end > 0x10000) { + n += end - max_uint32(start, 0x10000); + } + if (string_buffer_init2(ctx, b, n, end >= 0x100)) + return JS_EXCEPTION; + for(i = start; i < end; i++) { + string_buffer_putc(b, i); + } + return string_buffer_end(b); +} + +#if 0 +static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int c; + if (JS_ToInt32(ctx, &c, argv[0])) + return JS_EXCEPTION; + return JS_NewBool(ctx, lre_is_space(c)); +} +#endif + +static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + JSString *p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = JS_NAN; + } else { + if (p->is_wide_char) + c = p->u.str16[idx]; + else + c = p->u.str8[idx]; + ret = JS_NewInt32(ctx, c); + } + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + JSString *p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = js_new_string8(ctx, NULL, 0); + } else { + if (p->is_wide_char) + c = p->u.str16[idx]; + else + c = p->u.str8[idx]; + ret = js_new_string_char(ctx, c); + } + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + JSString *p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = JS_UNDEFINED; + } else { + c = string_getc(p, &idx); + ret = JS_NewInt32(ctx, c); + } + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue r; + int i; + + /* XXX: Use more efficient method */ + /* XXX: This method is OK if r has a single refcount */ + /* XXX: should use string_buffer? */ + r = JS_ToStringCheckObject(ctx, this_val); + for (i = 0; i < argc; i++) { + if (JS_IsException(r)) + break; + r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i])); + } + return r; +} + +static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len) +{ + int i, c1, c2; + for (i = 0; i < len; i++) { + if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i))) + return c1 - c2; + } + return 0; +} + +static int string_indexof_char(JSString *p, int c, int from) +{ + /* assuming 0 <= from <= p->len */ + int i, len = p->len; + if (p->is_wide_char) { + for (i = from; i < len; i++) { + if (p->u.str16[i] == c) + return i; + } + } else { + if ((c & ~0xff) == 0) { + for (i = from; i < len; i++) { + if (p->u.str8[i] == (uint8_t)c) + return i; + } + } + } + return -1; +} + +static int string_indexof(JSString *p1, JSString *p2, int from) +{ + /* assuming 0 <= from <= p1->len */ + int c, i, j, len1 = p1->len, len2 = p2->len; + if (len2 == 0) + return from; + for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) { + j = string_indexof_char(p1, c, i); + if (j < 0 || j + len2 > len1) + break; + if (!string_cmp(p1, p2, j + 1, 1, len2 - 1)) + return j; + } + return -1; +} + +static int string_advance_index(JSString *p, int index, BOOL unicode) +{ + if (!unicode || (unsigned)index >= p->len || !p->is_wide_char) { + index++; + } else { + string_getc(p, &index); + } + return index; +} + +static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int lastIndexOf) +{ + JSValue str, v; + int i, len, v_len, pos, start, stop, ret, inc; + JSString *p; + JSString *p1; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + v = JS_ToString(ctx, argv[0]); + if (JS_IsException(v)) + goto fail; + p = JS_VALUE_GET_STRING(str); + p1 = JS_VALUE_GET_STRING(v); + len = p->len; + v_len = p1->len; + if (lastIndexOf) { + pos = len - v_len; + if (argc > 1) { + double d; + if (JS_ToFloat64(ctx, &d, argv[1])) + goto fail; + if (!isnan(d)) { + if (d <= 0) + pos = 0; + else if (d < pos) + pos = d; + } + } + start = pos; + stop = 0; + inc = -1; + } else { + pos = 0; + if (argc > 1) { + if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) + goto fail; + } + start = pos; + stop = len - v_len; + inc = 1; + } + ret = -1; + if (len >= v_len && inc * (stop - start) >= 0) { + for (i = start;; i += inc) { + if (!string_cmp(p, p1, i, 0, v_len)) { + ret = i; + break; + } + if (i == stop) + break; + } + } + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_NewInt32(ctx, ret); + +fail: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_EXCEPTION; +} + +/* return < 0 if exception or TRUE/FALSE */ +static int js_is_regexp(JSContext *ctx, JSValueConst obj); + +static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue str, v = JS_UNDEFINED; + int i, len, v_len, pos, start, stop, ret; + JSString *p; + JSString *p1; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + ret = js_is_regexp(ctx, argv[0]); + if (ret) { + if (ret > 0) + JS_ThrowTypeError(ctx, "regex not supported"); + goto fail; + } + v = JS_ToString(ctx, argv[0]); + if (JS_IsException(v)) + goto fail; + p = JS_VALUE_GET_STRING(str); + p1 = JS_VALUE_GET_STRING(v); + len = p->len; + v_len = p1->len; + pos = (magic & 2) ? len : 0; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) + goto fail; + } + len -= v_len; + start = pos; + stop = len; + if (magic & 1) { + stop = pos; + } + if (magic & 2) { + pos -= v_len; + start = stop = pos; + } + ret = 0; + 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; + } + } + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_NewBool(ctx, ret); + +fail: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_EXCEPTION; +} + +static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp) +{ + int ret; + JSValue flags; + + ret = js_is_regexp(ctx, regexp); + if (ret < 0) + return -1; + if (ret) { + flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags); + if (JS_IsException(flags)) + return -1; + if (JS_IsUndefined(flags) || JS_IsNull(flags)) { + JS_ThrowTypeError(ctx, "cannot convert to object"); + return -1; + } + flags = JS_ToStringFree(ctx, flags); + if (JS_IsException(flags)) + return -1; + ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0); + JS_FreeValue(ctx, flags); + if (ret < 0) { + JS_ThrowTypeError(ctx, "regexp must have the 'g' flag"); + return -1; + } + } + return 0; +} + +static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int atom) +{ + // match(rx), search(rx), matchAll(rx) + // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll + JSValueConst O = this_val, regexp = argv[0], args[2]; + JSValue matcher, S, rx, result, str; + int args_len; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) { + matcher = JS_GetProperty(ctx, regexp, atom); + if (JS_IsException(matcher)) + return JS_EXCEPTION; + if (atom == JS_ATOM_Symbol_matchAll) { + if (check_regexp_g_flag(ctx, regexp) < 0) { + JS_FreeValue(ctx, matcher); + return JS_EXCEPTION; + } + } + if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) { + return JS_CallFree(ctx, matcher, regexp, 1, &O); + } + } + S = JS_ToString(ctx, O); + if (JS_IsException(S)) + return JS_EXCEPTION; + args_len = 1; + args[0] = regexp; + str = JS_UNDEFINED; + if (atom == JS_ATOM_Symbol_matchAll) { + str = JS_NewString(ctx, "g"); + if (JS_IsException(str)) + goto fail; + args[args_len++] = (JSValueConst)str; + } + rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); + JS_FreeValue(ctx, str); + if (JS_IsException(rx)) { + fail: + JS_FreeValue(ctx, S); + return JS_EXCEPTION; + } + result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S); + JS_FreeValue(ctx, S); + return result; +} + +static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // GetSubstitution(matched, str, position, captures, namedCaptures, rep) + JSValueConst matched, str, captures, namedCaptures, rep; + JSValue capture, name, s; + uint32_t position, len, matched_len, captures_len; + int i, j, j0, k, k1; + int c, c1; + StringBuffer b_s, *b = &b_s; + JSString *sp, *rp; + + matched = argv[0]; + str = argv[1]; + captures = argv[3]; + namedCaptures = argv[4]; + rep = argv[5]; + + if (!JS_IsString(rep) || !JS_IsString(str)) + return JS_ThrowTypeError(ctx, "not a string"); + + sp = JS_VALUE_GET_STRING(str); + rp = JS_VALUE_GET_STRING(rep); + + string_buffer_init(ctx, b, 0); + + captures_len = 0; + if (!JS_IsUndefined(captures)) { + if (js_get_length32(ctx, &captures_len, captures)) + goto exception; + } + if (js_get_length32(ctx, &matched_len, matched)) + goto exception; + if (JS_ToUint32(ctx, &position, argv[2]) < 0) + goto exception; + + len = rp->len; + i = 0; + for(;;) { + j = string_indexof_char(rp, '$', i); + if (j < 0 || j + 1 >= len) + break; + string_buffer_concat(b, rp, i, j); + j0 = j++; + c = string_get(rp, j++); + if (c == '$') { + string_buffer_putc8(b, '$'); + } else if (c == '&') { + if (string_buffer_concat_value(b, matched)) + goto exception; + } else if (c == '`') { + string_buffer_concat(b, sp, 0, position); + } else if (c == '\'') { + string_buffer_concat(b, sp, position + matched_len, sp->len); + } else if (c >= '0' && c <= '9') { + k = c - '0'; + c1 = string_get(rp, j); + if (c1 >= '0' && c1 <= '9') { + /* This behavior is specified in ES6 and refined in ECMA 2019 */ + /* ECMA 2019 does not have the extra test, but + Test262 S15.5.4.11_A3_T1..3 require this behavior */ + k1 = k * 10 + c1 - '0'; + if (k1 >= 1 && k1 < captures_len) { + k = k1; + j++; + } + } + if (k >= 1 && k < captures_len) { + s = JS_GetPropertyInt64(ctx, captures, k); + if (JS_IsException(s)) + goto exception; + if (!JS_IsUndefined(s)) { + if (string_buffer_concat_value_free(b, s)) + goto exception; + } + } else { + goto norep; + } + } else if (c == '<' && !JS_IsUndefined(namedCaptures)) { + k = string_indexof_char(rp, '>', j); + if (k < 0) + goto norep; + name = js_sub_string(ctx, rp, j, k); + if (JS_IsException(name)) + goto exception; + capture = JS_GetPropertyValue(ctx, namedCaptures, name); + if (JS_IsException(capture)) + goto exception; + if (!JS_IsUndefined(capture)) { + if (string_buffer_concat_value_free(b, capture)) + goto exception; + } + j = k + 1; + } else { + norep: + string_buffer_concat(b, rp, j0, j); + } + i = j; + } + string_buffer_concat(b, rp, i, rp->len); + return string_buffer_end(b); +exception: + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int is_replaceAll) +{ + // replace(rx, rep) + JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1]; + JSValueConst args[6]; + JSValue str, search_str, replaceValue_str, repl_str; + JSString *sp, *searchp; + StringBuffer b_s, *b = &b_s; + int pos, functionalReplace, endOfLastMatch; + BOOL is_first; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + search_str = JS_UNDEFINED; + replaceValue_str = JS_UNDEFINED; + repl_str = JS_UNDEFINED; + + if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) { + JSValue replacer; + if (is_replaceAll) { + if (check_regexp_g_flag(ctx, searchValue) < 0) + return JS_EXCEPTION; + } + replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace); + if (JS_IsException(replacer)) + return JS_EXCEPTION; + if (!JS_IsUndefined(replacer)) { + args[0] = O; + args[1] = replaceValue; + return JS_CallFree(ctx, replacer, searchValue, 2, args); + } + } + string_buffer_init(ctx, b, 0); + + str = JS_ToString(ctx, O); + if (JS_IsException(str)) + goto exception; + search_str = JS_ToString(ctx, searchValue); + if (JS_IsException(search_str)) + goto exception; + functionalReplace = JS_IsFunction(ctx, replaceValue); + if (!functionalReplace) { + replaceValue_str = JS_ToString(ctx, replaceValue); + if (JS_IsException(replaceValue_str)) + goto exception; + } + + sp = JS_VALUE_GET_STRING(str); + searchp = JS_VALUE_GET_STRING(search_str); + endOfLastMatch = 0; + is_first = TRUE; + for(;;) { + if (unlikely(searchp->len == 0)) { + if (is_first) + pos = 0; + else if (endOfLastMatch >= sp->len) + pos = -1; + else + pos = endOfLastMatch + 1; + } else { + pos = string_indexof(sp, searchp, endOfLastMatch); + } + if (pos < 0) { + if (is_first) { + string_buffer_free(b); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + return str; + } else { + break; + } + } + if (functionalReplace) { + args[0] = search_str; + args[1] = JS_NewInt32(ctx, pos); + args[2] = str; + repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args)); + } else { + args[0] = search_str; + args[1] = str; + args[2] = JS_NewInt32(ctx, pos); + args[3] = JS_UNDEFINED; + args[4] = JS_UNDEFINED; + args[5] = replaceValue_str; + repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); + } + if (JS_IsException(repl_str)) + goto exception; + + string_buffer_concat(b, sp, endOfLastMatch, pos); + string_buffer_concat_value_free(b, repl_str); + endOfLastMatch = pos + searchp->len; + is_first = FALSE; + if (!is_replaceAll) + break; + } + string_buffer_concat(b, sp, endOfLastMatch, sp->len); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +exception: + string_buffer_free(b); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + JS_FreeValue(ctx, str); + return JS_EXCEPTION; +} + +static JSValue js_string_split(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // split(sep, limit) + JSValueConst O = this_val, separator = argv[0], limit = argv[1]; + JSValueConst args[2]; + JSValue S, A, R, T; + uint32_t lim, lengthA; + int64_t p, q, s, r, e; + JSString *sp, *rp; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + S = JS_UNDEFINED; + A = JS_UNDEFINED; + R = JS_UNDEFINED; + + if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) { + JSValue splitter; + splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split); + if (JS_IsException(splitter)) + return JS_EXCEPTION; + if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) { + args[0] = O; + args[1] = limit; + return JS_CallFree(ctx, splitter, separator, 2, args); + } + } + S = JS_ToString(ctx, O); + if (JS_IsException(S)) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + lengthA = 0; + if (JS_IsUndefined(limit)) { + lim = 0xffffffff; + } else { + if (JS_ToUint32(ctx, &lim, limit) < 0) + goto exception; + } + sp = JS_VALUE_GET_STRING(S); + s = sp->len; + R = JS_ToString(ctx, separator); + if (JS_IsException(R)) + goto exception; + rp = JS_VALUE_GET_STRING(R); + r = rp->len; + p = 0; + if (lim == 0) + goto done; + if (JS_IsUndefined(separator)) + goto add_tail; + if (s == 0) { + if (r != 0) + goto add_tail; + goto done; + } + q = p; + for (q = p; (q += !r) <= s - r - !r; q = p = e + r) { + e = string_indexof(sp, rp, q); + if (e < 0) + break; + T = js_sub_string(ctx, sp, p, e); + if (JS_IsException(T)) + goto exception; + if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) + goto exception; + if (lengthA == lim) + goto done; + } +add_tail: + T = js_sub_string(ctx, sp, p, s); + if (JS_IsException(T)) + goto exception; + if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0) + goto exception; +done: + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, R); + return A; + +exception: + JS_FreeValue(ctx, A); + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, R); + return JS_EXCEPTION; +} + +static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str, ret; + int a, b, start, end; + JSString *p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + b = p->len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + if (a < b) { + start = a; + end = b; + } else { + start = b; + end = a; + } + ret = js_sub_string(ctx, p, start, end); + JS_FreeValue(ctx, str); + return ret; +} + +static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str, ret; + int a, len, n; + JSString *p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + n = len - a; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + ret = js_sub_string(ctx, p, a, a + n); + JS_FreeValue(ctx, str); + return ret; +} + +static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str, ret; + int len, start, end; + JSString *p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + end = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + ret = js_sub_string(ctx, p, start, max_int(end, start)); + JS_FreeValue(ctx, str); + return ret; +} + +static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int padEnd) +{ + JSValue str, v = JS_UNDEFINED; + StringBuffer b_s, *b = &b_s; + JSString *p, *p1 = NULL; + int n, len, c = ' '; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + goto fail1; + if (JS_ToInt32Sat(ctx, &n, argv[0])) + goto fail2; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (len >= n) + return str; + if (n > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + goto fail2; + } + if (argc > 1 && !JS_IsUndefined(argv[1])) { + v = JS_ToString(ctx, argv[1]); + if (JS_IsException(v)) + goto fail2; + p1 = JS_VALUE_GET_STRING(v); + if (p1->len == 0) { + JS_FreeValue(ctx, v); + return str; + } + if (p1->len == 1) { + c = string_get(p1, 0); + p1 = NULL; + } + } + if (string_buffer_init(ctx, b, n)) + goto fail3; + n -= len; + if (padEnd) { + if (string_buffer_concat(b, p, 0, len)) + goto fail; + } + if (p1) { + while (n > 0) { + int chunk = min_int(n, p1->len); + if (string_buffer_concat(b, p1, 0, chunk)) + goto fail; + n -= chunk; + } + } else { + if (string_buffer_fill(b, c, n)) + goto fail; + } + if (!padEnd) { + if (string_buffer_concat(b, p, 0, len)) + goto fail; + } + JS_FreeValue(ctx, v); + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + string_buffer_free(b); +fail3: + JS_FreeValue(ctx, v); +fail2: + JS_FreeValue(ctx, str); +fail1: + return JS_EXCEPTION; +} + +static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int64_t val; + int n, len; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + goto fail; + if (JS_ToInt64Sat(ctx, &val, argv[0])) + goto fail; + if (val < 0 || val > 2147483647) { + JS_ThrowRangeError(ctx, "invalid repeat count"); + goto fail; + } + n = val; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (len == 0 || n == 1) + return str; + if (val * len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + goto fail; + } + if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) + goto fail; + if (len == 1) { + string_buffer_fill(b, string_get(p, 0), n); + } else { + while (n-- > 0) { + string_buffer_concat(b, p, 0, len); + } + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + JS_FreeValue(ctx, str); + return JS_EXCEPTION; +} + +static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue str, ret; + int a, b, len; + JSString *p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + a = 0; + b = len = p->len; + if (magic & 1) { + while (a < len && lre_is_space(string_get(p, a))) + a++; + } + if (magic & 2) { + while (b > a && lre_is_space(string_get(p, b - 1))) + b--; + } + ret = js_sub_string(ctx, p, a, b); + JS_FreeValue(ctx, str); + return ret; +} + +static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToQuotedString(ctx, this_val); +} + +/* return 0 if before the first char */ +static int string_prevc(JSString *p, int *pidx) +{ + int idx, c, c1; + + idx = *pidx; + if (idx <= 0) + return 0; + idx--; + if (p->is_wide_char) { + c = p->u.str16[idx]; + if (c >= 0xdc00 && c < 0xe000 && idx > 0) { + c1 = p->u.str16[idx - 1]; + if (c1 >= 0xd800 && c1 <= 0xdc00) { + c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; + idx--; + } + } + } else { + c = p->u.str8[idx]; + } + *pidx = idx; + return c; +} + +static BOOL test_final_sigma(JSString *p, int sigma_pos) +{ + int k, c1; + + /* before C: skip case ignorable chars and check there is + a cased letter */ + k = sigma_pos; + for(;;) { + c1 = string_prevc(p, &k); + if (!lre_is_case_ignorable(c1)) + break; + } + if (!lre_is_cased(c1)) + return FALSE; + + /* after C: skip case ignorable chars and check there is + no cased letter */ + k = sigma_pos + 1; + for(;;) { + if (k >= p->len) + return TRUE; + c1 = string_getc(p, &k); + if (!lre_is_case_ignorable(c1)) + break; + } + return !lre_is_cased(c1); +} + +static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue a, b; + int cmp; + + a = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(a)) + return JS_EXCEPTION; + b = JS_ToString(ctx, argv[0]); + if (JS_IsException(b)) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); + JS_FreeValue(ctx, a); + JS_FreeValue(ctx, b); + return JS_NewInt32(ctx, cmp); +} + +static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int to_lower) +{ + JSValue val; + StringBuffer b_s, *b = &b_s; + JSString *p; + int i, c, j, l; + uint32_t res[LRE_CC_RES_LEN_MAX]; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (p->len == 0) + return val; + if (string_buffer_init(ctx, b, p->len)) + goto fail; + for(i = 0; i < p->len;) { + c = string_getc(p, &i); + if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) { + res[0] = 0x3c2; /* final sigma */ + l = 1; + } else { + l = lre_case_conv(res, c, to_lower); + } + for(j = 0; j < l; j++) { + if (string_buffer_putc(b, res[j])) + goto fail; + } + } + JS_FreeValue(ctx, val); + return string_buffer_end(b); + fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} + +#ifdef CONFIG_ALL_UNICODE + +/* return (-1, NULL) if exception, otherwise (len, buf) */ +static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1) +{ + JSValue val; + JSString *p; + uint32_t *buf; + int i, j, len; + + val = JS_ToString(ctx, val1); + if (JS_IsException(val)) + return -1; + p = JS_VALUE_GET_STRING(val); + len = p->len; + /* UTF32 buffer length is len minus the number of correct surrogates pairs */ + buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1)); + if (!buf) { + JS_FreeValue(ctx, val); + goto fail; + } + for(i = j = 0; i < len;) + buf[j++] = string_getc(p, &i); + JS_FreeValue(ctx, val); + *pbuf = buf; + return j; + fail: + *pbuf = NULL; + return -1; +} + +static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len) +{ + int i; + StringBuffer b_s, *b = &b_s; + if (string_buffer_init(ctx, b, len)) + return JS_EXCEPTION; + for(i = 0; i < len; i++) { + if (string_buffer_putc(b, buf[i])) + goto fail; + } + return string_buffer_end(b); + fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *form, *p; + size_t form_len; + int is_compat, buf_len, out_len; + UnicodeNormalizationEnum n_type; + JSValue val; + uint32_t *buf, *out_buf; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + buf_len = JS_ToUTF32String(ctx, &buf, val); + JS_FreeValue(ctx, val); + if (buf_len < 0) + return JS_EXCEPTION; + + if (argc == 0 || JS_IsUndefined(argv[0])) { + n_type = UNICODE_NFC; + } else { + form = JS_ToCStringLen(ctx, &form_len, argv[0]); + if (!form) + goto fail1; + p = form; + if (p[0] != 'N' || p[1] != 'F') + goto bad_form; + p += 2; + is_compat = FALSE; + if (*p == 'K') { + is_compat = TRUE; + p++; + } + if (*p == 'C' || *p == 'D') { + n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C'); + if ((p + 1 - form) != form_len) + goto bad_form; + } else { + bad_form: + JS_FreeCString(ctx, form); + JS_ThrowRangeError(ctx, "bad normalization form"); + fail1: + js_free(ctx, buf); + return JS_EXCEPTION; + } + JS_FreeCString(ctx, form); + } + + out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, + ctx->rt, (DynBufReallocFunc *)js_realloc_rt); + js_free(ctx, buf); + if (out_len < 0) + return JS_EXCEPTION; + val = JS_NewUTF32String(ctx, out_buf, out_len); + js_free(ctx, out_buf); + return val; +} +#endif /* CONFIG_ALL_UNICODE */ + +/* also used for String.prototype.valueOf */ +static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisStringValue(ctx, this_val); +} + +#if 0 +static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToStringCheckObject(ctx, argv[0]); +} + +static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToString(ctx, argv[0]); +} + +static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst + this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + int idx; + BOOL is_unicode; + JSString *p; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + if (JS_ToInt32Sat(ctx, &idx, argv[1])) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + is_unicode = JS_ToBool(ctx, argv[2]); + p = JS_VALUE_GET_STRING(str); + if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) { + idx++; + } else { + string_getc(p, &idx); + } + JS_FreeValue(ctx, str); + return JS_NewInt32(ctx, idx); +} +#endif + +/* String Iterator */ + +static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSArrayIteratorData *it; + uint32_t idx, c, start; + JSString *p; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR); + if (!it) { + *pdone = FALSE; + return JS_EXCEPTION; + } + if (JS_IsUndefined(it->obj)) + goto done; + p = JS_VALUE_GET_STRING(it->obj); + idx = it->idx; + if (idx >= p->len) { + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + *pdone = TRUE; + return JS_UNDEFINED; + } + + start = idx; + c = string_getc(p, (int *)&idx); + it->idx = idx; + *pdone = FALSE; + if (c <= 0xffff) { + return js_new_string_char(ctx, c); + } else { + return js_new_string16(ctx, p->u.str16 + start, 2); + } +} + +/* ES6 Annex B 2.3.2 etc. */ +enum { + magic_string_anchor, + magic_string_big, + magic_string_blink, + magic_string_bold, + magic_string_fixed, + magic_string_fontcolor, + magic_string_fontsize, + magic_string_italics, + magic_string_link, + magic_string_small, + magic_string_strike, + magic_string_sub, + magic_string_sup, +}; + +static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue str; + const JSString *p; + StringBuffer b_s, *b = &b_s; + static struct { const char *tag, *attr; } const defs[] = { + { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL }, + { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL }, + { "a", "href" }, { "small", NULL }, { "strike", NULL }, + { "sub", NULL }, { "sup", NULL }, + }; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + string_buffer_init(ctx, b, 7); + string_buffer_putc8(b, '<'); + string_buffer_puts8(b, defs[magic].tag); + if (defs[magic].attr) { + // r += " " + attr + "=\"" + value + "\""; + JSValue value; + int i; + + string_buffer_putc8(b, ' '); + string_buffer_puts8(b, defs[magic].attr); + string_buffer_puts8(b, "=\""); + value = JS_ToStringCheckObject(ctx, argv[0]); + if (JS_IsException(value)) { + JS_FreeValue(ctx, str); + string_buffer_free(b); + return JS_EXCEPTION; + } + p = JS_VALUE_GET_STRING(value); + for (i = 0; i < p->len; i++) { + int c = string_get(p, i); + if (c == '"') { + string_buffer_puts8(b, """); + } else { + string_buffer_putc16(b, c); + } + } + JS_FreeValue(ctx, value); + string_buffer_putc8(b, '\"'); + } + // return r + ">" + str + ""; + string_buffer_putc8(b, '>'); + string_buffer_concat_value_free(b, str); + string_buffer_puts8(b, "'); + return string_buffer_end(b); +} + +static const JSCFunctionListEntry js_string_funcs[] = { + JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ), + JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ), + JS_CFUNC_DEF("raw", 1, js_string_raw ), + //JS_CFUNC_DEF("__toString", 1, js_string___toString ), + //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ), + //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ), + //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ), + //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ), +}; + +static const JSCFunctionListEntry js_string_proto_funcs[] = { + JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ), + JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ), + JS_CFUNC_DEF("charAt", 1, js_string_charAt ), + JS_CFUNC_DEF("concat", 1, js_string_concat ), + JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ), + JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ), + JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ), + JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ), + JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ), + JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ), + JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ), + JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ), + JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ), + JS_CFUNC_DEF("split", 2, js_string_split ), + JS_CFUNC_DEF("substring", 2, js_string_substring ), + JS_CFUNC_DEF("substr", 2, js_string_substr ), + JS_CFUNC_DEF("slice", 2, js_string_slice ), + JS_CFUNC_DEF("repeat", 1, js_string_repeat ), + JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ), + JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ), + JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ), + JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ), + JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ), + JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ), + JS_ALIAS_DEF("trimRight", "trimEnd" ), + JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ), + JS_ALIAS_DEF("trimLeft", "trimStart" ), + JS_CFUNC_DEF("toString", 0, js_string_toString ), + JS_CFUNC_DEF("valueOf", 0, js_string_toString ), + JS_CFUNC_DEF("__quote", 1, js_string___quote ), + JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ), + JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), + JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ), + JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ), + JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ), + JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ), + /* ES6 Annex B 2.3.2 etc. */ + JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ), + JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ), + JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ), + JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ), + JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ), + JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ), + JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ), + JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ), + JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ), + JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ), + JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ), + JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ), + JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ), +}; + +static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ), +}; + +#ifdef CONFIG_ALL_UNICODE +static const JSCFunctionListEntry js_string_proto_normalize[] = { + JS_CFUNC_DEF("normalize", 0, js_string_normalize ), +}; +#endif + +void JS_AddIntrinsicStringNormalize(JSContext *ctx) +{ +#ifdef CONFIG_ALL_UNICODE + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize, + countof(js_string_proto_normalize)); +#endif +} + +/* Math */ + +/* precondition: a and b are not NaN */ +static double js_fmin(double a, double b) +{ + if (a == 0 && b == 0) { + JSFloat64Union a1, b1; + a1.d = a; + b1.d = b; + a1.u64 |= b1.u64; + return a1.d; + } else { + return fmin(a, b); + } +} + +/* precondition: a and b are not NaN */ +static double js_fmax(double a, double b) +{ + if (a == 0 && b == 0) { + JSFloat64Union a1, b1; + a1.d = a; + b1.d = b; + a1.u64 &= b1.u64; + return a1.d; + } else { + return fmax(a, b); + } +} + +#ifdef CONFIG_BIGNUM + +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_s, *r = &r_s; + JSFloatEnv *fe; + int rnd_mode; + JSValue op1; + + op1 = JS_ToNumber(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + a = JS_ToBigFloat(ctx, &a_s, op1); + fe = &ctx->fp_env; + if (argc > 1) { + fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); + if (!fe) { + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + } + + bf_init(ctx->bf_ctx, r); + 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_RNDNU; + 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 JS_NewBigFloat(ctx, r); +} + +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; + + op1 = JS_ToNumber(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + op2 = JS_ToNumber(ctx, argv[1]); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return op2; + } + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + fe = &ctx->fp_env; + if (argc > 2) { + fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); + if (!fe) { + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + } + + bf_init(ctx->bf_ctx, r); + 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_QUICKS); + break; + case MATH_OP_FMOD: + fe->status |= bf_fmod(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_REM: + fe->status |= bf_remainder(r, a, b, fe->prec, fe->flags); + 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 JS_NewBigFloat(ctx, r); +} + +static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + BOOL is_max = magic; + JSValue val, ret; + 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) { + ret = JS_NewInt32(ctx, 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); + } + ret = JS_NewInt32(ctx, r1); + } else { + ret = JS_ToNumber(ctx, argv[0]); + if (JS_IsException(ret)) + return ret; + i = 1; + generic_case: + for(; i < argc; i++) { + val = JS_ToNumber(ctx, argv[i]); + if (JS_IsException(val)) { + JS_FreeValue(ctx, ret); + return val; + } + if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(ret)) && + JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(val))) { + double r, a; + r = JS_VALUE_GET_FLOAT64(ret); + a = JS_VALUE_GET_FLOAT64(val); + if (!isnan(r)) { + if (isnan(a)) { + r = a; + } else { + if (is_max) + r = js_fmax(r, a); + else + r = js_fmin(r, a); + } + ret = __JS_NewFloat64(ctx, r); + } + } else { + bf_t a_s, *a, r_s, *r; + int res; + + r = JS_ToBigFloat(ctx, &r_s, ret); + if (!bf_is_nan(r)) { + a = JS_ToBigFloat(ctx, &a_s, val); + res = bf_cmp_full(a, r); + if (is_max) + res = -res; + if (bf_is_nan(a) || res < 0) { + JS_FreeValue(ctx, ret); + ret = JS_DupValue(ctx, val); + } + if (a == &a_s) + bf_delete(a); + } + if (r == &r_s) + bf_delete(r); + JS_FreeValue(ctx, val); + } + } + } + return ret; +} + +static JSValue js_math_abs(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + uint32_t tag; + + val = JS_ToNumeric(ctx, argv[0]); + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + if (JS_VALUE_GET_INT(val) < 0) + val = JS_NewInt64(ctx, -(int64_t)JS_VALUE_GET_INT(val)); + break; + case JS_TAG_FLOAT64: + val = __JS_NewFloat64(ctx, fabs(JS_VALUE_GET_FLOAT64(val))); + break; + case JS_TAG_BIG_FLOAT: + case JS_TAG_BIG_INT: + { + 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); + r->sign = 0; + JS_FreeValue(ctx, val); + if (tag == JS_TAG_BIG_FLOAT) + val = JS_NewBigFloat(ctx, r); + else + val = JS_NewBigInt2(ctx, r, TRUE); + } + break; + default: + break; + } + return val; +} + +#if 0 +/* XXX: should give exact rounding */ +/* XXX: correct NaN/Infinity handling */ +static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + bf_t a_s, *a, r_s, *r = &r_s, r2_s, *r2 = &r2_s; + JSValue val; + int i; + BOOL is_float; + + bf_init(ctx->bf_ctx, r); + bf_set_si(r, 0); + for(i = 0; i < argc; i++) { + val = JS_ToNumber(ctx, argv[i]); + if (JS_IsException(val)) { + bf_delete(r); + return val; + } + a = JS_ToBigFloat(ctx, &is_float, &a_s, val); + bf_add(r, r, a, ctx->fp_env.prec, ctx->fp_env.flags); + if (a == &a_s) + bf_delete(a); + } + bf_init(ctx->bf_ctx, r2); + bf_sqrt(r2, r, ctx->fp_env.prec, ctx->fp_env.flags); + bf_delete(r); + return JS_NewBigFloat(ctx, r2); +} +#endif + +#else + +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); + } +} + +#endif /* !CONFIG_BIGNUM */ + +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, b; + int i; + + if (argc == 2) { + /* use the more precise built-in function when possible */ + if (JS_ToFloat64(ctx, &a, argv[0])) + return JS_EXCEPTION; + if (JS_ToFloat64(ctx, &b, argv[1])) + return JS_EXCEPTION; + r = hypot(a, b); + } else { + r = 0; + for(i = 0; i < argc; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + r += a; + } + r = sqrt(r); + } + return JS_NewFloat64(ctx, r); +} + +static double js_math_fround(double a) +{ + return (float)a; +} + +static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int a, b; + + if (JS_ToInt32(ctx, &a, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &b, argv[1])) + return JS_EXCEPTION; + /* purposely ignoring overflow */ + return JS_NewInt32(ctx, a * b); +} + +static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint32_t a, r; + + if (JS_ToUint32(ctx, &a, argv[0])) + return JS_EXCEPTION; + if (a == 0) + r = 32; + else + r = clz32(a); + return JS_NewInt32(ctx, r); +} + +/* xorshift* random number generator by Marsaglia */ +static uint64_t xorshift64star(uint64_t *pstate) +{ + uint64_t x; + x = *pstate; + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + *pstate = x; + return x * 0x2545F4914F6CDD1D; +} + +static void js_random_init(JSContext *ctx) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; + /* the state must be non zero */ + if (ctx->random_state == 0) + ctx->random_state = 1; +} + +static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSFloat64Union u; + uint64_t v; + + v = xorshift64star(&ctx->random_state); + /* 1.0 <= u.d < 2 */ + u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); + return __JS_NewFloat64(ctx, u.d - 1.0); +} + +static const JSCFunctionListEntry js_math_funcs[] = { + JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), + JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), +#ifdef CONFIG_BIGNUM + JS_CFUNC_DEF("abs", 1, js_math_abs ), +#else + JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), +#endif + JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), + JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), + JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), + JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ), + + JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ), + JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ), + JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ), + JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ), + JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ), + JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ), + JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ), + JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ), + JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ), + JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ), + /* ES6 */ + JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ), + JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ), + JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ), + JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ), + JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ), + JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ), + JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ), + JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ), + JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ), + JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ), + JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ), + JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ), + JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ), + JS_CFUNC_DEF("hypot", 2, js_math_hypot ), + JS_CFUNC_DEF("random", 0, js_math_random ), + JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), + JS_CFUNC_DEF("imul", 2, js_math_imul ), + JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ), + JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ), + JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ), + JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ), + JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ), + JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ), + JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ), + JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ), + JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ), +}; + +static const JSCFunctionListEntry js_math_obj[] = { + JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +/* Date */ + +#if 0 +/* OS dependent: return the UTC time in ms since 1970. */ +static JSValue js___date_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t d; + struct timeval tv; + gettimeofday(&tv, NULL); + d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); + return JS_NewInt64(ctx, d); +} +#endif + +/* OS dependent: return the UTC time in microseconds since 1970. */ +static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t d; + struct timeval tv; + gettimeofday(&tv, NULL); + d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; + return JS_NewInt64(ctx, d); +} + +/* OS dependent. d = argv[0] is in ms from 1970. Return the difference + between local time and UTC time 'd' in minutes */ +static int getTimezoneOffset(int64_t time) { +#if defined(_WIN32) + /* XXX: TODO */ + return 0; +#else + time_t ti; + struct tm tm; + + time /= 1000; /* convert to seconds */ + if (sizeof(time_t) == 4) { + /* on 32-bit systems, we need to clamp the time value to the + range of `time_t`. This is better than truncating values to + 32 bits and hopefully provides the same result as 64-bit + implementation of localtime_r. + */ + if ((time_t)-1 < 0) { + if (time < INT32_MIN) { + time = INT32_MIN; + } else if (time > INT32_MAX) { + time = INT32_MAX; + } + } else { + if (time < 0) { + time = 0; + } else if (time > UINT32_MAX) { + time = UINT32_MAX; + } + } + } + ti = time; + localtime_r(&ti, &tm); + return -tm.tm_gmtoff / 60; +#endif +} + +#if 0 +static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + double dd; + + if (JS_ToFloat64(ctx, &dd, argv[0])) + return JS_EXCEPTION; + if (isnan(dd)) + return __JS_NewFloat64(ctx, dd); + else + return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd)); +} + +/* create a new date object */ +static JSValue js___date_create(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, proto; + proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]); + if (JS_IsException(proto)) + return proto; + obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE); + JS_FreeValue(ctx, proto); + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2])); + return obj; +} +#endif + +/* RegExp */ + +static void js_regexp_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExp *re = &p->u.regexp; + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); +} + +/* create a string containing the RegExp bytecode */ +static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, + JSValueConst flags) +{ + const char *str; + int re_flags, mask; + uint8_t *re_bytecode_buf; + size_t i, len; + int re_bytecode_len; + JSValue ret; + char error_msg[64]; + + re_flags = 0; + if (!JS_IsUndefined(flags)) { + str = JS_ToCStringLen(ctx, &len, flags); + if (!str) + return JS_EXCEPTION; + /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ + for (i = 0; i < len; i++) { + switch(str[i]) { + case 'g': + mask = LRE_FLAG_GLOBAL; + break; + case 'i': + mask = LRE_FLAG_IGNORECASE; + break; + case 'm': + mask = LRE_FLAG_MULTILINE; + break; + case 's': + mask = LRE_FLAG_DOTALL; + break; + case 'u': + mask = LRE_FLAG_UTF16; + break; + case 'y': + mask = LRE_FLAG_STICKY; + break; + default: + goto bad_flags; + } + if ((re_flags & mask) != 0) { + bad_flags: + JS_FreeCString(ctx, str); + return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); + } + re_flags |= mask; + } + JS_FreeCString(ctx, str); + } + + str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16)); + if (!str) + return JS_EXCEPTION; + re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, + sizeof(error_msg), str, len, re_flags, ctx); + JS_FreeCString(ctx, str); + if (!re_bytecode_buf) { + JS_ThrowSyntaxError(ctx, "%s", error_msg); + return JS_EXCEPTION; + } + + ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len); + js_free(ctx, re_bytecode_buf); + return ret; +} + +/* create a RegExp object from a string containing the RegExp bytecode + and the source pattern */ +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, + JSValue pattern, JSValue bc) +{ + JSValue obj; + JSObject *p; + JSRegExp *re; + + /* sanity check */ + if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || + JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) { + JS_ThrowTypeError(ctx, "string expected"); + fail: + JS_FreeValue(ctx, bc); + JS_FreeValue(ctx, pattern); + return JS_EXCEPTION; + } + + obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP); + if (JS_IsException(obj)) + goto fail; + p = JS_VALUE_GET_OBJ(obj); + re = &p->u.regexp; + re->pattern = JS_VALUE_GET_STRING(pattern); + re->bytecode = JS_VALUE_GET_STRING(bc); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0), + JS_PROP_WRITABLE); + return obj; +} + +static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error) +{ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_REGEXP) + return &p->u.regexp; + } + if (throw_error) { + JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); + } + return NULL; +} + +/* return < 0 if exception or TRUE/FALSE */ +static int js_is_regexp(JSContext *ctx, JSValueConst obj) +{ + JSValue m; + + if (!JS_IsObject(obj)) + return FALSE; + m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match); + if (JS_IsException(m)) + return -1; + if (!JS_IsUndefined(m)) + return JS_ToBoolFree(ctx, m); + return js_get_regexp(ctx, obj, FALSE) != NULL; +} + +static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue pattern, flags, bc, val; + JSValueConst pat, flags1; + JSRegExp *re; + int pat_is_regexp; + + pat = argv[0]; + flags1 = argv[1]; + pat_is_regexp = js_is_regexp(ctx, pat); + if (pat_is_regexp < 0) + return JS_EXCEPTION; + if (JS_IsUndefined(new_target)) { + /* called as a function */ + new_target = JS_GetActiveFunction(ctx); + if (pat_is_regexp && JS_IsUndefined(flags1)) { + JSValue ctor; + BOOL res; + ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + res = js_same_value(ctx, ctor, new_target); + JS_FreeValue(ctx, ctor); + if (res) + return JS_DupValue(ctx, pat); + } + } + re = js_get_regexp(ctx, pat, FALSE); + if (re) { + pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); + if (JS_IsUndefined(flags1)) { + bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + goto no_compilation; + } else { + flags = JS_ToString(ctx, flags1); + if (JS_IsException(flags)) + goto fail; + } + } else { + flags = JS_UNDEFINED; + if (pat_is_regexp) { + pattern = JS_GetProperty(ctx, pat, JS_ATOM_source); + if (JS_IsException(pattern)) + goto fail; + if (JS_IsUndefined(flags1)) { + flags = JS_GetProperty(ctx, pat, JS_ATOM_flags); + if (JS_IsException(flags)) + goto fail; + } else { + flags = JS_DupValue(ctx, flags1); + } + } else { + pattern = JS_DupValue(ctx, pat); + flags = JS_DupValue(ctx, flags1); + } + if (JS_IsUndefined(pattern)) { + pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); + } else { + val = pattern; + pattern = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(pattern)) + goto fail; + } + } + bc = js_compile_regexp(ctx, pattern, flags); + if (JS_IsException(bc)) + goto fail; + JS_FreeValue(ctx, flags); + no_compilation: + return js_regexp_constructor_internal(ctx, new_target, pattern, bc); + fail: + JS_FreeValue(ctx, pattern); + JS_FreeValue(ctx, flags); + return JS_EXCEPTION; +} + +static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRegExp *re1, *re; + JSValueConst pattern1, flags1; + JSValue bc, pattern; + + re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + pattern1 = argv[0]; + flags1 = argv[1]; + re1 = js_get_regexp(ctx, pattern1, FALSE); + if (re1) { + if (!JS_IsUndefined(flags1)) + return JS_ThrowTypeError(ctx, "flags must be undefined"); + pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern)); + bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode)); + } else { + bc = JS_UNDEFINED; + if (JS_IsUndefined(pattern1)) + pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); + else + pattern = JS_ToString(ctx, pattern1); + if (JS_IsException(pattern)) + goto fail; + bc = js_compile_regexp(ctx, pattern, flags1); + if (JS_IsException(bc)) + goto fail; + } + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + re->pattern = JS_VALUE_GET_STRING(pattern); + re->bytecode = JS_VALUE_GET_STRING(bc); + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, 0)) < 0) + return JS_EXCEPTION; + return JS_DupValue(ctx, this_val); + fail: + JS_FreeValue(ctx, pattern); + JS_FreeValue(ctx, bc); + return JS_EXCEPTION; +} + +#if 0 +static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); +} + +static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + int flags; + + if (!re) + return JS_EXCEPTION; + flags = lre_get_flags(re->bytecode->u.str8); + return JS_NewInt32(ctx, flags); +} +#endif + +static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val) +{ + JSRegExp *re; + JSString *p; + StringBuffer b_s, *b = &b_s; + int i, n, c, c2, bra; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) + goto empty_regex; + + re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + + p = re->pattern; + + if (p->len == 0) { + empty_regex: + return JS_NewString(ctx, "(?:)"); + } + string_buffer_init2(ctx, b, p->len, p->is_wide_char); + + /* Escape '/' and newline sequences as needed */ + bra = 0; + for (i = 0, n = p->len; i < n;) { + c2 = -1; + switch (c = string_get(p, i++)) { + case '\\': + if (i < n) + c2 = string_get(p, i++); + break; + case ']': + bra = 0; + break; + case '[': + if (!bra) { + if (i < n && string_get(p, i) == ']') + c2 = string_get(p, i++); + bra = 1; + } + break; + case '\n': + c = '\\'; + c2 = 'n'; + break; + case '\r': + c = '\\'; + c2 = 'r'; + break; + case '/': + if (!bra) { + c = '\\'; + c2 = '/'; + } + break; + } + string_buffer_putc16(b, c); + if (c2 >= 0) + string_buffer_putc16(b, c2); + } + return string_buffer_end(b); +} + +static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask) +{ + JSRegExp *re; + int flags; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) + return JS_UNDEFINED; + + re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + + flags = lre_get_flags(re->bytecode->u.str8); + return JS_NewBool(ctx, (flags & mask) != 0); +} + +static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) +{ + char str[8], *p = str; + int res; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global)); + if (res < 0) + goto exception; + if (res) + *p++ = 'g'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase")); + if (res < 0) + goto exception; + if (res) + *p++ = 'i'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline")); + if (res < 0) + goto exception; + if (res) + *p++ = 'm'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll")); + if (res < 0) + goto exception; + if (res) + *p++ = 's'; + res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode)); + if (res < 0) + goto exception; + if (res) + *p++ = 'u'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky")); + if (res < 0) + goto exception; + if (res) + *p++ = 'y'; + return JS_NewStringLen(ctx, str, p - str); + +exception: + return JS_EXCEPTION; +} + +static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue pattern, flags; + StringBuffer b_s, *b = &b_s; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + string_buffer_init(ctx, b, 0); + string_buffer_putc8(b, '/'); + pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source); + if (string_buffer_concat_value_free(b, pattern)) + goto fail; + string_buffer_putc8(b, '/'); + flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags); + if (string_buffer_concat_value_free(b, flags)) + goto fail; + return string_buffer_end(b); + +fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) +{ + JSContext *ctx = opaque; + return js_check_stack_overflow(ctx, alloca_size); +} + +void *lre_realloc(void *opaque, void *ptr, size_t size) +{ + JSContext *ctx = opaque; + /* No JS exception is raised here */ + return js_realloc_rt(ctx->rt, ptr, size); +} + +static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + JSString *str; + JSValue str_val, obj, val, groups = JS_UNDEFINED; + uint8_t *re_bytecode; + int ret; + uint8_t **capture, *str_buf; + int capture_count, shift, i, re_flags; + int64_t last_index; + const char *group_name_ptr; + + if (!re) + return JS_EXCEPTION; + str_val = JS_ToString(ctx, argv[0]); + if (JS_IsException(str_val)) + return str_val; + val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); + if (JS_IsException(val) || + JS_ToLengthFree(ctx, &last_index, val)) { + JS_FreeValue(ctx, str_val); + return JS_EXCEPTION; + } + re_bytecode = re->bytecode->u.str8; + re_flags = lre_get_flags(re_bytecode); + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { + last_index = 0; + } + str = JS_VALUE_GET_STRING(str_val); + capture_count = lre_get_capture_count(re_bytecode); + capture = NULL; + if (capture_count > 0) { + capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); + if (!capture) { + JS_FreeValue(ctx, str_val); + return JS_EXCEPTION; + } + } + shift = str->is_wide_char; + str_buf = str->u.str8; + if (last_index > str->len) { + ret = 2; + } else { + ret = lre_exec(capture, re_bytecode, + str_buf, last_index, str->len, + shift, ctx); + } + obj = JS_NULL; + if (ret != 1) { + if (ret >= 0) { + if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, 0)) < 0) + goto fail; + } + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + goto fail; + } + JS_FreeValue(ctx, str_val); + } else { + int prop_flags; + if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0) + goto fail; + } + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + goto fail; + prop_flags = JS_PROP_C_W_E | JS_PROP_THROW; + group_name_ptr = NULL; + if (re_flags & LRE_FLAG_NAMED_GROUPS) { + uint32_t re_bytecode_len; + groups = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(groups)) + goto fail; + re_bytecode_len = get_u32(re_bytecode + 3); + group_name_ptr = (char *)(re_bytecode + 7 + re_bytecode_len); + } + + for(i = 0; i < capture_count; i++) { + int start, end; + JSValue val; + if (capture[2 * i] == NULL || + capture[2 * i + 1] == NULL) { + val = JS_UNDEFINED; + } else { + start = (capture[2 * i] - str_buf) >> shift; + end = (capture[2 * i + 1] - str_buf) >> shift; + val = js_sub_string(ctx, str, start, end); + if (JS_IsException(val)) + goto fail; + } + if (group_name_ptr && i > 0) { + if (*group_name_ptr) { + if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr, + JS_DupValue(ctx, val), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + group_name_ptr += strlen(group_name_ptr) + 1; + } + if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0) + goto fail; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, + groups, prop_flags) < 0) + goto fail; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, + JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0) + goto fail; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0) + goto fail1; + } + js_free(ctx, capture); + return obj; +fail: + JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, str_val); +fail1: + JS_FreeValue(ctx, obj); + js_free(ctx, capture); + return JS_EXCEPTION; +} + +/* delete partions of a string that match a given regex */ +static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + JSString *str; + JSValue str_val, val; + uint8_t *re_bytecode; + int ret; + uint8_t **capture, *str_buf; + int capture_count, shift, re_flags; + int next_src_pos, start, end; + int64_t last_index; + StringBuffer b_s, *b = &b_s; + + if (!re) + return JS_EXCEPTION; + + string_buffer_init(ctx, b, 0); + + capture = NULL; + str_val = JS_ToString(ctx, arg); + if (JS_IsException(str_val)) + goto fail; + str = JS_VALUE_GET_STRING(str_val); + re_bytecode = re->bytecode->u.str8; + re_flags = lre_get_flags(re_bytecode); + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { + last_index = 0; + } else { + val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); + if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) + goto fail; + } + capture_count = lre_get_capture_count(re_bytecode); + if (capture_count > 0) { + capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); + if (!capture) + goto fail; + } + shift = str->is_wide_char; + str_buf = str->u.str8; + next_src_pos = 0; + for (;;) { + if (last_index > str->len) + break; + + ret = lre_exec(capture, re_bytecode, + str_buf, last_index, str->len, shift, ctx); + if (ret != 1) { + if (ret >= 0) { + if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, 0)) < 0) + goto fail; + } + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + goto fail; + } + break; + } + start = (capture[0] - str_buf) >> shift; + end = (capture[1] - str_buf) >> shift; + last_index = end; + if (next_src_pos < start) { + if (string_buffer_concat(b, str, next_src_pos, start)) + goto fail; + } + next_src_pos = end; + if (!(re_flags & LRE_FLAG_GLOBAL)) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, end)) < 0) + goto fail; + break; + } + if (end == start) { + if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) { + end++; + } else { + string_getc(str, &end); + } + } + last_index = end; + } + if (string_buffer_concat(b, str, next_src_pos, str->len)) + goto fail; + JS_FreeValue(ctx, str_val); + js_free(ctx, capture); + return string_buffer_end(b); +fail: + JS_FreeValue(ctx, str_val); + js_free(ctx, capture); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s) +{ + JSValue method, ret; + + method = JS_GetProperty(ctx, r, JS_ATOM_exec); + if (JS_IsException(method)) + return method; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, r, 1, &s); + if (JS_IsException(ret)) + return ret; + if (!JS_IsObject(ret) && !JS_IsNull(ret)) { + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null"); + } + return ret; + } + JS_FreeValue(ctx, method); + return js_regexp_exec(ctx, r, 1, &s); +} + +#if 0 +static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_RegExpExec(ctx, argv[0], argv[1]); +} +static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_RegExpDelete(ctx, argv[0], argv[1]); +} +#endif + +static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + BOOL ret; + + val = JS_RegExpExec(ctx, this_val, argv[0]); + if (JS_IsException(val)) + return JS_EXCEPTION; + ret = !JS_IsNull(val); + JS_FreeValue(ctx, val); + return JS_NewBool(ctx, ret); +} + +static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // [Symbol.match](str) + JSValueConst rx = this_val; + JSValue A, S, result, matchStr; + int global, n, fullUnicode, isEmpty; + JSString *p; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + A = JS_UNDEFINED; + result = JS_UNDEFINED; + matchStr = JS_UNDEFINED; + S = JS_ToString(ctx, argv[0]); + if (JS_IsException(S)) + goto exception; + + global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global)); + if (global < 0) + goto exception; + + if (!global) { + A = JS_RegExpExec(ctx, rx, S); + } else { + fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); + if (fullUnicode < 0) + goto exception; + + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + n = 0; + for(;;) { + JS_FreeValue(ctx, result); + result = JS_RegExpExec(ctx, rx, S); + if (JS_IsException(result)) + goto exception; + if (JS_IsNull(result)) + break; + matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matchStr)) + goto exception; + isEmpty = JS_IsEmptyString(matchStr); + if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0) + goto exception; + if (isEmpty) { + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, + JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) + goto exception; + p = JS_VALUE_GET_STRING(S); + nextIndex = thisIndex + 1; + if (thisIndex < p->len) + nextIndex = string_advance_index(p, thisIndex, fullUnicode); + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) + goto exception; + } + } + if (n == 0) { + JS_FreeValue(ctx, A); + A = JS_NULL; + } + } + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, S); + return A; + +exception: + JS_FreeValue(ctx, A); + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, S); + return JS_EXCEPTION; +} + +typedef struct JSRegExpStringIteratorData { + JSValue iterating_regexp; + JSValue iterated_string; + BOOL global; + BOOL unicode; + BOOL done; +} JSRegExpStringIteratorData; + +static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; + if (it) { + JS_FreeValueRT(rt, it->iterating_regexp); + JS_FreeValueRT(rt, it->iterated_string); + js_free_rt(rt, it); + } +} + +static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; + if (it) { + JS_MarkValue(rt, it->iterating_regexp, mark_func); + JS_MarkValue(rt, it->iterated_string, mark_func); + } +} + +static JSValue js_regexp_string_iterator_next(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSRegExpStringIteratorData *it; + JSValueConst R, S; + JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED; + JSString *sp; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR); + if (!it) + goto exception; + if (it->done) { + *pdone = TRUE; + return JS_UNDEFINED; + } + R = it->iterating_regexp; + S = it->iterated_string; + match = JS_RegExpExec(ctx, R, S); + if (JS_IsException(match)) + goto exception; + if (JS_IsNull(match)) { + it->done = TRUE; + *pdone = TRUE; + return JS_UNDEFINED; + } else if (it->global) { + matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0)); + if (JS_IsException(matchStr)) + goto exception; + if (JS_IsEmptyString(matchStr)) { + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, + JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0) + goto exception; + sp = JS_VALUE_GET_STRING(S); + nextIndex = string_advance_index(sp, thisIndex, it->unicode); + if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex, + JS_NewInt32(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_NewInt32(ctx, lastIndex)) < 0) + goto exception; + + iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); + if (JS_IsException(iter)) + goto exception; + it = js_malloc(ctx, sizeof(*it)); + if (!it) + goto exception; + it->iterating_regexp = matcher; + it->iterated_string = S; + strp = JS_VALUE_GET_STRING(flags); + it->global = string_indexof_char(strp, 'g', 0) >= 0; + it->unicode = string_indexof_char(strp, 'u', 0) >= 0; + it->done = FALSE; + JS_SetOpaque(iter, it); + + JS_FreeValue(ctx, C); + JS_FreeValue(ctx, flags); + return iter; + exception: + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, C); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, matcher); + JS_FreeValue(ctx, iter); + return JS_EXCEPTION; +} + +typedef struct ValueBuffer { + JSContext *ctx; + JSValue *arr; + JSValue def[4]; + int len; + int size; + int error_status; +} ValueBuffer; + +static int value_buffer_init(JSContext *ctx, ValueBuffer *b) +{ + b->ctx = ctx; + b->len = 0; + b->size = 4; + b->error_status = 0; + b->arr = b->def; + return 0; +} + +static void value_buffer_free(ValueBuffer *b) +{ + while (b->len > 0) + JS_FreeValue(b->ctx, b->arr[--b->len]); + if (b->arr != b->def) + js_free(b->ctx, b->arr); + b->arr = b->def; + b->size = 4; +} + +static int value_buffer_append(ValueBuffer *b, JSValue val) +{ + if (b->error_status) + return -1; + + if (b->len >= b->size) { + int new_size = (b->len + (b->len >> 1) + 31) & ~16; + size_t slack; + JSValue *new_arr; + + if (b->arr == b->def) { + new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack); + if (new_arr) + memcpy(new_arr, b->def, sizeof b->def); + } else { + new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack); + } + if (!new_arr) { + value_buffer_free(b); + JS_FreeValue(b->ctx, val); + b->error_status = -1; + return -1; + } + new_size += slack / sizeof(*new_arr); + b->arr = new_arr; + b->size = new_size; + } + b->arr[b->len++] = val; + return 0; +} + +static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx) +{ + JSValue val; + int res; + + val = JS_GetProperty(ctx, rx, JS_ATOM_constructor); + if (JS_IsException(val)) + return -1; + // rx.constructor === RegExp + res = js_same_value(ctx, val, ctx->regexp_ctor); + JS_FreeValue(ctx, val); + if (res) { + val = JS_GetProperty(ctx, rx, JS_ATOM_exec); + if (JS_IsException(val)) + return -1; + // rx.exec === RE_exec + res = JS_IsCFunction(ctx, val, js_regexp_exec, 0); + JS_FreeValue(ctx, val); + } + return res; +} + +static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // [Symbol.replace](str, rep) + JSValueConst rx = this_val, rep = argv[1]; + JSValueConst args[6]; + JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res; + JSString *sp, *rp; + StringBuffer b_s, *b = &b_s; + ValueBuffer v_b, *results = &v_b; + int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode; + uint32_t nCaptures; + int64_t position; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + string_buffer_init(ctx, b, 0); + value_buffer_init(ctx, results); + + rep_val = JS_UNDEFINED; + matched = JS_UNDEFINED; + tab = JS_UNDEFINED; + rep_str = JS_UNDEFINED; + namedCaptures = JS_UNDEFINED; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + + sp = JS_VALUE_GET_STRING(str); + rp = NULL; + functionalReplace = JS_IsFunction(ctx, rep); + if (!functionalReplace) { + rep_val = JS_ToString(ctx, rep); + if (JS_IsException(rep_val)) + goto exception; + rp = JS_VALUE_GET_STRING(rep_val); + } + fullUnicode = 0; + is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global)); + if (is_global < 0) + goto exception; + if (is_global) { + fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); + if (fullUnicode < 0) + goto exception; + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) + goto exception; + } + + if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) { + /* use faster version for simple cases */ + res = JS_RegExpDelete(ctx, rx, str); + goto done; + } + for(;;) { + JSValue result; + result = JS_RegExpExec(ctx, rx, str); + if (JS_IsException(result)) + goto exception; + if (JS_IsNull(result)) + break; + if (value_buffer_append(results, result) < 0) + goto exception; + if (!is_global) + break; + JS_FreeValue(ctx, matched); + matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matched)) + goto exception; + if (JS_IsEmptyString(matched)) { + /* always advance of at least one char */ + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) + goto exception; + nextIndex = string_advance_index(sp, thisIndex, fullUnicode); + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, nextIndex)) < 0) + goto exception; + } + } + nextSourcePosition = 0; + for(j = 0; j < results->len; j++) { + JSValueConst result; + result = results->arr[j]; + if (js_get_length32(ctx, &nCaptures, result) < 0) + goto exception; + JS_FreeValue(ctx, matched); + matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matched)) + goto exception; + if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index))) + goto exception; + if (position > sp->len) + position = sp->len; + else if (position < 0) + position = 0; + /* ignore substition if going backward (can happen + with custom regexp object) */ + JS_FreeValue(ctx, tab); + tab = JS_NewArray(ctx); + if (JS_IsException(tab)) + goto exception; + if (JS_SetPropertyInt64(ctx, tab, 0, JS_DupValue(ctx, matched)) < 0) + goto exception; + for(n = 1; n < nCaptures; n++) { + JSValue capN; + capN = JS_GetPropertyInt64(ctx, result, n); + if (JS_IsException(capN)) + goto exception; + if (!JS_IsUndefined(capN)) { + capN = JS_ToStringFree(ctx, capN); + if (JS_IsException(capN)) + goto exception; + } + if (JS_SetPropertyInt64(ctx, tab, n, capN) < 0) + goto exception; + } + JS_FreeValue(ctx, namedCaptures); + namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups); + if (JS_IsException(namedCaptures)) + goto exception; + if (functionalReplace) { + if (JS_SetPropertyInt64(ctx, tab, n++, JS_NewInt32(ctx, position)) < 0) + goto exception; + if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, str)) < 0) + goto exception; + if (!JS_IsUndefined(namedCaptures)) { + if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures)) < 0) + goto exception; + } + args[0] = JS_UNDEFINED; + args[1] = tab; + JS_FreeValue(ctx, rep_str); + rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0)); + } else { + args[0] = matched; + args[1] = str; + args[2] = JS_NewInt32(ctx, position); + args[3] = tab; + args[4] = namedCaptures; + args[5] = rep_val; + JS_FreeValue(ctx, rep_str); + rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); + } + if (JS_IsException(rep_str)) + goto exception; + if (position >= nextSourcePosition) { + string_buffer_concat(b, sp, nextSourcePosition, position); + string_buffer_concat_value(b, rep_str); + nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len; + } + } + string_buffer_concat(b, sp, nextSourcePosition, sp->len); + res = string_buffer_end(b); + goto done1; + +exception: + res = JS_EXCEPTION; +done: + string_buffer_free(b); +done1: + value_buffer_free(results); + JS_FreeValue(ctx, rep_val); + JS_FreeValue(ctx, matched); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, rep_str); + JS_FreeValue(ctx, namedCaptures); + JS_FreeValue(ctx, str); + return res; +} + +static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst rx = this_val; + JSValue str, previousLastIndex, currentLastIndex, result, index; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + result = JS_UNDEFINED; + currentLastIndex = JS_UNDEFINED; + previousLastIndex = JS_UNDEFINED; + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + + previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); + if (JS_IsException(previousLastIndex)) + goto exception; + + if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) { + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) { + goto exception; + } + } + result = JS_RegExpExec(ctx, rx, str); + if (JS_IsException(result)) + goto exception; + currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); + if (JS_IsException(currentLastIndex)) + goto exception; + if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { + JS_FreeValue(ctx, previousLastIndex); + } else { + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) + goto exception; + } + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, currentLastIndex); + + if (JS_IsNull(result)) { + return JS_NewInt32(ctx, -1); + } else { + index = JS_GetProperty(ctx, result, JS_ATOM_index); + JS_FreeValue(ctx, result); + return index; + } + +exception: + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, currentLastIndex); + JS_FreeValue(ctx, previousLastIndex); + return JS_EXCEPTION; +} + +static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // [Symbol.split](str, limit) + JSValueConst rx = this_val; + JSValueConst args[2]; + JSValue str, ctor, splitter, A, flags, z, sub; + JSString *strp; + uint32_t lim, size, p, q; + int unicodeMatching; + int64_t lengthA, e, numberOfCaptures, i; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + ctor = JS_UNDEFINED; + splitter = JS_UNDEFINED; + A = JS_UNDEFINED; + flags = JS_UNDEFINED; + z = JS_UNDEFINED; + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor); + if (JS_IsException(ctor)) + goto exception; + flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags)); + if (JS_IsException(flags)) + goto exception; + strp = JS_VALUE_GET_STRING(flags); + unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0; + if (string_indexof_char(strp, 'y', 0) < 0) { + flags = JS_ConcatString3(ctx, "", flags, "y"); + if (JS_IsException(flags)) + goto exception; + } + args[0] = rx; + args[1] = flags; + splitter = JS_CallConstructor(ctx, ctor, 2, args); + if (JS_IsException(splitter)) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + lengthA = 0; + if (JS_IsUndefined(argv[1])) { + lim = 0xffffffff; + } else { + if (JS_ToUint32(ctx, &lim, argv[1]) < 0) + goto exception; + if (lim == 0) + goto done; + } + strp = JS_VALUE_GET_STRING(str); + p = q = 0; + size = strp->len; + if (size == 0) { + z = JS_RegExpExec(ctx, splitter, str); + if (JS_IsException(z)) + goto exception; + if (JS_IsNull(z)) + goto add_tail; + goto done; + } + while (q < size) { + if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0) + goto exception; + JS_FreeValue(ctx, z); + z = JS_RegExpExec(ctx, splitter, str); + if (JS_IsException(z)) + goto exception; + if (JS_IsNull(z)) { + q = string_advance_index(strp, q, unicodeMatching); + } else { + if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex))) + goto exception; + if (e > size) + e = size; + if (e == p) { + q = string_advance_index(strp, q, unicodeMatching); + } else { + sub = js_sub_string(ctx, strp, p, q); + if (JS_IsException(sub)) + goto exception; + if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + goto exception; + if (lengthA == lim) + goto done; + p = e; + if (js_get_length64(ctx, &numberOfCaptures, z)) + goto exception; + for(i = 1; i < numberOfCaptures; i++) { + sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); + if (JS_IsException(sub)) + goto exception; + if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + goto exception; + if (lengthA == lim) + goto done; + } + q = p; + } + } + } +add_tail: + if (p > size) + p = size; + sub = js_sub_string(ctx, strp, p, size); + if (JS_IsException(sub)) + goto exception; + if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + goto exception; + goto done; +exception: + JS_FreeValue(ctx, A); + A = JS_EXCEPTION; +done: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, ctor); + JS_FreeValue(ctx, splitter); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, z); + return A; +} + +static const JSCFunctionListEntry js_regexp_funcs[] = { + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), + //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ), + //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ), +}; + +static const JSCFunctionListEntry js_regexp_proto_funcs[] = { + JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), + JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), + JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ), + JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ), + JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ), + JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ), + JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ), + JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ), + JS_CFUNC_DEF("exec", 1, js_regexp_exec ), + JS_CFUNC_DEF("compile", 2, js_regexp_compile ), + JS_CFUNC_DEF("test", 1, js_regexp_test ), + JS_CFUNC_DEF("toString", 0, js_regexp_toString ), + JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ), + JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ), + JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ), + JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ), + JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ), + //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ), + //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ), +}; + +static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicRegExpCompiler(JSContext *ctx) +{ + ctx->compile_regexp = js_compile_regexp; +} + +void JS_AddIntrinsicRegExp(JSContext *ctx) +{ + JSValueConst obj; + + JS_AddIntrinsicRegExpCompiler(ctx); + + ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs, + countof(js_regexp_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2, + ctx->class_proto[JS_CLASS_REGEXP]); + ctx->regexp_ctor = JS_DupValue(ctx, obj); + JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs)); + + ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] = + JS_NewObjectProto(ctx, ctx->iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR], + js_regexp_string_iterator_proto_funcs, + countof(js_regexp_string_iterator_proto_funcs)); +} + +/* JSON */ + +/* XXX: this parser is less strict than the JSON standard because we + reuse the Javascript tokenizer. It could be improved by adding a + specific JSON parse flag. */ +static JSValue json_parse_value(JSParseState *s) +{ + JSContext *ctx = s->ctx; + JSValue val = JS_NULL; + BOOL is_neg; + int ret; + + switch(s->token.val) { + case '{': + { + JSValue prop_val, prop_str; + + if (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) { + js_parse_error(s, "expecting property name"); + goto fail; + } + prop_str = JS_DupValue(ctx, s->token.u.str.str); + if (next_token(s)) { + JS_FreeValue(ctx, prop_str); + goto fail; + } + if (js_parse_expect(s, ':')) { + JS_FreeValue(ctx, prop_str); + goto fail; + } + prop_val = json_parse_value(s); + if (JS_IsException(prop_val)) { + JS_FreeValue(ctx, prop_str); + goto fail; + } + ret = JS_DefinePropertyValueValue(ctx, val, prop_str, + prop_val, JS_PROP_C_W_E); + if (ret < 0) + goto fail; + + if (s->token.val != ',') + break; + if (next_token(s)) + goto fail; + } + } + if (js_parse_expect(s, '}')) + goto fail; + } + break; + case '[': + { + JSValue el; + uint32_t idx; + + if (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 (next_token(s)) + goto fail; + idx++; + } + } + if (js_parse_expect(s, ']')) + goto fail; + } + break; + case TOK_STRING: + val = JS_DupValue(ctx, s->token.u.str.str); + if (next_token(s)) + goto fail; + break; + case TOK_NUMBER: + is_neg = 0; + goto number; + case '-': + if (next_token(s)) + goto fail; + if (s->token.val != TOK_NUMBER) { + js_parse_error(s, "number expected"); + goto fail; + } + is_neg = 1; + number: +#ifdef CONFIG_BIGNUM + val = JS_DupValue(ctx, s->token.u.num.val); + if (is_neg) { + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p; + p = JS_VALUE_GET_PTR(val); + bf_neg(&p->num); + } + break; + case JS_TAG_FLOAT64: + { + double d; + d = JS_VALUE_GET_FLOAT64(val); + val = __JS_NewFloat64(ctx, -d); + } + break; + default: + case JS_TAG_INT: + { + int v; + v = JS_VALUE_GET_INT(val); + if (v == 0 && !is_bigint_mode(s->ctx)) + val = __JS_NewFloat64(ctx, -0.0); + else + val = JS_NewInt64(ctx, -(int64_t)v); + } + break; + } + } +#else + val = s->token.u.num.val; + if (is_neg) { + double d; + JS_ToFloat64(ctx, &d, val); /* no exception possible */ + val = JS_NewFloat64(ctx, -d); + } +#endif + if (next_token(s)) + goto fail; + break; + case TOK_FALSE: + case TOK_TRUE: + val = JS_NewBool(ctx, s->token.val - TOK_FALSE); + if (next_token(s)) + goto fail; + break; + case TOK_NULL: + if (next_token(s)) + goto fail; + break; + default: + if (s->token.val == TOK_EOF) { + js_parse_error(s, "unexpected end of input"); + } else { + js_parse_error(s, "unexpected token: '%.*s'", + (int)(s->buf_ptr - s->token.ptr), s->token.ptr); + } + goto fail; + } + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename) +{ + JSParseState s1, *s = &s1; + JSValue val; + + js_parse_init(ctx, s, buf, buf_len, filename); + + if (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: + free_token(s, &s->token); + return JS_EXCEPTION; +} + +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, 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, ""); + 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; +} + +#ifdef CONFIG_BIGNUM +static inline BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v); +#endif + +static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, + JSValueConst holder, JSValue val, JSValueConst key) +{ + JSValue v; + JSValueConst args[2]; + + if (JS_IsObject(val) +#ifdef CONFIG_BIGNUM + || JS_IsBigInt(ctx, val) /* XXX: probably useless */ +#endif + ) { + JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); + if (JS_IsException(f)) + goto exception; + if (JS_IsFunction(ctx, f)) { + v = JS_CallFree(ctx, f, val, 1, &key); + JS_FreeValue(ctx, val); + val = v; + if (JS_IsException(val)) + goto exception; + } else { + JS_FreeValue(ctx, f); + } + } + + if (!JS_IsUndefined(jsc->replacer_func)) { + args[0] = key; + args[1] = val; + v = JS_Call(ctx, jsc->replacer_func, holder, 2, args); + JS_FreeValue(ctx, val); + val = v; + if (JS_IsException(val)) + goto exception; + } + + switch (JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_OBJECT: + if (JS_IsFunction(ctx, val)) + break; + case JS_TAG_STRING: + case JS_TAG_INT: + case JS_TAG_FLOAT64: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: +#endif + case JS_TAG_BOOL: + case JS_TAG_NULL: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: +#endif + case JS_TAG_EXCEPTION: + return val; + default: + break; + } + JS_FreeValue(ctx, val); + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, + JSValueConst holder, JSValue val, + JSValueConst indent) +{ + JSValue indent1, sep, sep1, tab, v, prop; + JSObject *p; + int64_t i, len; + int cl, ret; + BOOL has_content; + + indent1 = JS_UNDEFINED; + sep = JS_UNDEFINED; + sep1 = JS_UNDEFINED; + tab = JS_UNDEFINED; + prop = JS_UNDEFINED; + + switch (JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_OBJECT: + p = JS_VALUE_GET_OBJ(val); + cl = p->class_id; + if (cl == JS_CLASS_STRING) { + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + goto exception; + val = JS_ToQuotedStringFree(ctx, val); + if (JS_IsException(val)) + goto exception; + return string_buffer_concat_value_free(jsc->b, val); + } else if (cl == JS_CLASS_NUMBER) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + goto exception; + return string_buffer_concat_value_free(jsc->b, val); + } else if (cl == JS_CLASS_BOOLEAN) { + ret = string_buffer_concat_value(jsc->b, p->u.object_data); + JS_FreeValue(ctx, val); + return ret; + } +#ifdef CONFIG_BIGNUM + else if (cl == JS_CLASS_BIG_FLOAT) { + return string_buffer_concat_value_free(jsc->b, val); + } else if (cl == JS_CLASS_BIG_INT) { + JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); + goto exception; + } +#endif + v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); + if (JS_IsException(v)) + goto exception; + if (JS_ToBoolFree(ctx, v)) { + JS_ThrowTypeError(ctx, "circular reference"); + goto exception; + } + indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap)); + if (JS_IsException(indent1)) + goto exception; + if (!JS_IsEmptyString(jsc->gap)) { + sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), ""); + if (JS_IsException(sep)) + goto exception; + sep1 = JS_NewString(ctx, " "); + if (JS_IsException(sep1)) + goto exception; + } else { + sep = JS_DupValue(ctx, jsc->empty); + sep1 = JS_DupValue(ctx, jsc->empty); + } + v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); + if (check_exception_free(ctx, v)) + goto exception; + ret = JS_IsArray(ctx, val); + if (ret < 0) + goto exception; + if (ret) { + if (js_get_length64(ctx, &len, val)) + goto exception; + string_buffer_putc8(jsc->b, '['); + for(i = 0; i < len; i++) { + if (i > 0) + string_buffer_putc8(jsc->b, ','); + string_buffer_concat_value(jsc->b, sep); + v = JS_GetPropertyInt64(ctx, val, i); + if (JS_IsException(v)) + goto exception; + /* XXX: could do this string conversion only when needed */ + prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i)); + if (JS_IsException(prop)) + goto exception; + v = js_json_check(ctx, jsc, val, v, prop); + JS_FreeValue(ctx, prop); + prop = JS_UNDEFINED; + if (JS_IsException(v)) + goto exception; + if (JS_IsUndefined(v)) + v = JS_NULL; + if (js_json_to_str(ctx, jsc, val, v, indent1)) + goto exception; + } + if (len > 0 && !JS_IsEmptyString(jsc->gap)) { + string_buffer_putc8(jsc->b, '\n'); + string_buffer_concat_value(jsc->b, indent); + } + string_buffer_putc8(jsc->b, ']'); + } else { + if (!JS_IsUndefined(jsc->property_list)) + tab = JS_DupValue(ctx, jsc->property_list); + else + tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY); + if (JS_IsException(tab)) + goto exception; + if (js_get_length64(ctx, &len, tab)) + goto exception; + string_buffer_putc8(jsc->b, '{'); + has_content = FALSE; + for(i = 0; i < len; i++) { + JS_FreeValue(ctx, prop); + prop = JS_GetPropertyInt64(ctx, tab, i); + if (JS_IsException(prop)) + goto exception; + v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop)); + if (JS_IsException(v)) + goto exception; + v = js_json_check(ctx, jsc, val, v, prop); + if (JS_IsException(v)) + goto exception; + if (!JS_IsUndefined(v)) { + if (has_content) + string_buffer_putc8(jsc->b, ','); + prop = JS_ToQuotedStringFree(ctx, prop); + if (JS_IsException(prop)) { + JS_FreeValue(ctx, v); + goto exception; + } + string_buffer_concat_value(jsc->b, sep); + string_buffer_concat_value(jsc->b, prop); + string_buffer_putc8(jsc->b, ':'); + string_buffer_concat_value(jsc->b, sep1); + if (js_json_to_str(ctx, jsc, val, v, indent1)) + goto exception; + has_content = TRUE; + } + } + if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) { + string_buffer_putc8(jsc->b, '\n'); + string_buffer_concat_value(jsc->b, indent); + } + string_buffer_putc8(jsc->b, '}'); + } + if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0))) + goto exception; + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, sep1); + JS_FreeValue(ctx, indent1); + JS_FreeValue(ctx, prop); + return 0; + case JS_TAG_STRING: + val = JS_ToQuotedStringFree(ctx, val); + if (JS_IsException(val)) + goto exception; + goto concat_value; + case JS_TAG_FLOAT64: + if (!isfinite(JS_VALUE_GET_FLOAT64(val))) { + val = JS_NULL; + } + goto concat_value; + case JS_TAG_INT: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: +#endif + case JS_TAG_BOOL: + case JS_TAG_NULL: + concat_value: + return string_buffer_concat_value_free(jsc->b, val); +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); + goto exception; +#endif + default: + JS_FreeValue(ctx, val); + return 0; + } + +exception: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, sep1); + JS_FreeValue(ctx, indent1); + JS_FreeValue(ctx, prop); + return -1; +} + +JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, + JSValueConst replacer, JSValueConst space0) +{ + StringBuffer b_s; + JSONStringifyContext jsc_s, *jsc = &jsc_s; + JSValue val, v, space, ret, wrapper; + int res; + int64_t i, j, n; + + jsc->replacer_func = JS_UNDEFINED; + jsc->stack = JS_UNDEFINED; + jsc->property_list = JS_UNDEFINED; + jsc->gap = JS_UNDEFINED; + jsc->b = &b_s; + jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string); + ret = JS_UNDEFINED; + wrapper = JS_UNDEFINED; + + string_buffer_init(ctx, jsc->b, 0); + jsc->stack = JS_NewArray(ctx); + if (JS_IsException(jsc->stack)) + goto exception; + if (JS_IsFunction(ctx, replacer)) { + jsc->replacer_func = replacer; + } else { + res = JS_IsArray(ctx, replacer); + if (res < 0) + goto exception; + if (res) { + /* XXX: enumeration is not fully correct */ + jsc->property_list = JS_NewArray(ctx); + if (JS_IsException(jsc->property_list)) + goto exception; + if (js_get_length64(ctx, &n, replacer)) + goto exception; + for (i = j = 0; i < n; i++) { + JSValue present; + v = JS_GetPropertyInt64(ctx, replacer, i); + if (JS_IsException(v)) + goto exception; + if (JS_IsObject(v)) { + JSObject *p = JS_VALUE_GET_OBJ(v); + if (p->class_id == JS_CLASS_STRING || + p->class_id == JS_CLASS_NUMBER) { + v = JS_ToStringFree(ctx, v); + if (JS_IsException(v)) + goto exception; + } else { + JS_FreeValue(ctx, v); + continue; + } + } else if (JS_IsNumber(v)) { + v = JS_ToStringFree(ctx, v); + if (JS_IsException(v)) + goto exception; + } else if (!JS_IsString(v)) { + JS_FreeValue(ctx, v); + continue; + } + present = js_array_includes(ctx, jsc->property_list, + 1, (JSValueConst *)&v); + if (JS_IsException(present)) { + JS_FreeValue(ctx, v); + goto exception; + } + if (!JS_ToBoolFree(ctx, present)) { + JS_SetPropertyInt64(ctx, jsc->property_list, j++, v); + } else { + JS_FreeValue(ctx, v); + } + } + } + } + space = JS_DupValue(ctx, space0); + if (JS_IsObject(space)) { + JSObject *p = JS_VALUE_GET_OBJ(space); + if (p->class_id == JS_CLASS_NUMBER) { + space = JS_ToNumberFree(ctx, space); + } else if (p->class_id == JS_CLASS_STRING) { + space = JS_ToStringFree(ctx, space); + } + if (JS_IsException(space)) { + JS_FreeValue(ctx, space); + goto exception; + } + } + if (JS_IsNumber(space)) { + int n; + if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0)) + goto exception; + jsc->gap = JS_NewStringLen(ctx, " ", n); + } else if (JS_IsString(space)) { + JSString *p = JS_VALUE_GET_STRING(space); + jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10)); + } else { + jsc->gap = JS_DupValue(ctx, jsc->empty); + } + JS_FreeValue(ctx, space); + if (JS_IsException(jsc->gap)) + goto exception; + wrapper = JS_NewObject(ctx); + if (JS_IsException(wrapper)) + goto exception; + if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string, + JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0) + goto exception; + val = JS_DupValue(ctx, obj); + + val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); + if (JS_IsException(val)) + goto exception; + if (JS_IsUndefined(val)) { + ret = JS_UNDEFINED; + goto done1; + } + if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty)) + goto exception; + + ret = string_buffer_end(jsc->b); + goto done; + +exception: + ret = JS_EXCEPTION; +done1: + string_buffer_free(jsc->b); +done: + JS_FreeValue(ctx, wrapper); + JS_FreeValue(ctx, jsc->empty); + JS_FreeValue(ctx, jsc->gap); + JS_FreeValue(ctx, jsc->property_list); + JS_FreeValue(ctx, jsc->stack); + return ret; +} + +static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // stringify(val, replacer, space) + return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]); +} + +static const JSCFunctionListEntry js_json_funcs[] = { + JS_CFUNC_DEF("parse", 2, js_json_parse ), + JS_CFUNC_DEF("stringify", 3, js_json_stringify ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_json_obj[] = { + JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicJSON(JSContext *ctx) +{ + /* add JSON as autoinit object */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj)); +} + +/* Reflect */ + +static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 0); +} + +static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst func, array_arg, new_target; + JSValue *tab, ret; + uint32_t len; + + func = argv[0]; + array_arg = argv[1]; + if (argc > 2) { + new_target = argv[2]; + if (!JS_IsConstructor(ctx, new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + } else { + new_target = func; + } + tab = build_arg_list(ctx, &len, array_arg); + if (!tab) + return JS_EXCEPTION; + ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab); + free_arg_list(ctx, tab, len); + return ret; +} + +static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj; + JSAtom atom; + int ret; + + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, argv[1]); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_DeleteProperty(ctx, obj, atom, 0); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj, prop, receiver; + JSAtom atom; + JSValue ret; + + obj = argv[0]; + prop = argv[1]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (argc > 2) + receiver = argv[2]; + else + receiver = obj; + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE); + JS_FreeAtom(ctx, atom); + return ret; +} + +static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj, prop; + JSAtom atom; + int ret; + + obj = argv[0]; + prop = argv[1]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_HasProperty(ctx, obj, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj, prop, val, receiver; + int ret; + JSAtom atom; + + obj = argv[0]; + prop = argv[1]; + val = argv[2]; + if (argc > 3) + receiver = argv[3]; + else + receiver = obj; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(obj), atom, + JS_DupValue(ctx, val), receiver, 0); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int ret; + ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK, + JS_ITERATOR_KIND_KEY); +} + +static const JSCFunctionListEntry js_reflect_funcs[] = { + JS_CFUNC_DEF("apply", 3, js_reflect_apply ), + JS_CFUNC_DEF("construct", 2, js_reflect_construct ), + JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ), + JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ), + JS_CFUNC_DEF("get", 2, js_reflect_get ), + JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ), + JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ), + JS_CFUNC_DEF("has", 2, js_reflect_has ), + JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ), + JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ), + JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ), + JS_CFUNC_DEF("set", 3, js_reflect_set ), + JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ), +}; + +static const JSCFunctionListEntry js_reflect_obj[] = { + JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +/* Proxy */ + +static void js_proxy_finalizer(JSRuntime *rt, JSValue val) +{ + JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); + if (s) { + JS_FreeValueRT(rt, s->target); + JS_FreeValueRT(rt, s->handler); + JS_FreeValueRT(rt, s->proto); + 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); + JS_MarkValue(rt, s->proto, 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, 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 JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) +{ + JSProxyData *s; + JSValue method, ret; + JSValueConst proto1; + int res; + + /* must check for timeout to avoid infinite loop in instanceof */ + if (js_poll_interrupts(ctx)) + return JS_EXCEPTION; + + 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)) { + fail: + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); + } + } + /* store the prototype in the proxy so that its refcount is at least 1 */ + set_value(ctx, &s->proto, ret); + return (JSValueConst)ret; +} + +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, BOOL throw_flag) +{ + JSProxyData *s; + JSValue method, ret; + JSValueConst args[2], proto1; + 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_ThrowTypeError(ctx, "proxy: inconsistent prototype"); + return -1; + } + } + return TRUE; +} + +static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) +{ + JSProxyData *s; + JSValue method, ret; + BOOL res; + int res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_IsExtensible(ctx, s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + res2 = JS_IsExtensible(ctx, s->target); + if (res2 < 0) + return res2; + if (res != res2) { + JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible"); + return -1; + } + return res; +} + +static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) +{ + JSProxyData *s; + JSValue method, ret; + BOOL res; + int res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_PreventExtensions(ctx, s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + if (res) { + res2 = JS_IsExtensible(ctx, s->target); + if (res2 < 0) + return res2; + if (res2) { + JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions"); + return -1; + } + } + return res; +} + +static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom) +{ + JSProxyData *s; + JSValue method, ret1, atom_val; + int ret, res; + JSObject *p; + JSValueConst args[2]; + BOOL res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_has); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_HasProperty(ctx, s->target, atom); + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + ret1 = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (!ret) { + JSPropertyDescriptor desc; + p = JS_VALUE_GET_OBJ(s->target); + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) + return -1; + if (res) { + res2 = !(desc.flags & JS_PROP_CONFIGURABLE); + js_free_desc(ctx, &desc); + if (res2 || !p->extensible) { + JS_ThrowTypeError(ctx, "proxy: inconsistent has"); + return -1; + } + } + } + return ret; +} + +static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst receiver) +{ + JSProxyData *s; + JSValue method, ret, atom_val; + int res; + JSValueConst args[3]; + JSPropertyDescriptor desc; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_get); + if (!s) + return JS_EXCEPTION; + /* Note: recursion is possible thru the prototype of s->target */ + if (JS_IsUndefined(method)) + return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE); + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return JS_EXCEPTION; + } + args[0] = s->target; + args[1] = atom_val; + args[2] = receiver; + ret = JS_CallFree(ctx, method, s->handler, 3, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret)) + return JS_EXCEPTION; + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res < 0) + return JS_EXCEPTION; + if (res) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { + if (!js_same_value(ctx, desc.value, ret)) { + goto fail; + } + } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) { + if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) { + fail: + js_free_desc(ctx, &desc); + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "proxy: inconsistent get"); + } + } + js_free_desc(ctx, &desc); + } + return ret; +} + +static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst value, JSValueConst receiver, int flags) +{ + JSProxyData *s; + JSValue method, ret1, atom_val; + int ret, res; + JSValueConst args[4]; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_set); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(s->target), atom, + JS_DupValue(ctx, value), receiver, + flags); + } + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + args[2] = value; + args[3] = receiver; + ret1 = JS_CallFree(ctx, method, s->handler, 4, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (ret) { + JSPropertyDescriptor desc; + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res < 0) + return -1; + if (res) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { + if (!js_same_value(ctx, desc.value, value)) { + goto fail; + } + } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) { + fail: + js_free_desc(ctx, &desc); + JS_ThrowTypeError(ctx, "proxy: inconsistent set"); + return -1; + } + js_free_desc(ctx, &desc); + } + } else { + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "proxy: cannot set property"); + return -1; + } + } + return ret; +} + +static JSValue js_create_desc(JSContext *ctx, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags) +{ + JSValue ret; + ret = JS_NewObject(ctx); + if (JS_IsException(ret)) + return ret; + if (flags & JS_PROP_HAS_GET) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_SET) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_VALUE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_WRITABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, + JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_ENUMERABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, + JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_CONFIGURABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, + JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0), + JS_PROP_C_W_E); + } + return ret; +} + +static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, + JSValueConst obj, JSAtom prop) +{ + JSProxyData *s; + JSValue method, trap_result_obj, prop_val; + int res, target_desc_ret, ret; + JSObject *p; + JSValueConst args[2]; + JSPropertyDescriptor result_desc, target_desc; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor); + if (!s) + return -1; + p = JS_VALUE_GET_OBJ(s->target); + if (JS_IsUndefined(method)) { + return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop); + } + prop_val = JS_AtomToValue(ctx, prop); + if (JS_IsException(prop_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = prop_val; + trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, prop_val); + if (JS_IsException(trap_result_obj)) + return -1; + if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) { + JS_FreeValue(ctx, trap_result_obj); + goto fail; + } + target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop); + if (target_desc_ret < 0) { + JS_FreeValue(ctx, trap_result_obj); + return -1; + } + if (target_desc_ret) + js_free_desc(ctx, &target_desc); + if (JS_IsUndefined(trap_result_obj)) { + if (target_desc_ret) { + if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible) + goto fail; + } + ret = FALSE; + } else { + int flags1, extensible_target; + extensible_target = JS_IsExtensible(ctx, s->target); + if (extensible_target < 0) { + JS_FreeValue(ctx, trap_result_obj); + return -1; + } + res = js_obj_to_desc(ctx, &result_desc, trap_result_obj); + JS_FreeValue(ctx, trap_result_obj); + if (res < 0) + return -1; + + if (target_desc_ret) { + /* convert result_desc.flags to defineProperty flags */ + flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE; + if (result_desc.flags & JS_PROP_GETSET) + flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET; + else + flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE; + /* XXX: not complete check: need to compare value & + getter/setter as in defineproperty */ + if (!check_define_prop_flags(target_desc.flags, flags1)) + goto fail1; + } else { + if (!extensible_target) + goto fail1; + } + if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) { + if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE)) + goto fail1; + if ((result_desc.flags & + (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 && + target_desc_ret && + (target_desc.flags & JS_PROP_WRITABLE) != 0) { + /* proxy-missing-checks */ + fail1: + js_free_desc(ctx, &result_desc); + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor"); + return -1; + } + } + ret = TRUE; + if (pdesc) { + *pdesc = result_desc; + } else { + js_free_desc(ctx, &result_desc); + } + } + return ret; +} + +static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags) +{ + JSProxyData *s; + JSValue method, ret1, prop_val, desc_val; + int res, ret; + JSObject *p; + JSValueConst args[3]; + JSPropertyDescriptor desc; + BOOL setting_not_configurable; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags); + } + prop_val = JS_AtomToValue(ctx, prop); + if (JS_IsException(prop_val)) { + JS_FreeValue(ctx, method); + return -1; + } + desc_val = js_create_desc(ctx, val, getter, setter, flags); + if (JS_IsException(desc_val)) { + JS_FreeValue(ctx, prop_val); + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = prop_val; + args[2] = desc_val; + ret1 = JS_CallFree(ctx, method, s->handler, 3, args); + JS_FreeValue(ctx, prop_val); + JS_FreeValue(ctx, desc_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (!ret) { + if (flags & JS_PROP_THROW) { + JS_ThrowTypeError(ctx, "proxy: defineProperty exception"); + return -1; + } else { + return 0; + } + } + p = JS_VALUE_GET_OBJ(s->target); + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + return -1; + setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE | + JS_PROP_CONFIGURABLE)) == + JS_PROP_HAS_CONFIGURABLE); + if (!res) { + if (!p->extensible || setting_not_configurable) + goto fail; + } else { + if (!check_define_prop_flags(desc.flags, flags) || + ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) { + goto fail1; + } + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == + JS_PROP_GETSET) { + if ((flags & JS_PROP_HAS_GET) && + !js_same_value(ctx, getter, desc.getter)) { + goto fail1; + } + if ((flags & JS_PROP_HAS_SET) && + !js_same_value(ctx, setter, desc.setter)) { + goto fail1; + } + } + } else if (flags & JS_PROP_HAS_VALUE) { + if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == + JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) { + /* missing-proxy-check feature */ + goto fail1; + } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && + !js_same_value(ctx, val, desc.value)) { + goto fail1; + } + } + if (flags & JS_PROP_HAS_WRITABLE) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) { + /* proxy-missing-checks */ + fail1: + js_free_desc(ctx, &desc); + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); + return -1; + } + } + js_free_desc(ctx, &desc); + } + return 1; +} + +static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, + JSAtom atom) +{ + JSProxyData *s; + JSValue method, ret, atom_val; + int res, res2, is_extensible; + JSValueConst args[2]; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_DeleteProperty(ctx, s->target, atom, 0); + } + atom_val = JS_AtomToValue(ctx, atom);; + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + ret = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + if (res) { + JSPropertyDescriptor desc; + res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res2 < 0) + return -1; + if (res2) { + if (!(desc.flags & JS_PROP_CONFIGURABLE)) + goto fail; + is_extensible = JS_IsExtensible(ctx, s->target); + if (is_extensible < 0) + goto fail1; + if (!is_extensible) { + /* proxy-missing-checks */ + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty"); + fail1: + js_free_desc(ctx, &desc); + return -1; + } + js_free_desc(ctx, &desc); + } + } + return res; +} + +/* return the index of the property or -1 if not found */ +static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom) +{ + int i; + for(i = 0; i < n; i++) { + if (tab[i].atom == atom) + return i; + } + return -1; +} + +static int js_proxy_get_own_property_names(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSValueConst obj) +{ + JSProxyData *s; + JSValue method, prop_array, val; + uint32_t len, i, len2; + JSPropertyEnum *tab, *tab2; + JSAtom atom; + JSPropertyDescriptor desc; + int res, is_extensible, idx; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, + JS_VALUE_GET_OBJ(s->target), + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); + } + prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); + if (JS_IsException(prop_array)) + return -1; + tab = NULL; + len = 0; + tab2 = NULL; + len2 = 0; + if (js_get_length32(ctx, &len, prop_array)) + goto fail; + if (len > 0) { + tab = js_mallocz(ctx, sizeof(tab[0]) * len); + if (!tab) + goto fail; + } + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(ctx, prop_array, i); + if (JS_IsException(val)) + goto fail; + if (!JS_IsString(val) && !JS_IsSymbol(val)) { + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols"); + goto fail; + } + atom = JS_ValueToAtom(ctx, val); + JS_FreeValue(ctx, val); + if (atom == JS_ATOM_NULL) + goto fail; + tab[i].atom = atom; + tab[i].is_enumerable = FALSE; /* XXX: redundant? */ + } + + /* check duplicate properties (XXX: inefficient, could store the + * properties an a temporary object to use the hash) */ + for(i = 1; i < len; i++) { + if (find_prop_key(tab, i, tab[i].atom) >= 0) { + JS_ThrowTypeError(ctx, "proxy: duplicate property"); + goto fail; + } + } + + is_extensible = JS_IsExtensible(ctx, s->target); + if (is_extensible < 0) + goto fail; + + /* check if there are non configurable properties */ + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + goto fail; + } + if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target), + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) + goto fail; + for(i = 0; i < len2; i++) { + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + goto fail; + } + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), + tab2[i].atom); + if (res < 0) + goto fail; + if (res) { /* safety, property should be found */ + js_free_desc(ctx, &desc); + if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) { + idx = find_prop_key(tab, len, tab2[i].atom); + if (idx < 0) { + JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys"); + goto fail; + } + /* mark the property as found */ + if (!is_extensible) + tab[idx].is_enumerable = TRUE; + } + } + } + if (!is_extensible) { + /* check that all property in 'tab' were checked */ + for(i = 0; i < len; i++) { + if (!tab[i].is_enumerable) { + JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy"); + goto fail; + } + } + } + + js_free_prop_enum(ctx, tab2, len2); + JS_FreeValue(ctx, prop_array); + *ptab = tab; + *plen = len; + return 0; + fail: + js_free_prop_enum(ctx, tab2, len2); + js_free_prop_enum(ctx, tab, len); + JS_FreeValue(ctx, prop_array); + return -1; +} + +static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSProxyData *s; + JSValue method, arg_array, ret; + JSValueConst args[3]; + + s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct); + if (!s) + return JS_EXCEPTION; + if (!JS_IsConstructor(ctx, s->target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (JS_IsUndefined(method)) + return JS_CallConstructor2(ctx, s->target, new_target, argc, argv); + arg_array = js_create_array(ctx, argc, argv); + if (JS_IsException(arg_array)) { + ret = JS_EXCEPTION; + goto fail; + } + args[0] = s->target; + args[1] = arg_array; + args[2] = new_target; + ret = JS_Call(ctx, method, s->handler, 3, args); + if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, ret); + ret = JS_ThrowTypeErrorNotAnObject(ctx); + } + fail: + JS_FreeValue(ctx, method); + JS_FreeValue(ctx, arg_array); + return ret; +} + +static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSProxyData *s; + JSValue method, arg_array, ret; + JSValueConst args[3]; + + if (flags & JS_CALL_FLAG_CONSTRUCTOR) + return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv); + + s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply); + if (!s) + return JS_EXCEPTION; + if (!s->is_func) { + JS_FreeValue(ctx, method); + return JS_ThrowTypeError(ctx, "not a function"); + } + if (JS_IsUndefined(method)) + return JS_Call(ctx, s->target, this_obj, argc, argv); + arg_array = js_create_array(ctx, argc, argv); + if (JS_IsException(arg_array)) { + ret = JS_EXCEPTION; + goto fail; + } + args[0] = s->target; + args[1] = this_obj; + args[2] = arg_array; + ret = JS_Call(ctx, method, s->handler, 3, args); + fail: + JS_FreeValue(ctx, method); + JS_FreeValue(ctx, arg_array); + return ret; +} + +static int js_proxy_isArray(JSContext *ctx, JSValueConst obj) +{ + JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); + if (!s) + return FALSE; + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return -1; + } + return JS_IsArray(ctx, s->target); +} + +static const JSClassExoticMethods js_proxy_exotic_methods = { + .get_own_property = js_proxy_get_own_property, + .define_own_property = js_proxy_define_own_property, + .delete_property = js_proxy_delete_property, + .get_own_property_names = js_proxy_get_own_property_names, + .has_property = js_proxy_has, + .get_property = js_proxy_get, + .set_property = js_proxy_set, +}; + +static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst target, handler; + JSValue obj; + JSProxyData *s; + + target = argv[0]; + handler = argv[1]; + if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT || + JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + s = JS_GetOpaque(target, JS_CLASS_PROXY); + if (s && s->is_revoked) + goto revoked_proxy; + s = JS_GetOpaque(handler, JS_CLASS_PROXY); + if (s && s->is_revoked) { + revoked_proxy: + return JS_ThrowTypeErrorRevokedProxy(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->proto = JS_NULL; + s->is_func = JS_IsFunction(ctx, target); + s->is_revoked = FALSE; + JS_SetOpaque(obj, s); + JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target)); + return obj; +} + +static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, + JSValue *func_data) +{ + JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY); + if (s) { + /* We do not free the handler and target in case they are + referenced as constants in the C call stack */ + s->is_revoked = TRUE; + JS_FreeValue(ctx, func_data[0]); + func_data[0] = JS_NULL; + } + return JS_UNDEFINED; +} + +static JSValue js_proxy_revoke_constructor(JSContext *ctx, + JSValueConst proxy_obj) +{ + return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj); +} + +static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj; + + proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv); + if (JS_IsException(proxy_obj)) + goto fail; + revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj); + if (JS_IsException(revoke_obj)) + goto fail; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + goto fail; + // XXX: exceptions? + JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E); + return obj; + fail: + JS_FreeValue(ctx, proxy_obj); + JS_FreeValue(ctx, revoke_obj); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_proxy_funcs[] = { + JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ), +}; + +static const JSClassShortDef js_proxy_class_def[] = { + { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */ +}; + +void JS_AddIntrinsicProxy(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + + if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) { + init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY, + countof(js_proxy_class_def)); + rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods; + rt->class_array[JS_CLASS_PROXY].call = js_proxy_call; + } + + obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2, + JS_CFUNC_constructor, 0); + JS_SetConstructorBit(ctx, obj1, TRUE); + JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs, + countof(js_proxy_funcs)); + JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy", + obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); +} + +/* Symbol */ + +static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue str; + JSString *p; + + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (argc == 0 || JS_IsUndefined(argv[0])) { + p = NULL; + } else { + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return JS_EXCEPTION; + p = JS_VALUE_GET_STRING(str); + } + return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL); +} + +static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_SYMBOL) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + val = js_thisSymbolValue(ctx, this_val); + if (JS_IsException(val)) + return val; + /* XXX: use JS_ToStringInternal() with a flags */ + ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisSymbolValue(ctx, this_val); +} + +static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val) +{ + JSValue val, ret; + JSAtomStruct *p; + + val = js_thisSymbolValue(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_PTR(val); + if (p->len == 0 && p->is_wide_char != 0) { + ret = JS_UNDEFINED; + } else { + ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)); + } + JS_FreeValue(ctx, val); + return ret; +} + +static const JSCFunctionListEntry js_symbol_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_symbol_toString ), + JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ), + // XXX: should have writable: false + JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ), + JS_CGETSET_DEF("description", js_symbol_get_description, NULL ), +}; + +static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return JS_EXCEPTION; + return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL); +} + +static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSAtomStruct *p; + + if (!JS_IsSymbol(argv[0])) + return JS_ThrowTypeError(ctx, "not a symbol"); + p = JS_VALUE_GET_PTR(argv[0]); + if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL) + return JS_UNDEFINED; + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); +} + +static const JSCFunctionListEntry js_symbol_funcs[] = { + JS_CFUNC_DEF("for", 1, js_symbol_for ), + JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ), +}; + +/* Set/Map/WeakSet/WeakMap */ + +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + BOOL empty; /* TRUE if the record is deleted */ + struct JSMapState *map; + struct JSMapRecord *next_weak_ref; + struct list_head link; + struct list_head hash_link; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapState { + BOOL is_weak; /* TRUE if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + +#define MAGIC_SET (1 << 0) +#define MAGIC_WEAK (1 << 1) + +static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s; + JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; + JSValueConst arr; + BOOL is_set, is_weak; + + is_set = magic & MAGIC_SET; + is_weak = ((magic & MAGIC_WEAK) != 0); + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic); + if (JS_IsException(obj)) + return JS_EXCEPTION; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + init_list_head(&s->records); + s->is_weak = is_weak; + JS_SetOpaque(obj, s); + s->hash_size = 1; + s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size); + if (!s->hash_table) + goto fail; + init_list_head(&s->hash_table[0]); + s->record_count_threshold = 4; + + arr = JS_UNDEFINED; + if (argc > 0) + arr = argv[0]; + if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) { + JSValue item, ret; + BOOL done; + + adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set); + if (JS_IsException(adder)) + goto fail; + if (!JS_IsFunction(ctx, adder)) { + JS_ThrowTypeError(ctx, "set/add is not a function"); + goto fail; + } + + iter = JS_GetIterator(ctx, arr, FALSE); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + + for(;;) { + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail; + if (done) { + JS_FreeValue(ctx, item); + break; + } + if (is_set) { + ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, item); + goto fail; + } + } else { + JSValue key, value; + JSValueConst args[2]; + key = JS_UNDEFINED; + value = JS_UNDEFINED; + if (!JS_IsObject(item)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail1; + } + key = JS_GetPropertyUint32(ctx, item, 0); + if (JS_IsException(key)) + goto fail1; + value = JS_GetPropertyUint32(ctx, item, 1); + if (JS_IsException(value)) + goto fail1; + args[0] = key; + args[1] = value; + ret = JS_Call(ctx, adder, obj, 2, args); + if (JS_IsException(ret)) { + fail1: + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, key); + JS_FreeValue(ctx, value); + goto fail; + } + JS_FreeValue(ctx, key); + JS_FreeValue(ctx, value); + } + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, item); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, adder); + } + return obj; + fail: + if (JS_IsObject(iter)) { + /* close the iterator object, preserving pending exception */ + JS_IteratorClose(ctx, iter, TRUE); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, adder); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +/* XXX: could normalize strings to speed up comparison */ +static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key) +{ + uint32_t tag = JS_VALUE_GET_TAG(key); + /* convert -0.0 to +0.0 */ + if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) { + key = JS_NewInt32(ctx, 0); + } + return key; +} + +/* XXX: better hash ? */ +static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) +{ + uint32_t tag = JS_VALUE_GET_NORM_TAG(key); + uint32_t h; + double d; + JSFloat64Union u; + + switch(tag) { + case JS_TAG_BOOL: + h = JS_VALUE_GET_INT(key); + break; + case JS_TAG_STRING: + h = hash_string(JS_VALUE_GET_STRING(key), 0); + break; + case JS_TAG_OBJECT: + case JS_TAG_SYMBOL: + h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; + break; + case JS_TAG_INT: + d = JS_VALUE_GET_INT(key) * 3163; + goto hash_float64; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(key); + /* normalize the NaN */ + if (isnan(d)) + d = JS_FLOAT64_NAN; + hash_float64: + u.d = d; + h = (u.u32[0] ^ u.u32[1]) * 3163; + break; + default: + h = 0; /* XXX: bignum support */ + break; + } + h ^= tag; + return h; +} + +static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, + JSValueConst key) +{ + struct list_head *el; + JSMapRecord *mr; + uint32_t h; + h = map_hash_key(ctx, key) & (s->hash_size - 1); + list_for_each(el, &s->hash_table[h]) { + mr = list_entry(el, JSMapRecord, hash_link); + if (js_same_value_zero(ctx, mr->key, key)) + return mr; + } + return NULL; +} + +static void map_hash_resize(JSContext *ctx, JSMapState *s) +{ + uint32_t new_hash_size, i, h; + size_t slack; + struct list_head *new_hash_table, *el; + JSMapRecord *mr; + + /* XXX: no reporting of memory allocation failure */ + if (s->hash_size == 1) + new_hash_size = 4; + else + new_hash_size = s->hash_size * 2; + new_hash_table = js_realloc2(ctx, s->hash_table, + sizeof(new_hash_table[0]) * new_hash_size, &slack); + if (!new_hash_table) + return; + new_hash_size += slack / sizeof(*new_hash_table); + + for(i = 0; i < new_hash_size; i++) + init_list_head(&new_hash_table[i]); + + list_for_each(el, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + h = map_hash_key(ctx, mr->key) & (new_hash_size - 1); + list_add_tail(&mr->hash_link, &new_hash_table[h]); + } + } + s->hash_table = new_hash_table; + s->hash_size = new_hash_size; + s->record_count_threshold = new_hash_size * 2; +} + +static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, + JSValueConst key) +{ + uint32_t h; + JSMapRecord *mr; + + mr = js_malloc(ctx, sizeof(*mr)); + if (!mr) + return NULL; + mr->ref_count = 1; + mr->map = s; + mr->empty = FALSE; + if (s->is_weak) { + JSObject *p = JS_VALUE_GET_OBJ(key); + /* Add the weak reference */ + mr->next_weak_ref = p->first_weak_ref; + p->first_weak_ref = mr; + } else { + JS_DupValue(ctx, key); + } + mr->key = (JSValue)key; + h = map_hash_key(ctx, key) & (s->hash_size - 1); + list_add_tail(&mr->hash_link, &s->hash_table[h]); + list_add_tail(&mr->link, &s->records); + s->record_count++; + if (s->record_count >= s->record_count_threshold) { + map_hash_resize(ctx, s); + } + return mr; +} + +/* Remove the weak reference from the object weak + reference list. we don't use a doubly linked list to + save space, assuming a given object has few weak + references to it */ +static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr) +{ + JSMapRecord **pmr, *mr1; + JSObject *p; + + p = JS_VALUE_GET_OBJ(mr->key); + pmr = &p->first_weak_ref; + for(;;) { + mr1 = *pmr; + assert(mr1 != NULL); + if (mr1 == mr) + break; + pmr = &mr1->next_weak_ref; + } + *pmr = mr1->next_weak_ref; +} + +static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) +{ + if (mr->empty) + return; + list_del(&mr->hash_link); + if (s->is_weak) { + delete_weak_ref(rt, mr); + } else { + JS_FreeValueRT(rt, mr->key); + } + JS_FreeValueRT(rt, mr->value); + if (--mr->ref_count == 0) { + list_del(&mr->link); + js_free_rt(rt, mr); + } else { + /* keep a zombie record for iterators */ + mr->empty = TRUE; + mr->key = JS_UNDEFINED; + mr->value = JS_UNDEFINED; + } + s->record_count--; +} + +static void map_decref_record(JSRuntime *rt, JSMapRecord *mr) +{ + if (--mr->ref_count == 0) { + /* the record can be safely removed */ + assert(mr->empty); + list_del(&mr->link); + js_free_rt(rt, mr); + } +} + +static void reset_weak_ref(JSRuntime *rt, JSObject *p) +{ + JSMapRecord *mr, *mr_next; + JSMapState *s; + + /* first pass to remove the records from the WeakMap/WeakSet + lists */ + for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) { + s = mr->map; + assert(s->is_weak); + assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ + list_del(&mr->hash_link); + list_del(&mr->link); + } + + /* second pass to free the values to avoid modifying the weak + reference list while traversing it. */ + for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) { + mr_next = mr->next_weak_ref; + JS_FreeValueRT(rt, mr->value); + js_free_rt(rt, mr); + } + + p->first_weak_ref = NULL; /* fail safe */ +} + +static JSValue js_map_set(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValueConst key, value; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + if (s->is_weak && !JS_IsObject(key)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (magic & MAGIC_SET) + value = JS_UNDEFINED; + else + value = argv[1]; + mr = map_find_record(ctx, s, key); + if (mr) { + JS_FreeValue(ctx, mr->value); + } else { + mr = map_add_record(ctx, s, key); + if (!mr) + return JS_EXCEPTION; + } + mr->value = JS_DupValue(ctx, value); + return JS_DupValue(ctx, this_val); +} + +static JSValue js_map_get(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValueConst key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + if (!mr) + return JS_UNDEFINED; + else + return JS_DupValue(ctx, mr->value); +} + +static JSValue js_map_has(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValueConst key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + return JS_NewBool(ctx, (mr != NULL)); +} + +static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValueConst key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + if (!mr) + return JS_FALSE; + map_delete_record(ctx->rt, s, mr); + return JS_TRUE; +} + +static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + struct list_head *el, *el1; + JSMapRecord *mr; + + if (!s) + return JS_EXCEPTION; + list_for_each_safe(el, el1, &s->records) { + mr = list_entry(el, JSMapRecord, link); + map_delete_record(ctx->rt, s, mr); + } + return JS_UNDEFINED; +} + +static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + if (!s) + return JS_EXCEPTION; + return JS_NewUint32(ctx, s->record_count); +} + +static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSValueConst func, this_arg; + JSValue ret, args[3]; + struct list_head *el; + JSMapRecord *mr; + + if (!s) + return JS_EXCEPTION; + func = argv[0]; + if (argc > 1) + this_arg = argv[1]; + else + this_arg = JS_UNDEFINED; + if (check_function(ctx, func)) + return JS_EXCEPTION; + /* Note: the list can be modified while traversing it, but the + current element is locked */ + el = s->records.next; + while (el != &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + mr->ref_count++; + /* must duplicate in case the record is deleted */ + args[1] = JS_DupValue(ctx, mr->key); + if (magic) + args[0] = args[1]; + else + args[0] = JS_DupValue(ctx, mr->value); + args[2] = (JSValue)this_val; + ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); + JS_FreeValue(ctx, args[0]); + if (!magic) + JS_FreeValue(ctx, args[1]); + el = el->next; + map_decref_record(ctx->rt, mr); + if (JS_IsException(ret)) + return ret; + JS_FreeValue(ctx, ret); + } else { + el = el->next; + } + } + return JS_UNDEFINED; +} + +static void js_map_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p; + JSMapState *s; + struct list_head *el, *el1; + JSMapRecord *mr; + + p = JS_VALUE_GET_OBJ(val); + s = p->u.map_state; + if (s) { + /* if the object is deleted we are sure that no iterator is + using it */ + list_for_each_safe(el, el1, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + if (s->is_weak) + delete_weak_ref(rt, mr); + else + JS_FreeValueRT(rt, mr->key); + JS_FreeValueRT(rt, mr->value); + } + js_free_rt(rt, mr); + } + js_free_rt(rt, s->hash_table); + js_free_rt(rt, s); + } +} + +static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSMapState *s; + struct list_head *el; + JSMapRecord *mr; + + s = p->u.map_state; + if (s) { + list_for_each(el, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!s->is_weak) + JS_MarkValue(rt, mr->key, mark_func); + JS_MarkValue(rt, mr->value, mark_func); + } + } +} + +/* Map Iterator */ + +typedef struct JSMapIteratorData { + JSValue obj; + JSIteratorKindEnum kind; + JSMapRecord *cur_record; +} JSMapIteratorData; + +static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p; + JSMapIteratorData *it; + + p = JS_VALUE_GET_OBJ(val); + it = p->u.map_iterator_data; + if (it) { + /* During the GC sweep phase the Map finalizer may be + called before the Map iterator finalizer */ + if (JS_IsLiveObject(rt, it->obj) && it->cur_record) { + map_decref_record(rt, it->cur_record); + } + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); + } +} + +static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSMapIteratorData *it; + it = p->u.map_iterator_data; + if (it) { + /* the record is already marked by the object */ + JS_MarkValue(rt, it->obj, mark_func); + } +} + +static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSIteratorKindEnum kind; + JSMapState *s; + JSMapIteratorData *it; + JSValue enum_obj; + + kind = magic >> 2; + magic &= 3; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + if (!s) + return JS_EXCEPTION; + enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic); + if (JS_IsException(enum_obj)) + goto fail; + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, enum_obj); + goto fail; + } + it->obj = JS_DupValue(ctx, this_val); + it->kind = kind; + it->cur_record = NULL; + JS_SetOpaque(enum_obj, it); + return enum_obj; + fail: + return JS_EXCEPTION; +} + +static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSMapIteratorData *it; + JSMapState *s; + JSMapRecord *mr; + struct list_head *el; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic); + if (!it) { + *pdone = FALSE; + return JS_EXCEPTION; + } + if (JS_IsUndefined(it->obj)) + goto done; + s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic); + assert(s != NULL); + if (!it->cur_record) { + el = s->records.next; + } else { + mr = it->cur_record; + el = mr->link.next; + map_decref_record(ctx->rt, mr); /* the record can be freed here */ + } + for(;;) { + if (el == &s->records) { + /* no more record */ + it->cur_record = NULL; + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + /* end of enumeration */ + *pdone = TRUE; + return JS_UNDEFINED; + } + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) + break; + /* get the next record */ + el = mr->link.next; + } + + /* lock the record so that it won't be freed */ + mr->ref_count++; + it->cur_record = mr; + *pdone = FALSE; + + if (it->kind == JS_ITERATOR_KIND_KEY) { + return JS_DupValue(ctx, mr->key); + } else { + JSValueConst args[2]; + args[0] = mr->key; + if (magic) + args[1] = mr->key; + else + args[1] = mr->value; + if (it->kind == JS_ITERATOR_KIND_VALUE) { + return JS_DupValue(ctx, args[1]); + } else { + return js_create_array(ctx, 2, args); + } + } +} + +static const JSCFunctionListEntry js_map_funcs[] = { + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static const JSCFunctionListEntry js_map_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ), + JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ), + JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ), + JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ), + JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ), + JS_ALIAS_DEF("[Symbol.iterator]", "entries" ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_set_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ), + JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ), + JS_ALIAS_DEF("keys", "values" ), + JS_ALIAS_DEF("[Symbol.iterator]", "values" ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_weak_map_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_weak_set_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = { + js_map_proto_funcs, + js_set_proto_funcs, + js_weak_map_proto_funcs, + js_weak_set_proto_funcs, + js_map_iterator_proto_funcs, + js_set_iterator_proto_funcs, +}; + +static const uint8_t js_map_proto_funcs_count[6] = { + countof(js_map_proto_funcs), + countof(js_set_proto_funcs), + countof(js_weak_map_proto_funcs), + countof(js_weak_set_proto_funcs), + countof(js_map_iterator_proto_funcs), + countof(js_set_iterator_proto_funcs), +}; + +void JS_AddIntrinsicMapSet(JSContext *ctx) +{ + int i; + JSValue obj1; + char buf[ATOM_GET_STR_BUF_SIZE]; + + for(i = 0; i < 4; i++) { + const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf), + JS_ATOM_Map + i); + ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i], + js_map_proto_funcs_ptr[i], + js_map_proto_funcs_count[i]); + obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0, + JS_CFUNC_constructor_magic, i); + if (i < 2) { + JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs, + countof(js_map_funcs)); + } + JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]); + } + + for(i = 0; i < 2; i++) { + ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] = + JS_NewObjectProto(ctx, ctx->iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i], + js_map_proto_funcs_ptr[i + 4], + js_map_proto_funcs_count[i + 4]); + } +} + +/* Generator */ +static const JSCFunctionListEntry js_generator_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE), +}; + +static const JSCFunctionListEntry js_generator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ), + JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ), + JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE), +}; + +/* Promise */ + +typedef enum JSPromiseStateEnum { + JS_PROMISE_PENDING, + JS_PROMISE_FULFILLED, + JS_PROMISE_REJECTED, +} JSPromiseStateEnum; + +typedef struct JSPromiseData { + JSPromiseStateEnum promise_state; + /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ + struct list_head promise_reactions[2]; + BOOL is_handled; /* Note: only useful to debug */ + JSValue promise_result; +} JSPromiseData; + +typedef struct JSPromiseFunctionDataResolved { + int ref_count; + BOOL already_resolved; +} JSPromiseFunctionDataResolved; + +typedef struct JSPromiseFunctionData { + JSValue promise; + JSPromiseFunctionDataResolved *presolved; +} JSPromiseFunctionData; + +typedef struct JSPromiseReactionData { + struct list_head link; /* not used in promise_reaction_job */ + JSValue resolving_funcs[2]; + JSValue handler; +} JSPromiseReactionData; + +static int js_create_resolving_functions(JSContext *ctx, JSValue *args, + JSValueConst promise); + +static void promise_reaction_data_free(JSRuntime *rt, + JSPromiseReactionData *rd) +{ + JS_FreeValueRT(rt, rd->resolving_funcs[0]); + JS_FreeValueRT(rt, rd->resolving_funcs[1]); + JS_FreeValueRT(rt, rd->handler); + js_free_rt(rt, rd); +} + +static JSValue promise_reaction_job(JSContext *ctx, int argc, + JSValueConst *argv) +{ + JSValueConst handler, arg, func; + JSValue res, res2; + BOOL is_reject; + + assert(argc == 5); + handler = argv[2]; + is_reject = JS_ToBool(ctx, argv[3]); + arg = argv[4]; +#ifdef DUMP_PROMISE + printf("promise_reaction_job: is_reject=%d\n", is_reject); +#endif + + if (JS_IsUndefined(handler)) { + if (is_reject) { + res = JS_Throw(ctx, JS_DupValue(ctx, arg)); + } else { + res = JS_DupValue(ctx, arg); + } + } else { + res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg); + } + is_reject = JS_IsException(res); + if (is_reject) + res = JS_GetException(ctx); + func = argv[is_reject]; + /* as an extension, we support undefined as value to avoid + creating a dummy promise in the 'await' implementation of async + functions */ + if (!JS_IsUndefined(func)) { + res2 = JS_Call(ctx, func, JS_UNDEFINED, + 1, (JSValueConst *)&res); + } else { + res2 = JS_UNDEFINED; + } + JS_FreeValue(ctx, res); + + return res2; +} + +void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, + JSHostPromiseRejectionTracker *cb, + void *opaque) +{ + rt->host_promise_rejection_tracker = cb; + rt->host_promise_rejection_tracker_opaque = opaque; +} + +static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, + JSValueConst value, BOOL is_reject) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + struct list_head *el, *el1; + JSPromiseReactionData *rd; + JSValueConst args[5]; + + if (!s || s->promise_state != JS_PROMISE_PENDING) + return; /* should never happen */ + set_value(ctx, &s->promise_result, JS_DupValue(ctx, value)); + s->promise_state = JS_PROMISE_FULFILLED + is_reject; +#ifdef DUMP_PROMISE + printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject); +#endif + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) { + rt->host_promise_rejection_tracker(ctx, promise, value, FALSE, + rt->host_promise_rejection_tracker_opaque); + } + } + + list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { + rd = list_entry(el, JSPromiseReactionData, link); + args[0] = rd->resolving_funcs[0]; + args[1] = rd->resolving_funcs[1]; + args[2] = rd->handler; + args[3] = JS_NewBool(ctx, is_reject); + args[4] = value; + JS_EnqueueJob(ctx, promise_reaction_job, 5, args); + list_del(&rd->link); + promise_reaction_data_free(ctx->rt, rd); + } + + list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) { + rd = list_entry(el, JSPromiseReactionData, link); + list_del(&rd->link); + promise_reaction_data_free(ctx->rt, rd); + } +} + +static void reject_promise(JSContext *ctx, JSValueConst promise, + JSValueConst value) +{ + fulfill_or_reject_promise(ctx, promise, value, TRUE); +} + +static JSValue js_promise_resolve_thenable_job(JSContext *ctx, + int argc, JSValueConst *argv) +{ + JSValueConst promise, thenable, then; + JSValue args[2], res; + +#ifdef DUMP_PROMISE + printf("js_promise_resolve_thenable_job\n"); +#endif + assert(argc == 3); + promise = argv[0]; + thenable = argv[1]; + then = argv[2]; + if (js_create_resolving_functions(ctx, args, promise) < 0) + return JS_EXCEPTION; + res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); + if (JS_IsException(res)) { + JSValue error = JS_GetException(ctx); + res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + } + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + return res; +} + +static void js_promise_resolve_function_free_resolved(JSRuntime *rt, + JSPromiseFunctionDataResolved *sr) +{ + if (--sr->ref_count == 0) { + js_free_rt(rt, sr); + } +} + +static int js_create_resolving_functions(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst promise) + +{ + JSValue obj; + JSPromiseFunctionData *s; + JSPromiseFunctionDataResolved *sr; + int i, ret; + + sr = js_malloc(ctx, sizeof(*sr)); + if (!sr) + return -1; + sr->ref_count = 1; + sr->already_resolved = FALSE; /* must be shared between the two functions */ + ret = 0; + for(i = 0; i < 2; i++) { + obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_PROMISE_RESOLVE_FUNCTION + i); + if (JS_IsException(obj)) + goto fail; + s = js_malloc(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + fail: + + if (i != 0) + JS_FreeValue(ctx, resolving_funcs[0]); + ret = -1; + break; + } + sr->ref_count++; + s->presolved = sr; + s->promise = JS_DupValue(ctx, promise); + JS_SetOpaque(obj, s); + js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1); + resolving_funcs[i] = obj; + } + js_promise_resolve_function_free_resolved(ctx->rt, sr); + return ret; +} + +static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; + if (s) { + js_promise_resolve_function_free_resolved(rt, s->presolved); + JS_FreeValueRT(rt, s->promise); + js_free_rt(rt, s); + } +} + +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; + if (s) { + JS_MarkValue(rt, s->promise, mark_func); + } +} + +static JSValue js_promise_resolve_function_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSPromiseFunctionData *s; + JSValueConst resolution, args[3]; + JSValue then; + BOOL is_reject; + + s = p->u.promise_function_data; + if (!s || s->presolved->already_resolved) + return JS_UNDEFINED; + s->presolved->already_resolved = TRUE; + is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION; + if (argc > 0) + resolution = argv[0]; + else + resolution = JS_UNDEFINED; +#ifdef DUMP_PROMISE + printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject); + JS_DumpValue(ctx, resolution); + printf("\n"); +#endif + if (is_reject || !JS_IsObject(resolution)) { + goto done; + } else if (js_same_value(ctx, resolution, s->promise)) { + JS_ThrowTypeError(ctx, "promise self resolution"); + goto fail_reject; + } + then = JS_GetProperty(ctx, resolution, JS_ATOM_then); + if (JS_IsException(then)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + reject_promise(ctx, s->promise, error); + JS_FreeValue(ctx, error); + } else if (!JS_IsFunction(ctx, then)) { + JS_FreeValue(ctx, then); + done: + fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject); + } else { + args[0] = s->promise; + args[1] = resolution; + args[2] = then; + JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args); + JS_FreeValue(ctx, then); + } + return JS_UNDEFINED; +} + +static void js_promise_finalizer(JSRuntime *rt, JSValue val) +{ + JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); + struct list_head *el, *el1; + int i; + + if (!s) + return; + for(i = 0; i < 2; i++) { + list_for_each_safe(el, el1, &s->promise_reactions[i]) { + JSPromiseReactionData *rd = + list_entry(el, JSPromiseReactionData, link); + promise_reaction_data_free(rt, rd); + } + } + JS_FreeValueRT(rt, s->promise_result); + js_free_rt(rt, s); +} + +static void js_promise_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); + struct list_head *el; + int i; + + if (!s) + return; + for(i = 0; i < 2; i++) { + list_for_each(el, &s->promise_reactions[i]) { + JSPromiseReactionData *rd = + list_entry(el, JSPromiseReactionData, link); + JS_MarkValue(rt, rd->resolving_funcs[0], mark_func); + JS_MarkValue(rt, rd->resolving_funcs[1], mark_func); + JS_MarkValue(rt, rd->handler, mark_func); + } + } + JS_MarkValue(rt, s->promise_result, mark_func); +} + +static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValueConst executor; + JSValue obj; + JSPromiseData *s; + JSValue args[2], ret; + int i; + + executor = argv[0]; + if (check_function(ctx, executor)) + return JS_EXCEPTION; + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + s->promise_state = JS_PROMISE_PENDING; + s->is_handled = FALSE; + for(i = 0; i < 2; i++) + init_list_head(&s->promise_reactions[i]); + s->promise_result = JS_UNDEFINED; + JS_SetOpaque(obj, s); + if (js_create_resolving_functions(ctx, args, obj)) + goto fail; + ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args); + if (JS_IsException(ret)) { + JSValue ret2, error; + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret2)) + goto fail1; + JS_FreeValue(ctx, ret2); + } + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + return obj; + fail1: + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_promise_executor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + int i; + + for(i = 0; i < 2; i++) { + if (!JS_IsUndefined(func_data[i])) + return JS_ThrowTypeError(ctx, "resolving function already set"); + func_data[i] = JS_DupValue(ctx, argv[i]); + } + return JS_UNDEFINED; +} + +static JSValue js_promise_executor_new(JSContext *ctx) +{ + JSValueConst func_data[2]; + + func_data[0] = JS_UNDEFINED; + func_data[1] = JS_UNDEFINED; + return JS_NewCFunctionData(ctx, js_promise_executor, 2, + 0, 2, func_data); +} + +static JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst ctor) +{ + JSValue executor, result_promise; + JSCFunctionDataRecord *s; + int i; + + executor = js_promise_executor_new(ctx); + if (JS_IsException(executor)) + return executor; + + if (JS_IsUndefined(ctor)) { + result_promise = js_promise_constructor(ctx, ctor, 1, + (JSValueConst *)&executor); + } else { + result_promise = JS_CallConstructor(ctx, ctor, 1, + (JSValueConst *)&executor); + } + if (JS_IsException(result_promise)) + goto fail; + s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA); + for(i = 0; i < 2; i++) { + if (check_function(ctx, s->data[i])) + goto fail; + } + for(i = 0; i < 2; i++) + resolving_funcs[i] = JS_DupValue(ctx, s->data[i]); + JS_FreeValue(ctx, executor); + return result_promise; + fail: + JS_FreeValue(ctx, executor); + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; +} + +JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) +{ + return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); +} + +static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue result_promise, resolving_funcs[2], ret; + BOOL is_reject = magic; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) { + JSValue ctor; + BOOL is_same; + ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + is_same = js_same_value(ctx, ctor, this_val); + JS_FreeValue(ctx, ctor); + if (is_same) + return JS_DupValue(ctx, argv[0]); + } + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, result_promise); + return ret; + } + JS_FreeValue(ctx, ret); + return result_promise; +} + +#if 0 +static JSValue js_promise___newPromiseCapability(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue result_promise, resolving_funcs[2], obj; + JSValueConst ctor; + ctor = argv[0]; + if (!JS_IsObject(ctor)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + if (JS_IsException(result_promise)) + return result_promise; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; + } + JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); + return obj; +} +#endif + +static __exception int remainingElementsCount_add(JSContext *ctx, + JSValueConst resolve_element_env, + int addend) +{ + JSValue val; + int remainingElementsCount; + + val = JS_GetPropertyUint32(ctx, resolve_element_env, 0); + if (JS_IsException(val)) + return -1; + if (JS_ToInt32Free(ctx, &remainingElementsCount, val)) + return -1; + remainingElementsCount += addend; + if (JS_SetPropertyUint32(ctx, resolve_element_env, 0, + JS_NewInt32(ctx, remainingElementsCount)) < 0) + return -1; + return (remainingElementsCount == 0); +} + +static JSValue js_promise_all_resolve_element(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, + JSValue *func_data) +{ + int is_allSettled = magic & 1; + int is_reject = magic & 2; + 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 (is_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) { + 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], iter, 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; + JSValueConst then_args[2], resolve_element_data[5]; + BOOL done, is_allSettled = magic; + int index, is_zero; + + 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; + 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; + + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject1; + + 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[0]; + resolve_element_data[4] = resolve_element_env; + resolve_element = + JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, + is_allSettled, 5, resolve_element_data); + if (JS_IsException(resolve_element)) { + JS_FreeValue(ctx, next_promise); + goto fail_reject1; + } + + if (is_allSettled) { + reject_element = + JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, + is_allSettled | 2, 5, resolve_element_data); + if (JS_IsException(reject_element)) { + JS_FreeValue(ctx, next_promise); + goto fail_reject1; + } + } 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) { + ret = JS_Call(ctx, resolving_funcs[0], 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], iter, item, next_promise, ret; + JSValue next_method = 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; + 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; + + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject1; + + for(;;) { + /* XXX: conformance: should close the iterator if error on 'done' + access, but not on 'value' access */ + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail_reject; + if (done) + break; + next_promise = JS_Call(ctx, promise_resolve, + this_val, 1, (JSValueConst *)&item); + JS_FreeValue(ctx, item); + if (JS_IsException(next_promise)) { + fail_reject1: + JS_IteratorClose(ctx, iter, TRUE); + goto fail_reject; + } + ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, + (JSValueConst *)resolving_funcs); + if (check_exception_free(ctx, ret)) + goto fail_reject1; + } + } + done: + JS_FreeValue(ctx, promise_resolve); + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return result_promise; + fail: + //JS_FreeValue(ctx, next_method); // why not??? + JS_FreeValue(ctx, result_promise); + result_promise = JS_EXCEPTION; + goto done; +} + +static __exception int perform_promise_then(JSContext *ctx, + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + JSPromiseReactionData *rd_array[2], *rd; + int i, j; + + rd_array[0] = NULL; + rd_array[1] = NULL; + for(i = 0; i < 2; i++) { + JSValueConst handler; + rd = js_mallocz(ctx, sizeof(*rd)); + if (!rd) { + if (i == 1) + promise_reaction_data_free(ctx->rt, rd_array[0]); + return -1; + } + for(j = 0; j < 2; j++) + rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]); + handler = resolve_reject[i]; + if (!JS_IsFunction(ctx, handler)) + handler = JS_UNDEFINED; + rd->handler = JS_DupValue(ctx, handler); + rd_array[i] = rd; + } + + if (s->promise_state == JS_PROMISE_PENDING) { + for(i = 0; i < 2; i++) + list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); + } else { + JSValueConst args[5]; + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) { + rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, + TRUE, rt->host_promise_rejection_tracker_opaque); + } + } + i = s->promise_state - JS_PROMISE_FULFILLED; + rd = rd_array[i]; + args[0] = rd->resolving_funcs[0]; + args[1] = rd->resolving_funcs[1]; + args[2] = rd->handler; + args[3] = JS_NewBool(ctx, i); + args[4] = s->promise_result; + JS_EnqueueJob(ctx, promise_reaction_job, 5, args); + for(i = 0; i < 2; i++) + promise_reaction_data_free(ctx->rt, rd_array[i]); + } + s->is_handled = TRUE; + return 0; +} + +static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue ctor, result_promise, resolving_funcs[2]; + JSPromiseData *s; + int i, ret; + + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); + if (!s) + return JS_EXCEPTION; + + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + JS_FreeValue(ctx, ctor); + if (JS_IsException(result_promise)) + return result_promise; + ret = perform_promise_then(ctx, this_val, argv, + (JSValueConst *)resolving_funcs); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (ret) { + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; + } + return result_promise; +} + +static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst args[2]; + args[0] = JS_UNDEFINED; + args[1] = argv[0]; + return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); +} + +static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return JS_DupValue(ctx, func_data[0]); +} + +static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return JS_Throw(ctx, JS_DupValue(ctx, func_data[0])); +} + +static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + JSValueConst ctor = func_data[0]; + JSValueConst onFinally = func_data[1]; + JSValue res, promise, resolving_funcs[2], ret, then_func; + + res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); + if (JS_IsException(res)) + return res; + promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + if (JS_IsException(promise)) { + JS_FreeValue(ctx, res); + return JS_EXCEPTION; + } + ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst*)&res); + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, promise); + return ret; + } + JS_FreeValue(ctx, ret); + if (magic == 0) { + then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 1, + 0, 1, argv); + } else { + then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 1, + 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, 0 ), + JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, 1 ), + JS_CFUNC_DEF("race", 1, js_promise_race ), + //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), +}; + +static const JSCFunctionListEntry js_promise_proto_funcs[] = { + JS_CFUNC_DEF("then", 2, js_promise_then ), + JS_CFUNC_DEF("catch", 1, js_promise_catch ), + JS_CFUNC_DEF("finally", 1, js_promise_finally ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ), +}; + +/* AsyncFunction */ +static const JSCFunctionListEntry js_async_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), +}; + +static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), + JS_ToBool(ctx, func_data[0])); +} + +static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, + BOOL done) +{ + JSValueConst func_data[1]; + + func_data[0] = (JSValueConst)JS_NewBool(ctx, done); + return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, + 1, 0, 1, func_data); +} + +/* AsyncIteratorPrototype */ + +static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { + JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ), +}; + +/* AsyncFromSyncIteratorPrototype */ + +typedef struct JSAsyncFromSyncIteratorData { + JSValue sync_iter; + JSValue next_method; +} JSAsyncFromSyncIteratorData; + +static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSAsyncFromSyncIteratorData *s = + JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (s) { + JS_FreeValueRT(rt, s->sync_iter); + JS_FreeValueRT(rt, s->next_method); + js_free_rt(rt, s); + } +} + +static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSAsyncFromSyncIteratorData *s = + JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (s) { + JS_MarkValue(rt, s->sync_iter, mark_func); + JS_MarkValue(rt, s->next_method, mark_func); + } +} + +static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, + JSValueConst sync_iter) +{ + JSValue async_iter, next_method; + JSAsyncFromSyncIteratorData *s; + + next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next); + if (JS_IsException(next_method)) + return JS_EXCEPTION; + async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (JS_IsException(async_iter)) { + JS_FreeValue(ctx, next_method); + return async_iter; + } + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, async_iter); + JS_FreeValue(ctx, next_method); + return JS_EXCEPTION; + } + s->sync_iter = JS_DupValue(ctx, sync_iter); + s->next_method = next_method; + JS_SetOpaque(async_iter, s); + return async_iter; +} + +static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + JSValue promise, resolving_funcs[2], value, err, method; + JSAsyncFromSyncIteratorData *s; + int done; + int is_reject; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (!s) { + JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator"); + goto reject; + } + + if (magic == GEN_MAGIC_NEXT) { + method = JS_DupValue(ctx, s->next_method); + } else { + method = JS_GetProperty(ctx, s->sync_iter, + magic == GEN_MAGIC_RETURN ? JS_ATOM_return : + JS_ATOM_throw); + if (JS_IsException(method)) + goto reject; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + if (magic == GEN_MAGIC_RETURN) { + err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE); + is_reject = 0; + } else { + err = JS_DupValue(ctx, argv[0]); + is_reject = 1; + } + goto done_resolve; + } + } + value = JS_IteratorNext2(ctx, s->sync_iter, method, 1, argv, &done); + JS_FreeValue(ctx, method); + if (JS_IsException(value)) + goto reject; + if (done == 2) { + JSValue obj = value; + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + JS_FreeValue(ctx, obj); + if (JS_IsException(value)) + goto reject; + } + + if (JS_IsException(value)) { + JSValue res2; + reject: + err = JS_GetException(ctx); + is_reject = 1; + done_resolve: + res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + { + JSValue value_wrapper_promise, resolve_reject[2]; + int res; + + value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + if (JS_IsException(value_wrapper_promise)) { + JS_FreeValue(ctx, value); + goto reject; + } + + resolve_reject[0] = + js_async_from_sync_iterator_unwrap_func_create(ctx, done); + if (JS_IsException(resolve_reject[0])) { + JS_FreeValue(ctx, value_wrapper_promise); + goto fail; + } + JS_FreeValue(ctx, value); + resolve_reject[1] = JS_UNDEFINED; + + res = perform_promise_then(ctx, value_wrapper_promise, + (JSValueConst *)resolve_reject, + (JSValueConst *)resolving_funcs); + JS_FreeValue(ctx, resolve_reject[0]); + JS_FreeValue(ctx, value_wrapper_promise); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (res) { + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; + } + } + return promise; + fail: + JS_FreeValue(ctx, value); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ), + JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ), + JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ), +}; + +/* AsyncGeneratorFunction */ + +static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ), +}; + +/* AsyncGenerator prototype */ + +static const JSCFunctionListEntry js_async_generator_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ), + JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ), + JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ), +}; + +static JSClassShortDef const js_async_class_def[] = { + { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */ + { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */ + { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */ + { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */ + { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */ + { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */ + { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */ +}; + +void JS_AddIntrinsicPromise(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + + if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { + init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, + countof(js_async_class_def)); + rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call; + rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call; + rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call; + } + + /* Promise */ + ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE], + js_promise_proto_funcs, + countof(js_promise_proto_funcs)); + obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1, + JS_CFUNC_constructor, 0); + ctx->promise_ctor = JS_DupValue(ctx, obj1); + JS_SetPropertyFunctionList(ctx, obj1, + js_promise_funcs, + countof(js_promise_funcs)); + JS_NewGlobalCConstructor2(ctx, obj1, "Promise", + ctx->class_proto[JS_CLASS_PROMISE]); + + /* AsyncFunction */ + ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); + obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + "AsyncFunction", 1, + JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, + ctx->function_ctor); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], + js_async_function_proto_funcs, + countof(js_async_function_proto_funcs)); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); + + /* AsyncIteratorPrototype */ + ctx->async_iterator_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto, + js_async_iterator_proto_funcs, + countof(js_async_iterator_proto_funcs)); + + /* AsyncFromSyncIteratorPrototype */ + ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] = + JS_NewObjectProto(ctx, ctx->async_iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR], + js_async_from_sync_iterator_proto_funcs, + countof(js_async_from_sync_iterator_proto_funcs)); + + /* AsyncGeneratorPrototype */ + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] = + JS_NewObjectProto(ctx, ctx->async_iterator_proto); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], + js_async_generator_proto_funcs, + countof(js_async_generator_proto_funcs)); + + /* AsyncGeneratorFunction */ + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] = + JS_NewObjectProto(ctx, ctx->function_proto); + obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + "AsyncGeneratorFunction", 1, + JS_CFUNC_constructor_or_func_magic, + JS_FUNC_ASYNC_GENERATOR, + ctx->function_ctor); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + js_async_generator_function_proto_funcs, + countof(js_async_generator_function_proto_funcs)); + JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], + JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); +} + +/* URI handling */ + +static int string_get_hex(JSString *p, int k, int n) { + int c = 0, h; + while (n-- > 0) { + if ((h = from_hex(string_get(p, k++))) < 0) + return -1; + c = (c << 4) | h; + } + return c; +} + +static int isURIReserved(int c) { + return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL; +} + +static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap); + va_end(ap); + return -1; +} + +static int hex_decode(JSContext *ctx, JSString *p, int k) { + int c; + + if (k >= p->len || string_get(p, k) != '%') + return js_throw_URIError(ctx, "expecting %%"); + if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0) + return js_throw_URIError(ctx, "expecting hex digit"); + + return c; +} + +static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int isComponent) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int k, c, c1, n, c_min; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + + string_buffer_init(ctx, b, 0); + + p = JS_VALUE_GET_STRING(str); + for (k = 0; k < p->len;) { + c = string_get(p, k); + if (c == '%') { + c = hex_decode(ctx, p, k); + if (c < 0) + goto fail; + k += 3; + if (c < 0x80) { + if (!isComponent && isURIReserved(c)) { + c = '%'; + k -= 2; + } + } else { + /* Decode URI-encoded UTF-8 sequence */ + if (c >= 0xc0 && c <= 0xdf) { + n = 1; + c_min = 0x80; + c &= 0x1f; + } else if (c >= 0xe0 && c <= 0xef) { + n = 2; + c_min = 0x800; + c &= 0xf; + } else if (c >= 0xf0 && c <= 0xf7) { + n = 3; + c_min = 0x10000; + c &= 0x7; + } else { + n = 0; + c_min = 1; + c = 0; + } + while (n-- > 0) { + c1 = hex_decode(ctx, p, k); + if (c1 < 0) + goto fail; + k += 3; + if ((c1 & 0xc0) != 0x80) { + c = 0; + break; + } + c = (c << 6) | (c1 & 0x3f); + } + if (c < c_min || c > 0x10FFFF || + (c >= 0xd800 && c < 0xe000)) { + js_throw_URIError(ctx, "malformed UTF-8"); + goto fail; + } + } + } else { + k++; + } + string_buffer_putc(b, c); + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + JS_FreeValue(ctx, str); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static int isUnescaped(int c) { + static char const unescaped_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "@*_+-./"; + return c < 0x100 && + memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1); +} + +static int isURIUnescaped(int c, int isComponent) { + return c < 0x100 && + ((c >= 0x61 && c <= 0x7a) || + (c >= 0x41 && c <= 0x5a) || + (c >= 0x30 && c <= 0x39) || + memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL || + (!isComponent && isURIReserved(c))); +} + +static int encodeURI_hex(StringBuffer *b, int c) { + uint8_t buf[6]; + int n = 0; + const char *hex = "0123456789ABCDEF"; + + buf[n++] = '%'; + if (c >= 256) { + buf[n++] = 'u'; + buf[n++] = hex[(c >> 12) & 15]; + buf[n++] = hex[(c >> 8) & 15]; + } + buf[n++] = hex[(c >> 4) & 15]; + buf[n++] = hex[(c >> 0) & 15]; + return string_buffer_write8(b, buf, n); +} + +static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int isComponent) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int k, c, c1; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + + p = JS_VALUE_GET_STRING(str); + string_buffer_init(ctx, b, p->len); + for (k = 0; k < p->len;) { + c = string_get(p, k); + k++; + if (isURIUnescaped(c, isComponent)) { + string_buffer_putc16(b, c); + } else { + if (c >= 0xdc00 && c <= 0xdfff) { + js_throw_URIError(ctx, "invalid character"); + goto fail; + } else if (c >= 0xd800 && c <= 0xdbff) { + if (k >= p->len) { + js_throw_URIError(ctx, "expecting surrogate pair"); + goto fail; + } + c1 = string_get(p, k); + k++; + if (c1 < 0xdc00 || c1 > 0xdfff) { + js_throw_URIError(ctx, "expecting surrogate pair"); + goto fail; + } + c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + } + if (c < 0x80) { + encodeURI_hex(b, c); + } else { + /* XXX: use C UTF-8 conversion ? */ + if (c < 0x800) { + encodeURI_hex(b, (c >> 6) | 0xc0); + } else { + if (c < 0x10000) { + encodeURI_hex(b, (c >> 12) | 0xe0); + } else { + encodeURI_hex(b, (c >> 18) | 0xf0); + encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80); + } + encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80); + } + encodeURI_hex(b, (c & 0x3f) | 0x80); + } + } + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + JS_FreeValue(ctx, str); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int i, len, c; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + + p = JS_VALUE_GET_STRING(str); + string_buffer_init(ctx, b, p->len); + for (i = 0, len = p->len; i < len; i++) { + c = string_get(p, i); + if (isUnescaped(c)) { + string_buffer_putc16(b, c); + } else { + encodeURI_hex(b, c); + } + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); +} + +static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString *p; + int i, len, c, n; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + + string_buffer_init(ctx, b, 0); + p = JS_VALUE_GET_STRING(str); + for (i = 0, len = p->len; i < len; i++) { + c = string_get(p, i); + if (c == '%') { + if (i + 6 <= len + && string_get(p, i + 1) == 'u' + && (n = string_get_hex(p, i + 2, 4)) >= 0) { + c = n; + i += 6 - 1; + } else + if (i + 3 <= len + && (n = string_get_hex(p, i + 1, 2)) >= 0) { + c = n; + i += 3 - 1; + } + } + string_buffer_putc16(b, c); + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); +} + +/* global object */ + +static const JSCFunctionListEntry js_global_funcs[] = { + JS_CFUNC_DEF("parseInt", 2, js_parseInt ), + JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ), + JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ), + JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ), + + JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ), + JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ), + JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ), + JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ), + JS_CFUNC_DEF("escape", 1, js_global_escape ), + JS_CFUNC_DEF("unescape", 1, js_global_unescape ), + JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), + JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), + JS_PROP_UNDEFINED_DEF("undefined", 0 ), + + /* for the 'Date' implementation */ + JS_CFUNC_DEF("__date_clock", 0, js___date_clock ), + //JS_CFUNC_DEF("__date_now", 0, js___date_now ), + //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ), + //JS_CFUNC_DEF("__date_create", 3, js___date_create ), +}; + +/* Date */ + +static int64_t math_mod(int64_t a, int64_t b) { + /* return positive modulo */ + int64_t m = a % b; + return m + (m < 0) * b; +} + +static int64_t floor_div(int64_t a, int64_t b) { + /* integer division rounding toward -Infinity */ + int64_t m = a % b; + return (a - (m + (m < 0) * b)) / b; +} + +static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) + return JS_ToFloat64(ctx, valp, p->u.object_data); + } + JS_ThrowTypeError(ctx, "not a Date object"); + return -1; +} + +static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_DATE) { + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = __JS_NewFloat64(ctx, v); + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a Date object"); +} + +static int64_t days_from_year(int64_t y) { + return 365 * (y - 1970) + floor_div(y - 1969, 4) - + floor_div(y - 1901, 100) + floor_div(y - 1601, 400); +} + +static int64_t days_in_year(int64_t y) { + return 365 + !(y % 4) - !(y % 100) + !(y % 400); +} + +/* return the year, update days */ +static int64_t year_from_days(int64_t *days) { + int64_t y, d1, nd, d = *days; + y = floor_div(d * 10000, 3652425) + 1970; + /* the initial approximation is very good, so only a few + iterations are necessary */ + for(;;) { + d1 = d - days_from_year(y); + if (d1 < 0) { + y--; + d1 += days_in_year(y); + } else { + nd = days_in_year(y); + if (d1 < nd) + break; + d1 -= nd; + y++; + } + } + *days = d1; + return y; +} + +static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; +static char const day_names[] = "SunMonTueWedThuFriSat"; + +static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, + int64_t fields[9], int is_local, int force) +{ + double dval; + int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; + + if (JS_ThisTimeValue(ctx, &dval, obj)) + return -1; + + if (isnan(dval)) { + if (!force) + return FALSE; /* NaN */ + d = 0; /* initialize all fields to 0 */ + } else { + d = dval; + if (is_local) { + tz = -getTimezoneOffset(d); + d += tz * 60000; + } + } + + /* result is >= 0, we can use % */ + h = math_mod(d, 86400000); + days = (d - h) / 86400000; + ms = h % 1000; + h = (h - ms) / 1000; + s = h % 60; + h = (h - s) / 60; + m = h % 60; + h = (h - m) / 60; + wd = math_mod(days + 4, 7); /* week day */ + y = year_from_days(&days); + + for(i = 0; i < 11; i++) { + md = month_days[i]; + if (i == 1) + md += days_in_year(y) - 365; + if (days < md) + break; + days -= md; + } + fields[0] = y; + fields[1] = i; + fields[2] = days + 1; + fields[3] = h; + fields[4] = m; + fields[5] = s; + fields[6] = ms; + fields[7] = wd; + fields[8] = tz; + return TRUE; +} + +static double time_clip(double t) { + if (t >= -8.64e15 && t <= 8.64e15) + return trunc(t) + 0.0; /* convert -0 to +0 */ + else + return NAN; +} + +static double set_date_fields(int64_t fields[], int is_local) { + int64_t days, y, m, md, h, d, i; + + i = fields[1]; + m = math_mod(i, 12); + y = fields[0] + (i - m) / 12; + days = days_from_year(y); + + for(i = 0; i < m; i++) { + md = month_days[i]; + if (i == 1) + md += days_in_year(y) - 365; + days += md; + } + days += fields[2] - 1; + h = ((fields[3] * 60 + fields[4]) * 60 + fields[5]) * 1000 + fields[6]; + d = days * 86400000 + h; + if (is_local) + d += getTimezoneOffset(d) * 60000; + return time_clip(d); +} + +static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + // get_date_field(obj, n, is_local) + int64_t fields[9]; + int res, n, is_local; + + is_local = magic & 0x0F; + n = (magic >> 4) & 0x0F; + res = get_date_fields(ctx, this_val, fields, is_local, 0); + if (res < 0) + return JS_EXCEPTION; + if (!res) + return JS_NAN; + + if (magic & 0x100) { // getYear + fields[0] -= 1900; + } + return JS_NewInt64(ctx, fields[n]); +} + +static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + // _field(obj, first_field, end_field, args, is_local) + int64_t fields[9]; + int res, first_field, end_field, is_local, i, n; + double d, a; + + d = NAN; + first_field = (magic >> 8) & 0x0F; + end_field = (magic >> 4) & 0x0F; + is_local = magic & 0x0F; + + res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); + if (res < 0) + return JS_EXCEPTION; + if (res && argc > 0) { + n = end_field - first_field; + if (argc < n) + n = argc; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + goto done; + fields[first_field + i] = trunc(a); + } + d = set_date_fields(fields, is_local); + } +done: + return JS_SetThisTimeValue(ctx, this_val, d); +} + +/* fmt: + 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" + 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" + 2: toISOString: "2018-01-02T23:02:56.927Z" + 3: toLocaleString: "1/2/2018, 11:40:40 PM" + part: 1=date, 2=time 3=all + XXX: should use a variant of strftime(). + */ +static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + // _string(obj, fmt, part) + char buf[64]; + int64_t fields[9]; + int res, fmt, part, pos; + int y, mon, d, h, m, s, ms, wd, tz; + + fmt = (magic >> 4) & 0x0F; + part = magic & 0x0F; + + res = get_date_fields(ctx, this_val, fields, fmt & 1, 0); + if (res < 0) + return JS_EXCEPTION; + if (!res) { + if (fmt == 2) + return JS_ThrowRangeError(ctx, "Date value is NaN"); + else + return JS_NewString(ctx, "Invalid Date"); + } + + y = fields[0]; + mon = fields[1]; + d = fields[2]; + h = fields[3]; + m = fields[4]; + s = fields[5]; + ms = fields[6]; + wd = fields[7]; + tz = fields[8]; + + pos = 0; + + if (part & 1) { /* date part */ + switch(fmt) { + case 0: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%.3s, %02d %.3s %0*d ", + day_names + wd * 3, d, + month_names + mon * 3, 4 + (y < 0), y); + break; + case 1: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%.3s %.3s %02d %0*d", + day_names + wd * 3, + month_names + mon * 3, d, 4 + (y < 0), y); + if (part == 3) { + buf[pos++] = ' '; + } + break; + case 2: + if (y >= 0 && y <= 9999) { + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%04d", y); + } else { + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%+07d", y); + } + pos += snprintf(buf + pos, sizeof(buf) - pos, + "-%02d-%02dT", mon + 1, d); + break; + case 3: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y); + if (part == 3) { + buf[pos++] = ','; + buf[pos++] = ' '; + } + break; + } + } + if (part & 2) { /* time part */ + switch(fmt) { + case 0: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d GMT", h, m, s); + break; + case 1: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d GMT", h, m, s); + if (tz < 0) { + buf[pos++] = '-'; + tz = -tz; + } else { + buf[pos++] = '+'; + } + /* tz is >= 0, can use % */ + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d%02d", tz / 60, tz % 60); + /* XXX: tack the time zone code? */ + break; + case 2: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d.%03dZ", h, m, s, ms); + break; + case 3: + pos += snprintf(buf + pos, sizeof(buf) - pos, + "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s, + (h < 12) ? 'A' : 'P'); + break; + } + } + return JS_NewStringLen(ctx, buf, pos); +} + +/* OS dependent: return the UTC time in ms since 1970. */ +static int64_t date_now(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); +} + +static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + // Date(y, mon, d, h, m, s, ms) + JSValue rv; + int i, n; + double a, val; + + if (JS_IsUndefined(new_target)) { + /* invoked as function */ + argc = 0; + } + n = argc; + if (n == 0) { + val = date_now(); + } else if (n == 1) { + JSValue v, dv; + if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) { + if (JS_ToFloat64(ctx, &val, p->u.object_data)) + return JS_EXCEPTION; + val = time_clip(val); + goto has_val; + } + } + v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); + if (JS_IsString(v)) { + dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v); + JS_FreeValue(ctx, v); + if (JS_IsException(dv)) + return JS_EXCEPTION; + if (JS_ToFloat64Free(ctx, &val, dv)) + return JS_EXCEPTION; + } else { + if (JS_ToFloat64Free(ctx, &val, v)) + return JS_EXCEPTION; + } + val = time_clip(val); + } else { + int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + if (n > 7) + n = 7; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + break; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; + } + val = (i == n) ? set_date_fields(fields, 1) : NAN; + } +has_val: +#if 0 + JSValueConst args[3]; + args[0] = new_target; + args[1] = ctx->class_proto[JS_CLASS_DATE]; + args[2] = __JS_NewFloat64(ctx, val); + rv = js___date_create(ctx, JS_UNDEFINED, 3, args); +#else + rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); + if (!JS_IsException(rv)) + JS_SetObjectData(ctx, rv, __JS_NewFloat64(ctx, val)); +#endif + if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { + /* invoked as a function, return (new Date()).toString(); */ + JSValue s; + s = get_date_string(ctx, rv, 0, NULL, 0x13); + JS_FreeValue(ctx, rv); + rv = s; + } + return rv; +} + +static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // UTC(y, mon, d, h, m, s, ms) + int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + int i, n; + double a; + + n = argc; + if (n == 0) + return JS_NAN; + if (n > 7) + n = 7; + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + return JS_NAN; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; + } + return __JS_NewFloat64(ctx, set_date_fields(fields, 0)); +} + +static void string_skip_spaces(JSString *sp, int *pp) { + while (*pp < sp->len && string_get(sp, *pp) == ' ') + *pp += 1; +} + +static void string_skip_non_spaces(JSString *sp, int *pp) { + while (*pp < sp->len && string_get(sp, *pp) != ' ') + *pp += 1; +} + +/* parse a numeric field */ +static int string_get_field(JSString *sp, int *pp, int64_t *pval) { + int64_t v = 0; + int c, p = *pp; + + /* skip non digits, should only skip spaces? */ + while (p < sp->len) { + c = string_get(sp, p); + if (c >= '0' && c <= '9') + break; + p++; + } + if (p >= sp->len) + return -1; + while (p < sp->len) { + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) + break; + v = v * 10 + c - '0'; + p++; + } + *pval = v; + *pp = p; + return 0; +} + +/* parse a fixed width numeric field */ +static int string_get_digits(JSString *sp, int *pp, int n, int64_t *pval) { + int64_t v = 0; + int i, c, p = *pp; + + for(i = 0; i < n; i++) { + if (p >= sp->len) + return -1; + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) + return -1; + v = v * 10 + c - '0'; + p++; + } + *pval = v; + *pp = p; + return 0; +} + +/* parse a signed numeric field */ +static int string_get_signed_field(JSString *sp, int *pp, int64_t *pval) { + int sgn, res; + + if (*pp >= sp->len) + return -1; + + sgn = string_get(sp, *pp); + if (sgn == '-' || sgn == '+') + *pp += 1; + + res = string_get_field(sp, pp, pval); + if (res == 0 && sgn == '-') + *pval = -*pval; + return res; +} + +static int find_abbrev(JSString *sp, int p, const char *list, int count) { + int n, i; + + if (p + 3 <= sp->len) { + for (n = 0; n < count; n++) { + for (i = 0; i < 3; i++) { + if (string_get(sp, p + i) != month_names[n * 3 + i]) + goto next; + } + return n; + next:; + } + } + return -1; +} + +static int string_get_month(JSString *sp, int *pp, int64_t *pval) { + int n; + + string_skip_spaces(sp, pp); + n = find_abbrev(sp, *pp, month_names, 12); + if (n < 0) + return -1; + + *pval = n; + *pp += 3; + return 0; +} + +static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // parse(s) + JSValue s, rv; + int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 }; + int64_t tz, hh, mm; + double d; + int p, i, c, sgn; + JSString *sp; + + rv = JS_NAN; + + s = JS_ToString(ctx, argv[0]); + if (JS_IsException(s)) + return JS_EXCEPTION; + + sp = JS_VALUE_GET_STRING(s); + p = 0; + if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) { + /* ISO format */ + /* year field can be negative */ + /* XXX: could be stricter */ + if (string_get_signed_field(sp, &p, &fields[0])) + goto done; + + for (i = 1; i < 6; i++) { + if (string_get_field(sp, &p, &fields[i])) + break; + } + if (i == 6 && p < sp->len && string_get(sp, p) == '.') { + /* parse milliseconds as a fractional part, round to nearest */ + /* XXX: the spec does not indicate which rounding should be used */ + int mul = 1000, ms = 0; + while (++p < sp->len) { + int c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) + break; + if (mul == 1 && c >= '5') + ms += 1; + ms += (c - '0') * (mul /= 10); + } + fields[6] = ms; + } + fields[1] -= 1; + + /* parse the time zone offset if present: [+-]HH:mm */ + tz = 0; + if (p < sp->len && ((sgn = string_get(sp, p)) == '+' || sgn == '-')) { + if (string_get_field(sp, &p, &hh)) + goto done; + if (string_get_field(sp, &p, &mm)) + goto done; + tz = hh * 60 + mm; + if (sgn == '-') + tz = -tz; + } + } else { + /* toString or toUTCString format */ + /* skip the day of the week */ + string_skip_non_spaces(sp, &p); + string_skip_spaces(sp, &p); + if (p >= sp->len) + goto done; + c = string_get(sp, p); + if (c >= '0' && c <= '9') { + /* day of month first */ + if (string_get_field(sp, &p, &fields[2])) + goto done; + if (string_get_month(sp, &p, &fields[1])) + goto done; + } else { + /* month first */ + if (string_get_month(sp, &p, &fields[1])) + goto done; + if (string_get_field(sp, &p, &fields[2])) + goto done; + } + string_skip_spaces(sp, &p); + if (string_get_signed_field(sp, &p, &fields[0])) + goto done; + + /* hour, min, seconds */ + for(i = 0; i < 3; i++) { + if (string_get_field(sp, &p, &fields[3 + i])) + goto done; + } + // XXX: parse optional milliseconds? + + /* parse the time zone offset if present: [+-]HHmm */ + tz = 0; + for (tz = 0; p < sp->len; p++) { + sgn = string_get(sp, p); + if (sgn == '+' || sgn == '-') { + p++; + if (string_get_digits(sp, &p, 2, &hh)) + goto done; + if (string_get_digits(sp, &p, 2, &mm)) + goto done; + tz = hh * 60 + mm; + if (sgn == '-') + tz = -tz; + break; + } + } + } + d = set_date_fields(fields, 0) - tz * 60000; + rv = __JS_NewFloat64(ctx, d); + +done: + JS_FreeValue(ctx, s); + return rv; +} + +static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // now() + return JS_NewInt64(ctx, date_now()); +} + +static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // Symbol_toPrimitive(hint) + JSValueConst obj = this_val; + JSAtom hint = JS_ATOM_NULL; + int hint_num; + + if (!JS_IsObject(obj)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + if (JS_IsString(argv[0])) { + hint = JS_ValueToAtom(ctx, argv[0]); + if (hint == JS_ATOM_NULL) + return JS_EXCEPTION; + JS_FreeAtom(ctx, hint); + } + switch (hint) { + case JS_ATOM_number: +#ifdef CONFIG_BIGNUM + case JS_ATOM_integer: +#endif + hint_num = HINT_NUMBER; + break; + case JS_ATOM_string: + case JS_ATOM_default: + hint_num = HINT_STRING; + break; + default: + return JS_ThrowTypeError(ctx, "invalid hint"); + } + return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); +} + +static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // getTimezoneOffset() + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + if (isnan(v)) + return JS_NAN; + else + return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); +} + +static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // getTime() + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + return __JS_NewFloat64(ctx, v); +} + +static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // setTime(v) + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) + return JS_EXCEPTION; + return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); +} + +static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // setYear(y) + double y; + JSValueConst args[1]; + + if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) + return JS_EXCEPTION; + y = +y; + if (isfinite(y)) { + y = trunc(y); + if (y >= 0 && y < 100) + y += 1900; + } + args[0] = __JS_NewFloat64(ctx, y); + return set_date_field(ctx, this_val, 1, args, 0x011); +} + +static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // toJSON(key) + JSValue obj, tv, method, rv; + double d; + + rv = JS_EXCEPTION; + tv = JS_UNDEFINED; + + obj = JS_ToObject(ctx, this_val); + tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); + if (JS_IsException(tv)) + goto exception; + if (JS_IsNumber(tv)) { + if (JS_ToFloat64(ctx, &d, tv) < 0) + goto exception; + if (!isfinite(d)) { + rv = JS_NULL; + goto done; + } + } + method = JS_GetPropertyStr(ctx, obj, "toISOString"); + if (JS_IsException(method)) + goto exception; + if (!JS_IsFunction(ctx, method)) { + JS_ThrowTypeError(ctx, "object needs toISOString method"); + JS_FreeValue(ctx, method); + goto exception; + } + rv = JS_CallFree(ctx, method, obj, 0, NULL); +exception: +done: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, tv); + return rv; +} + +static const JSCFunctionListEntry js_date_funcs[] = { + JS_CFUNC_DEF("now", 0, js_Date_now ), + JS_CFUNC_DEF("parse", 1, js_Date_parse ), + JS_CFUNC_DEF("UTC", 7, js_Date_UTC ), +}; + +static const JSCFunctionListEntry js_date_proto_funcs[] = { + JS_CFUNC_DEF("valueOf", 0, js_date_getTime ), + JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ), + JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ), + JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ), + JS_ALIAS_DEF("toGMTString", "toUTCString" ), + JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ), + JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ), + JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ), + JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ), + JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ), + JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ), + JS_CFUNC_DEF("getTime", 0, js_date_getTime ), + JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ), + JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ), + JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ), + JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ), + JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ), + JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ), + JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ), + JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ), + JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ), + JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ), + JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ), + JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ), + JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ), + JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ), + JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ), + JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ), + JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ), + JS_CFUNC_DEF("setTime", 1, js_date_setTime ), + JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ), + JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ), + JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ), + JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ), + JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ), + JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ), + JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ), + JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ), + JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ), + JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ), + JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ), + JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ), + JS_CFUNC_DEF("setYear", 1, js_date_setYear ), + JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ), + JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ), + JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ), +}; + +void JS_AddIntrinsicDate(JSContext *ctx) +{ + JSValueConst obj; + + /* Date */ + ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs, + countof(js_date_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7, + ctx->class_proto[JS_CLASS_DATE]); + JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs)); +} + +/* eval */ + +void JS_AddIntrinsicEval(JSContext *ctx) +{ + ctx->eval_internal = __JS_EvalInternal; +} + +#ifdef CONFIG_BIGNUM + +/* BigInt */ + +static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + BOOL is_legacy; + int ret; + + is_legacy = is_bigint_mode(ctx) ^ 1; + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + if (is_legacy) { + bf_t r_s, *r = &r_s; + bf_init(ctx->bf_ctx, r); + bf_set_si(r, JS_VALUE_GET_INT(val)); + val = JS_NewBigInt2(ctx, r, TRUE); + } else { + val = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); + } + break; + case JS_TAG_BIG_INT: + break; + case JS_TAG_FLOAT64: + case JS_TAG_BIG_FLOAT: + { + bf_t *a, a_s, r_s, *r = &r_s; + a = JS_ToBigFloat(ctx, &a_s, val); + bf_init(ctx->bf_ctx, r); + if (!bf_is_finite(a)) { + JS_FreeValue(ctx, val); + val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint"); + } else { + bf_set(r, a); + ret = bf_rint(r, BF_RNDZ); + JS_FreeValue(ctx, val); + if (is_legacy && (ret & BF_ST_INEXACT)) { + bf_delete(r); + val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer"); + } else { + val = JS_NewBigInt2(ctx, r, is_legacy); + } + } + if (a == &a_s) + bf_delete(a); + } + break; + case JS_TAG_STRING: + val = JS_StringToBigIntErr(ctx, val); + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, + is_legacy ? HINT_NUMBER : HINT_INTEGER); + if (JS_IsException(val)) + break; + goto redo; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert to bigint"); + } + return val; +} + +static JSValue js_bigint_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); +} + +static inline BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + if (tag == JS_TAG_BIG_INT) + return TRUE; + if (is_bigint_mode(ctx)) + return tag == JS_TAG_INT; + else + return FALSE; +} + +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) { + /* XXX: may relax the check in case the object comes from + bignum mode */ + 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 { + if (JS_ToInt32Sat(ctx, &base, argv[0])) + goto fail; + if (base < 2 || base > 36) { + JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + goto fail; + } + } + ret = js_bigint_to_string1(ctx, val, base); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisBigIntValue(ctx, this_val); +} + +static JSValue js_bigint_div(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, b_s, *a, *b, r_s, *r = &r_s, q_s, *q = &q_s; + int status; + + b = NULL; + a = JS_ToBigInt(ctx, &a_s, argv[0]); + if (!a) + return JS_EXCEPTION; + b = JS_ToBigInt(ctx, &b_s, argv[1]); + if (!b) { + JS_FreeBigInt(ctx, a, &a_s); + return JS_EXCEPTION; + } + bf_init(ctx->bf_ctx, q); + bf_init(ctx->bf_ctx, r); + 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)) { + bf_delete(q); + bf_delete(r); + throw_bf_exception(ctx, status); + return JS_EXCEPTION; + } + if (magic & 0x10) { + JSValue ret; + /* XXX: handle exceptions */ + ret = JS_NewArray(ctx); + JS_SetPropertyUint32(ctx, ret, 0, JS_NewBigInt(ctx, q)); + JS_SetPropertyUint32(ctx, ret, 1, JS_NewBigInt(ctx, r)); + return ret; + } else { + bf_delete(r); + return JS_NewBigInt(ctx, q); + } +} + +static JSValue js_bigint_sqrt(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, r_s, *r = &r_s, rem_s, *rem = &rem_s; + int status; + + a = JS_ToBigInt(ctx, &a_s, argv[0]); + if (!a) + return JS_EXCEPTION; + bf_init(ctx->bf_ctx, r); + bf_init(ctx->bf_ctx, rem); + status = bf_sqrtrem(r, rem, a); + JS_FreeBigInt(ctx, a, &a_s); + if (unlikely(status & ~BF_ST_INEXACT)) { + bf_delete(r); + bf_delete(rem); + return throw_bf_exception(ctx, status); + } + if (magic) { + JSValue ret; + /* XXX: handle exceptions */ + ret = JS_NewArray(ctx); + JS_SetPropertyUint32(ctx, ret, 0, JS_NewBigInt(ctx, r)); + JS_SetPropertyUint32(ctx, ret, 1, JS_NewBigInt(ctx, rem)); + return ret; + } else { + bf_delete(rem); + return JS_NewBigInt(ctx, r); + } +} + +static JSValue js_bigint_op1(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + bf_t a_s, *a; + int64_t res; + + a = JS_ToBigInt(ctx, &a_s, argv[0]); + if (!a) + return JS_EXCEPTION; + switch(magic) { + case 0: /* floorLog2 */ + if (a->sign || a->expn <= 0) { + res = -1; + } else { + res = a->expn - 1; + } + break; + case 1: /* ctz */ + if (bf_is_zero(a)) { + res = -1; + } else { + res = bf_get_exp_min(a); + } + break; + default: + abort(); + } + JS_FreeBigInt(ctx, a, &a_s); + return JS_NewBigInt64(ctx, res); +} + +static JSValue js_bigint_asUintN(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, int asIntN) +{ + uint64_t bits; + bf_t a_s, *a = &a_s, r_s, *r = &r_s, mask_s, *mask = &mask_s; + + if (JS_ToIndex(ctx, &bits, argv[0])) + return JS_EXCEPTION; + a = JS_ToBigInt(ctx, &a_s, argv[1]); + if (!a) + return JS_EXCEPTION; + /* XXX: optimize */ + bf_init(ctx->bf_ctx, r); + 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_NewBigInt(ctx, r); +} + +static const JSCFunctionListEntry js_bigint_funcs[] = { + JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ), + JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ), + /* QuickJS extensions */ + JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ), + JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ), + JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ), + JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ), + JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ), + JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ), + JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ), + JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ), + JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ), + JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ), + JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ), + JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ), +}; + +static const JSCFunctionListEntry js_bigint_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigint_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicBigInt(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + + rt->bigint_ops.to_string = js_bigint_to_string; + rt->bigint_ops.from_string = js_string_to_bigint; + rt->bigint_ops.unary_arith = js_unary_arith_bigint; + rt->bigint_ops.binary_arith = js_binary_arith_bigint; + rt->bigint_ops.compare = js_compare_bigfloat; + + ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], + js_bigint_proto_funcs, + countof(js_bigint_proto_funcs)); + obj1 = JS_NewCFunction(ctx, js_bigint_constructor, "BigInt", 1); + JS_NewGlobalCConstructor2(ctx, obj1, "BigInt", + ctx->class_proto[JS_CLASS_BIG_INT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, + countof(js_bigint_funcs)); +} + +/* BigFloat */ + +static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_IsBigFloat(this_val)) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_BIG_FLOAT) { + if (JS_IsBigFloat(p->u.object_data)) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a bigfloat"); +} + +static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int base; + JSValue ret; + + val = js_thisBigFloatValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (argc == 0 || JS_IsUndefined(argv[0])) { + base = 10; + } else { + if (JS_ToInt32Sat(ctx, &base, argv[0])) + goto fail; + if (base < 2 || base > 36) { + JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + 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 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 res, rnd_mode; + bf_t a_s, *a, b; + + 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; + if (argc > 1) { + rnd_mode = get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + + a = JS_ToBigFloat(ctx, &a_s, val); + if (!a) + goto fail; + bf_init(ctx->bf_ctx, &b); + bf_set_float64(&b, 1e21); + res = bf_cmpu(a, &b); + bf_delete(&b); + if (a == &a_s) + bf_delete(a); + if (res >= 0) { + ret = JS_ToString(ctx, val); + } else { + ret = js_ftoa(ctx, val, 10, 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; + + 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; + if (argc > 1) { + rnd_mode = get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_ftoa(ctx, val, 10, 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; + + 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; + if (argc > 1) { + rnd_mode = get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_ftoa(ctx, val, 10, p, rnd_mode | BF_FTOA_FORMAT_FIXED); + } + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ), + JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ), + JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ), + JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ), +}; + +static JSValue js_bigfloat_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + if (argc == 0) { + bf_t r_s, *r = &r_s; + bf_init(ctx->bf_ctx, r); + bf_set_zero(r, 0); + val = JS_NewBigFloat(ctx, r); + } 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_s, *r = &r_s; + bf_init(ctx->bf_ctx, r); + bf_set_float64(r, JS_VALUE_GET_FLOAT64(val)); + val = JS_NewBigFloat(ctx, r); + } + break; + case JS_TAG_INT: + { + bf_t r_s, *r = &r_s; + bf_init(ctx->bf_ctx, r); + bf_set_si(r, JS_VALUE_GET_INT(val)); + val = JS_NewBigFloat(ctx, r); + } + 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_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_s, *r = &r_s; + bf_init(ctx->bf_ctx, r); + bf_set_si(r, 0); + val = JS_NewBigFloat(ctx, r); + 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; +} + +static JSValue js_bigfloat_get_const(JSContext *ctx, + JSValueConst this_val, int magic) +{ + bf_t r_s, *r = &r_s; + bf_init(ctx->bf_ctx, r); + 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 JS_NewBigFloat(ctx, r); +} + +static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + bf_t a_s, *a = &a_s; + 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; + } + bf_init(ctx->bf_ctx, a); + /* XXX: use js_atof() */ + bf_atof(a, str, NULL, radix, fe->prec, fe->flags); + ret = JS_NewBigFloat(ctx, a); + 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_finite(&p->num)); +} + +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)); +} + +/* temporary fix for string conversion overflows */ +#define BF_EXP_BITS_MAX1 (BF_EXP_BITS_MAX - 1) + +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_MAX1; + + 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_MAX1) + return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); + } + + flags = BF_RNDN | bf_set_exp_bits(exp_bits); + if (exp_bits != BF_EXP_BITS_MAX1) + flags |= BF_FLAG_SUBNORMAL; + + saved_prec = ctx->fp_env.prec; + saved_flags = ctx->fp_env.flags; + + ctx->fp_env.prec = prec; + ctx->fp_env.flags = flags; + + ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); + /* always restore the floating point precision */ + ctx->fp_env.prec = saved_prec; + ctx->fp_env.flags = saved_flags; + return ret; +} + +#define FE_PREC (-1) +#define FE_EXP (-2) +#define FE_RNDMODE (-3) +#define FE_SUBNORMAL (-4) + +static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic) +{ + JSFloatEnv *fe; + fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); + if (!fe) + return JS_EXCEPTION; + switch(magic) { + case FE_PREC: + return JS_NewInt64(ctx, fe->prec); + case FE_EXP: + return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags)); + case FE_RNDMODE: + return JS_NewInt32(ctx, fe->flags & BF_RND_MASK); + case FE_SUBNORMAL: + return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0); + default: + return JS_NewBool(ctx, (fe->status & magic) != 0); + } +} + +static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) +{ + JSFloatEnv *fe; + int b; + int64_t prec; + + fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); + if (!fe) + return JS_EXCEPTION; + switch(magic) { + case FE_PREC: + if (JS_ToInt64Sat(ctx, &prec, val)) + return JS_EXCEPTION; + if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) + return JS_ThrowRangeError(ctx, "invalid precision"); + fe->prec = prec; + break; + case FE_EXP: + if (JS_ToInt32Sat(ctx, &b, val)) + return JS_EXCEPTION; + if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX1) + return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); + if (b == BF_EXP_BITS_MAX1) + fe->flags &= ~BF_FLAG_SUBNORMAL; + fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | + bf_set_exp_bits(b); + break; + case FE_RNDMODE: + b = 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); + if (bf_get_exp_bits(fe->flags) != BF_EXP_BITS_MAX1) { + 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("RNDNU", BF_RNDNU, 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_MAX1, 0 ), +}; + +static const JSCFunctionListEntry js_float_env_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_PREC ), + JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_EXP ), + JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_RNDMODE ), + JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_SUBNORMAL ), + JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_INVALID_OP ), + JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ), + JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_OVERFLOW ), + JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_UNDERFLOW ), + JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_INEXACT ), + JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ), +}; + +void JS_AddIntrinsicBigFloat(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + JSValueConst obj2; + + rt->bigfloat_ops.to_string = js_bigfloat_to_string; + rt->bigfloat_ops.from_string = js_string_to_bigfloat; + rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat; + rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat; + rt->bigfloat_ops.compare = js_compare_bigfloat; + rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64; + rt->bigfloat_ops.mul_pow10 = js_mul_pow10; + + ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT], + js_bigfloat_proto_funcs, + countof(js_bigfloat_proto_funcs)); + obj1 = JS_NewCFunction(ctx, js_bigfloat_constructor, "BigFloat", 1); + JS_NewGlobalCConstructor2(ctx, obj1, "BigFloat", + ctx->class_proto[JS_CLASS_BIG_FLOAT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs, + countof(js_bigfloat_funcs)); + + ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV], + js_float_env_proto_funcs, + countof(js_float_env_proto_funcs)); + obj2 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", + js_float_env_constructor, 1, + ctx->class_proto[JS_CLASS_FLOAT_ENV]); + JS_SetPropertyFunctionList(ctx, obj2, js_float_env_funcs, + countof(js_float_env_funcs)); +} + +/* BigDecimal */ + +static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, + BOOL allow_null_or_undefined) +{ + redo: + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_BIG_DECIMAL: + break; + case JS_TAG_NULL: + if (!allow_null_or_undefined) + goto fail; + /* fall thru */ + case JS_TAG_BOOL: + case JS_TAG_INT: + { + bfdec_t r_s, *r = &r_s; + bfdec_init(ctx->bf_ctx, r); + bfdec_set_si(r, JS_VALUE_GET_INT(val)); + val = JS_NewBigDecimal(ctx, r); + } + 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_s, *r = &r_s; + bfdec_init(ctx->bf_ctx, r); + bfdec_set_zero(r, 0); + val = JS_NewBigDecimal(ctx, r); + 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_s, *r = &r_s; + if (!allow_null_or_undefined) + goto fail; + bfdec_init(ctx->bf_ctx, r); + bfdec_set_nan(r); + val = JS_NewBigDecimal(ctx, r); + } + break; + default: + fail: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal"); + } + return val; +} + +static JSValue js_bigdecimal_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + if (argc == 0) { + bfdec_t r_s, *r = &r_s; + bfdec_init(ctx->bf_ctx, r); + bfdec_set_zero(r, 0); + val = JS_NewBigDecimal(ctx, r); + } 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); +} + +typedef struct { + int64_t prec; + bf_flags_t flags; +} BigDecimalEnv; + +static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, + JSValueConst obj) +{ + JSValue prop; + const char *str; + size_t size; + int64_t val; + BOOL has_prec; + + if (!JS_IsObject(obj)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode); + if (JS_IsException(prop)) + return -1; + str = JS_ToCStringLen(ctx, &size, prop); + JS_FreeValue(ctx, prop); + if (!str) + return -1; + if (strlen(str) != size) + goto invalid_rounding_mode; + if (!strcmp(str, "floor")) { + fe->flags = BF_RNDD; + } else if (!strcmp(str, "ceiling")) { + fe->flags = BF_RNDU; + } else if (!strcmp(str, "down")) { + fe->flags = BF_RNDZ; + } else if (!strcmp(str, "half-even")) { + fe->flags = BF_RNDN; + } else if (!strcmp(str, "half-up")) { + fe->flags = BF_RNDNA; + } else { + invalid_rounding_mode: + JS_FreeCString(ctx, str); + JS_ThrowTypeError(ctx, "invalid rounding mode"); + return -1; + } + JS_FreeCString(ctx, str); + + 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 < 0 || 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; + 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])) { + fail: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + if ((fe->flags & BF_FLAG_RADPNT_PREC) && + magic != MATH_OP_DIV && + magic != MATH_OP_ROUND) { + JS_ThrowTypeError(ctx, "maximumFractionDigits is not supported for this operation"); + goto fail; + } + } + + bfdec_init(ctx->bf_ctx, r); + 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_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) { + bfdec_delete(r); + return throw_bf_exception(ctx, ret); + } else { + return JS_NewBigDecimal(ctx, r); + } +} + +static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ), +}; + +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("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ), + JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ), +}; + +void JS_AddIntrinsicBigDecimal(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + + rt->bigdecimal_ops.to_string = js_bigdecimal_to_string; + rt->bigdecimal_ops.from_string = js_string_to_bigdecimal; + rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal; + rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal; + rt->bigdecimal_ops.compare = js_compare_bigdecimal; + + ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL], + js_bigdecimal_proto_funcs, + countof(js_bigdecimal_proto_funcs)); + obj1 = JS_NewCFunction(ctx, js_bigdecimal_constructor, "BigDecimal", 1); + JS_NewGlobalCConstructor2(ctx, obj1, "BigDecimal", + ctx->class_proto[JS_CLASS_BIG_DECIMAL]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs, + countof(js_bigdecimal_funcs)); +} + +void JS_EnableBignumExt(JSContext *ctx, BOOL enable) +{ + ctx->bignum_ext = enable; +} + +#endif /* CONFIG_BIGNUM */ + +static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = { + "EvalError", "RangeError", "ReferenceError", + "SyntaxError", "TypeError", "URIError", + "InternalError", +}; + +/* Minimum amount of objects to be able to compile code and display + error messages. No JSAtom should be allocated by this function. */ +static void JS_AddIntrinsicBasicObjects(JSContext *ctx) +{ + JSValue proto; + int i; + + ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL); + ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0, + JS_CFUNC_generic, 0, + ctx->class_proto[JS_CLASS_OBJECT]); + ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto); + ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx); +#if 0 + /* these are auto-initialized from js_error_proto_funcs, + but delaying might be a problem */ + JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name, + JS_AtomToString(ctx, JS_ATOM_Error), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message, + JS_AtomToString(ctx, JS_ATOM_empty_string), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); +#endif + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR], + js_error_proto_funcs, + countof(js_error_proto_funcs)); + + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]); + JS_DefinePropertyValue(ctx, proto, JS_ATOM_name, + JS_NewAtomString(ctx, native_error_name[i]), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, proto, JS_ATOM_message, + JS_AtomToString(ctx, JS_ATOM_empty_string), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + ctx->native_error_proto[i] = proto; + } + + /* the array prototype is an array */ + ctx->class_proto[JS_CLASS_ARRAY] = + JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_ARRAY); + + ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]), + JS_PROP_INITIAL_HASH_SIZE, 1); + add_shape_property(ctx, &ctx->array_shape, NULL, + JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH); + + /* XXX: could test it on first context creation to ensure that no + new atoms are created in JS_AddIntrinsicBasicObjects(). It is + necessary to avoid useless renumbering of atoms after + JS_EvalBinary() if it is done just after + JS_AddIntrinsicBasicObjects(). */ + // assert(ctx->rt->atom_count == JS_ATOM_END); +} + +void JS_AddIntrinsicBaseObjects(JSContext *ctx) +{ + int i; + JSValueConst obj, number_obj; + JSValue obj1; + + ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); + + /* add caller and arguments properties to throw a TypeError */ + obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0); + JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED, + obj1, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); + JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED, + obj1, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1)); + + ctx->global_obj = JS_NewObject(ctx); + ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL); + + /* Object */ + obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1, + ctx->class_proto[JS_CLASS_OBJECT]); + JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs)); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT], + js_object_proto_funcs, countof(js_object_proto_funcs)); + + /* Function */ + JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs)); + ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor, + "Function", 1, JS_CFUNC_constructor_or_func_magic, + JS_FUNC_NORMAL); + JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function", + ctx->function_proto); + + /* Error */ + obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor, + "Error", 1, JS_CFUNC_constructor_or_func_magic, -1); + JS_NewGlobalCConstructor2(ctx, obj1, + "Error", ctx->class_proto[JS_CLASS_ERROR]); + + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JSValue func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor, + native_error_name[i], 1, + 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]); + JS_SetPropertyFunctionList(ctx, obj, js_array_funcs, + countof(js_array_funcs)); + + /* XXX: create auto_initializer */ + { + /* initialize Array.prototype[Symbol.unscopables] */ + char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0" + "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0"; + const char *p = unscopables; + obj1 = JS_NewObjectProto(ctx, JS_NULL); + for(p = unscopables; *p; p += strlen(p) + 1) { + JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E); + } + JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY], + JS_ATOM_Symbol_unscopables, obj1, + JS_PROP_CONFIGURABLE); + } + + /* needed to initialize arguments[Symbol.iterator] */ + ctx->array_proto_values = + JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values); + + ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR], + js_array_iterator_proto_funcs, + countof(js_array_iterator_proto_funcs)); + + /* 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_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs, + countof(js_global_funcs)); + + JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis, + JS_DupValue(ctx, ctx->global_obj), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); +} + +/* Typed Arrays */ + +static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { + 0, 0, 0, 1, 1, 2, 2, +#ifdef CONFIG_BIGNUM + 3, 3, /* BigInt64Array, BigUint64Array */ +#endif + 2, 3 +}; + +static JSValue js_array_buffer_constructor3(JSContext *ctx, + JSValueConst new_target, + uint64_t len, JSClassID class_id, + uint8_t *buf, + JSFreeArrayBufferDataFunc *free_func, + void *opaque, BOOL alloc_flag) +{ + 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) { + /* the allocation must be done after the object creation */ + abuf->data = js_mallocz(ctx, max_int(len, 1)); + if (!abuf->data) + goto fail; + } else { + abuf->data = buf; + } + init_list_head(&abuf->array_list); + abuf->detached = FALSE; + abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER); + abuf->opaque = opaque; + abuf->free_func = free_func; + if (alloc_flag && buf) + memcpy(abuf->data, buf, len); + JS_SetOpaque(obj, abuf); + return obj; + fail: + JS_FreeValue(ctx, obj); + js_free(ctx, abuf); + return JS_EXCEPTION; +} + +static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr) +{ + js_free_rt(rt, ptr); +} + +static JSValue js_array_buffer_constructor2(JSContext *ctx, + JSValueConst new_target, + uint64_t len, JSClassID class_id) +{ + return js_array_buffer_constructor3(ctx, new_target, len, class_id, + NULL, js_array_buffer_free, NULL, + TRUE); +} + +static JSValue js_array_buffer_constructor1(JSContext *ctx, + JSValueConst new_target, + uint64_t len) +{ + return js_array_buffer_constructor2(ctx, new_target, len, + JS_CLASS_ARRAY_BUFFER); +} + +JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, + JSFreeArrayBufferDataFunc *free_func, void *opaque, + BOOL is_shared) +{ + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, + is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER, + buf, free_func, opaque, FALSE); +} + +/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */ +JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len) +{ + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, + JS_CLASS_ARRAY_BUFFER, + (uint8_t *)buf, + js_array_buffer_free, NULL, + TRUE); +} + +static JSValue js_array_buffer_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + uint64_t len; + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + return js_array_buffer_constructor1(ctx, new_target, len); +} + +static JSValue js_shared_array_buffer_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + uint64_t len; + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + return js_array_buffer_constructor2(ctx, new_target, len, + JS_CLASS_SHARED_ARRAY_BUFFER); +} + +/* also used for SharedArrayBuffer */ +static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf) { + /* The ArrayBuffer finalizer may be called before the typed + array finalizers using it, so abuf->array_list is not + necessarily empty. */ + // assert(list_empty(&abuf->array_list)); + if (abuf->free_func) + abuf->free_func(rt, abuf->opaque, abuf->data); + js_free_rt(rt, abuf); + } +} + +static JSValue js_array_buffer_isView(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSObject *p; + BOOL res; + res = FALSE; + if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_DATAVIEW) { + res = TRUE; + } + } + return JS_NewBool(ctx, res); +} + +static const JSCFunctionListEntry js_array_buffer_funcs[] = { + JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "ArrayBuffer is detached"); +} + +static JSValue js_array_buffer_get_byteLength(JSContext *ctx, + JSValueConst this_val, + int class_id) +{ + JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return JS_NewUint32(ctx, abuf->byte_length); +} + +void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj) +{ + JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); + struct list_head *el; + + if (!abuf || abuf->detached) + return; + if (abuf->free_func) + abuf->free_func(ctx->rt, abuf->opaque, abuf->data); + abuf->data = NULL; + abuf->byte_length = 0; + abuf->detached = TRUE; + + list_for_each(el, &abuf->array_list) { + JSTypedArray *ta; + JSObject *p; + + ta = list_entry(el, JSTypedArray, link); + p = ta->obj; + /* Note: the typed array length and offset fields are not modified */ + if (p->class_id != JS_CLASS_DATAVIEW) { + p->u.array.count = 0; + p->u.array.u.ptr = NULL; + } + } +} + +/* get an ArrayBuffer or SharedArrayBuffer */ +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ARRAY_BUFFER && + p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { + fail: + JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER); + return NULL; + } + return p->u.array_buffer; +} + +/* return NULL if exception. WARNING: any JS call can detach the + buffer and render the returned pointer invalid */ +uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj) +{ + JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj); + if (!abuf) + goto fail; + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + *psize = abuf->byte_length; + return abuf->data; + fail: + *psize = 0; + return NULL; +} + +static JSValue js_array_buffer_slice(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, int class_id) +{ + JSArrayBuffer *abuf, *new_abuf; + int64_t len, start, end, new_len; + JSValue ctor, new_obj; + + abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + len = abuf->byte_length; + + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + return JS_EXCEPTION; + + end = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + new_len = max_int64(end - start, 0); + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsUndefined(ctor)) { + new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, + class_id); + } else { + JSValue args[1]; + args[0] = JS_NewInt64(ctx, new_len); + new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args); + JS_FreeValue(ctx, ctor); + JS_FreeValue(ctx, args[0]); + } + if (JS_IsException(new_obj)) + return new_obj; + new_abuf = JS_GetOpaque2(ctx, new_obj, class_id); + if (!new_abuf) + goto fail; + if (js_same_value(ctx, new_obj, this_val)) { + JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer"); + goto fail; + } + if (new_abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + if (new_abuf->byte_length < new_len) { + JS_ThrowTypeError(ctx, "new ArrayBuffer is too small"); + goto fail; + } + /* must test again because of side effects */ + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + memcpy(new_abuf->data, abuf->data + start, new_len); + return new_obj; + fail: + JS_FreeValue(ctx, new_obj); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ), +}; + +/* SharedArrayBuffer */ + +static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = { + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ), +}; + +static JSObject *get_typed_array(JSContext *ctx, + JSValueConst this_val, + int is_dataview) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(this_val); + if (is_dataview) { + if (p->class_id != JS_CLASS_DATAVIEW) + goto fail; + } else { + if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { + fail: + JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray"); + return NULL; + } + } + return p; +} + +/* WARNING: 'p' must be a typed array */ +static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p) +{ + JSTypedArray *ta = p->u.typed_array; + JSArrayBuffer *abuf = ta->buffer->u.array_buffer; + /* XXX: could simplify test by ensuring that + p->u.array.u.ptr is NULL iff it is detached */ + return abuf->detached; +} + +/* WARNING: 'p' must be a typed array. Works even if the array buffer + is detached */ +static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p) +{ + JSTypedArray *ta = p->u.typed_array; + int size_log2 = typed_array_size_log2(p->class_id); + return ta->length >> size_log2; +} + +static int validate_typed_array(JSContext *ctx, JSValueConst this_val) +{ + JSObject *p; + p = get_typed_array(ctx, this_val, 0); + if (!p) + return -1; + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return -1; + } + return 0; +} + +static JSValue js_typed_array_get_length(JSContext *ctx, + JSValueConst this_val) +{ + JSObject *p; + p = get_typed_array(ctx, this_val, 0); + if (!p) + return JS_EXCEPTION; + return JS_NewInt32(ctx, p->u.array.count); +} + +static JSValue js_typed_array_get_buffer(JSContext *ctx, + JSValueConst this_val, int is_dataview) +{ + JSObject *p; + JSTypedArray *ta; + p = get_typed_array(ctx, this_val, is_dataview); + if (!p) + return JS_EXCEPTION; + ta = p->u.typed_array; + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); +} + +static JSValue js_typed_array_get_byteLength(JSContext *ctx, + JSValueConst this_val, + int is_dataview) +{ + JSObject *p; + JSTypedArray *ta; + p = get_typed_array(ctx, this_val, is_dataview); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_detached(ctx, p)) { + if (is_dataview) { + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + } else { + return JS_NewInt32(ctx, 0); + } + } + ta = p->u.typed_array; + return JS_NewInt32(ctx, ta->length); +} + +static JSValue js_typed_array_get_byteOffset(JSContext *ctx, + JSValueConst this_val, + int is_dataview) +{ + JSObject *p; + JSTypedArray *ta; + p = get_typed_array(ctx, this_val, is_dataview); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_detached(ctx, p)) { + if (is_dataview) { + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + } else { + return JS_NewInt32(ctx, 0); + } + } + ta = p->u.typed_array; + return JS_NewInt32(ctx, ta->offset); +} + +/* Return the buffer associated to the typed array or an exception if + it is not a typed array or if the buffer is detached. pbyte_offset, + pbyte_length or pbytes_per_element can be NULL. */ +JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, + size_t *pbyte_offset, + size_t *pbyte_length, + size_t *pbytes_per_element) +{ + JSObject *p; + JSTypedArray *ta; + p = get_typed_array(ctx, obj, FALSE); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + ta = p->u.typed_array; + if (pbyte_offset) + *pbyte_offset = ta->offset; + if (pbyte_length) + *pbyte_length = ta->length; + if (pbytes_per_element) { + *pbytes_per_element = 1 << typed_array_size_log2(p->class_id); + } + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); +} + +static JSValue js_typed_array_get_toStringTag(JSContext *ctx, + JSValueConst this_val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_UNDEFINED; + p = JS_VALUE_GET_OBJ(this_val); + if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY)) + return JS_UNDEFINED; + return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name); +} + +static JSValue js_typed_array_set_internal(JSContext *ctx, + JSValueConst dst, + JSValueConst src, + JSValueConst off) +{ + JSObject *p; + JSObject *src_p; + uint32_t i; + int64_t src_len, offset; + JSValue val, src_obj = JS_UNDEFINED; + + p = get_typed_array(ctx, dst, 0); + if (!p) + goto fail; + if (JS_ToInt64Sat(ctx, &offset, off)) + goto fail; + if (offset < 0) + goto range_error; + if (typed_array_is_detached(ctx, p)) { + detached: + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + src_obj = JS_ToObject(ctx, src); + if (JS_IsException(src_obj)) + goto fail; + src_p = JS_VALUE_GET_OBJ(src_obj); + if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY && + src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + JSTypedArray *dest_ta = p->u.typed_array; + JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer; + JSTypedArray *src_ta = src_p->u.typed_array; + JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer; + int shift = typed_array_size_log2(p->class_id); + + if (src_abuf->detached) + goto detached; + + src_len = src_p->u.array.count; + if (offset > (int64_t)(p->u.array.count - src_len)) + goto range_error; + + /* copying between typed objects */ + if (src_p->class_id == p->class_id) { + /* same type, use memmove */ + memmove(dest_abuf->data + dest_ta->offset + (offset << shift), + src_abuf->data + src_ta->offset, src_len << shift); + goto done; + } + if (dest_abuf->data == src_abuf->data) { + /* copying between the same buffer using different types of mappings + would require a temporary buffer */ + } + /* otherwise, default behavior is slow but correct */ + } else { + if (js_get_length64(ctx, &src_len, src_obj)) + goto fail; + if (offset > (int64_t)(p->u.array.count - src_len)) { + range_error: + JS_ThrowRangeError(ctx, "invalid array length"); + goto fail; + } + } + for(i = 0; i < src_len; i++) { + val = JS_GetPropertyUint32(ctx, src_obj, i); + if (JS_IsException(val)) + goto fail; + if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0) + goto fail; + } +done: + JS_FreeValue(ctx, src_obj); + return JS_UNDEFINED; +fail: + JS_FreeValue(ctx, src_obj); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_set(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst offset = JS_UNDEFINED; + if (argc > 1) { + offset = argv[1]; + } + return js_typed_array_set_internal(ctx, this_val, argv[0], offset); +} + +static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + if (validate_typed_array(ctx, this_val)) + return JS_EXCEPTION; + return js_create_array_iterator(ctx, this_val, argc, argv, magic); +} + +/* return < 0 if exception */ +static int js_typed_array_get_length_internal(JSContext *ctx, + JSValueConst obj) +{ + JSObject *p; + p = get_typed_array(ctx, obj, 0); + if (!p) + return -1; + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return -1; + } + return p->u.array.count; +} + +#if 0 +/* validate a typed array and return its length */ +static JSValue js_typed_array___getLength(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + BOOL ignore_detached = JS_ToBool(ctx, argv[1]); + + if (ignore_detached) { + return js_typed_array_get_length(ctx, argv[0]); + } else { + int len; + len = js_typed_array_get_length_internal(ctx, argv[0]); + if (len < 0) + return JS_EXCEPTION; + return JS_NewInt32(ctx, len); + } +} +#endif + +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int classid); + +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); + shift = typed_array_size_log2(p->class_id); + memmove(p->u.array.u.uint8_ptr + (to << shift), + p->u.array.u.uint8_ptr + (from << shift), + count << shift); + } + return JS_DupValue(ctx, this_val); +} + +static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSObject *p; + int len, k, final, shift; + uint64_t v64; + + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(this_val); + + if (p->class_id == JS_CLASS_UINT8C_ARRAY) { + int32_t v; + if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0]))) + return JS_EXCEPTION; + v64 = v; + } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) { + uint32_t v; + if (JS_ToUint32(ctx, &v, argv[0])) + return JS_EXCEPTION; + v64 = v; + } else +#ifdef CONFIG_BIGNUM + if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0])) + return JS_EXCEPTION; + } else +#endif + { + double d; + if (JS_ToFloat64(ctx, &d, argv[0])) + return JS_EXCEPTION; + if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { + union { + float f; + uint32_t u32; + } u; + u.f = d; + v64 = u.u32; + } else { + JSFloat64Union u; + u.d = d; + v64 = u.u64; + } + } + + k = 0; + if (argc > 1) { + if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) + return JS_EXCEPTION; + } + + shift = typed_array_size_log2(p->class_id); + switch(shift) { + case 0: + if (k < final) { + memset(p->u.array.u.uint8_ptr + k, v64, final - k); + } + break; + case 1: + for(; k < final; k++) { + p->u.array.u.uint16_ptr[k] = v64; + } + break; + case 2: + for(; k < final; k++) { + p->u.array.u.uint32_ptr[k] = v64; + } + break; + case 3: + for(; k < final; k++) { + p->u.array.u.uint64_ptr[k] = v64; + } + break; + default: + abort(); + } + return JS_DupValue(ctx, this_val); +} + +static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int findIndex) +{ + JSValueConst func, this_arg; + JSValueConst args[3]; + JSValue val, index_val, res; + int len, k; + + val = JS_UNDEFINED; + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + goto exception; + + func = argv[0]; + if (check_function(ctx, func)) + goto exception; + + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + for(k = 0; k < len; k++) { + index_val = JS_NewInt32(ctx, k); + val = JS_GetPropertyValue(ctx, this_val, index_val); + if (JS_IsException(val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = this_val; + res = JS_Call(ctx, func, this_arg, 3, args); + if (JS_IsException(res)) + goto exception; + if (JS_ToBoolFree(ctx, res)) { + if (findIndex) { + JS_FreeValue(ctx, val); + return index_val; + } else { + return val; + } + } + JS_FreeValue(ctx, val); + } + if (findIndex) + return JS_NewInt32(ctx, -1); + else + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +#define special_indexOf 0 +#define special_lastIndexOf 1 +#define special_includes -1 + +static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int special) +{ + JSObject *p; + int len, tag, is_int, is_big, 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; + } + + is_big = 0; + is_int = 0; /* avoid warning */ + v64 = 0; /* avoid warning */ + tag = JS_VALUE_GET_NORM_TAG(argv[0]); + if (tag == JS_TAG_INT) { + is_int = 1; + v64 = JS_VALUE_GET_INT(argv[0]); + d = v64; + } else + if (tag == JS_TAG_FLOAT64) { + d = JS_VALUE_GET_FLOAT64(argv[0]); + v64 = d; + is_int = (v64 == d); + } else +#ifdef CONFIG_BIGNUM + if (tag == JS_TAG_BIG_INT || tag == JS_TAG_BIG_FLOAT) { + /* will a generic loop for bigint and bigfloat */ + /* XXX: should use the generic loop in math_mode? */ + is_big = 1; + } else +#endif + { + goto done; + } + + p = JS_VALUE_GET_OBJ(this_val); + switch (p->class_id) { + case JS_CLASS_INT8_ARRAY: + if (is_int && (int8_t)v64 == v64) + goto scan8; + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (is_int && (uint8_t)v64 == v64) { + const uint8_t *pv, *pp; + uint16_t v; + scan8: + pv = p->u.array.u.uint8_ptr; + v = v64; + if (inc > 0) { + pp = memchr(pv + k, v, len - k); + if (pp) + res = pp - pv; + } else { + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + } + break; + case JS_CLASS_INT16_ARRAY: + if (is_int && (int16_t)v64 == v64) + goto scan16; + break; + case JS_CLASS_UINT16_ARRAY: + if (is_int && (uint16_t)v64 == v64) { + const uint16_t *pv; + uint16_t v; + scan16: + pv = p->u.array.u.uint16_ptr; + v = v64; + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + break; + case JS_CLASS_INT32_ARRAY: + if (is_int && (int32_t)v64 == v64) + goto scan32; + break; + case JS_CLASS_UINT32_ARRAY: + if (is_int && (uint32_t)v64 == v64) { + const uint32_t *pv; + uint32_t v; + scan32: + pv = p->u.array.u.uint32_ptr; + v = v64; + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + break; + case JS_CLASS_FLOAT32_ARRAY: + if (is_big) + 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_big) + break; + if (isnan(d)) { + const double *pv = p->u.array.u.double_ptr; + /* special case: indexOf returns -1, includes finds NaN */ + if (special != special_includes) + goto done; + for (; k != stop; k += inc) { + if (isnan(pv[k])) { + res = k; + break; + } + } + } else { + const double *pv = p->u.array.u.double_ptr; + for (; k != stop; k += inc) { + if (pv[k] == d) { + res = k; + break; + } + } + } + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + if (is_big || is_strict_mode(ctx)) { + /* generic loop for bignums, argv[0] is a bignum != NaN */ + /* XXX: optimize with explicit values */ + for (; k != stop; k += inc) { + JSValue v = JS_GetPropertyUint32(ctx, this_val, k); + int ret; + if (JS_IsException(v)) + goto exception; + ret = js_same_value_zero(ctx, v, argv[0]); + JS_FreeValue(ctx, v); + if (ret) { + if (ret < 0) + goto exception; + res = k; + break; + } + } + } + break; +#endif + } + +done: + if (special == special_includes) + return JS_NewBool(ctx, res >= 0); + else + return JS_NewInt32(ctx, res); + +exception: + return JS_EXCEPTION; +} + +static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int toLocaleString) +{ + JSValue sep = JS_UNDEFINED, el; + StringBuffer b_s, *b = &b_s; + JSString *p = NULL; + int i, n; + int c; + + n = js_typed_array_get_length_internal(ctx, this_val); + if (n < 0) + goto exception; + + c = ','; /* default separator */ + if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { + sep = JS_ToString(ctx, argv[0]); + if (JS_IsException(sep)) + goto exception; + p = JS_VALUE_GET_STRING(sep); + if (p->len == 1 && !p->is_wide_char) + c = p->u.str8[0]; + else + c = -1; + } + string_buffer_init(ctx, b, 0); + + /* XXX: optimize with direct access */ + for(i = 0; i < n; i++) { + if (i > 0) { + if (c >= 0) { + if (string_buffer_putc8(b, c)) + goto fail; + } else { + if (string_buffer_concat(b, p, 0, p->len)) + goto fail; + } + } + el = JS_GetPropertyUint32(ctx, this_val, i); + if (JS_IsException(el)) + goto fail; + if (toLocaleString) { + el = JS_ToLocaleStringFree(ctx, el); + } + if (string_buffer_concat_value_free(b, el)) + goto fail; + } + JS_FreeValue(ctx, sep); + return string_buffer_end(b); + +fail: + string_buffer_free(b); + JS_FreeValue(ctx, sep); +exception: + return JS_EXCEPTION; +} + +static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSObject *p; + int len; + + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + return JS_EXCEPTION; + if (len > 0) { + p = JS_VALUE_GET_OBJ(this_val); + switch (typed_array_size_log2(p->class_id)) { + case 0: + { + uint8_t *p1 = p->u.array.u.uint8_ptr; + uint8_t *p2 = p1 + len - 1; + while (p1 < p2) { + uint8_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } + break; + case 1: + { + uint16_t *p1 = p->u.array.u.uint16_ptr; + uint16_t *p2 = p1 + len - 1; + while (p1 < p2) { + uint16_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } + break; + case 2: + { + uint32_t *p1 = p->u.array.u.uint32_ptr; + uint32_t *p2 = p1 + len - 1; + while (p1 < p2) { + uint32_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } + break; + case 3: + { + uint64_t *p1 = p->u.array.u.uint64_ptr; + uint64_t *p2 = p1 + len - 1; + while (p1 < p2) { + uint64_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } + break; + default: + abort(); + } + } + return JS_DupValue(ctx, this_val); +} + +static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst args[2]; + JSValue arr, val; + JSObject *p, *p1; + int n, len, start, final, count, shift; + + arr = JS_UNDEFINED; + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + goto exception; + + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int(final - start, 0); + + p = get_typed_array(ctx, this_val, 0); + if (p == NULL) + goto exception; + shift = typed_array_size_log2(p->class_id); + + args[0] = this_val; + args[1] = JS_NewInt32(ctx, count); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(arr)) + goto exception; + + if (count > 0) { + if (validate_typed_array(ctx, this_val) + || validate_typed_array(ctx, arr)) + goto exception; + + p1 = get_typed_array(ctx, arr, 0); + if (p1 != NULL && p->class_id == p1->class_id && + typed_array_get_length(ctx, p1) >= count && + typed_array_get_length(ctx, p) >= start + count) { + memcpy(p1->u.array.u.uint8_ptr, + p->u.array.u.uint8_ptr + (start << shift), + count << shift); + } else { + for (n = 0; n < count; n++) { + val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n)); + if (JS_IsException(val)) + goto exception; + if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val, + JS_PROP_THROW) < 0) + goto exception; + } + } + } + return arr; + + exception: + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst args[4]; + JSValue arr, byteOffset, ta_buffer; + JSObject *p; + int len, start, final, count, shift, offset; + + p = get_typed_array(ctx, this_val, 0); + if (!p) + goto exception; + len = p->u.array.count; + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int(final - start, 0); + byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0); + if (JS_IsException(byteOffset)) + goto exception; + shift = typed_array_size_log2(p->class_id); + offset = JS_VALUE_GET_INT(byteOffset) + (start << shift); + JS_FreeValue(ctx, byteOffset); + ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0); + if (JS_IsException(ta_buffer)) + goto exception; + args[0] = this_val; + args[1] = ta_buffer; + args[2] = JS_NewInt32(ctx, offset); + args[3] = JS_NewInt32(ctx, count); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args); + JS_FreeValue(ctx, ta_buffer); + return arr; + + exception: + return JS_EXCEPTION; +} + +/* TypedArray.prototype.sort */ + +static int js_cmp_doubles(double x, double y) +{ + if (isnan(x)) return isnan(y) ? 0 : +1; + if (isnan(y)) return -1; + if (x < y) return -1; + if (x > y) return 1; + if (x != 0) return 0; + if (signbit(x)) return signbit(y) ? 0 : -1; + else return signbit(y) ? 1 : 0; +} + +static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) { + return *(const int8_t *)a - *(const int8_t *)b; +} + +static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) { + return *(const uint8_t *)a - *(const uint8_t *)b; +} + +static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) { + return *(const int16_t *)a - *(const int16_t *)b; +} + +static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) { + return *(const uint16_t *)a - *(const uint16_t *)b; +} + +static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) { + int32_t x = *(const int32_t *)a; + int32_t y = *(const int32_t *)b; + return (y < x) - (y > x); +} + +static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) { + uint32_t x = *(const uint32_t *)a; + uint32_t y = *(const uint32_t *)b; + return (y < x) - (y > x); +} + +#ifdef CONFIG_BIGNUM +static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) { + int64_t x = *(const int64_t *)a; + int64_t y = *(const int64_t *)b; + return (y < x) - (y > x); +} + +static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) { + uint64_t x = *(const uint64_t *)a; + uint64_t y = *(const uint64_t *)b; + return (y < x) - (y > x); +} +#endif + +static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) { + return js_cmp_doubles(*(const float *)a, *(const float *)b); +} + +static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) { + return js_cmp_doubles(*(const double *)a, *(const double *)b); +} + +static JSValue js_TA_get_int8(JSContext *ctx, const void *a) { + return JS_NewInt32(ctx, *(const int8_t *)a); +} + +static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) { + return JS_NewInt32(ctx, *(const uint8_t *)a); +} + +static JSValue js_TA_get_int16(JSContext *ctx, const void *a) { + return JS_NewInt32(ctx, *(const int16_t *)a); +} + +static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) { + return JS_NewInt32(ctx, *(const uint16_t *)a); +} + +static JSValue js_TA_get_int32(JSContext *ctx, const void *a) { + return JS_NewInt32(ctx, *(const int32_t *)a); +} + +static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) { + return JS_NewUint32(ctx, *(const uint32_t *)a); +} + +#ifdef CONFIG_BIGNUM +static JSValue js_TA_get_int64(JSContext *ctx, const void *a) { + return JS_NewBigInt64(ctx, *(int64_t *)a); +} + +static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { + return JS_NewBigUint64(ctx, *(uint64_t *)a); +} +#endif + +static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { + return __JS_NewFloat64(ctx, *(const float *)a); +} + +static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { + return __JS_NewFloat64(ctx, *(const double *)a); +} + +struct TA_sort_context { + JSContext *ctx; + int exception; + JSValueConst arr; + JSValueConst cmp; + JSValue (*getfun)(JSContext *ctx, const void *a); + uint8_t *array_ptr; /* cannot change unless the array is detached */ + int elt_size; +}; + +static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { + struct TA_sort_context *psc = opaque; + JSContext *ctx = psc->ctx; + uint32_t a_idx, b_idx; + JSValueConst argv[2]; + JSValue res; + int cmp; + + cmp = 0; + if (!psc->exception) { + a_idx = *(uint32_t *)a; + b_idx = *(uint32_t *)b; + argv[0] = psc->getfun(ctx, psc->array_ptr + + a_idx * (size_t)psc->elt_size); + argv[1] = psc->getfun(ctx, psc->array_ptr + + b_idx * (size_t)(psc->elt_size)); + res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); + if (JS_IsException(res)) { + psc->exception = 1; + goto done; + } + if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { + int val = JS_VALUE_GET_INT(res); + cmp = (val > 0) - (val < 0); + } else { + double val; + if (JS_ToFloat64Free(ctx, &val, res) < 0) { + psc->exception = 1; + goto done; + } else { + cmp = (val > 0) - (val < 0); + } + } + if (cmp == 0) { + /* make sort stable: compare array offsets */ + cmp = (a_idx > b_idx) - (a_idx < b_idx); + } + if (validate_typed_array(ctx, psc->arr) < 0) { + psc->exception = 1; + } + done: + JS_FreeValue(ctx, (JSValue)argv[0]); + JS_FreeValue(ctx, (JSValue)argv[1]); + } + return cmp; +} + +static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSObject *p; + int len; + size_t elt_size; + struct TA_sort_context tsc; + void *array_ptr; + int (*cmpfun)(const void *a, const void *b, void *opaque); + + tsc.ctx = ctx; + tsc.exception = 0; + tsc.arr = this_val; + tsc.cmp = argv[0]; + + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + return JS_EXCEPTION; + if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) + return JS_EXCEPTION; + + if (len > 1) { + p = JS_VALUE_GET_OBJ(this_val); + switch (p->class_id) { + case JS_CLASS_INT8_ARRAY: + tsc.getfun = js_TA_get_int8; + cmpfun = js_TA_cmp_int8; + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + tsc.getfun = js_TA_get_uint8; + cmpfun = js_TA_cmp_uint8; + break; + case JS_CLASS_INT16_ARRAY: + tsc.getfun = js_TA_get_int16; + cmpfun = js_TA_cmp_int16; + break; + case JS_CLASS_UINT16_ARRAY: + tsc.getfun = js_TA_get_uint16; + cmpfun = js_TA_cmp_uint16; + break; + case JS_CLASS_INT32_ARRAY: + tsc.getfun = js_TA_get_int32; + cmpfun = js_TA_cmp_int32; + break; + case JS_CLASS_UINT32_ARRAY: + tsc.getfun = js_TA_get_uint32; + cmpfun = js_TA_cmp_uint32; + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + tsc.getfun = js_TA_get_int64; + cmpfun = js_TA_cmp_int64; + break; + case JS_CLASS_BIG_UINT64_ARRAY: + tsc.getfun = js_TA_get_uint64; + cmpfun = js_TA_cmp_uint64; + break; +#endif + case JS_CLASS_FLOAT32_ARRAY: + tsc.getfun = js_TA_get_float32; + cmpfun = js_TA_cmp_float32; + break; + case JS_CLASS_FLOAT64_ARRAY: + tsc.getfun = js_TA_get_float64; + cmpfun = js_TA_cmp_float64; + break; + default: + abort(); + } + array_ptr = p->u.array.u.ptr; + elt_size = 1 << typed_array_size_log2(p->class_id); + if (!JS_IsUndefined(tsc.cmp)) { + uint32_t *array_idx; + void *array_tmp; + size_t i, j; + + /* XXX: a stable sort would use less memory */ + array_idx = js_malloc(ctx, len * sizeof(array_idx[0])); + if (!array_idx) + return JS_EXCEPTION; + for(i = 0; i < len; i++) + array_idx[i] = i; + tsc.array_ptr = array_ptr; + tsc.elt_size = elt_size; + rqsort(array_idx, len, sizeof(array_idx[0]), + js_TA_cmp_generic, &tsc); + if (tsc.exception) + goto fail; + array_tmp = js_malloc(ctx, len * elt_size); + if (!array_tmp) { + fail: + js_free(ctx, array_idx); + return JS_EXCEPTION; + } + memcpy(array_tmp, array_ptr, len * elt_size); + switch(elt_size) { + case 1: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j]; + } + break; + case 2: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j]; + } + break; + case 4: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j]; + } + break; + case 8: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j]; + } + break; + default: + abort(); + } + js_free(ctx, array_tmp); + js_free(ctx, array_idx); + } else { + rqsort(array_ptr, len, elt_size, cmpfun, &tsc); + if (tsc.exception) + return JS_EXCEPTION; + } + } + return JS_DupValue(ctx, this_val); +} + +static const JSCFunctionListEntry js_typed_array_base_funcs[] = { + JS_CFUNC_DEF("from", 1, js_typed_array_from ), + JS_CFUNC_DEF("of", 0, js_typed_array_of ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), + //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ), + //JS_CFUNC_DEF("__create", 2, js_typed_array___create ), + //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ), +}; + +static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { + JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ), + JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ), + JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ), + JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ), + JS_CFUNC_DEF("set", 1, js_typed_array_set ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ), + JS_ALIAS_DEF("[Symbol.iterator]", "values" ), + JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), + JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ), + JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ), + JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ), + JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ), + JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ), + JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ), + JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ), + JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ), + JS_CFUNC_DEF("fill", 1, js_typed_array_fill ), + JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0 ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1 ), + JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ), + JS_CFUNC_DEF("slice", 2, js_typed_array_slice ), + JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ), + JS_CFUNC_DEF("sort", 1, js_typed_array_sort ), + JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ), + JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ), + JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ), + JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ), + //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@ +}; + +static JSValue js_typed_array_base_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ThrowTypeError(ctx, "cannot be called"); +} + +/* 'obj' must be an allocated typed array object */ +static int typed_array_init(JSContext *ctx, JSValueConst obj, + JSValue buffer, uint64_t offset, uint64_t len) +{ + JSTypedArray *ta; + JSObject *p, *pbuffer; + JSArrayBuffer *abuf; + int size_log2; + + p = JS_VALUE_GET_OBJ(obj); + size_log2 = typed_array_size_log2(p->class_id); + ta = js_malloc(ctx, sizeof(*ta)); + if (!ta) { + JS_FreeValue(ctx, buffer); + return -1; + } + pbuffer = JS_VALUE_GET_OBJ(buffer); + abuf = pbuffer->u.array_buffer; + ta->obj = p; + ta->buffer = pbuffer; + ta->offset = offset; + ta->length = len << size_log2; + list_add_tail(&ta->link, &abuf->array_list); + p->u.typed_array = ta; + p->u.array.count = len; + p->u.array.u.ptr = abuf->data + offset; + return 0; +} + + +static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, + JSValueConst obj, JSValueConst method) +{ + JSValue arr, iter, next_method = JS_UNDEFINED, val; + BOOL done; + uint32_t k; + + *plen = 0; + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return arr; + iter = JS_GetIterator2(ctx, obj, method); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + k = 0; + for(;;) { + val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(val)) + goto fail; + if (done) { + JS_FreeValue(ctx, val); + break; + } + if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) + goto fail; + k++; + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + *plen = k; + return arr; + fail: + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_constructor_obj(JSContext *ctx, + JSValueConst new_target, + JSValueConst obj, + int classid) +{ + JSValue iter, ret, arr = JS_UNDEFINED, val, buffer; + uint32_t i; + int size_log2; + int64_t len; + + size_log2 = typed_array_size_log2(classid); + ret = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(ret)) + return JS_EXCEPTION; + + iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto fail; + if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { + uint32_t len1; + arr = js_array_from_iterator(ctx, &len1, obj, iter); + JS_FreeValue(ctx, iter); + if (JS_IsException(arr)) + goto fail; + len = len1; + } else { + if (js_get_length64(ctx, &len, obj)) + goto fail; + arr = JS_DupValue(ctx, obj); + } + + buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, + len << size_log2); + if (JS_IsException(buffer)) + goto fail; + if (typed_array_init(ctx, ret, buffer, 0, len)) + goto fail; + + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(ctx, arr, i); + if (JS_IsException(val)) + goto fail; + if (JS_SetPropertyUint32(ctx, ret, i, val) < 0) + goto fail; + } + JS_FreeValue(ctx, arr); + return ret; + fail: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_constructor_ta(JSContext *ctx, + JSValueConst new_target, + JSValueConst src_obj, + int classid) +{ + JSObject *p, *src_buffer; + JSTypedArray *ta; + JSValue ctor, obj, buffer; + uint32_t len, i; + int size_log2; + JSArrayBuffer *src_abuf, *abuf; + + obj = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_GET_OBJ(src_obj); + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + ta = p->u.typed_array; + len = p->u.array.count; + src_buffer = ta->buffer; + src_abuf = src_buffer->u.array_buffer; + if (!src_abuf->shared) { + ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer), + JS_UNDEFINED); + if (JS_IsException(ctor)) + goto fail; + } else { + /* force ArrayBuffer default constructor */ + ctor = JS_UNDEFINED; + } + size_log2 = typed_array_size_log2(classid); + buffer = js_array_buffer_constructor1(ctx, ctor, + (uint64_t)len << size_log2); + JS_FreeValue(ctx, ctor); + if (JS_IsException(buffer)) + goto fail; + /* necessary because it could have been detached */ + if (typed_array_is_detached(ctx, p)) { + JS_FreeValue(ctx, buffer); + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER); + if (typed_array_init(ctx, obj, buffer, 0, len)) + goto fail; + if (p->class_id == classid) { + /* same type: copy the content */ + memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length); + } else { + for(i = 0; i < len; i++) { + JSValue val; + val = JS_GetPropertyUint32(ctx, src_obj, i); + if (JS_IsException(val)) + goto fail; + if (JS_SetPropertyUint32(ctx, obj, i, val) < 0) + goto fail; + } + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv, + int classid) +{ + JSValue buffer, obj; + JSArrayBuffer *abuf; + int size_log2; + uint64_t len, offset; + + size_log2 = typed_array_size_log2(classid); + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) { + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, + len << size_log2); + if (JS_IsException(buffer)) + return JS_EXCEPTION; + offset = 0; + } else { + JSObject *p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id == JS_CLASS_ARRAY_BUFFER || + p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { + abuf = p->u.array_buffer; + if (JS_ToIndex(ctx, &offset, argv[1])) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((offset & ((1 << size_log2) - 1)) != 0 || + offset > abuf->byte_length) + return JS_ThrowRangeError(ctx, "invalid offset"); + if (JS_IsUndefined(argv[2])) { + if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) + goto invalid_length; + len = (abuf->byte_length - offset) >> size_log2; + } else { + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((offset + (len << size_log2)) > abuf->byte_length) { + invalid_length: + return JS_ThrowRangeError(ctx, "invalid length"); + } + } + buffer = JS_DupValue(ctx, argv[0]); + } else { + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid); + } else { + return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid); + } + } + } + + obj = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, buffer); + return JS_EXCEPTION; + } + if (typed_array_init(ctx, obj, buffer, offset, len)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSTypedArray *ta = p->u.typed_array; + if (ta) { + /* during the GC the finalizers are called in an arbitrary + order so the ArrayBuffer finalizer may have been called */ + if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) { + list_del(&ta->link); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); + js_free_rt(rt, ta); + } +} + +static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSTypedArray *ta = p->u.typed_array; + if (ta) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func); + } +} + +static JSValue js_dataview_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSArrayBuffer *abuf; + uint64_t offset; + uint32_t len; + JSValueConst buffer; + JSValue obj; + JSTypedArray *ta; + JSObject *p; + + buffer = argv[0]; + abuf = js_get_array_buffer(ctx, buffer); + if (!abuf) + return JS_EXCEPTION; + offset = 0; + if (argc > 1) { + if (JS_ToIndex(ctx, &offset, argv[1])) + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (offset > abuf->byte_length) + return JS_ThrowRangeError(ctx, "invalid byteOffset"); + len = abuf->byte_length - offset; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + uint64_t l; + if (JS_ToIndex(ctx, &l, argv[2])) + return JS_EXCEPTION; + if (l > len) + return JS_ThrowRangeError(ctx, "invalid byteLength"); + len = l; + } + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (abuf->detached) { + /* could have been detached in js_create_from_ctor() */ + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + ta = js_malloc(ctx, sizeof(*ta)); + if (!ta) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p = JS_VALUE_GET_OBJ(obj); + ta->obj = p; + ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer)); + ta->offset = offset; + ta->length = len; + list_add_tail(&ta->link, &abuf->array_list); + p->u.typed_array = ta; + return obj; +} + +static JSValue js_dataview_getValue(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, int class_id) +{ + JSTypedArray *ta; + JSArrayBuffer *abuf; + int is_swap, size; + uint8_t *ptr; + uint32_t v; + uint64_t pos; + + ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); + if (!ta) + return JS_EXCEPTION; + size = 1 << typed_array_size_log2(class_id); + if (JS_ToIndex(ctx, &pos, argv[0])) + return JS_EXCEPTION; + is_swap = FALSE; + if (argc > 1) + is_swap = JS_ToBool(ctx, argv[1]); +#ifndef WORDS_BIGENDIAN + is_swap ^= 1; +#endif + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((pos + size) > ta->length) + return JS_ThrowRangeError(ctx, "out of bound"); + ptr = abuf->data + ta->offset + pos; + + switch(class_id) { + case JS_CLASS_INT8_ARRAY: + return JS_NewInt32(ctx, *(int8_t *)ptr); + case JS_CLASS_UINT8_ARRAY: + return JS_NewInt32(ctx, *(uint8_t *)ptr); + case JS_CLASS_INT16_ARRAY: + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return JS_NewInt32(ctx, (int16_t)v); + case JS_CLASS_UINT16_ARRAY: + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return JS_NewInt32(ctx, v); + case JS_CLASS_INT32_ARRAY: + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + return JS_NewInt32(ctx, v); + case JS_CLASS_UINT32_ARRAY: + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + return JS_NewUint32(ctx, v); +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + { + uint64_t v; + v = get_u64(ptr); + if (is_swap) + v = bswap64(v); + return JS_NewBigInt64(ctx, v); + } + break; + case JS_CLASS_BIG_UINT64_ARRAY: + { + uint64_t v; + v = get_u64(ptr); + if (is_swap) + v = bswap64(v); + return JS_NewBigUint64(ctx, v); + } + break; +#endif + case JS_CLASS_FLOAT32_ARRAY: + { + union { + float f; + uint32_t i; + } u; + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + u.i = v; + return __JS_NewFloat64(ctx, u.f); + } + case JS_CLASS_FLOAT64_ARRAY: + { + union { + double f; + uint64_t i; + } u; + u.i = get_u64(ptr); + if (is_swap) + u.i = bswap64(u.i); + return __JS_NewFloat64(ctx, u.f); + } + default: + abort(); + } +} + +static JSValue js_dataview_setValue(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, int class_id) +{ + JSTypedArray *ta; + JSArrayBuffer *abuf; + int is_swap, size; + uint8_t *ptr; + uint64_t v64; + uint32_t v; + uint64_t pos; + JSValueConst val; + + ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); + if (!ta) + return JS_EXCEPTION; + size = 1 << typed_array_size_log2(class_id); + if (JS_ToIndex(ctx, &pos, argv[0])) + return JS_EXCEPTION; + val = argv[1]; + v = 0; /* avoid warning */ + v64 = 0; /* avoid warning */ + if (class_id <= JS_CLASS_UINT32_ARRAY) { + if (JS_ToUint32(ctx, &v, val)) + return JS_EXCEPTION; + } else +#ifdef CONFIG_BIGNUM + if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + if (JS_ToBigInt64(ctx, (int64_t *)&v64, val)) + return JS_EXCEPTION; + } else +#endif + { + double d; + if (JS_ToFloat64(ctx, &d, val)) + return JS_EXCEPTION; + if (class_id == JS_CLASS_FLOAT32_ARRAY) { + union { + float f; + uint32_t i; + } u; + u.f = d; + v = u.i; + } else { + JSFloat64Union u; + u.d = d; + v64 = u.u64; + } + } + is_swap = FALSE; + if (argc > 2) + is_swap = JS_ToBool(ctx, argv[2]); +#ifndef WORDS_BIGENDIAN + is_swap ^= 1; +#endif + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((pos + size) > ta->length) + return JS_ThrowRangeError(ctx, "out of bound"); + ptr = abuf->data + ta->offset + pos; + + switch(class_id) { + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + *ptr = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + if (is_swap) + v = bswap16(v); + put_u16(ptr, v); + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + if (is_swap) + v = bswap32(v); + put_u32(ptr, v); + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: +#endif + case JS_CLASS_FLOAT64_ARRAY: + if (is_swap) + v64 = bswap64(v64); + put_u64(ptr, v64); + break; + default: + abort(); + } + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_dataview_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ), + JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ), + JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ), + JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ), + JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ), + JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ), +#ifdef CONFIG_BIGNUM + JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ), +#endif + JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ), + JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ), + JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ), + JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ), +#ifdef CONFIG_BIGNUM + JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ), + JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ), +#endif + JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ), + JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ), +}; + +/* Atomics */ +#ifdef CONFIG_ATOMICS + +typedef enum AtomicsOpEnum { + ATOMICS_OP_ADD, + ATOMICS_OP_AND, + ATOMICS_OP_OR, + ATOMICS_OP_SUB, + ATOMICS_OP_XOR, + ATOMICS_OP_EXCHANGE, + ATOMICS_OP_COMPARE_EXCHANGE, + ATOMICS_OP_LOAD, +} AtomicsOpEnum; + +static void *js_atomics_get_ptr(JSContext *ctx, + int *psize_log2, JSClassID *pclass_id, + JSValueConst obj, JSValueConst idx_val, + BOOL is_waitable) +{ + JSObject *p; + JSTypedArray *ta; + JSArrayBuffer *abuf; + void *ptr; + uint64_t idx; + BOOL err; + int size_log2; + + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(obj); +#ifdef CONFIG_BIGNUM + if (is_waitable) + err = (p->class_id != JS_CLASS_INT32_ARRAY && + p->class_id != JS_CLASS_BIG_INT64_ARRAY); + else + err = !(p->class_id >= JS_CLASS_INT8_ARRAY && + p->class_id <= JS_CLASS_BIG_UINT64_ARRAY); +#else + if (is_waitable) + err = (p->class_id != JS_CLASS_INT32_ARRAY); + else + err = !(p->class_id >= JS_CLASS_INT8_ARRAY && + p->class_id <= JS_CLASS_UINT32_ARRAY); +#endif + if (err) { + fail: + JS_ThrowTypeError(ctx, "integer TypedArray expected"); + return NULL; + } + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (!abuf->shared) { + JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); + return NULL; + } + if (JS_ToIndex(ctx, &idx, idx_val)) { + return NULL; + } + 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 (psize_log2) + *psize_log2 = size_log2; + if (pclass_id) + *pclass_id = p->class_id; + return ptr; +} + +static JSValue js_atomics_op(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, int op) +{ + int size_log2; +#ifdef CONFIG_BIGNUM + uint64_t v, a, rep_val; +#else + uint32_t v, a, rep_val; +#endif + void *ptr; + JSValue ret; + JSClassID class_id; + + ptr = js_atomics_get_ptr(ctx, &size_log2, &class_id, + argv[0], argv[1], FALSE); + if (!ptr) + return JS_EXCEPTION; + rep_val = 0; + if (op == ATOMICS_OP_LOAD) { + v = 0; + } else +#ifdef CONFIG_BIGNUM + if (size_log2 == 3) { + int64_t v64; + if (JS_ToBigInt64(ctx, &v64, argv[2])) + return JS_EXCEPTION; + v = v64; + if (op == ATOMICS_OP_COMPARE_EXCHANGE) { + if (JS_ToBigInt64(ctx, &v64, argv[3])) + return JS_EXCEPTION; + rep_val = v64; + } + } else +#endif + { + uint32_t v32; + if (JS_ToUint32(ctx, &v32, argv[2])) + return JS_EXCEPTION; + v = v32; + if (op == ATOMICS_OP_COMPARE_EXCHANGE) { + if (JS_ToUint32(ctx, &v32, argv[3])) + return JS_EXCEPTION; + rep_val = v32; + } + } + switch(op | (size_log2 << 3)) { + +#ifdef CONFIG_BIGNUM +#define OP(op_name, func_name) \ + case ATOMICS_OP_ ## op_name | (0 << 3): \ + a = func_name((_Atomic(uint8_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (1 << 3): \ + a = func_name((_Atomic(uint16_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (2 << 3): \ + a = func_name((_Atomic(uint32_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (3 << 3): \ + a = func_name((_Atomic(uint64_t) *)ptr, v); \ + break; +#else +#define OP(op_name, func_name) \ + case ATOMICS_OP_ ## op_name | (0 << 3): \ + a = func_name((_Atomic(uint8_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (1 << 3): \ + a = func_name((_Atomic(uint16_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (2 << 3): \ + a = func_name((_Atomic(uint32_t) *)ptr, v); \ + break; +#endif + OP(ADD, atomic_fetch_add) + OP(AND, atomic_fetch_and) + OP(OR, atomic_fetch_or) + OP(SUB, atomic_fetch_sub) + OP(XOR, atomic_fetch_xor) + OP(EXCHANGE, atomic_exchange) +#undef OP + + case ATOMICS_OP_LOAD | (0 << 3): + a = atomic_load((_Atomic(uint8_t) *)ptr); + break; + case ATOMICS_OP_LOAD | (1 << 3): + a = atomic_load((_Atomic(uint16_t) *)ptr); + break; + case ATOMICS_OP_LOAD | (2 << 3): + a = atomic_load((_Atomic(uint32_t) *)ptr); + break; +#ifdef CONFIG_BIGNUM + case ATOMICS_OP_LOAD | (3 << 3): + a = atomic_load((_Atomic(uint64_t) *)ptr); + break; +#endif + + case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3): + { + uint8_t v1 = v; + atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val); + a = v1; + } + break; + case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3): + { + uint16_t v1 = v; + atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val); + a = v1; + } + break; + case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3): + { + uint32_t v1 = v; + atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val); + a = v1; + } + break; +#ifdef CONFIG_BIGNUM + case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3): + { + uint64_t v1 = v; + atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val); + a = v1; + } + break; +#endif + default: + abort(); + } + + switch(class_id) { + case JS_CLASS_INT8_ARRAY: + a = (int8_t)a; + goto done; + case JS_CLASS_UINT8_ARRAY: + a = (uint8_t)a; + goto done; + case JS_CLASS_INT16_ARRAY: + a = (int16_t)a; + goto done; + case JS_CLASS_UINT16_ARRAY: + a = (uint16_t)a; + goto done; + case JS_CLASS_INT32_ARRAY: + done: + ret = JS_NewInt32(ctx, a); + break; + case JS_CLASS_UINT32_ARRAY: + ret = JS_NewUint32(ctx, a); + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + ret = JS_NewBigInt64(ctx, a); + break; + case JS_CLASS_BIG_UINT64_ARRAY: + ret = JS_NewBigUint64(ctx, a); + break; +#endif + default: + abort(); + } + return ret; +} + +static JSValue js_atomics_store(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + int size_log2; + void *ptr; + JSValue ret; + + ptr = js_atomics_get_ptr(ctx, &size_log2, NULL, argv[0], argv[1], FALSE); + if (!ptr) + return JS_EXCEPTION; +#ifdef CONFIG_BIGNUM + if (size_log2 == 3) { + int64_t v64; + ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2])); + if (JS_IsException(ret)) + return ret; + if (JS_ToBigInt64(ctx, &v64, ret)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + atomic_store((_Atomic(uint64_t) *)ptr, v64); + } else +#endif + { + uint32_t v; + /* XXX: spec, would be simpler to return the written value */ + ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2])); + if (JS_IsException(ret)) + return ret; + if (JS_ToUint32(ctx, &v, ret)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + switch(size_log2) { + case 0: + atomic_store((_Atomic(uint8_t) *)ptr, v); + break; + case 1: + atomic_store((_Atomic(uint16_t) *)ptr, v); + break; + case 2: + atomic_store((_Atomic(uint32_t) *)ptr, v); + break; + default: + abort(); + } + } + return ret; +} + +static JSValue js_atomics_isLockFree(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + int v, ret; + if (JS_ToInt32Sat(ctx, &v, argv[0])) + return JS_EXCEPTION; + ret = (v == 1 || v == 2 || v == 4 +#ifdef CONFIG_BIGNUM + || v == 8 +#endif + ); + return JS_NewBool(ctx, ret); +} + +typedef struct JSAtomicsWaiter { + struct list_head link; + BOOL linked; + pthread_cond_t cond; + int32_t *ptr; +} JSAtomicsWaiter; + +static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct list_head js_atomics_waiter_list = + LIST_HEAD_INIT(js_atomics_waiter_list); + +static JSValue js_atomics_wait(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + int64_t v; + int32_t v32; + void *ptr; + int64_t timeout; + struct timespec ts; + JSAtomicsWaiter waiter_s, *waiter; + int ret, size_log2, res; + double d; + + ptr = js_atomics_get_ptr(ctx, &size_log2, NULL, argv[0], argv[1], TRUE); + if (!ptr) + return JS_EXCEPTION; +#ifdef CONFIG_BIGNUM + if (size_log2 == 3) { + if (JS_ToBigInt64(ctx, &v, argv[2])) + return JS_EXCEPTION; + } else +#endif + { + if (JS_ToInt32(ctx, &v32, argv[2])) + return JS_EXCEPTION; + v = v32; + } + if (JS_ToFloat64(ctx, &d, argv[3])) + return JS_EXCEPTION; + if (isnan(d) || d > INT64_MAX) + timeout = INT64_MAX; + else if (d < 0) + timeout = 0; + else + timeout = (int64_t)d; + if (!ctx->rt->can_block) + return JS_ThrowTypeError(ctx, "cannot block in this thread"); + + /* XXX: inefficient if large number of waiters, should hash on + 'ptr' value */ + /* XXX: use Linux futexes when available ? */ + pthread_mutex_lock(&js_atomics_mutex); + if (size_log2 == 3) { + res = *(int64_t *)ptr != v; + } else { + res = *(int32_t *)ptr != v; + } + if (res) { + pthread_mutex_unlock(&js_atomics_mutex); + return JS_AtomToString(ctx, JS_ATOM_not_equal); + } + + waiter = &waiter_s; + waiter->ptr = ptr; + pthread_cond_init(&waiter->cond, NULL); + waiter->linked = TRUE; + list_add_tail(&waiter->link, &js_atomics_waiter_list); + + if (timeout == INT64_MAX) { + pthread_cond_wait(&waiter->cond, &js_atomics_mutex); + ret = 0; + } else { + /* XXX: use clock monotonic */ + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += timeout / 1000; + ts.tv_nsec += (timeout % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex, + &ts); + } + if (waiter->linked) + list_del(&waiter->link); + pthread_mutex_unlock(&js_atomics_mutex); + pthread_cond_destroy(&waiter->cond); + if (ret == ETIMEDOUT) { + return JS_AtomToString(ctx, JS_ATOM_timed_out); + } else { + return JS_AtomToString(ctx, JS_ATOM_ok); + } +} + +static JSValue js_atomics_notify(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + struct list_head *el, *el1, waiter_list; + int32_t count, n; + void *ptr; + JSAtomicsWaiter *waiter; + + ptr = js_atomics_get_ptr(ctx, NULL, NULL, argv[0], argv[1], TRUE); + 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; + } + + n = 0; + if (count > 0) { + pthread_mutex_lock(&js_atomics_mutex); + init_list_head(&waiter_list); + list_for_each_safe(el, el1, &js_atomics_waiter_list) { + waiter = list_entry(el, JSAtomicsWaiter, link); + if (waiter->ptr == ptr) { + list_del(&waiter->link); + waiter->linked = FALSE; + list_add_tail(&waiter->link, &waiter_list); + n++; + if (n >= count) + break; + } + } + list_for_each(el, &waiter_list) { + waiter = list_entry(el, JSAtomicsWaiter, link); + pthread_cond_signal(&waiter->cond); + } + pthread_mutex_unlock(&js_atomics_mutex); + } + return JS_NewInt32(ctx, n); +} + +static const JSCFunctionListEntry js_atomics_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ), + JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ), + JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ), + JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ), + JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ), + JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ), + JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ), + JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ), + JS_CFUNC_DEF("store", 3, js_atomics_store ), + JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ), + JS_CFUNC_DEF("wait", 4, js_atomics_wait ), + JS_CFUNC_DEF("notify", 3, js_atomics_notify ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ), +}; + +static const JSCFunctionListEntry js_atomics_obj[] = { + JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicAtomics(JSContext *ctx) +{ + /* add Atomics as autoinit object */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj)); +} + +#endif /* CONFIG_ATOMICS */ + +void JS_AddIntrinsicTypedArrays(JSContext *ctx) +{ + JSValue typed_array_base_proto, typed_array_base_func; + JSValueConst array_buffer_func, shared_array_buffer_func; + int i; + + ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER], + js_array_buffer_proto_funcs, + countof(js_array_buffer_proto_funcs)); + + array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer", + js_array_buffer_constructor, 1, + ctx->class_proto[JS_CLASS_ARRAY_BUFFER]); + JS_SetPropertyFunctionList(ctx, array_buffer_func, + js_array_buffer_funcs, + countof(js_array_buffer_funcs)); + + ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER], + js_shared_array_buffer_proto_funcs, + countof(js_shared_array_buffer_proto_funcs)); + + shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer", + js_shared_array_buffer_constructor, 1, + ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]); + JS_SetPropertyFunctionList(ctx, shared_array_buffer_func, + js_shared_array_buffer_funcs, + countof(js_shared_array_buffer_funcs)); + + typed_array_base_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, typed_array_base_proto, + js_typed_array_base_proto_funcs, + countof(js_typed_array_base_proto_funcs)); + + /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */ + JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString); + /* XXX: should use alias method in JSCFunctionListEntry */ //@@@ + JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + + typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor, + "TypedArray", 0); + JS_SetPropertyFunctionList(ctx, typed_array_base_func, + js_typed_array_base_funcs, + countof(js_typed_array_base_funcs)); + JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); + + for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) { + JSValue func_obj; + char buf[ATOM_GET_STR_BUF_SIZE]; + const char *name; + + ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto); + JS_DefinePropertyValueStr(ctx, ctx->class_proto[i], + "BYTES_PER_ELEMENT", + JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), + 0); + name = JS_AtomGetStr(ctx, buf, sizeof(buf), + JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); + func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_typed_array_constructor, + name, 3, JS_CFUNC_constructor_magic, i, + typed_array_base_func); + JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]); + JS_DefinePropertyValueStr(ctx, func_obj, + "BYTES_PER_ELEMENT", + JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), + 0); + } + JS_FreeValue(ctx, typed_array_base_proto); + JS_FreeValue(ctx, typed_array_base_func); + + /* DataView */ + ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW], + js_dataview_proto_funcs, + countof(js_dataview_proto_funcs)); + JS_NewGlobalCConstructorOnly(ctx, "DataView", + js_dataview_constructor, 1, + ctx->class_proto[JS_CLASS_DATAVIEW]); + /* Atomics */ +#ifdef CONFIG_ATOMICS + JS_AddIntrinsicAtomics(ctx); +#endif +} diff --git a/quickjs.h b/quickjs.h new file mode 100644 index 0000000..8fcd7e5 --- /dev/null +++ b/quickjs.h @@ -0,0 +1,976 @@ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2020 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_H +#define QUICKJS_H + +#include +#include + +#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 defined(__x86_64__) || defined(__aarch64__) +#define JS_PTR64 +#define JS_PTR64_DEF(a) a +#else +#define JS_PTR64_DEF(a) +#endif + +#ifndef JS_PTR64 +#define JS_NAN_BOXING +#endif + +enum { + /* all tags with a reference count are negative */ + JS_TAG_FIRST = -11, /* first negative tag */ + JS_TAG_BIG_DECIMAL = -11, + JS_TAG_BIG_INT = -10, + JS_TAG_BIG_FLOAT = -9, + JS_TAG_SYMBOL = -8, + JS_TAG_STRING = -7, + JS_TAG_MODULE = -3, /* used internally */ + JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ + JS_TAG_OBJECT = -1, + + JS_TAG_INT = 0, + JS_TAG_BOOL = 1, + JS_TAG_NULL = 2, + JS_TAG_UNDEFINED = 3, + JS_TAG_UNINITIALIZED = 4, + JS_TAG_CATCH_OFFSET = 5, + JS_TAG_EXCEPTION = 6, + JS_TAG_FLOAT64 = 7, + /* any larger tag is FLOAT64 if JS_NAN_BOXING */ +}; + +typedef struct JSRefCountHeader { + int ref_count; +} JSRefCountHeader; + +#define JS_FLOAT64_NAN NAN + +#ifdef CONFIG_CHECK_JSVALUE +/* JSValue consistency : it is not possible to run the code in this + mode, but it is useful to detect simple reference counting + errors. It would be interesting to modify a static C analyzer to + handle specific annotations (clang has such annotations but only + for objective C) */ +typedef struct __JSValue *JSValue; +typedef const struct __JSValue *JSValueConst; + +#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf) +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) +#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) +#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v) +#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v) +#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf) + +#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag)) +#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag)) + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) + +#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1) + +static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) +{ + return JS_MKVAL(JS_TAG_FLOAT64, (int)d); +} + +static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) +{ + return 0; +} + +#elif defined(JS_NAN_BOXING) + +typedef uint64_t JSValue; + +#define JSValueConst JSValue + +#define JS_VALUE_GET_TAG(v) (int)((v) >> 32) +#define JS_VALUE_GET_INT(v) (int)(v) +#define JS_VALUE_GET_BOOL(v) (int)(v) +#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) + +#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) +#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) + +#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */ + +static inline double JS_VALUE_GET_FLOAT64(JSValue v) +{ + union { + JSValue v; + double d; + } u; + u.v = v; + u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32; + return u.d; +} + +#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) + +static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) +{ + union { + double d; + uint64_t u64; + } u; + JSValue v; + u.d = d; + /* normalize NaN */ + if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000)) + v = JS_NAN; + else + v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32); + return v; +} + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) + +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +static inline int JS_VALUE_GET_NORM_TAG(JSValue v) +{ + uint32_t tag; + tag = JS_VALUE_GET_TAG(v); + if (JS_TAG_IS_FLOAT64(tag)) + return JS_TAG_FLOAT64; + else + return tag; +} + +static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) +{ + uint32_t tag; + tag = JS_VALUE_GET_TAG(v); + return tag == (JS_NAN >> 32); +} + +#else /* !JS_NAN_BOXING */ + +typedef union JSValueUnion { + int32_t int32; + double float64; + void *ptr; +} JSValueUnion; + +typedef struct JSValue { + JSValueUnion u; + int64_t tag; +} JSValue; + +#define JSValueConst JSValue + +#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag) +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) +#define JS_VALUE_GET_INT(v) ((v).u.int32) +#define JS_VALUE_GET_BOOL(v) ((v).u.int32) +#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) +#define JS_VALUE_GET_PTR(v) ((v).u.ptr) + +#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } +#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) + +#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } + +static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) +{ + JSValue v; + v.tag = JS_TAG_FLOAT64; + v.u.float64 = d; + return v; +} + +static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) +{ + union { + double d; + uint64_t u64; + } u; + if (v.tag != JS_TAG_FLOAT64) + return 0; + u.d = v.u.float64; + return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; +} + +#endif /* !JS_NAN_BOXING */ + +#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) +#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) + +#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) + +/* special values */ +#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0) +#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0) +#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0) +#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1) +#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0) +#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0) + +/* flags for object properties */ +#define JS_PROP_CONFIGURABLE (1 << 0) +#define JS_PROP_WRITABLE (1 << 1) +#define JS_PROP_ENUMERABLE (1 << 2) +#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE) +#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ +#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ +#define JS_PROP_NORMAL (0 << 4) +#define JS_PROP_GETSET (1 << 4) +#define JS_PROP_VARREF (2 << 4) /* used internally */ +#define JS_PROP_AUTOINIT (3 << 4) /* used internally */ + +/* flags for JS_DefineProperty */ +#define JS_PROP_HAS_SHIFT 8 +#define JS_PROP_HAS_CONFIGURABLE (1 << 8) +#define JS_PROP_HAS_WRITABLE (1 << 9) +#define JS_PROP_HAS_ENUMERABLE (1 << 10) +#define JS_PROP_HAS_GET (1 << 11) +#define JS_PROP_HAS_SET (1 << 12) +#define JS_PROP_HAS_VALUE (1 << 13) + +/* throw an exception if false would be returned + (JS_DefineProperty/JS_SetProperty) */ +#define JS_PROP_THROW (1 << 14) +/* throw an exception if false would be returned in strict mode + (JS_SetProperty) */ +#define JS_PROP_THROW_STRICT (1 << 15) + +#define JS_PROP_NO_ADD (1 << 16) /* internal use */ +#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ + +#define JS_DEFAULT_STACK_SIZE (256 * 1024) + +/* JS_Eval() flags */ +#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ +#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */ +#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ +#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ +#define JS_EVAL_TYPE_MASK (3 << 0) + +#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ +#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */ +/* compile but do not run. The result is an object with a + JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed + with JS_EvalFunction(). */ +#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) +/* don't include the stack frames before this eval in the Error() backtraces */ +#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) + +typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); +typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data); + +typedef struct JSMallocState { + size_t malloc_count; + size_t malloc_size; + size_t malloc_limit; + void *opaque; /* user opaque */ +} JSMallocState; + +typedef struct JSMallocFunctions { + void *(*js_malloc)(JSMallocState *s, size_t size); + void (*js_free)(JSMallocState *s, void *ptr); + void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size); + size_t (*js_malloc_usable_size)(const void *ptr); +} JSMallocFunctions; + +typedef struct JSGCObjectHeader JSGCObjectHeader; + +JSRuntime *JS_NewRuntime(void); +/* info lifetime must exceed that of rt */ +void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); +void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); +void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); +JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); +void JS_FreeRuntime(JSRuntime *rt); +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); +void *JS_GetContextOpaque(JSContext *ctx); +void JS_SetContextOpaque(JSContext *ctx, void *opaque); +JSRuntime *JS_GetRuntime(JSContext *ctx); +void JS_SetMaxStackSize(JSContext *ctx, size_t stack_size); +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 "use bigint", "use math" and operator overloading */ +void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); + +JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +void *js_malloc_rt(JSRuntime *rt, size_t size); +void js_free_rt(JSRuntime *rt, void *ptr); +void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); +size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); +void *js_mallocz_rt(JSRuntime *rt, size_t size); + +void *js_malloc(JSContext *ctx, size_t size); +void js_free(JSContext *ctx, void *ptr); +void *js_realloc(JSContext *ctx, void *ptr, size_t size); +size_t js_malloc_usable_size(JSContext *ctx, const void *ptr); +void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack); +void *js_mallocz(JSContext *ctx, size_t size); +char *js_strdup(JSContext *ctx, const char *str); +char *js_strndup(JSContext *ctx, const char *s, size_t n); + +typedef struct JSMemoryUsage { + int64_t malloc_size, malloc_limit, memory_used_size; + int64_t malloc_count; + int64_t memory_used_count; + int64_t atom_count, atom_size; + int64_t str_count, str_size; + int64_t obj_count, obj_size; + int64_t prop_count, prop_size; + int64_t shape_count, shape_size; + int64_t js_func_count, js_func_size, js_func_code_size; + int64_t js_func_pc2line_count, js_func_pc2line_size; + int64_t c_func_count, array_count; + int64_t fast_array_count, fast_array_elements; + int64_t binary_object_count, binary_object_size; +} JSMemoryUsage; + +void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); +void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); + +/* atom support */ +JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); +JSAtom JS_NewAtom(JSContext *ctx, const char *str); +JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); +JSAtom JS_DupAtom(JSContext *ctx, JSAtom v); +void JS_FreeAtom(JSContext *ctx, JSAtom v); +void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); +JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); +JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); +const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); +JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); + +/* object class support */ + +typedef struct JSPropertyEnum { + JS_BOOL is_enumerable; + JSAtom atom; +} JSPropertyEnum; + +typedef struct JSPropertyDescriptor { + int flags; + JSValue value; + JSValue getter; + JSValue setter; +} JSPropertyDescriptor; + +typedef struct JSClassExoticMethods { + /* Return -1 if exception (can only happen in case of Proxy object), + FALSE if the property does not exists, TRUE if it exists. If 1 is + returned, the property descriptor 'desc' is filled if != NULL. */ + int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop); + /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, + -1 if exception. The 'is_enumerable' field is ignored. + */ + int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, + JSValueConst obj); + /* return < 0 if exception, or TRUE/FALSE */ + int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop); + /* return < 0 if exception or TRUE/FALSE */ + int (*define_own_property)(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); + /* The following methods can be emulated with the previous ones, + so they are usually not needed */ + /* return < 0 if exception or TRUE/FALSE */ + int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom); + JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst receiver); + /* return < 0 if exception or TRUE/FALSE */ + int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst value, JSValueConst receiver, int flags); +} JSClassExoticMethods; + +typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); +typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) +typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_val, int argc, JSValueConst *argv, + int flags); + +typedef struct JSClassDef { + const char *class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + /* if call != NULL, the object is a function. If (flags & + JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a + constructor. In this case, 'this_val' is new.target. A + constructor call only happens if the object constructor bit is + set (see JS_SetConstructorBit()). */ + JSClassCall *call; + /* XXX: suppress this indirection ? It is here only to save memory + because only a few classes need these methods */ + JSClassExoticMethods *exotic; +} JSClassDef; + +JSClassID JS_NewClassID(JSClassID *pclass_id); +int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); +int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); + +/* value handling */ + +static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) +{ + return JS_MKVAL(JS_TAG_BOOL, val); +} + +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); +} + +JSValue JS_NewInt64(JSContext *ctx, int64_t v); +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); + +static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) +{ + JSValue v; + int32_t val; + union { + double d; + uint64_t u; + } u, t; + u.d = d; + val = (int32_t)d; + t.d = val; + /* -0 cannot be represented as integer, so we compare the bit + representation */ + if (u.u == t.u) { + v = JS_MKVAL(JS_TAG_INT, val); + } else { + v = __JS_NewFloat64(ctx, d); + } + return v; +} + +JS_BOOL JS_IsNumber(JSValueConst v); + +static inline JS_BOOL JS_IsInteger(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT; +} + +static inline JS_BOOL JS_IsBigFloat(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_FLOAT; +} + +static inline JS_BOOL JS_IsBigDecimal(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_DECIMAL; +} + +static inline JS_BOOL JS_IsBool(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; +} + +static inline JS_BOOL JS_IsNull(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; +} + +static inline JS_BOOL JS_IsUndefined(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; +} + +static inline JS_BOOL JS_IsException(JSValueConst v) +{ + return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION); +} + +static inline JS_BOOL JS_IsUninitialized(JSValueConst v) +{ + return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED); +} + +static inline JS_BOOL JS_IsString(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; +} + +static inline JS_BOOL JS_IsSymbol(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; +} + +static inline JS_BOOL JS_IsObject(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; +} + +JSValue JS_Throw(JSContext *ctx, JSValue obj); +JSValue JS_GetException(JSContext *ctx); +JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); +void JS_ResetUncatchableError(JSContext *ctx); +JSValue JS_NewError(JSContext *ctx); +JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); +JSValue JS_ThrowOutOfMemory(JSContext *ctx); + +void __JS_FreeValue(JSContext *ctx, JSValue v); +static inline void JS_FreeValue(JSContext *ctx, JSValue v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + if (--p->ref_count <= 0) { + __JS_FreeValue(ctx, v); + } + } +} +void __JS_FreeValueRT(JSRuntime *rt, JSValue v); +static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + if (--p->ref_count <= 0) { + __JS_FreeValueRT(rt, v); + } + } +} + +static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + p->ref_count++; + } + return (JSValue)v; +} + +static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + p->ref_count++; + } + return (JSValue)v; +} + +int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ +int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); +static int inline 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); +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); + +JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); +JSValue JS_NewString(JSContext *ctx, const char *str); +JSValue JS_NewAtomString(JSContext *ctx, const char *str); +JSValue JS_ToString(JSContext *ctx, JSValueConst val); +JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); +const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8); +static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1) +{ + return JS_ToCStringLen2(ctx, plen, val1, 0); +} +static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) +{ + return JS_ToCStringLen2(ctx, NULL, val1, 0); +} +void JS_FreeCString(JSContext *ctx, const char *ptr); + +JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id); +JSValue JS_NewObjectClass(JSContext *ctx, int class_id); +JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); +JSValue JS_NewObject(JSContext *ctx); + +JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val); +JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val); +JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val); + +JSValue JS_NewArray(JSContext *ctx); +int JS_IsArray(JSContext *ctx, JSValueConst val); + +JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst receiver, + JS_BOOL throw_ref_error); +static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop) +{ + return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0); +} +JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop); +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx); + +int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue val, + int flags); +static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue val) +{ + return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); +} +int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val); +int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, + int64_t idx, JSValue val); +int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val); +int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop); +int JS_IsExtensible(JSContext *ctx, JSValueConst obj); +int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); +int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags); +int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); +JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val); + +#define JS_GPN_STRING_MASK (1 << 0) +#define JS_GPN_SYMBOL_MASK (1 << 1) +#define JS_GPN_PRIVATE_MASK (1 << 2) +/* only include the enumerable properties */ +#define JS_GPN_ENUM_ONLY (1 << 4) +/* set theJSPropertyEnum.is_enumerable field */ +#define JS_GPN_SET_ENUM (1 << 5) + +int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValueConst obj, int flags); +int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop); + +JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv); +JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, + int argc, JSValueConst *argv); +JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, + int argc, JSValueConst *argv); +JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv); +JS_BOOL JS_DetectModule(const char *input, size_t input_len); +/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ +JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags); +JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); +JSValue JS_GetGlobalObject(JSContext *ctx); +int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); +int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, int flags); +int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue val, int flags); +int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, + uint32_t idx, JSValue val, int flags); +int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, + const char *prop, JSValue val, int flags); +int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, + JSAtom prop, JSValue getter, JSValue setter, + int flags); +void JS_SetOpaque(JSValue obj, void *opaque); +void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); +void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); + +/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ +JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename); +JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, + JSValueConst replacer, JSValueConst space0); + +typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); +JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, + JSFreeArrayBufferDataFunc *free_func, void *opaque, + JS_BOOL is_shared); +JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); +void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); +uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); +JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, + size_t *pbyte_offset, + size_t *pbyte_length, + size_t *pbytes_per_element); + +JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); + +/* is_handled = TRUE means that the rejection is handled */ +typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise, + JSValueConst reason, + JS_BOOL is_handled, void *opaque); +void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque); + +/* return != 0 if the JS code needs to be interrupted */ +typedef int JSInterruptHandler(JSRuntime *rt, void *opaque); +void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); +/* if can_block is TRUE, Atomics.wait() can be used */ +void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block); + +typedef struct JSModuleDef JSModuleDef; + +/* return the module specifier (allocated with js_malloc()) or NULL if + exception */ +typedef char *JSModuleNormalizeFunc(JSContext *ctx, + const char *module_base_name, + const char *module_name, void *opaque); +typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx, + const char *module_name, void *opaque); + +/* module_normalize = NULL is allowed and invokes the default module + filename normalizer */ +void JS_SetModuleLoaderFunc(JSRuntime *rt, + JSModuleNormalizeFunc *module_normalize, + JSModuleLoaderFunc *module_loader, void *opaque); +/* return the import.meta object of a module */ +JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); +JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); + +/* JS Job support */ + +typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv); +int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv); + +JS_BOOL JS_IsJobPending(JSRuntime *rt); +int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); + +/* Object Writer/Reader (currently only used to handle precompiled code) */ +#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ +uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags); +#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */ +JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags); +/* load the dependencies of the module 'obj'. Useful when JS_ReadObject() + returns a module. */ +int JS_ResolveModule(JSContext *ctx, JSValueConst obj); + +/* C function definition */ +typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ + JS_CFUNC_generic, + JS_CFUNC_generic_magic, + JS_CFUNC_constructor, + JS_CFUNC_constructor_magic, + JS_CFUNC_constructor_or_func, + JS_CFUNC_constructor_or_func_magic, + JS_CFUNC_f_f, + JS_CFUNC_f_f_f, + JS_CFUNC_getter, + JS_CFUNC_setter, + JS_CFUNC_getter_magic, + JS_CFUNC_setter_magic, + JS_CFUNC_iterator_next, +} JSCFunctionEnum; + +typedef union JSCFunctionType { + JSCFunction *generic; + JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); + JSCFunction *constructor; + JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic); + JSCFunction *constructor_or_func; + double (*f_f)(double); + double (*f_f_f)(double, double); + JSValue (*getter)(JSContext *ctx, JSValueConst this_val); + JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val); + JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic); + JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic); + JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int *pdone, int magic); +} JSCFunctionType; + +JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic); +JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, + int length, int magic, int data_len, + JSValueConst *data); + +static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, + int length) +{ + return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); +} + +static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic) +{ + return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); +} +void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst proto); + +/* C property definition */ + +typedef struct JSCFunctionListEntry { + const char *name; + uint8_t prop_flags; + uint8_t def_type; + int16_t magic; + union { + struct { + uint8_t length; /* XXX: should move outside union */ + uint8_t cproto; /* XXX: should move outside union */ + JSCFunctionType cfunc; + } func; + struct { + JSCFunctionType get; + JSCFunctionType set; + } getset; + struct { + const char *name; + int base; + } alias; + struct { + const struct JSCFunctionListEntry *tab; + int len; + } prop_list; + const char *str; + int32_t i32; + int64_t i64; + double f64; + } u; +} JSCFunctionListEntry; + +#define JS_DEF_CFUNC 0 +#define JS_DEF_CGETSET 1 +#define JS_DEF_CGETSET_MAGIC 2 +#define JS_DEF_PROP_STRING 3 +#define JS_DEF_PROP_INT32 4 +#define JS_DEF_PROP_INT64 5 +#define JS_DEF_PROP_DOUBLE 6 +#define JS_DEF_PROP_UNDEFINED 7 +#define JS_DEF_OBJECT 8 +#define JS_DEF_ALIAS 9 + +/* Note: c++ does not like nested designators */ +#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } +#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } } +#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } } +#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } } +#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } +#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } } +#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } } +#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } } +#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } } +#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } } +#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } } +#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } } +#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } } +#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } } + +void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, + const JSCFunctionListEntry *tab, + int len); + +/* C module definition */ + +typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m); + +JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, + JSModuleInitFunc *func); +/* can only be called before the module is instantiated */ +int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str); +int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len); +/* can only be called after the module is instantiated */ +int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, + JSValue val); +int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, + const JSCFunctionListEntry *tab, int len); + +#undef js_unlikely +#undef js_force_inline + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_H */ diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..789521d --- /dev/null +++ b/readme.txt @@ -0,0 +1 @@ +The main documentation is in doc/quickjs.pdf or doc/quickjs.html. diff --git a/release.sh b/release.sh new file mode 100755 index 0000000..7c59b87 --- /dev/null +++ b/release.sh @@ -0,0 +1,107 @@ +#!/bin/sh +# Release the QuickJS source code + +set -e + +version=`cat VERSION` + +if [ "$1" = "-h" ] ; then + echo "release.sh [all]" + echo "" + echo "all: build all the archives. Otherwise only build the quickjs source archive." + exit 1 +fi + +extras="no" +binary="no" +quickjs="no" + +if [ "$1" = "all" ] ; then + extras="yes" + binary="yes" + quickjs="yes" +elif [ "$1" = "binary" ] ; then + binary="yes" +else + quickjs="yes" +fi + +#################################################" +# extras + +if [ "$extras" = "yes" ] ; then + +d="quickjs-${version}" +name="quickjs-extras-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir $outdir/unicode $outdir/tests + +cp unicode/* $outdir/unicode +cp -a tests/bench-v8 $outdir/tests + +( cd /tmp && tar Jcvf /tmp/${name}.tar.xz ${d} ) + +fi + +#################################################" +# binary release + +if [ "$binary" = "yes" ] ; then + +d="quickjs-linux-x86_64-${version}" +name="quickjs-linux-x86_64-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir + +files="qjs run-test262" + +make -j4 $files + +strip $files +cp $files $outdir + +( cd /tmp/$d && rm -f ../${name}.zip && zip -r ../${name}.zip . ) + +fi + +#################################################" +# quickjs + +if [ "$quickjs" = "yes" ] ; then + +make build_doc + +d="quickjs-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir $outdir/doc $outdir/tests $outdir/examples + +cp Makefile VERSION TODO Changelog readme.txt release.sh unicode_download.sh \ + qjs.c qjsc.c qjscalc.js repl.js \ + quickjs.c quickjs.h quickjs-atom.h \ + quickjs-libc.c quickjs-libc.h quickjs-opcode.h \ + cutils.c cutils.h list.h \ + libregexp.c libregexp.h libregexp-opcode.h \ + libunicode.c libunicode.h libunicode-table.h \ + libbf.c libbf.h \ + jscompress.c unicode_gen.c unicode_gen_def.h \ + run-test262.c test262o.conf test262.conf \ + test262o_errors.txt test262_errors.txt \ + $outdir + +cp tests/*.js tests/*.patch tests/bjson.c $outdir/tests + +cp examples/*.js examples/*.c $outdir/examples + +cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \ + doc/jsbignum.texi doc/jsbignum.html doc/jsbignum.pdf \ + $outdir/doc + +( cd /tmp && tar Jcvf /tmp/${d}.tar.xz ${d} ) + +fi diff --git a/repl.js b/repl.js new file mode 100644 index 0000000..47c8636 --- /dev/null +++ b/repl.js @@ -0,0 +1,1544 @@ +/* + * QuickJS Read Eval Print Loop + * + * Copyright (c) 2017-2019 Fabrice Bellard + * Copyright (c) 2017-2019 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. + */ +"use strip"; + +import * as std from "std"; +import * as os from "os"; + +(function(g) { + /* add 'os' and 'std' bindings */ + g.os = os; + g.std = std; + + /* close global objects */ + var Object = g.Object; + var String = g.String; + var Array = g.Array; + var Date = g.Date; + var Math = g.Math; + var isFinite = g.isFinite; + var parseFloat = g.parseFloat; + + /* XXX: use preprocessor ? */ + var config_numcalc = (typeof os.open === "undefined"); + var has_jscalc = (typeof Fraction === "function"); + var has_bignum = (typeof BigFloat === "function"); + + var colors = { + none: "\x1b[0m", + black: "\x1b[30m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + magenta: "\x1b[35m", + cyan: "\x1b[36m", + white: "\x1b[37m", + gray: "\x1b[30;1m", + grey: "\x1b[30;1m", + bright_red: "\x1b[31;1m", + bright_green: "\x1b[32;1m", + bright_yellow: "\x1b[33;1m", + bright_blue: "\x1b[34;1m", + bright_magenta: "\x1b[35;1m", + bright_cyan: "\x1b[36;1m", + bright_white: "\x1b[37;1m", + }; + + var styles; + if (config_numcalc) { + styles = { + 'default': 'black', + 'comment': 'white', + 'string': 'green', + 'regex': 'cyan', + 'number': 'green', + 'keyword': 'blue', + 'function': 'gray', + 'type': 'bright_magenta', + 'identifier': 'yellow', + 'error': 'bright_red', + 'result': 'black', + 'error_msg': 'bright_red', + }; + } else { + styles = { + 'default': 'bright_green', + 'comment': 'white', + 'string': 'bright_cyan', + 'regex': 'cyan', + 'number': 'green', + 'keyword': 'bright_white', + 'function': 'bright_yellow', + 'type': 'bright_magenta', + 'identifier': 'bright_green', + 'error': 'red', + 'result': 'bright_white', + 'error_msg': 'bright_red', + }; + } + + var history = []; + var clip_board = ""; + var prec; + var expBits; + var log2_10; + + var pstate = ""; + var prompt = ""; + var plen = 0; + var ps1; + if (config_numcalc) + ps1 = "> "; + else + ps1 = "qjs > "; + var ps2 = " ... "; + var utf8 = true; + var show_time = false; + var show_colors = true; + var eval_time = 0; + + var mexpr = ""; + var level = 0; + var cmd = ""; + var cursor_pos = 0; + var last_cmd = ""; + var last_cursor_pos = 0; + var history_index; + var this_fun, last_fun; + var quote_flag = false; + + var utf8_state = 0; + var utf8_val = 0; + + var term_fd; + var term_read_buf; + var term_width; + /* current X position of the cursor in the terminal */ + var term_cursor_x = 0; + + function termInit() { + var tab; + term_fd = std.in.fileno(); + + /* get the terminal size */ + term_width = 80; + if (os.isatty(term_fd)) { + if (os.ttyGetWinSize) { + tab = os.ttyGetWinSize(term_fd); + if (tab) + term_width = tab[0]; + } + if (os.ttySetRaw) { + /* set the TTY to raw mode */ + os.ttySetRaw(term_fd); + } + } + + /* install a Ctrl-C signal handler */ + os.signal(os.SIGINT, sigint_handler); + + /* install a handler to read stdin */ + term_read_buf = new Uint8Array(64); + os.setReadHandler(term_fd, term_read_handler); + } + + function sigint_handler() { + /* send Ctrl-C to readline */ + handle_byte(3); + } + + function term_read_handler() { + var l, i; + l = os.read(term_fd, term_read_buf.buffer, 0, term_read_buf.length); + for(i = 0; i < l; i++) + handle_byte(term_read_buf[i]); + } + + function handle_byte(c) { + if (!utf8) { + handle_char(c); + } else if (utf8_state !== 0 && (c >= 0x80 && c < 0xc0)) { + utf8_val = (utf8_val << 6) | (c & 0x3F); + utf8_state--; + if (utf8_state === 0) { + handle_char(utf8_val); + } + } else if (c >= 0xc0 && c < 0xf8) { + utf8_state = 1 + (c >= 0xe0) + (c >= 0xf0); + utf8_val = c & ((1 << (6 - utf8_state)) - 1); + } else { + utf8_state = 0; + handle_char(c); + } + } + + function is_alpha(c) { + return typeof c === "string" && + ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); + } + + function is_digit(c) { + return typeof c === "string" && (c >= '0' && c <= '9'); + } + + function is_word(c) { + return typeof c === "string" && + (is_alpha(c) || is_digit(c) || c == '_' || c == '$'); + } + + function is_balanced(a, b) { + switch (a + b) { + case "()": + case "[]": + case "{}": + return true; + } + return false; + } + + function print_color_text(str, start, style_names) { + var i, j; + for (j = start; j < str.length;) { + var style = style_names[i = j]; + while (++j < str.length && style_names[j] == style) + continue; + std.puts(colors[styles[style] || 'default']); + std.puts(str.substring(i, j)); + std.puts(colors['none']); + } + } + + function print_csi(n, code) { + std.puts("\x1b[" + ((n != 1) ? n : "") + code); + } + + function move_cursor(delta) { + var i, l; + if (delta > 0) { + while (delta != 0) { + if (term_cursor_x == (term_width - 1)) { + std.puts("\n"); /* translated to CRLF */ + term_cursor_x = 0; + delta--; + } else { + l = Math.min(term_width - 1 - term_cursor_x, delta); + print_csi(l, "C"); /* right */ + delta -= l; + term_cursor_x += l; + } + } + } else { + delta = -delta; + while (delta != 0) { + if (term_cursor_x == 0) { + print_csi(1, "A"); /* up */ + print_csi(term_width - 1, "C"); /* right */ + delta--; + term_cursor_x = term_width - 1; + } else { + l = Math.min(delta, term_cursor_x); + print_csi(l, "D"); /* left */ + delta -= l; + term_cursor_x -= l; + } + } + } + } + + function update() { + var i; + + if (cmd != last_cmd) { + if (!show_colors && last_cmd.substring(0, last_cursor_pos) == cmd.substring(0, last_cursor_pos)) { + /* optimize common case */ + std.puts(cmd.substring(last_cursor_pos)); + } else { + /* goto the start of the line */ + move_cursor(-last_cursor_pos); + if (show_colors) { + var str = mexpr ? mexpr + '\n' + cmd : cmd; + var start = str.length - cmd.length; + var colorstate = colorize_js(str); + print_color_text(str, start, colorstate[2]); + } else { + std.puts(cmd); + } + } + /* Note: assuming no surrogate pairs */ + term_cursor_x = (term_cursor_x + cmd.length) % term_width; + if (term_cursor_x == 0) { + /* show the cursor on the next line */ + std.puts(" \x08"); + } + /* remove the trailing characters */ + std.puts("\x1b[J"); + last_cmd = cmd; + last_cursor_pos = cmd.length; + } + move_cursor(cursor_pos - last_cursor_pos); + last_cursor_pos = cursor_pos; + std.out.flush(); + } + + /* editing commands */ + function insert(str) { + if (str) { + cmd = cmd.substring(0, cursor_pos) + str + cmd.substring(cursor_pos); + cursor_pos += str.length; + } + } + + function quoted_insert() { + quote_flag = true; + } + + function abort() { + cmd = ""; + cursor_pos = 0; + return -2; + } + + function alert() { + } + + function beginning_of_line() { + cursor_pos = 0; + } + + function end_of_line() { + cursor_pos = cmd.length; + } + + function forward_char() { + if (cursor_pos < cmd.length) + cursor_pos++; + } + + function backward_char() { + if (cursor_pos > 0) + cursor_pos--; + } + + function skip_word_forward(pos) { + while (pos < cmd.length && !is_word(cmd.charAt(pos))) + pos++; + while (pos < cmd.length && is_word(cmd.charAt(pos))) + pos++; + return pos; + } + + function skip_word_backward(pos) { + while (pos > 0 && !is_word(cmd.charAt(pos - 1))) + pos--; + while (pos > 0 && is_word(cmd.charAt(pos - 1))) + pos--; + return pos; + } + + function forward_word() { + cursor_pos = skip_word_forward(cursor_pos); + } + + function backward_word() { + cursor_pos = skip_word_backward(cursor_pos); + } + + function accept_line() { + std.puts("\n"); + history_add(cmd); + return -1; + } + + function history_add(str) { + if (str) { + history.push(str); + } + history_index = history.length; + } + + function previous_history() { + if (history_index > 0) { + if (history_index == history.length) { + history.push(cmd); + } + history_index--; + cmd = history[history_index]; + cursor_pos = cmd.length; + } + } + + function next_history() { + if (history_index < history.length - 1) { + history_index++; + cmd = history[history_index]; + cursor_pos = cmd.length; + } + } + + function history_search(dir) { + var pos = cursor_pos; + for (var i = 1; i <= history.length; i++) { + var index = (history.length + i * dir + history_index) % history.length; + if (history[index].substring(0, pos) == cmd.substring(0, pos)) { + history_index = index; + cmd = history[index]; + return; + } + } + } + + function history_search_backward() { + return history_search(-1); + } + + function history_search_forward() { + return history_search(1); + } + + function delete_char_dir(dir) { + var start = cursor_pos - (dir < 0); + var end = start + 1; + if (start >= 0 && start < cmd.length) { + if (last_fun === kill_region) { + kill_region(start, end, dir); + } else { + cmd = cmd.substring(0, start) + cmd.substring(end); + cursor_pos = start; + } + } + } + + function delete_char() { + delete_char_dir(1); + } + + function control_d() { + if (cmd.length == 0) { + std.puts("\n"); + return -3; /* exit read eval print loop */ + } else { + delete_char_dir(1); + } + } + + function backward_delete_char() { + delete_char_dir(-1); + } + + function transpose_chars() { + var pos = cursor_pos; + if (cmd.length > 1 && pos > 0) { + if (pos == cmd.length) + pos--; + cmd = cmd.substring(0, pos - 1) + cmd.substring(pos, pos + 1) + + cmd.substring(pos - 1, pos) + cmd.substring(pos + 1); + cursor_pos = pos + 1; + } + } + + function transpose_words() { + var p1 = skip_word_backward(cursor_pos); + var p2 = skip_word_forward(p1); + var p4 = skip_word_forward(cursor_pos); + var p3 = skip_word_backward(p4); + + if (p1 < p2 && p2 <= cursor_pos && cursor_pos <= p3 && p3 < p4) { + cmd = cmd.substring(0, p1) + cmd.substring(p3, p4) + + cmd.substring(p2, p3) + cmd.substring(p1, p2); + cursor_pos = p4; + } + } + + function upcase_word() { + var end = skip_word_forward(cursor_pos); + cmd = cmd.substring(0, cursor_pos) + + cmd.substring(cursor_pos, end).toUpperCase() + + cmd.substring(end); + } + + function downcase_word() { + var end = skip_word_forward(cursor_pos); + cmd = cmd.substring(0, cursor_pos) + + cmd.substring(cursor_pos, end).toLowerCase() + + cmd.substring(end); + } + + function kill_region(start, end, dir) { + var s = cmd.substring(start, end); + if (last_fun !== kill_region) + clip_board = s; + else if (dir < 0) + clip_board = s + clip_board; + else + clip_board = clip_board + s; + + cmd = cmd.substring(0, start) + cmd.substring(end); + if (cursor_pos > end) + cursor_pos -= end - start; + else if (cursor_pos > start) + cursor_pos = start; + this_fun = kill_region; + } + + function kill_line() { + kill_region(cursor_pos, cmd.length, 1); + } + + function backward_kill_line() { + kill_region(0, cursor_pos, -1); + } + + function kill_word() { + kill_region(cursor_pos, skip_word_forward(cursor_pos), 1); + } + + function backward_kill_word() { + kill_region(skip_word_backward(cursor_pos), cursor_pos, -1); + } + + function yank() { + insert(clip_board); + } + + function control_c() { + if (last_fun === control_c) { + std.puts("\n"); + std.exit(0); + } else { + std.puts("\n(Press Ctrl-C again to quit)\n"); + readline_print_prompt(); + } + } + + function reset() { + cmd = ""; + cursor_pos = 0; + } + + function get_context_word(line, pos) { + var s = ""; + while (pos > 0 && is_word(line[pos - 1])) { + pos--; + s = line[pos] + s; + } + return s; + } + function get_context_object(line, pos) { + var obj, base, c; + if (pos <= 0 || " ~!%^&*(-+={[|:;,<>?/".indexOf(line[pos - 1]) >= 0) + return g; + if (pos >= 2 && line[pos - 1] === ".") { + pos--; + obj = {}; + switch (c = line[pos - 1]) { + case '\'': + case '\"': + return "a"; + case ']': + return []; + case '}': + return {}; + case '/': + return / /; + default: + if (is_word(c)) { + base = get_context_word(line, pos); + if (["true", "false", "null", "this"].includes(base) || !isNaN(+base)) + return eval(base); + obj = get_context_object(line, pos - base.length); + if (obj === null || obj === void 0) + return obj; + if (obj === g && obj[base] === void 0) + return eval(base); + else + return obj[base]; + } + return {}; + } + } + return void 0; + } + + function get_completions(line, pos) { + var s, obj, ctx_obj, r, i, j, paren; + + s = get_context_word(line, pos); + ctx_obj = get_context_object(line, pos - s.length); + r = []; + /* enumerate properties from object and its prototype chain, + add non-numeric regular properties with s as e prefix + */ + for (i = 0, obj = ctx_obj; i < 10 && obj !== null && obj !== void 0; i++) { + var props = Object.getOwnPropertyNames(obj); + /* add non-numeric regular properties */ + for (j = 0; j < props.length; j++) { + var prop = props[j]; + if (typeof prop == "string" && ""+(+prop) != prop && prop.startsWith(s)) + r.push(prop); + } + obj = Object.getPrototypeOf(obj); + } + if (r.length > 1) { + /* sort list with internal names last and remove duplicates */ + function symcmp(a, b) { + if (a[0] != b[0]) { + if (a[0] == '_') + return 1; + if (b[0] == '_') + return -1; + } + if (a < b) + return -1; + if (a > b) + return +1; + return 0; + } + r.sort(symcmp); + for(i = j = 1; i < r.length; i++) { + if (r[i] != r[i - 1]) + r[j++] = r[i]; + } + r.length = j; + } + /* 'tab' = list of completions, 'pos' = cursor position inside + the completions */ + return { tab: r, pos: s.length, ctx: ctx_obj }; + } + + function completion() { + var tab, res, s, i, j, len, t, max_width, col, n_cols, row, n_rows; + res = get_completions(cmd, cursor_pos); + tab = res.tab; + if (tab.length === 0) + return; + s = tab[0]; + len = s.length; + /* add the chars which are identical in all the completions */ + for(i = 1; i < tab.length; i++) { + t = tab[i]; + for(j = 0; j < len; j++) { + if (t[j] !== s[j]) { + len = j; + break; + } + } + } + for(i = res.pos; i < len; i++) { + insert(s[i]); + } + if (last_fun === completion && tab.length == 1) { + /* append parentheses to function names */ + var m = res.ctx[tab[0]]; + if (typeof m == "function") { + insert('('); + if (m.length == 0) + insert(')'); + } else if (typeof m == "object") { + insert('.'); + } + } + /* show the possible completions */ + if (last_fun === completion && tab.length >= 2) { + max_width = 0; + for(i = 0; i < tab.length; i++) + max_width = Math.max(max_width, tab[i].length); + max_width += 2; + n_cols = Math.max(1, Math.floor((term_width + 1) / max_width)); + n_rows = Math.ceil(tab.length / n_cols); + std.puts("\n"); + /* display the sorted list column-wise */ + for (row = 0; row < n_rows; row++) { + for (col = 0; col < n_cols; col++) { + i = col * n_rows + row; + if (i >= tab.length) + break; + s = tab[i]; + if (col != n_cols - 1) + s = s.padEnd(max_width); + std.puts(s); + } + std.puts("\n"); + } + /* show a new prompt */ + readline_print_prompt(); + } + } + + var commands = { /* command table */ + "\x01": beginning_of_line, /* ^A - bol */ + "\x02": backward_char, /* ^B - backward-char */ + "\x03": control_c, /* ^C - abort */ + "\x04": control_d, /* ^D - delete-char or exit */ + "\x05": end_of_line, /* ^E - eol */ + "\x06": forward_char, /* ^F - forward-char */ + "\x07": abort, /* ^G - bell */ + "\x08": backward_delete_char, /* ^H - backspace */ + "\x09": completion, /* ^I - history-search-backward */ + "\x0a": accept_line, /* ^J - newline */ + "\x0b": kill_line, /* ^K - delete to end of line */ + "\x0d": accept_line, /* ^M - enter */ + "\x0e": next_history, /* ^N - down */ + "\x10": previous_history, /* ^P - up */ + "\x11": quoted_insert, /* ^Q - quoted-insert */ + "\x12": alert, /* ^R - reverse-search */ + "\x13": alert, /* ^S - search */ + "\x14": transpose_chars, /* ^T - transpose */ + "\x18": reset, /* ^X - cancel */ + "\x19": yank, /* ^Y - yank */ + "\x1bOA": previous_history, /* ^[OA - up */ + "\x1bOB": next_history, /* ^[OB - down */ + "\x1bOC": forward_char, /* ^[OC - right */ + "\x1bOD": backward_char, /* ^[OD - left */ + "\x1bOF": forward_word, /* ^[OF - ctrl-right */ + "\x1bOH": backward_word, /* ^[OH - ctrl-left */ + "\x1b[1;5C": forward_word, /* ^[[1;5C - ctrl-right */ + "\x1b[1;5D": backward_word, /* ^[[1;5D - ctrl-left */ + "\x1b[1~": beginning_of_line, /* ^[[1~ - bol */ + "\x1b[3~": delete_char, /* ^[[3~ - delete */ + "\x1b[4~": end_of_line, /* ^[[4~ - eol */ + "\x1b[5~": history_search_backward,/* ^[[5~ - page up */ + "\x1b[6~": history_search_forward, /* ^[[5~ - page down */ + "\x1b[A": previous_history, /* ^[[A - up */ + "\x1b[B": next_history, /* ^[[B - down */ + "\x1b[C": forward_char, /* ^[[C - right */ + "\x1b[D": backward_char, /* ^[[D - left */ + "\x1b[F": end_of_line, /* ^[[F - end */ + "\x1b[H": beginning_of_line, /* ^[[H - home */ + "\x1b\x7f": backward_kill_word, /* M-C-? - backward_kill_word */ + "\x1bb": backward_word, /* M-b - backward_word */ + "\x1bd": kill_word, /* M-d - kill_word */ + "\x1bf": forward_word, /* M-f - backward_word */ + "\x1bk": backward_kill_line, /* M-k - backward_kill_line */ + "\x1bl": downcase_word, /* M-l - downcase_word */ + "\x1bt": transpose_words, /* M-t - transpose_words */ + "\x1bu": upcase_word, /* M-u - upcase_word */ + "\x7f": backward_delete_char, /* ^? - delete */ + }; + + function dupstr(str, count) { + var res = ""; + while (count-- > 0) + res += str; + return res; + } + + var readline_keys; + var readline_state; + var readline_cb; + + function readline_print_prompt() + { + std.puts(prompt); + term_cursor_x = prompt.length % term_width; + last_cmd = ""; + last_cursor_pos = 0; + } + + function readline_start(defstr, cb) { + cmd = defstr || ""; + cursor_pos = cmd.length; + history_index = history.length; + readline_cb = cb; + + prompt = pstate; + + if (mexpr) { + prompt += dupstr(" ", plen - prompt.length); + prompt += ps2; + } else { + if (show_time) { + var t = Math.round(eval_time) + " "; + eval_time = 0; + t = dupstr("0", 5 - t.length) + t; + prompt += t.substring(0, t.length - 4) + "." + t.substring(t.length - 4); + } + plen = prompt.length; + prompt += ps1; + } + readline_print_prompt(); + update(); + readline_state = 0; + } + + function handle_char(c1) { + var c; + c = String.fromCharCode(c1); + switch(readline_state) { + case 0: + if (c == '\x1b') { /* '^[' - ESC */ + readline_keys = c; + readline_state = 1; + } else { + handle_key(c); + } + break; + case 1: /* '^[ */ + readline_keys += c; + if (c == '[') { + readline_state = 2; + } else if (c == 'O') { + readline_state = 3; + } else { + handle_key(readline_keys); + readline_state = 0; + } + break; + case 2: /* '^[[' - CSI */ + readline_keys += c; + if (!(c == ';' || (c >= '0' && c <= '9'))) { + handle_key(readline_keys); + readline_state = 0; + } + break; + case 3: /* '^[O' - ESC2 */ + readline_keys += c; + handle_key(readline_keys); + readline_state = 0; + break; + } + } + + function handle_key(keys) { + var fun; + + if (quote_flag) { + if (keys.length === 1) + insert(keys); + quote_flag = false; + } else if (fun = commands[keys]) { + this_fun = fun; + switch (fun(keys)) { + case -1: + readline_cb(cmd); + return; + case -2: + readline_cb(null); + return; + case -3: + /* uninstall a Ctrl-C signal handler */ + os.signal(os.SIGINT, null); + /* uninstall the stdin read handler */ + os.setReadHandler(term_fd, null); + return; + } + last_fun = this_fun; + } else if (keys.length === 1 && keys >= ' ') { + insert(keys); + last_fun = insert; + } else { + alert(); /* beep! */ + } + + cursor_pos = (cursor_pos < 0) ? 0 : + (cursor_pos > cmd.length) ? cmd.length : cursor_pos; + update(); + } + + var hex_mode = false; + var eval_mode = "std"; + + function bignum_typeof(a) { + "use bigint"; + return typeof a; + } + + function eval_mode_typeof(a) { + if (eval_mode === "std") + return typeof a; + else + return bignum_typeof(a); + } + + function number_to_string(a, radix) { + var s; + if (!isFinite(a)) { + /* NaN, Infinite */ + return a.toString(); + } else { + if (a == 0) { + if (1 / a < 0) + s = "-0"; + else + s = "0"; + } else { + if (radix == 16 && a === Math.floor(a)) { + var s; + if (a < 0) { + a = -a; + s = "-"; + } else { + s = ""; + } + s += "0x" + a.toString(16); + } else { + s = a.toString(); + } + } + if (eval_mode !== "std" && s.indexOf(".") < 0 && + ((radix == 16 && s.indexOf("p") < 0) || + (radix == 10 && s.indexOf("e") < 0))) { + /* add a decimal point so that the floating point type + is visible */ + s += ".0"; + } + return s; + } + } + + function bigfloat_to_string(a, radix) { + var s; + if (!BigFloat.isFinite(a)) { + /* NaN, Infinite */ + if (eval_mode !== "math") { + return "BigFloat(" + a.toString() + ")"; + } else { + return a.toString(); + } + } else { + if (a == 0) { + if (1 / a < 0) + s = "-0"; + else + s = "0"; + } else { + if (radix == 16) { + var s; + if (a < 0) { + a = -a; + s = "-"; + } else { + s = ""; + } + s += "0x" + a.toString(16); + } else { + s = a.toString(); + } + } + if (typeof a === "bigfloat" && eval_mode !== "math") { + s += "l"; + } else if (eval_mode !== "std" && s.indexOf(".") < 0 && + ((radix == 16 && s.indexOf("p") < 0) || + (radix == 10 && s.indexOf("e") < 0))) { + /* add a decimal point so that the floating point type + is visible */ + s += ".0"; + } + return s; + } + } + + function bigint_to_string(a, radix) { + var s; + if (radix == 16) { + var s; + if (a < 0) { + a = -a; + s = "-"; + } else { + s = ""; + } + s += "0x" + a.toString(16); + } else { + s = a.toString(); + } + if (eval_mode === "std") + s += "n"; + return s; + } + + function print(a) { + var stack = []; + + function print_rec(a) { + var n, i, keys, key, type, s; + + type = eval_mode_typeof(a); + if (type === "object") { + if (a === null) { + std.puts(a); + } else if (stack.indexOf(a) >= 0) { + std.puts("[circular]"); + } else if (has_jscalc && (a instanceof Fraction || + a instanceof Complex || + a instanceof Mod || + a instanceof Polynomial || + a instanceof PolyMod || + a instanceof RationalFunction || + a instanceof Series)) { + std.puts(a.toString()); + } else { + stack.push(a); + if (Array.isArray(a)) { + n = a.length; + std.puts("[ "); + for(i = 0; i < n; i++) { + if (i !== 0) + std.puts(", "); + if (i in a) { + print_rec(a[i]); + } else { + std.puts(""); + } + if (i > 20) { + std.puts("..."); + break; + } + } + std.puts(" ]"); + } else if (Object.__getClass(a) === "RegExp") { + std.puts(a.toString()); + } else { + keys = Object.keys(a); + n = keys.length; + std.puts("{ "); + for(i = 0; i < n; i++) { + if (i !== 0) + std.puts(", "); + key = keys[i]; + std.puts(key, ": "); + print_rec(a[key]); + } + std.puts(" }"); + } + stack.pop(a); + } + } else if (type === "string") { + s = a.__quote(); + if (s.length > 79) + s = s.substring(0, 75) + "...\""; + std.puts(s); + } else if (type === "number") { + std.puts(number_to_string(a, hex_mode ? 16 : 10)); + } else if (type === "bigint") { + std.puts(bigint_to_string(a, hex_mode ? 16 : 10)); + } else if (type === "bigfloat") { + std.puts(bigfloat_to_string(a, hex_mode ? 16 : 10)); + } else if (type === "bigdecimal") { + std.puts(a.toString() + "d"); + } else if (type === "symbol") { + std.puts(String(a)); + } else if (type === "function") { + std.puts("function " + a.name + "()"); + } else { + std.puts(a); + } + } + print_rec(a); + } + + function extract_directive(a) { + var pos; + if (a[0] !== '\\') + return ""; + for (pos = 1; pos < a.length; pos++) { + if (!is_alpha(a[pos])) + break; + } + return a.substring(1, pos); + } + + /* return true if the string after cmd can be evaluted as JS */ + function handle_directive(cmd, expr) { + var param, prec1, expBits1; + + if (cmd === "h" || cmd === "?" || cmd == "help") { + help(); + } else if (cmd === "load") { + var filename = expr.substring(cmd.length + 1).trim(); + if (filename.lastIndexOf(".") <= filename.lastIndexOf("/")) + filename += ".js"; + std.loadScript(filename); + return false; + } else if (cmd === "x") { + hex_mode = true; + } else if (cmd === "d") { + hex_mode = false; + } else if (cmd === "t") { + show_time = !show_time; + } else if (has_bignum && cmd === "p") { + param = expr.substring(cmd.length + 1).trim().split(" "); + if (param.length === 1 && param[0] === "") { + std.puts("BigFloat precision=" + prec + " bits (~" + + Math.floor(prec / log2_10) + + " digits), exponent size=" + expBits + " bits\n"); + } else if (param[0] === "f16") { + prec = 11; + expBits = 5; + } else if (param[0] === "f32") { + prec = 24; + expBits = 8; + } else if (param[0] === "f64") { + prec = 53; + expBits = 11; + } else if (param[0] === "f128") { + prec = 113; + expBits = 15; + } else { + prec1 = parseInt(param[0]); + if (param.length >= 2) + expBits1 = parseInt(param[1]); + else + expBits1 = BigFloatEnv.expBitsMax; + if (Number.isNaN(prec1) || + prec1 < BigFloatEnv.precMin || + prec1 > BigFloatEnv.precMax) { + std.puts("Invalid precision\n"); + return false; + } + if (Number.isNaN(expBits1) || + expBits1 < BigFloatEnv.expBitsMin || + expBits1 > BigFloatEnv.expBitsMax) { + std.puts("Invalid exponent bits\n"); + return false; + } + prec = prec1; + expBits = expBits1; + } + return false; + } else if (has_bignum && cmd === "digits") { + param = expr.substring(cmd.length + 1).trim(); + prec1 = Math.ceil(parseFloat(param) * log2_10); + if (prec1 < BigFloatEnv.precMin || + prec1 > BigFloatEnv.precMax) { + std.puts("Invalid precision\n"); + return false; + } + prec = prec1; + expBits = BigFloatEnv.expBitsMax; + return false; + } else if (has_bignum && cmd === "mode") { + param = expr.substring(cmd.length + 1).trim(); + if (param === "") { + std.puts("Running mode=" + eval_mode + "\n"); + } else if (param === "std" || param === "math" || + param === "bigint") { + eval_mode = param; + } else { + std.puts("Invalid mode\n"); + } + return false; + } else if (cmd === "clear") { + std.puts("\x1b[H\x1b[J"); + } else if (cmd === "q") { + std.exit(0); + } else if (has_jscalc && cmd === "a") { + algebraicMode = true; + } else if (has_jscalc && cmd === "n") { + algebraicMode = false; + } else { + std.puts("Unknown directive: " + cmd + "\n"); + return false; + } + return true; + } + + if (config_numcalc) { + /* called by the GUI */ + g.execCmd = function (cmd) { + switch(cmd) { + case "dec": + hex_mode = false; + break; + case "hex": + hex_mode = true; + break; + case "num": + algebraicMode = false; + break; + case "alg": + algebraicMode = true; + break; + } + } + } + + function help() { + function sel(n) { + return n ? "*": " "; + } + std.puts("\\h this help\n" + + "\\x " + sel(hex_mode) + "hexadecimal number display\n" + + "\\d " + sel(!hex_mode) + "decimal number display\n" + + "\\t " + sel(show_time) + "toggle timing display\n" + + "\\clear clear the terminal\n"); + if (has_jscalc) { + std.puts("\\a " + sel(algebraicMode) + "algebraic mode\n" + + "\\n " + sel(!algebraicMode) + "numeric mode\n"); + } + if (has_bignum) { + std.puts("\\p [m [e]] set the BigFloat precision to 'm' bits\n" + + "\\digits n set the BigFloat precision to 'ceil(n*log2(10))' bits\n"); + if (!has_jscalc) { + std.puts("\\mode [std|bigint|math] change the running mode (current = " + eval_mode + ")\n"); + } + } + if (!config_numcalc) { + std.puts("\\q exit\n"); + } + } + + function eval_and_print(expr) { + var result; + + try { + if (eval_mode === "math") + expr = '"use math"; void 0;' + expr; + else if (eval_mode === "bigint") + expr = '"use bigint"; void 0;' + expr; + var now = (new Date).getTime(); + /* eval as a script */ + result = std.evalScript(expr, { backtrace_barrier: true }); + eval_time = (new Date).getTime() - now; + std.puts(colors[styles.result]); + print(result); + std.puts("\n"); + std.puts(colors.none); + /* set the last result */ + g._ = result; + } catch (error) { + std.puts(colors[styles.error_msg]); + if (error instanceof Error) { + console.log(error); + if (error.stack) { + std.puts(error.stack); + } + } else { + std.puts("Throw: "); + console.log(error); + } + std.puts(colors.none); + } + } + + function cmd_start() { + if (!config_numcalc) { + if (has_jscalc) + std.puts('QJSCalc - Type "\\h" for help\n'); + else + std.puts('QuickJS - Type "\\h" for help\n'); + } + if (has_bignum) { + log2_10 = Math.log(10) / Math.log(2); + prec = 113; + expBits = 15; + if (has_jscalc) { + eval_mode = "math"; + /* XXX: numeric mode should always be the default ? */ + g.algebraicMode = config_numcalc; + } + } + + cmd_readline_start(); + } + + function cmd_readline_start() { + readline_start(dupstr(" ", level), readline_handle_cmd); + } + + function readline_handle_cmd(expr) { + handle_cmd(expr); + cmd_readline_start(); + } + + function handle_cmd(expr) { + var colorstate, cmd; + + if (expr === null) { + expr = ""; + return; + } + if (expr === "?") { + help(); + return; + } + cmd = extract_directive(expr); + if (cmd.length > 0) { + if (!handle_directive(cmd, expr)) + return; + expr = expr.substring(cmd.length + 1); + } + if (expr === "") + return; + + if (mexpr) + expr = mexpr + '\n' + expr; + colorstate = colorize_js(expr); + pstate = colorstate[0]; + level = colorstate[1]; + if (pstate) { + mexpr = expr; + return; + } + mexpr = ""; + + if (has_bignum) { + BigFloatEnv.setPrec(eval_and_print.bind(null, expr), + prec, expBits); + } else { + eval_and_print(expr); + } + level = 0; + + /* run the garbage collector after each command */ + std.gc(); + } + + function colorize_js(str) { + var i, c, start, n = str.length; + var style, state = "", level = 0; + var primary, can_regex = 1; + var r = []; + + function push_state(c) { state += c; } + function last_state(c) { return state.substring(state.length - 1); } + function pop_state(c) { + var c = last_state(); + state = state.substring(0, state.length - 1); + return c; + } + + function parse_block_comment() { + style = 'comment'; + push_state('/'); + for (i++; i < n - 1; i++) { + if (str[i] == '*' && str[i + 1] == '/') { + i += 2; + pop_state('/'); + break; + } + } + } + + function parse_line_comment() { + style = 'comment'; + for (i++; i < n; i++) { + if (str[i] == '\n') { + break; + } + } + } + + function parse_string(delim) { + style = 'string'; + push_state(delim); + while (i < n) { + c = str[i++]; + if (c == '\n') { + style = 'error'; + continue; + } + if (c == '\\') { + if (i >= n) + break; + i++; + } else + if (c == delim) { + pop_state(); + break; + } + } + } + + function parse_regex() { + style = 'regex'; + push_state('/'); + while (i < n) { + c = str[i++]; + if (c == '\n') { + style = 'error'; + continue; + } + if (c == '\\') { + if (i < n) { + i++; + } + continue; + } + if (last_state() == '[') { + if (c == ']') { + pop_state() + } + // ECMA 5: ignore '/' inside char classes + continue; + } + if (c == '[') { + push_state('['); + if (str[i] == '[' || str[i] == ']') + i++; + continue; + } + if (c == '/') { + pop_state(); + while (i < n && is_word(str[i])) + i++; + break; + } + } + } + + function parse_number() { + style = 'number'; + while (i < n && (is_word(str[i]) || (str[i] == '.' && (i == n - 1 || str[i + 1] != '.')))) { + i++; + } + } + + var js_keywords = "|" + + "break|case|catch|continue|debugger|default|delete|do|" + + "else|finally|for|function|if|in|instanceof|new|" + + "return|switch|this|throw|try|typeof|while|with|" + + "class|const|enum|import|export|extends|super|" + + "implements|interface|let|package|private|protected|" + + "public|static|yield|" + + "undefined|null|true|false|Infinity|NaN|" + + "eval|arguments|" + + "await|"; + + var js_no_regex = "|this|super|undefined|null|true|false|Infinity|NaN|arguments|"; + var js_types = "|void|var|"; + + function parse_identifier() { + can_regex = 1; + + while (i < n && is_word(str[i])) + i++; + + var w = '|' + str.substring(start, i) + '|'; + + if (js_keywords.indexOf(w) >= 0) { + style = 'keyword'; + if (js_no_regex.indexOf(w) >= 0) + can_regex = 0; + return; + } + + var i1 = i; + while (i1 < n && str[i1] == ' ') + i1++; + + if (i1 < n && str[i1] == '(') { + style = 'function'; + return; + } + + if (js_types.indexOf(w) >= 0) { + style = 'type'; + return; + } + + style = 'identifier'; + can_regex = 0; + } + + function set_style(from, to) { + while (r.length < from) + r.push('default'); + while (r.length < to) + r.push(style); + } + + for (i = 0; i < n;) { + style = null; + start = i; + switch (c = str[i++]) { + case ' ': + case '\t': + case '\r': + case '\n': + continue; + case '+': + case '-': + if (i < n && str[i] == c) { + i++; + continue; + } + can_regex = 1; + continue; + case '/': + if (i < n && str[i] == '*') { // block comment + parse_block_comment(); + break; + } + if (i < n && str[i] == '/') { // line comment + parse_line_comment(); + break; + } + if (can_regex) { + parse_regex(); + can_regex = 0; + break; + } + can_regex = 1; + continue; + case '\'': + case '\"': + case '`': + parse_string(c); + can_regex = 0; + break; + case '(': + case '[': + case '{': + can_regex = 1; + level++; + push_state(c); + continue; + case ')': + case ']': + case '}': + can_regex = 0; + if (level > 0 && is_balanced(last_state(), c)) { + level--; + pop_state(); + continue; + } + style = 'error'; + break; + default: + if (is_digit(c)) { + parse_number(); + can_regex = 0; + break; + } + if (is_word(c) || c == '$') { + parse_identifier(); + break; + } + can_regex = 1; + continue; + } + if (style) + set_style(start, i); + } + set_style(n, n); + return [ state, level, r ]; + } + + termInit(); + + cmd_start(); + +})(globalThis); diff --git a/run-test262.c b/run-test262.c new file mode 100644 index 0000000..6265200 --- /dev/null +++ b/run-test262.c @@ -0,0 +1,2098 @@ +/* + * ECMA Test 262 Runner for QuickJS + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "list.h" +#include "quickjs-libc.h" + +/* enable test262 thread support to test SharedArrayBuffer and Atomics */ +#define CONFIG_AGENT +/* cross-realm tests (not supported yet) */ +//#define CONFIG_REALM + +#define CMD_NAME "run-test262" + +typedef struct namelist_t { + char **array; + int count; + int size; + unsigned int sorted : 1; +} namelist_t; + +namelist_t test_list; +namelist_t exclude_list; +namelist_t exclude_dir_list; + +FILE *outfile; +enum test_mode_t { + TEST_DEFAULT_NOSTRICT, /* run tests as nostrict unless test is flagged as strictonly */ + TEST_DEFAULT_STRICT, /* run tests as strict unless test is flagged as nostrict */ + TEST_NOSTRICT, /* run tests as nostrict, skip strictonly tests */ + TEST_STRICT, /* run tests as strict, skip nostrict tests */ + TEST_ALL, /* run tests in both strict and nostrict, unless restricted by spec */ +} test_mode = TEST_DEFAULT_NOSTRICT; +int skip_async; +int skip_module; +int new_style; +int dump_memory; +int stats_count; +JSMemoryUsage stats_all, stats_avg, stats_min, stats_max; +char *stats_min_filename; +char *stats_max_filename; +int verbose; +char *harness_dir; +char *harness_exclude; +char *harness_features; +char *harness_skip_features; +char *error_filename; +char *error_file; +FILE *error_out; +char *report_filename; +int update_errors; +int test_count, test_failed, test_index, test_skipped, test_excluded; +int new_errors, changed_errors, fixed_errors; +int async_done; + +void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); +void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); + +void warning(const char *fmt, ...) +{ + va_list ap; + + fflush(stdout); + fprintf(stderr, "%s: ", CMD_NAME); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +void fatal(int errcode, const char *fmt, ...) +{ + va_list ap; + + fflush(stdout); + fprintf(stderr, "%s: ", CMD_NAME); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + + exit(errcode); +} + +void perror_exit(int errcode, const char *s) +{ + fflush(stdout); + fprintf(stderr, "%s: ", CMD_NAME); + perror(s); + exit(errcode); +} + +char *strdup_len(const char *str, int len) +{ + char *p = malloc(len + 1); + memcpy(p, str, len); + p[len] = '\0'; + return p; +} + +static inline int str_equal(const char *a, const char *b) { + return !strcmp(a, b); +} + +char *str_append(char **pp, const char *sep, const char *str) { + char *res, *p; + size_t len = 0; + p = *pp; + if (p) { + len = strlen(p) + strlen(sep); + } + res = malloc(len + strlen(str) + 1); + if (p) { + strcpy(res, p); + strcat(res, sep); + } + strcpy(res + len, str); + free(p); + return *pp = res; +} + +char *str_strip(char *p) +{ + size_t len = strlen(p); + while (len > 0 && isspace((unsigned char)p[len - 1])) + p[--len] = '\0'; + while (isspace((unsigned char)*p)) + p++; + return p; +} + +int has_prefix(const char *str, const char *prefix) +{ + return !strncmp(str, prefix, strlen(prefix)); +} + +char *skip_prefix(const char *str, const char *prefix) +{ + int i; + for (i = 0;; i++) { + if (prefix[i] == '\0') { /* skip the prefix */ + str += i; + break; + } + if (str[i] != prefix[i]) + break; + } + return (char *)str; +} + +char *get_basename(const char *filename) +{ + char *p; + + p = strrchr(filename, '/'); + if (!p) + return NULL; + return strdup_len(filename, p - filename); +} + +char *compose_path(const char *path, const char *name) +{ + int path_len, name_len; + char *d, *q; + + if (!path || path[0] == '\0' || *name == '/') { + d = strdup(name); + } else { + path_len = strlen(path); + name_len = strlen(name); + d = malloc(path_len + 1 + name_len + 1); + if (d) { + q = d; + memcpy(q, path, path_len); + q += path_len; + if (path[path_len - 1] != '/') + *q++ = '/'; + memcpy(q, name, name_len + 1); + } + } + return d; +} + +int namelist_cmp(const char *a, const char *b) +{ + /* compare strings in modified lexicographical order */ + for (;;) { + int ca = (unsigned char)*a++; + int cb = (unsigned char)*b++; + if (isdigit(ca) && isdigit(cb)) { + int na = ca - '0'; + int nb = cb - '0'; + while (isdigit(ca = (unsigned char)*a++)) + na = na * 10 + ca - '0'; + while (isdigit(cb = (unsigned char)*b++)) + nb = nb * 10 + cb - '0'; + if (na < nb) + return -1; + if (na > nb) + return +1; + } + if (ca < cb) + return -1; + if (ca > cb) + return +1; + if (ca == '\0') + return 0; + } +} + +int namelist_cmp_indirect(const void *a, const void *b) +{ + return namelist_cmp(*(const char **)a, *(const char **)b); +} + +void namelist_sort(namelist_t *lp) +{ + int i, count; + if (lp->count > 1) { + qsort(lp->array, lp->count, sizeof(*lp->array), namelist_cmp_indirect); + /* remove duplicates */ + for (count = i = 1; i < lp->count; i++) { + if (namelist_cmp(lp->array[count - 1], lp->array[i]) == 0) { + free(lp->array[i]); + } else { + lp->array[count++] = lp->array[i]; + } + } + lp->count = count; + } + lp->sorted = 1; +} + +int namelist_find(namelist_t *lp, const char *name) +{ + int a, b, m, cmp; + + if (!lp->sorted) { + namelist_sort(lp); + } + for (a = 0, b = lp->count; a < b;) { + m = a + (b - a) / 2; + cmp = namelist_cmp(lp->array[m], name); + if (cmp < 0) + a = m + 1; + else if (cmp > 0) + b = m; + else + return m; + } + return -1; +} + +void namelist_add(namelist_t *lp, const char *base, const char *name) +{ + char *s; + + s = compose_path(base, name); + if (!s) + goto fail; + if (lp->count == lp->size) { + size_t newsize = lp->size + (lp->size >> 1) + 4; + char **a = realloc(lp->array, sizeof(lp->array[0]) * newsize); + if (!a) + goto fail; + lp->array = a; + lp->size = newsize; + } + lp->array[lp->count] = s; + lp->count++; + return; +fail: + fatal(1, "allocation failure\n"); +} + +void namelist_load(namelist_t *lp, const char *filename) +{ + char buf[1024]; + char *base_name; + FILE *f; + + f = fopen(filename, "rb"); + if (!f) { + perror_exit(1, filename); + } + base_name = get_basename(filename); + + while (fgets(buf, sizeof(buf), f) != NULL) { + char *p = str_strip(buf); + if (*p == '#' || *p == ';' || *p == '\0') + continue; /* line comment */ + + namelist_add(lp, base_name, p); + } + free(base_name); + fclose(f); +} + +void namelist_add_from_error_file(namelist_t *lp, const char *file) +{ + const char *p, *p0; + char *pp; + + for (p = file; (p = strstr(p, ".js:")) != NULL; p++) { + for (p0 = p; p0 > file && p0[-1] != '\n'; p0--) + continue; + pp = strdup_len(p0, p + 3 - p0); + namelist_add(lp, NULL, pp); + free(pp); + } +} + +void namelist_free(namelist_t *lp) +{ + while (lp->count > 0) { + free(lp->array[--lp->count]); + } + free(lp->array); + lp->array = NULL; + lp->size = 0; +} + +static int add_test_file(const char *filename, const struct stat *ptr, int flag) +{ + namelist_t *lp = &test_list; + if (has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js")) + namelist_add(lp, NULL, filename); + return 0; +} + +/* find js files from the directory tree and sort the list */ +static void enumerate_tests(const char *path) +{ + namelist_t *lp = &test_list; + int start = lp->count; + ftw(path, add_test_file, 100); + qsort(lp->array + start, lp->count - start, sizeof(*lp->array), + namelist_cmp_indirect); +} + +static JSValue js_print(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int i; + const char *str; + + if (outfile) { + for (i = 0; i < argc; i++) { + if (i != 0) + fputc(' ', outfile); + str = JS_ToCString(ctx, argv[i]); + if (!str) + return JS_EXCEPTION; + if (!strcmp(str, "Test262:AsyncTestComplete")) + async_done++; + fputs(str, outfile); + JS_FreeCString(ctx, str); + } + fputc('\n', outfile); + } + return JS_UNDEFINED; +} + +static JSValue js_detachArrayBuffer(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JS_DetachArrayBuffer(ctx, argv[0]); + return JS_UNDEFINED; +} + +static JSValue js_evalScript(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *str; + size_t len; + JSValue ret; + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + ret = JS_Eval(ctx, str, len, "", JS_EVAL_TYPE_GLOBAL); + JS_FreeCString(ctx, str); + return ret; +} + +#ifdef CONFIG_AGENT + +#include + +typedef struct { + struct list_head link; + pthread_t tid; + char *script; + JSValue broadcast_func; + BOOL broadcast_pending; + JSValue broadcast_sab; /* in the main context */ + uint8_t *broadcast_sab_buf; + size_t broadcast_sab_size; + int32_t broadcast_val; +} Test262Agent; + +typedef struct { + struct list_head link; + char *str; +} AgentReport; + +static JSValue add_helpers1(JSContext *ctx); +static void add_helpers(JSContext *ctx); + +static pthread_mutex_t agent_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t agent_cond = PTHREAD_COND_INITIALIZER; +/* list of Test262Agent.link */ +static struct list_head agent_list = LIST_HEAD_INIT(agent_list); + +static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER; +/* list of AgentReport.link */ +static struct list_head report_list = LIST_HEAD_INIT(report_list); + +static void *agent_start(void *arg) +{ + Test262Agent *agent = arg; + JSRuntime *rt; + JSContext *ctx; + JSValue ret_val; + int ret; + + rt = JS_NewRuntime(); + if (rt == NULL) { + fatal(1, "JS_NewRuntime failure"); + } + ctx = JS_NewContext(rt); + if (ctx == NULL) { + JS_FreeRuntime(rt); + fatal(1, "JS_NewContext failure"); + } + JS_SetContextOpaque(ctx, agent); + JS_SetRuntimeInfo(rt, "agent"); + JS_SetCanBlock(rt, TRUE); + + add_helpers(ctx); + ret_val = JS_Eval(ctx, agent->script, strlen(agent->script), + "", JS_EVAL_TYPE_GLOBAL); + free(agent->script); + agent->script = NULL; + if (JS_IsException(ret_val)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, ret_val); + + for(;;) { + JSContext *ctx1; + ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (ret < 0) { + js_std_dump_error(ctx); + break; + } else if (ret == 0) { + if (JS_IsUndefined(agent->broadcast_func)) { + break; + } else { + JSValue args[2]; + + pthread_mutex_lock(&agent_mutex); + while (!agent->broadcast_pending) { + pthread_cond_wait(&agent_cond, &agent_mutex); + } + + agent->broadcast_pending = FALSE; + pthread_cond_signal(&agent_cond); + + pthread_mutex_unlock(&agent_mutex); + + args[0] = JS_NewArrayBuffer(ctx, agent->broadcast_sab_buf, + agent->broadcast_sab_size, + NULL, NULL, TRUE); + args[1] = JS_NewInt32(ctx, agent->broadcast_val); + ret_val = JS_Call(ctx, agent->broadcast_func, JS_UNDEFINED, + 2, (JSValueConst *)args); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + if (JS_IsException(ret_val)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, ret_val); + JS_FreeValue(ctx, agent->broadcast_func); + agent->broadcast_func = JS_UNDEFINED; + } + } + } + JS_FreeValue(ctx, agent->broadcast_func); + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return NULL; +} + +static JSValue js_agent_start(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *script; + Test262Agent *agent; + + if (JS_GetContextOpaque(ctx) != NULL) + return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); + + script = JS_ToCString(ctx, argv[0]); + if (!script) + return JS_EXCEPTION; + agent = malloc(sizeof(*agent)); + memset(agent, 0, sizeof(*agent)); + agent->broadcast_func = JS_UNDEFINED; + agent->broadcast_sab = JS_UNDEFINED; + agent->script = strdup(script); + JS_FreeCString(ctx, script); + list_add_tail(&agent->link, &agent_list); + pthread_create(&agent->tid, NULL, agent_start, agent); + return JS_UNDEFINED; +} + +static void js_agent_free(JSContext *ctx) +{ + struct list_head *el, *el1; + Test262Agent *agent; + + list_for_each_safe(el, el1, &agent_list) { + agent = list_entry(el, Test262Agent, link); + pthread_join(agent->tid, NULL); + JS_FreeValue(ctx, agent->broadcast_sab); + list_del(&agent->link); + free(agent); + } +} + +static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + Test262Agent *agent = JS_GetContextOpaque(ctx); + if (!agent) + return JS_ThrowTypeError(ctx, "must be called inside an agent"); + /* nothing to do */ + return JS_UNDEFINED; +} + +static BOOL is_broadcast_pending(void) +{ + struct list_head *el; + Test262Agent *agent; + list_for_each(el, &agent_list) { + agent = list_entry(el, Test262Agent, link); + if (agent->broadcast_pending) + return TRUE; + } + return FALSE; +} + +static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValueConst sab = argv[0]; + struct list_head *el; + Test262Agent *agent; + uint8_t *buf; + size_t buf_size; + int32_t val; + + if (JS_GetContextOpaque(ctx) != NULL) + return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); + + buf = JS_GetArrayBuffer(ctx, &buf_size, sab); + if (!buf) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &val, argv[1])) + return JS_EXCEPTION; + + /* broadcast the values and wait until all agents have started + calling their callbacks */ + pthread_mutex_lock(&agent_mutex); + list_for_each(el, &agent_list) { + agent = list_entry(el, Test262Agent, link); + agent->broadcast_pending = TRUE; + /* the shared array buffer is used by the thread, so increment + its refcount */ + agent->broadcast_sab = JS_DupValue(ctx, sab); + agent->broadcast_sab_buf = buf; + agent->broadcast_sab_size = buf_size; + agent->broadcast_val = val; + } + pthread_cond_broadcast(&agent_cond); + + while (is_broadcast_pending()) { + pthread_cond_wait(&agent_cond, &agent_mutex); + } + pthread_mutex_unlock(&agent_mutex); + return JS_UNDEFINED; +} + +static JSValue js_agent_receiveBroadcast(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + Test262Agent *agent = JS_GetContextOpaque(ctx); + if (!agent) + return JS_ThrowTypeError(ctx, "must be called inside an agent"); + if (!JS_IsFunction(ctx, argv[0])) + return JS_ThrowTypeError(ctx, "expecting function"); + JS_FreeValue(ctx, agent->broadcast_func); + agent->broadcast_func = JS_DupValue(ctx, argv[0]); + return JS_UNDEFINED; +} + +static JSValue js_agent_sleep(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint32_t duration; + if (JS_ToUint32(ctx, &duration, argv[0])) + return JS_EXCEPTION; + usleep(duration * 1000); + return JS_UNDEFINED; +} + +static int64_t get_clock_ms(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); +} + +static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_NewInt64(ctx, get_clock_ms()); +} + +static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + AgentReport *rep; + JSValue ret; + + pthread_mutex_lock(&report_mutex); + if (list_empty(&report_list)) { + rep = NULL; + } else { + rep = list_entry(report_list.next, AgentReport, link); + list_del(&rep->link); + } + pthread_mutex_unlock(&report_mutex); + if (rep) { + ret = JS_NewString(ctx, rep->str); + free(rep->str); + free(rep); + } else { + ret = JS_NULL; + } + return ret; +} + +static JSValue js_agent_report(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *str; + AgentReport *rep; + + str = JS_ToCString(ctx, argv[0]); + if (!str) + return JS_EXCEPTION; + rep = malloc(sizeof(*rep)); + rep->str = strdup(str); + JS_FreeCString(ctx, str); + + pthread_mutex_lock(&report_mutex); + list_add_tail(&rep->link, &report_list); + pthread_mutex_unlock(&report_mutex); + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_agent_funcs[] = { + /* only in main */ + JS_CFUNC_DEF("start", 1, js_agent_start ), + JS_CFUNC_DEF("getReport", 0, js_agent_getReport ), + JS_CFUNC_DEF("broadcast", 2, js_agent_broadcast ), + /* only in agent */ + JS_CFUNC_DEF("report", 1, js_agent_report ), + JS_CFUNC_DEF("leaving", 0, js_agent_leaving ), + JS_CFUNC_DEF("receiveBroadcast", 1, js_agent_receiveBroadcast ), + /* in both */ + JS_CFUNC_DEF("sleep", 1, js_agent_sleep ), + JS_CFUNC_DEF("monotonicNow", 0, js_agent_monotonicNow ), +}; + +static JSValue js_new_agent(JSContext *ctx) +{ + JSValue agent; + agent = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, agent, js_agent_funcs, + countof(js_agent_funcs)); + return agent; +} +#endif + +#ifdef CONFIG_REALM +static JSValue js_createRealm(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSContext *ctx1; + /* XXX: the context is not freed, need a refcount */ + ctx1 = JS_NewContext(JS_GetRuntime(ctx)); + if (!ctx1) + return JS_ThrowOutOfMemory(ctx); + return add_helpers1(ctx1); +} +#endif + +static JSValue add_helpers1(JSContext *ctx) +{ + JSValue global_obj; + JSValue obj262; + + global_obj = JS_GetGlobalObject(ctx); + + JS_SetPropertyStr(ctx, global_obj, "print", + JS_NewCFunction(ctx, js_print, "print", 1)); + + /* $262 special object used by the tests */ + obj262 = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj262, "detachArrayBuffer", + JS_NewCFunction(ctx, js_detachArrayBuffer, + "detachArrayBuffer", 1)); + JS_SetPropertyStr(ctx, obj262, "evalScript", + JS_NewCFunction(ctx, js_evalScript, + "evalScript", 1)); + JS_SetPropertyStr(ctx, obj262, "codePointRange", + JS_NewCFunction(ctx, js_string_codePointRange, + "codePointRange", 2)); +#ifdef CONFIG_AGENT + JS_SetPropertyStr(ctx, obj262, "agent", js_new_agent(ctx)); +#endif + + JS_SetPropertyStr(ctx, obj262, "global", + JS_DupValue(ctx, global_obj)); + +#ifdef CONFIG_REALM + JS_SetPropertyStr(ctx, obj262, "createRealm", + JS_NewCFunction(ctx, js_createRealm, + "createRealm", 0)); +#endif + + JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262)); + + JS_FreeValue(ctx, global_obj); + return obj262; +} + +static void add_helpers(JSContext *ctx) +{ + JS_FreeValue(ctx, add_helpers1(ctx)); +} + +static char *load_file(const char *filename, size_t *lenp) +{ + char *buf; + size_t buf_len; + buf = (char *)js_load_file(NULL, &buf_len, filename); + if (!buf) + perror_exit(1, filename); + if (lenp) + *lenp = buf_len; + return buf; +} + +static JSModuleDef *js_module_loader_test(JSContext *ctx, + const char *module_name, void *opaque) +{ + size_t buf_len; + uint8_t *buf; + JSModuleDef *m; + JSValue func_val; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + return m; +} + +int is_line_sep(char c) +{ + return (c == '\0' || c == '\n' || c == '\r'); +} + +char *find_line(const char *str, const char *line) +{ + if (str) { + const char *p; + int len = strlen(line); + for (p = str; (p = strstr(p, line)) != NULL; p += len + 1) { + if ((p == str || is_line_sep(p[-1])) && is_line_sep(p[len])) + return (char *)p; + } + } + return NULL; +} + +int is_word_sep(char c) +{ + return (c == '\0' || isspace((unsigned char)c) || c == ','); +} + +char *find_word(const char *str, const char *word) +{ + const char *p; + int len = strlen(word); + if (str && len) { + for (p = str; (p = strstr(p, word)) != NULL; p += len) { + if ((p == str || is_word_sep(p[-1])) && is_word_sep(p[len])) + return (char *)p; + } + } + return NULL; +} + +/* handle exclude directories */ +void update_exclude_dirs(void) +{ + namelist_t *lp = &test_list; + namelist_t *ep = &exclude_list; + namelist_t *dp = &exclude_dir_list; + char *name; + int i, j, count; + + /* split directpries from exclude_list */ + for (count = i = 0; i < ep->count; i++) { + name = ep->array[i]; + if (has_suffix(name, "/")) { + namelist_add(dp, NULL, name); + free(name); + } else { + ep->array[count++] = name; + } + } + ep->count = count; + + namelist_sort(dp); + + /* filter out excluded directories */ + for (count = i = 0; i < lp->count; i++) { + name = lp->array[i]; + for (j = 0; j < dp->count; j++) { + if (has_prefix(name, dp->array[j])) { + test_excluded++; + free(name); + name = NULL; + break; + } + } + if (name) { + lp->array[count++] = name; + } + } + lp->count = count; +} + +void load_config(const char *filename) +{ + char buf[1024]; + FILE *f; + char *base_name; + enum { + SECTION_NONE = 0, + SECTION_CONFIG, + SECTION_EXCLUDE, + SECTION_FEATURES, + SECTION_TESTS, + } section = SECTION_NONE; + int lineno = 0; + + f = fopen(filename, "rb"); + if (!f) { + perror_exit(1, filename); + } + base_name = get_basename(filename); + + while (fgets(buf, sizeof(buf), f) != NULL) { + char *p, *q; + lineno++; + p = str_strip(buf); + if (*p == '#' || *p == ';' || *p == '\0') + continue; /* line comment */ + + if (*p == "[]"[0]) { + /* new section */ + p++; + p[strcspn(p, "]")] = '\0'; + if (str_equal(p, "config")) + section = SECTION_CONFIG; + else if (str_equal(p, "exclude")) + section = SECTION_EXCLUDE; + else if (str_equal(p, "features")) + section = SECTION_FEATURES; + else if (str_equal(p, "tests")) + section = SECTION_TESTS; + else + section = SECTION_NONE; + continue; + } + q = strchr(p, '='); + if (q) { + /* setting: name=value */ + *q++ = '\0'; + q = str_strip(q); + } + switch (section) { + case SECTION_CONFIG: + if (!q) { + printf("%s:%d: syntax error\n", filename, lineno); + continue; + } + if (str_equal(p, "style")) { + new_style = str_equal(q, "new"); + continue; + } + if (str_equal(p, "testdir")) { + char *testdir = compose_path(base_name, q); + enumerate_tests(testdir); + free(testdir); + continue; + } + if (str_equal(p, "harnessdir")) { + harness_dir = compose_path(base_name, q); + continue; + } + if (str_equal(p, "harnessexclude")) { + str_append(&harness_exclude, " ", q); + continue; + } + if (str_equal(p, "features")) { + str_append(&harness_features, " ", q); + continue; + } + if (str_equal(p, "skip-features")) { + str_append(&harness_skip_features, " ", q); + continue; + } + if (str_equal(p, "mode")) { + if (str_equal(q, "default") || str_equal(q, "default-nostrict")) + test_mode = TEST_DEFAULT_NOSTRICT; + else if (str_equal(q, "default-strict")) + test_mode = TEST_DEFAULT_STRICT; + else if (str_equal(q, "nostrict")) + test_mode = TEST_NOSTRICT; + else if (str_equal(q, "strict")) + test_mode = TEST_STRICT; + else if (str_equal(q, "all") || str_equal(q, "both")) + test_mode = TEST_ALL; + else + fatal(2, "unknown test mode: %s", q); + continue; + } + if (str_equal(p, "strict")) { + if (str_equal(q, "skip") || str_equal(q, "no")) + test_mode = TEST_NOSTRICT; + continue; + } + if (str_equal(p, "nostrict")) { + if (str_equal(q, "skip") || str_equal(q, "no")) + test_mode = TEST_STRICT; + continue; + } + if (str_equal(p, "async")) { + skip_async = !str_equal(q, "yes"); + continue; + } + if (str_equal(p, "module")) { + skip_module = !str_equal(q, "yes"); + continue; + } + if (str_equal(p, "verbose")) { + verbose = str_equal(q, "yes"); + continue; + } + if (str_equal(p, "errorfile")) { + error_filename = compose_path(base_name, q); + continue; + } + if (str_equal(p, "excludefile")) { + char *path = compose_path(base_name, q); + namelist_load(&exclude_list, path); + free(path); + continue; + } + if (str_equal(p, "reportfile")) { + report_filename = compose_path(base_name, q); + continue; + } + case SECTION_EXCLUDE: + namelist_add(&exclude_list, base_name, p); + break; + case SECTION_FEATURES: + if (!q || str_equal(q, "yes")) + str_append(&harness_features, " ", p); + else + str_append(&harness_skip_features, " ", p); + break; + case SECTION_TESTS: + namelist_add(&test_list, base_name, p); + break; + default: + /* ignore settings in other sections */ + break; + } + } + fclose(f); + free(base_name); +} + +char *find_error(const char *filename, int *pline, int is_strict) +{ + if (error_file) { + size_t len = strlen(filename); + const char *p, *q, *r; + int line; + + for (p = error_file; (p = strstr(p, filename)) != NULL; p += len) { + if ((p == error_file || p[-1] == '\n' || p[-1] == '(') && p[len] == ':') { + q = p + len; + line = 1; + if (*q == ':') { + line = strtol(q + 1, (char**)&q, 10); + if (*q == ':') + q++; + } + while (*q == ' ') { + q++; + } + /* check strict mode indicator */ + if (!strstart(q, "strict mode: ", &q) != !is_strict) + continue; + r = q = skip_prefix(q, "unexpected error: "); + r += strcspn(r, "\n"); + while (r[0] == '\n' && r[1] && strncmp(r + 1, filename, 8)) { + r++; + r += strcspn(r, "\n"); + } + if (pline) + *pline = line; + return strdup_len(q, r - q); + } + } + } + return NULL; +} + +int skip_comments(const char *str, int line, int *pline) +{ + const char *p; + int c; + + p = str; + while ((c = (unsigned char)*p++) != '\0') { + if (isspace(c)) { + if (c == '\n') + line++; + continue; + } + if (c == '/' && *p == '/') { + while (*++p && *p != '\n') + continue; + continue; + } + if (c == '/' && *p == '*') { + for (p += 1; *p; p++) { + if (*p == '\n') { + line++; + continue; + } + if (*p == '*' && p[1] == '/') { + p += 2; + break; + } + } + continue; + } + break; + } + if (pline) + *pline = line; + + return p - str; +} + +int longest_match(const char *str, const char *find, int pos, int *ppos, int line, int *pline) +{ + int len, maxlen; + + maxlen = 0; + + if (*find) { + const char *p; + for (p = str + pos; *p; p++) { + if (*p == *find) { + for (len = 1; p[len] && p[len] == find[len]; len++) + continue; + if (len > maxlen) { + maxlen = len; + if (ppos) + *ppos = p - str; + if (pline) + *pline = line; + if (!find[len]) + break; + } + } + if (*p == '\n') + line++; + } + } + return maxlen; +} + +static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename, int is_test, int is_negative, + const char *error_type, FILE *outfile, int eval_flags, + int is_async) +{ + JSValue res_val, exception_val; + int ret, error_line, pos, pos_line; + BOOL is_error, has_error_line; + const char *error_name; + + pos = skip_comments(buf, 1, &pos_line); + error_line = pos_line; + has_error_line = FALSE; + exception_val = JS_UNDEFINED; + error_name = NULL; + + async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */ + + res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + + if (is_async && !JS_IsException(res_val)) { + JS_FreeValue(ctx, res_val); + for(;;) { + JSContext *ctx1; + ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (ret < 0) { + res_val = JS_EXCEPTION; + break; + } else if (ret == 0) { + /* test if the test called $DONE() once */ + if (async_done != 1) { + res_val = JS_ThrowTypeError(ctx, "$DONE() not called"); + } else { + res_val = JS_UNDEFINED; + } + break; + } + } + } + + if (JS_IsException(res_val)) { + exception_val = JS_GetException(ctx); + is_error = JS_IsError(ctx, exception_val); + /* XXX: should get the filename and line number */ + if (outfile) { + if (!is_error) + fprintf(outfile, "%sThrow: ", (eval_flags & JS_EVAL_FLAG_STRICT) ? + "strict mode: " : ""); + js_print(ctx, JS_NULL, 1, &exception_val); + } + if (is_error) { + JSValue name, stack; + const char *stack_str; + + name = JS_GetPropertyStr(ctx, exception_val, "name"); + error_name = JS_ToCString(ctx, name); + stack = JS_GetPropertyStr(ctx, exception_val, "stack"); + if (!JS_IsUndefined(stack)) { + stack_str = JS_ToCString(ctx, stack); + if (stack_str) { + const char *p; + int len; + + if (outfile) + fprintf(outfile, "%s", stack_str); + + len = strlen(filename); + p = strstr(stack_str, filename); + if (p != NULL && p[len] == ':') { + error_line = atoi(p + len + 1); + has_error_line = TRUE; + } + JS_FreeCString(ctx, stack_str); + } + } + JS_FreeValue(ctx, stack); + JS_FreeValue(ctx, name); + } + if (is_negative) { + ret = 0; + if (error_type) { + char *error_class; + const char *msg; + + msg = JS_ToCString(ctx, exception_val); + error_class = strdup_len(msg, strcspn(msg, ":")); + if (!str_equal(error_class, error_type)) + ret = -1; + free(error_class); + JS_FreeCString(ctx, msg); + } + } else { + ret = -1; + } + } else { + if (is_negative) + ret = -1; + else + ret = 0; + } + + if (verbose && is_test) { + JSValue msg_val = JS_UNDEFINED; + const char *msg = NULL; + int s_line; + char *s = find_error(filename, &s_line, eval_flags & JS_EVAL_FLAG_STRICT); + const char *strict_mode = (eval_flags & JS_EVAL_FLAG_STRICT) ? "strict mode: " : ""; + + if (!JS_IsUndefined(exception_val)) { + msg_val = JS_ToString(ctx, exception_val); + msg = JS_ToCString(ctx, msg_val); + } + if (is_negative) { // expect error + if (ret == 0) { + if (msg && s && + (str_equal(s, "expected error") || + strstart(s, "unexpected error type:", NULL) || + str_equal(s, msg))) { // did not have error yet + if (!has_error_line) { + longest_match(buf, msg, pos, &pos, pos_line, &error_line); + } + printf("%s:%d: %sOK, now has error %s\n", + filename, error_line, strict_mode, msg); + fixed_errors++; + } + } else { + if (!s) { // not yet reported + if (msg) { + fprintf(error_out, "%s:%d: %sunexpected error type: %s\n", + filename, error_line, strict_mode, msg); + } else { + fprintf(error_out, "%s:%d: %sexpected error\n", + filename, error_line, strict_mode); + } + new_errors++; + } + } + } else { // should not have error + if (msg) { + if (!s || !str_equal(s, msg)) { + if (!has_error_line) { + char *p = skip_prefix(msg, "Test262 Error: "); + if (strstr(p, "Test case returned non-true value!")) { + longest_match(buf, "runTestCase", pos, &pos, pos_line, &error_line); + } else { + longest_match(buf, p, pos, &pos, pos_line, &error_line); + } + } + fprintf(error_out, "%s:%d: %s%s%s\n", filename, error_line, strict_mode, + error_file ? "unexpected error: " : "", msg); + + if (s && (!str_equal(s, msg) || error_line != s_line)) { + printf("%s:%d: %sprevious error: %s\n", filename, s_line, strict_mode, s); + changed_errors++; + } else { + new_errors++; + } + } + } else { + if (s) { + printf("%s:%d: %sOK, fixed error: %s\n", filename, s_line, strict_mode, s); + fixed_errors++; + } + } + } + JS_FreeValue(ctx, msg_val); + JS_FreeCString(ctx, msg); + free(s); + } + JS_FreeCString(ctx, error_name); + JS_FreeValue(ctx, exception_val); + JS_FreeValue(ctx, res_val); + return ret; +} + +static int eval_file(JSContext *ctx, const char *base, const char *p, + int eval_flags) +{ + char *buf; + size_t buf_len; + char *filename = compose_path(base, p); + + buf = load_file(filename, &buf_len); + if (!buf) { + warning("cannot load %s", filename); + goto fail; + } + if (eval_buf(ctx, buf, buf_len, filename, FALSE, FALSE, NULL, stderr, + eval_flags, FALSE)) { + warning("error evaluating %s", filename); + goto fail; + } + free(buf); + free(filename); + return 0; + +fail: + free(buf); + free(filename); + return 1; +} + +char *extract_desc(const char *buf, char style) +{ + const char *p, *desc_start; + char *desc; + int len; + + p = buf; + while (*p != '\0') { + if (p[0] == '/' && p[1] == '*' && p[2] == style && p[3] != '/') { + p += 3; + desc_start = p; + while (*p != '\0' && (p[0] != '*' || p[1] != '/')) + p++; + if (*p == '\0') { + warning("Expecting end of desc comment"); + return NULL; + } + len = p - desc_start; + desc = malloc(len + 1); + memcpy(desc, desc_start, len); + desc[len] = '\0'; + return desc; + } else { + p++; + } + } + return NULL; +} + +static char *find_tag(char *desc, const char *tag, int *state) +{ + char *p; + p = strstr(desc, tag); + if (p) { + p += strlen(tag); + *state = 0; + } + return p; +} + +static char *get_option(char **pp, int *state) +{ + char *p, *p0, *option = NULL; + if (*pp) { + for (p = *pp;; p++) { + switch (*p) { + case '[': + *state += 1; + continue; + case ']': + *state -= 1; + if (*state > 0) + continue; + p = NULL; + break; + case ' ': + case '\t': + case '\r': + case ',': + case '-': + continue; + case '\n': + if (*state > 0 || p[1] == ' ') + continue; + p = NULL; + break; + case '\0': + p = NULL; + break; + default: + p0 = p; + p += strcspn(p0, " \t\r\n,]"); + option = strdup_len(p0, p - p0); + break; + } + break; + } + *pp = p; + } + return option; +} + +void update_stats(JSRuntime *rt, const char *filename) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + if (stats_count++ == 0) { + stats_avg = stats_all = stats_min = stats_max = stats; + stats_min_filename = strdup(filename); + stats_max_filename = strdup(filename); + } else { + if (stats_max.malloc_size < stats.malloc_size) { + stats_max = stats; + free(stats_max_filename); + stats_max_filename = strdup(filename); + } + if (stats_min.malloc_size > stats.malloc_size) { + stats_min = stats; + free(stats_min_filename); + stats_min_filename = strdup(filename); + } + +#define update(f) stats_avg.f = (stats_all.f += stats.f) / stats_count + update(malloc_count); + update(malloc_size); + update(memory_used_count); + update(memory_used_size); + update(atom_count); + update(atom_size); + update(str_count); + update(str_size); + update(obj_count); + update(obj_size); + update(prop_count); + update(prop_size); + update(shape_count); + update(shape_size); + update(js_func_count); + update(js_func_size); + update(js_func_code_size); + update(js_func_pc2line_count); + update(js_func_pc2line_size); + update(c_func_count); + update(array_count); + update(fast_array_count); + update(fast_array_elements); + } +#undef update +} + +int run_test_buf(const char *filename, char *harness, namelist_t *ip, + char *buf, size_t buf_len, const char* error_type, + int eval_flags, BOOL is_negative, BOOL is_async, + BOOL can_block) +{ + JSRuntime *rt; + JSContext *ctx; + int i, ret; + + rt = JS_NewRuntime(); + if (rt == NULL) { + fatal(1, "JS_NewRuntime failure"); + } + ctx = JS_NewContext(rt); + if (ctx == NULL) { + JS_FreeRuntime(rt); + fatal(1, "JS_NewContext failure"); + } + JS_SetRuntimeInfo(rt, filename); + + JS_SetCanBlock(rt, can_block); + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL); + + add_helpers(ctx); + + for (i = 0; i < ip->count; i++) { + if (eval_file(ctx, harness, ip->array[i], + JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRIP)) { + fatal(1, "error including %s for %s", ip->array[i], filename); + } + } + + ret = eval_buf(ctx, buf, buf_len, filename, TRUE, is_negative, + error_type, outfile, eval_flags, is_async); + ret = (ret != 0); + + if (dump_memory) { + update_stats(rt, filename); + } +#ifdef CONFIG_AGENT + js_agent_free(ctx); +#endif + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + test_count++; + if (ret) { + test_failed++; + if (outfile) { + /* do not output a failure number to minimize diff */ + fprintf(outfile, " FAILED\n"); + } + } + return ret; +} + +int run_test(const char *filename, int index) +{ + char harnessbuf[1024]; + char *harness; + char *buf; + size_t buf_len; + char *desc, *p; + char *error_type; + int ret, eval_flags, use_strict, use_nostrict; + BOOL is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip; + BOOL can_block; + namelist_t include_list = { 0 }, *ip = &include_list; + + is_nostrict = is_onlystrict = is_negative = is_async = is_module = skip = FALSE; + can_block = TRUE; + error_type = NULL; + buf = load_file(filename, &buf_len); + + harness = harness_dir; + + if (new_style) { + if (!harness) { + p = strstr(filename, "test/"); + if (p) { + snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s", + (int)(p - filename), filename, "harness"); + } + harness = harnessbuf; + } + namelist_add(ip, NULL, "sta.js"); + namelist_add(ip, NULL, "assert.js"); + /* extract the YAML frontmatter */ + desc = extract_desc(buf, '-'); + if (desc) { + char *ifile, *option; + int state; + p = find_tag(desc, "includes:", &state); + if (p) { + while ((ifile = get_option(&p, &state)) != NULL) { + // skip unsupported harness files + if (find_word(harness_exclude, ifile)) { + skip |= 1; + } else { + namelist_add(ip, NULL, ifile); + } + free(ifile); + } + } + p = find_tag(desc, "flags:", &state); + if (p) { + while ((option = get_option(&p, &state)) != NULL) { + if (str_equal(option, "noStrict") || + str_equal(option, "raw")) { + is_nostrict = TRUE; + skip |= (test_mode == TEST_STRICT); + } + else if (str_equal(option, "onlyStrict")) { + is_onlystrict = TRUE; + skip |= (test_mode == TEST_NOSTRICT); + } + else if (str_equal(option, "async")) { + is_async = TRUE; + skip |= skip_async; + } + else if (str_equal(option, "module")) { + is_module = TRUE; + skip |= skip_module; + } + else if (str_equal(option, "CanBlockIsFalse")) { + can_block = FALSE; + } + free(option); + } + } + p = find_tag(desc, "negative:", &state); + if (p) { + /* XXX: should extract the phase */ + char *q = find_tag(p, "type:", &state); + if (q) { + while (isspace(*q)) + q++; + error_type = strdup_len(q, strcspn(q, " \n")); + } + is_negative = TRUE; + } + p = find_tag(desc, "features:", &state); + if (p) { + while ((option = get_option(&p, &state)) != NULL) { + if (find_word(harness_features, option)) { + /* feature is enabled */ + } else if (find_word(harness_skip_features, option)) { + /* skip disabled feature */ + skip |= 1; + } else { + /* feature is not listed: skip and warn */ + printf("%s:%d: unknown feature: %s\n", filename, 1, option); + skip |= 1; + } + free(option); + } + } + free(desc); + } + if (is_async) + namelist_add(ip, NULL, "doneprintHandle.js"); + } else { + char *ifile; + + if (!harness) { + p = strstr(filename, "test/"); + if (p) { + snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s", + (int)(p - filename), filename, "test/harness"); + } + harness = harnessbuf; + } + + namelist_add(ip, NULL, "sta.js"); + + /* include extra harness files */ + for (p = buf; (p = strstr(p, "$INCLUDE(\"")) != NULL; p++) { + p += 10; + ifile = strdup_len(p, strcspn(p, "\"")); + // skip unsupported harness files + if (find_word(harness_exclude, ifile)) { + skip |= 1; + } else { + namelist_add(ip, NULL, ifile); + } + free(ifile); + } + + /* locate the old style configuration comment */ + desc = extract_desc(buf, '*'); + if (desc) { + if (strstr(desc, "@noStrict")) { + is_nostrict = TRUE; + skip |= (test_mode == TEST_STRICT); + } + if (strstr(desc, "@onlyStrict")) { + is_onlystrict = TRUE; + skip |= (test_mode == TEST_NOSTRICT); + } + if (strstr(desc, "@negative")) { + /* XXX: should extract the regex to check error type */ + is_negative = TRUE; + } + free(desc); + } + } + + if (outfile && index >= 0) { + fprintf(outfile, "%d: %s%s%s%s%s%s%s\n", index, filename, + is_nostrict ? " @noStrict" : "", + is_onlystrict ? " @onlyStrict" : "", + is_async ? " async" : "", + is_module ? " module" : "", + is_negative ? " @negative" : "", + skip ? " SKIPPED" : ""); + fflush(outfile); + } + + use_strict = use_nostrict = 0; + /* XXX: should remove 'test_mode' or simplify it just to force + strict or non strict mode for single file tests */ + switch (test_mode) { + case TEST_DEFAULT_NOSTRICT: + if (is_onlystrict) + use_strict = 1; + else + use_nostrict = 1; + break; + case TEST_DEFAULT_STRICT: + if (is_nostrict) + use_nostrict = 1; + else + use_strict = 1; + break; + case TEST_NOSTRICT: + if (!is_onlystrict) + use_nostrict = 1; + break; + case TEST_STRICT: + if (!is_nostrict) + use_strict = 1; + break; + case TEST_ALL: + if (is_module) { + use_nostrict = 1; + } else { + if (!is_nostrict) + use_strict = 1; + if (!is_onlystrict) + use_nostrict = 1; + } + break; + } + + if (skip || use_strict + use_nostrict == 0) { + test_skipped++; + ret = -2; + } else { + clock_t clocks; + + if (is_module) { + eval_flags = JS_EVAL_TYPE_MODULE; + } else { + eval_flags = JS_EVAL_TYPE_GLOBAL; + } + clocks = clock(); + ret = 0; + if (use_nostrict) { + ret = run_test_buf(filename, harness, ip, buf, buf_len, + error_type, eval_flags, is_negative, is_async, + can_block); + } + if (use_strict) { + ret |= run_test_buf(filename, harness, ip, buf, buf_len, + error_type, eval_flags | JS_EVAL_FLAG_STRICT, + is_negative, is_async, can_block); + } + clocks = clock() - clocks; + if (outfile && index >= 0 && clocks >= CLOCKS_PER_SEC / 10) { + /* output timings for tests that take more than 100 ms */ + fprintf(outfile, " time: %d ms\n", (int)(clocks * 1000LL / CLOCKS_PER_SEC)); + } + } + namelist_free(&include_list); + free(error_type); + free(buf); + + return ret; +} + +/* run a test when called by test262-harness+eshost */ +int run_test262_harness_test(const char *filename, BOOL is_module) +{ + JSRuntime *rt; + JSContext *ctx; + char *buf; + size_t buf_len; + int eval_flags, ret_code, ret; + JSValue res_val; + BOOL can_block; + + outfile = stdout; /* for js_print */ + + rt = JS_NewRuntime(); + if (rt == NULL) { + fatal(1, "JS_NewRuntime failure"); + } + ctx = JS_NewContext(rt); + if (ctx == NULL) { + JS_FreeRuntime(rt); + fatal(1, "JS_NewContext failure"); + } + JS_SetRuntimeInfo(rt, filename); + + can_block = TRUE; + JS_SetCanBlock(rt, can_block); + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL); + + add_helpers(ctx); + + buf = load_file(filename, &buf_len); + + if (is_module) { + eval_flags = JS_EVAL_TYPE_MODULE; + } else { + eval_flags = JS_EVAL_TYPE_GLOBAL; + } + res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + ret_code = 0; + if (JS_IsException(res_val)) { + js_std_dump_error(ctx); + ret_code = 1; + } else { + JS_FreeValue(ctx, res_val); + for(;;) { + JSContext *ctx1; + ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (ret < 0) { + js_std_dump_error(ctx1); + ret_code = 1; + } else if (ret == 0) { + break; + } + } + } + free(buf); +#ifdef CONFIG_AGENT + js_agent_free(ctx); +#endif + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return ret_code; +} + +clock_t last_clock; + +void show_progress(int force) { + clock_t t = clock(); + if (force || !last_clock || (t - last_clock) > CLOCKS_PER_SEC / 20) { + last_clock = t; + /* output progress indicator: erase end of line and return to col 0 */ + fprintf(stderr, "%d/%d/%d\033[K\r", + test_failed, test_count, test_skipped); + fflush(stderr); + } +} + +static int slow_test_threshold; + +void run_test_dir_list(namelist_t *lp, int start_index, int stop_index) +{ + int i; + + namelist_sort(lp); + for (i = 0; i < lp->count; i++) { + const char *p = lp->array[i]; + if (namelist_find(&exclude_list, p) >= 0) { + test_excluded++; + } else if (test_index < start_index) { + test_skipped++; + } else if (stop_index >= 0 && test_index > stop_index) { + test_skipped++; + } else { + int ti; + if (slow_test_threshold != 0) { + ti = get_clock_ms(); + } else { + ti = 0; + } + run_test(p, test_index); + if (slow_test_threshold != 0) { + ti = get_clock_ms() - ti; + if (ti >= slow_test_threshold) + fprintf(stderr, "\n%s (%d ms)\n", p, ti); + } + show_progress(FALSE); + } + test_index++; + } + show_progress(TRUE); +} + +void help(void) +{ + printf("run-test262 version " CONFIG_VERSION "\n" + "usage: run-test262 [options] {-f file ... | [dir_list] [index range]}\n" + "-h help\n" + "-a run tests in strict and nostrict modes\n" + "-m print memory usage summary\n" + "-n use new style harness\n" + "-N run test prepared by test262-harness+eshost\n" + "-s run tests in strict mode, skip @nostrict tests\n" + "-E only run tests from the error file\n" + "-u update error file\n" + "-v verbose: output error messages\n" + "-T duration display tests taking more than 'duration' ms\n" + "-c file read configuration from 'file'\n" + "-d dir run all test files in directory tree 'dir'\n" + "-e file load the known errors from 'file'\n" + "-f file execute single test from 'file'\n" + "-r file set the report file name (default=none)\n" + "-x file exclude tests listed in 'file'\n"); + exit(1); +} + +char *get_opt_arg(const char *option, char *arg) +{ + if (!arg) { + fatal(2, "missing argument for option %s", option); + } + return arg; +} + +int main(int argc, char **argv) +{ + int optind, start_index, stop_index; + BOOL is_dir_list; + BOOL only_check_errors = FALSE; + const char *filename; + BOOL is_test262_harness = FALSE; + BOOL is_module = FALSE; + +#if !defined(_WIN32) + /* Date tests assume California local time */ + setenv("TZ", "America/Los_Angeles", 1); +#endif + + /* cannot use getopt because we want to pass the command line to + the script */ + optind = 1; + is_dir_list = TRUE; + while (optind < argc) { + char *arg = argv[optind]; + if (*arg != '-') + break; + optind++; + if (str_equal(arg, "-h")) { + help(); + } else if (str_equal(arg, "-m")) { + dump_memory++; + } else if (str_equal(arg, "-n")) { + new_style++; + } else if (str_equal(arg, "-s")) { + test_mode = TEST_STRICT; + } else if (str_equal(arg, "-a")) { + test_mode = TEST_ALL; + } else if (str_equal(arg, "-u")) { + update_errors++; + } else if (str_equal(arg, "-v")) { + verbose++; + } else if (str_equal(arg, "-c")) { + load_config(get_opt_arg(arg, argv[optind++])); + } else if (str_equal(arg, "-d")) { + enumerate_tests(get_opt_arg(arg, argv[optind++])); + } else if (str_equal(arg, "-e")) { + error_filename = get_opt_arg(arg, argv[optind++]); + } else if (str_equal(arg, "-x")) { + namelist_load(&exclude_list, get_opt_arg(arg, argv[optind++])); + } else if (str_equal(arg, "-f")) { + is_dir_list = FALSE; + } else if (str_equal(arg, "-r")) { + report_filename = get_opt_arg(arg, argv[optind++]); + } else if (str_equal(arg, "-E")) { + only_check_errors = TRUE; + } else if (str_equal(arg, "-T")) { + slow_test_threshold = atoi(get_opt_arg(arg, argv[optind++])); + } else if (str_equal(arg, "-N")) { + is_test262_harness = TRUE; + } else if (str_equal(arg, "--module")) { + is_module = TRUE; + } else { + fatal(1, "unknown option: %s", arg); + break; + } + } + + if (optind >= argc && !test_list.count) + help(); + + if (is_test262_harness) { + return run_test262_harness_test(argv[optind], is_module); + } + + error_out = stdout; + if (error_filename) { + error_file = load_file(error_filename, NULL); + if (only_check_errors && error_file) { + namelist_free(&test_list); + namelist_add_from_error_file(&test_list, error_file); + } + if (update_errors) { + free(error_file); + error_file = NULL; + error_out = fopen(error_filename, "w"); + if (!error_out) { + perror_exit(1, error_filename); + } + } + } + + update_exclude_dirs(); + + if (is_dir_list) { + if (optind < argc && !isdigit(argv[optind][0])) { + filename = argv[optind++]; + namelist_load(&test_list, filename); + } + start_index = 0; + stop_index = -1; + if (optind < argc) { + start_index = atoi(argv[optind++]); + if (optind < argc) { + stop_index = atoi(argv[optind++]); + } + } + if (!report_filename || str_equal(report_filename, "none")) { + outfile = NULL; + } else if (str_equal(report_filename, "-")) { + outfile = stdout; + } else { + outfile = fopen(report_filename, "wb"); + if (!outfile) { + perror_exit(1, report_filename); + } + } + run_test_dir_list(&test_list, start_index, stop_index); + + if (outfile && outfile != stdout) { + fclose(outfile); + outfile = NULL; + } + } else { + outfile = stdout; + while (optind < argc) { + run_test(argv[optind++], -1); + } + } + + if (dump_memory) { + if (dump_memory > 1 && stats_count > 1) { + printf("\nMininum memory statistics for %s:\n\n", stats_min_filename); + JS_DumpMemoryUsage(stdout, &stats_min, NULL); + printf("\nMaximum memory statistics for %s:\n\n", stats_max_filename); + JS_DumpMemoryUsage(stdout, &stats_max, NULL); + } + printf("\nAverage memory statistics for %d tests:\n\n", stats_count); + JS_DumpMemoryUsage(stdout, &stats_avg, NULL); + printf("\n"); + } + + if (is_dir_list) { + fprintf(stderr, "Result: %d/%d error%s", + test_failed, test_count, test_count != 1 ? "s" : ""); + if (test_excluded) + fprintf(stderr, ", %d excluded", test_excluded); + if (test_skipped) + fprintf(stderr, ", %d skipped", test_skipped); + if (error_file) { + if (new_errors) + fprintf(stderr, ", %d new", new_errors); + if (changed_errors) + fprintf(stderr, ", %d changed", changed_errors); + if (fixed_errors) + fprintf(stderr, ", %d fixed", fixed_errors); + } + fprintf(stderr, "\n"); + } + + if (error_out && error_out != stdout) { + fclose(error_out); + error_out = NULL; + } + + namelist_free(&test_list); + namelist_free(&exclude_list); + namelist_free(&exclude_dir_list); + free(harness_dir); + free(harness_features); + free(harness_exclude); + free(error_file); + + return 0; +} diff --git a/test262.conf b/test262.conf new file mode 100644 index 0000000..91d3218 --- /dev/null +++ b/test262.conf @@ -0,0 +1,182 @@ +[config] +# general settings for test262 ES6 version + +# framework style: old, new +style=new + +# handle tests tagged as [noStrict]: yes, no, skip +nostrict=yes + +# handle tests tagged as [strictOnly]: yes, no, skip +strict=yes + +# test mode: default, default-nostrict, default-strict, strict, nostrict, both, all +mode=default + +# handle tests flagged as [async]: yes, no, skip +# for these, load 'harness/doneprintHandle.js' prior to test +# and expect `print('Test262:AsyncTestComplete')` to be called for +# successful termination +async=yes + +# handle tests flagged as [module]: yes, no, skip +module=yes + +# output error messages: yes, no +verbose=yes + +# load harness files from this directory +harnessdir=test262/harness + +# names of harness include files to skip +#harnessexclude= + +# name of the error file for known errors +errorfile=test262_errors.txt + +# exclude tests enumerated in this file (see also [exclude] section) +#excludefile=test262_exclude.txt + +# report test results to this file +reportfile=test262_report.txt + +# enumerate tests from this directory +testdir=test262/test + +[features] +# Standard language features and proposed extensions +# list the features that are included +# skipped features are tagged as such to avoid warnings + +AggregateError=skip +Array.prototype.flat +Array.prototype.flatMap +Array.prototype.flatten +Array.prototype.values +ArrayBuffer +arrow-function +async-functions +async-iteration +Atomics +BigInt +caller +class +class-fields-private +class-fields-public +class-methods-private +class-static-fields-public +class-static-fields-private +class-static-methods-private +coalesce-expression +computed-property-names +const +cross-realm=skip +DataView +DataView.prototype.getFloat32 +DataView.prototype.getFloat64 +DataView.prototype.getInt16 +DataView.prototype.getInt32 +DataView.prototype.getInt8 +DataView.prototype.getUint16 +DataView.prototype.getUint32 +DataView.prototype.setUint8 +default-arg +default-parameters +destructuring-assignment +destructuring-binding +dynamic-import +export-star-as-namespace-from-module +FinalizationGroup=skip +Float32Array +Float64Array +for-in-order +for-of +generators +globalThis +hashbang +host-gc-required=skip +import.meta +Int32Array +Int8Array +IsHTMLDDA=skip +json-superset +let +Map +new.target +numeric-separator-literal +object-rest +object-spread +Object.fromEntries +Object.is +optional-catch-binding +optional-chaining +Promise.allSettled +Promise.prototype.finally +Proxy +proxy-missing-checks +Reflect +Reflect.construct +Reflect.set +Reflect.setPrototypeOf +regexp-dotall +regexp-lookbehind +regexp-match-indices=skip +regexp-named-groups +regexp-unicode-property-escapes +rest-parameters +Set +SharedArrayBuffer +string-trimming +String.fromCodePoint +String.prototype.endsWith +String.prototype.includes +String.prototype.matchAll +String.prototype.replaceAll +String.prototype.trimEnd +String.prototype.trimStart +super +Symbol +Symbol.asyncIterator +Symbol.hasInstance +Symbol.isConcatSpreadable +Symbol.iterator +Symbol.match +Symbol.matchAll +Symbol.prototype.description +Symbol.replace +Symbol.search +Symbol.species +Symbol.split +Symbol.toPrimitive +Symbol.toStringTag +Symbol.unscopables +tail-call-optimization=skip +template +top-level-await=skip +TypedArray +u180e +Uint16Array +Uint8Array +Uint8ClampedArray +WeakMap +WeakRef=skip +WeakSet +well-formed-json-stringify + +[exclude] +# list excluded tests and directories here + +# intl not supported +test262/test/intl402/ + +# incompatible with the "caller" feature +test262/test/built-ins/Function/prototype/restricted-property-caller.js +test262/test/built-ins/Function/prototype/restricted-property-arguments.js +test262/test/built-ins/ThrowTypeError/unique-per-realm-function-proto.js + +# slow tests +#test262/test/built-ins/RegExp/CharacterClassEscapes/ +#test262/test/built-ins/RegExp/property-escapes/ + +[tests] +# list test files or use config.testdir diff --git a/test262_errors.txt b/test262_errors.txt new file mode 100644 index 0000000..90adb53 --- /dev/null +++ b/test262_errors.txt @@ -0,0 +1,4 @@ +test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called +test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called +test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: value has no property +test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: value has no property diff --git a/test262o.conf b/test262o.conf new file mode 100644 index 0000000..669dead --- /dev/null +++ b/test262o.conf @@ -0,0 +1,410 @@ +[config] +# general settings for test262 ES5 version + +# framework style: old, new +style=old + +# handle tests tagged as @noStrict: yes, no, skip +nostrict=yes + +# handle tests tagged as @strictOnly: yes, no, skip +strict=yes + +# test mode: default, default-nostrict, default-strict, strict, nostrict, both, all +mode=default + +# output error messages: yes, no +verbose=yes + +# load harness files this directory +harnessdir=test262o/test/harness + +# name of the error file for known errors +errorfile=test262o_errors.txt + +# exclude tests enumerated in this file +#excludefile=test262o_excluded.txt + +# report test results to this file +reportfile=test262o_report.txt + +# enumerate tests from this directory +testdir=test262o/test/suite + +[exclude] +# list excluded tests and directories here + +# intl not supported +test262o/test/suite/intl402/ + +# ES6 != ES5: block scoped function definitions allowed in strict mode +test262o/test/suite/bestPractice/Sbp_A1_T1.js +test262o/test/suite/bestPractice/Sbp_A2_T1.js +test262o/test/suite/bestPractice/Sbp_A2_T2.js +test262o/test/suite/bestPractice/Sbp_A3_T1.js +test262o/test/suite/bestPractice/Sbp_A3_T2.js +test262o/test/suite/bestPractice/Sbp_A4_T1.js +test262o/test/suite/bestPractice/Sbp_A4_T2.js +test262o/test/suite/bestPractice/Sbp_A5_T2.js + +# ES6 != ES5: `y={x};` is shorthand for `y={x:x}` +test262o/test/suite/ch12/12.1/S12.1_A4_T2.js +test262o/test/suite/ch12/12.6/12.6.4/S12.6.4_A15.js + +# ES6 != ES5: function length property is configurable +test262o/test/suite/ch11/11.4/11.4.1/11.4.1-5-a-28-s.js +test262o/test/suite/ch13/13.2/13.2-15-1.js +test262o/test/suite/ch15/15.1/15.1.2/15.1.2.1/S15.1.2.1_A4.2.js +test262o/test/suite/ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A9.2.js +test262o/test/suite/ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A7.2.js +test262o/test/suite/ch15/15.1/15.1.2/15.1.2.4/S15.1.2.4_A2.2.js +test262o/test/suite/ch15/15.1/15.1.2/15.1.2.5/S15.1.2.5_A2.2.js +test262o/test/suite/ch15/15.1/15.1.3/15.1.3.1/S15.1.3.1_A5.2.js +test262o/test/suite/ch15/15.1/15.1.3/15.1.3.2/S15.1.3.2_A5.2.js +test262o/test/suite/ch15/15.1/15.1.3/15.1.3.3/S15.1.3.3_A5.2.js +test262o/test/suite/ch15/15.1/15.1.3/15.1.3.4/S15.1.3.4_A5.2.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-186.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-187.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-191.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-194.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-201.js +test262o/test/suite/ch15/15.2/15.2.4/15.2.4.2/S15.2.4.2_A9.js +test262o/test/suite/ch15/15.2/15.2.4/15.2.4.3/S15.2.4.3_A9.js +test262o/test/suite/ch15/15.2/15.2.4/15.2.4.4/S15.2.4.4_A9.js +test262o/test/suite/ch15/15.2/15.2.4/15.2.4.5/S15.2.4.5_A9.js +test262o/test/suite/ch15/15.2/15.2.4/15.2.4.6/S15.2.4.6_A9.js +test262o/test/suite/ch15/15.2/15.2.4/15.2.4.7/S15.2.4.7_A9.js +test262o/test/suite/ch15/15.3/15.3.3/15.3.3.2/15.3.3.2-1.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.2/S15.3.4.2_A9.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.3/S15.3.4.3_A9.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.4/S15.3.4.4_A9.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-15-2.js +test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T1.js +test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T2.js +test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T3.js +test262o/test/suite/ch15/15.4/15.4.3/S15.4.3_A2.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.2/S15.4.4.2_A4.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.3/S15.4.4.3_A4.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.4/S15.4.4.4_A4.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.5/S15.4.4.5_A6.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A5.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A6.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.8/S15.4.4.8_A5.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.9/S15.4.4.9_A5.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A5.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.11/S15.4.4.11_A7.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A5.2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.13/S15.4.4.13_A5.2.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.4/S15.5.4.4_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.5/S15.5.4.5_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.6/S15.5.4.6_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.7/S15.5.4.7_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.8/S15.5.4.8_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.9/S15.5.4.9_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.10/S15.5.4.10_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.11/S15.5.4.11_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.12/S15.5.4.12_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.13/S15.5.4.13_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.14/S15.5.4.14_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.15/S15.5.4.15_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.16/S15.5.4.16_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.17/S15.5.4.17_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.18/S15.5.4.18_A9.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.19/S15.5.4.19_A9.js +test262o/test/suite/ch15/15.9/15.9.4/15.9.4.2/S15.9.4.2_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.4/15.9.4.3/S15.9.4.3_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.1/S15.9.5.1_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.2/S15.9.5.2_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.3/S15.9.5.3_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.4/S15.9.5.4_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.5/S15.9.5.5_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.6/S15.9.5.6_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.7/S15.9.5.7_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.8/S15.9.5.8_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.9/S15.9.5.9_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.10/S15.9.5.10_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.11/S15.9.5.11_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.12/S15.9.5.12_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.13/S15.9.5.13_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.14/S15.9.5.14_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.15/S15.9.5.15_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.16/S15.9.5.16_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.17/S15.9.5.17_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.18/S15.9.5.18_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.19/S15.9.5.19_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.20/S15.9.5.20_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.21/S15.9.5.21_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.22/S15.9.5.22_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.23/S15.9.5.23_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.24/S15.9.5.24_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.25/S15.9.5.25_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.26/S15.9.5.26_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.27/S15.9.5.27_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.28/S15.9.5.28_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.29/S15.9.5.29_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.30/S15.9.5.30_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.31/S15.9.5.31_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.32/S15.9.5.32_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.33/S15.9.5.33_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.34/S15.9.5.34_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.35/S15.9.5.35_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.36/S15.9.5.36_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.37/S15.9.5.37_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.38/S15.9.5.38_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.39/S15.9.5.39_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.40/S15.9.5.40_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.41/S15.9.5.41_A3_T2.js +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.42/S15.9.5.42_A3_T2.js +test262o/test/suite/ch15/15.10/15.10.6/15.10.6.2/S15.10.6.2_A9.js +test262o/test/suite/ch15/15.10/15.10.6/15.10.6.3/S15.10.6.3_A9.js +test262o/test/suite/ch15/15.10/15.10.6/15.10.6.4/S15.10.6.4_A9.js + +# ES6 != ES5: object literals may have duplicates +test262o/test/suite/ch11/11.1/11.1.5/11.1.5-4-4-a-1-s.js +test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-b-1.js +test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-b-2.js +test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-c-1.js +test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-c-2.js +test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-1.js +test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-2.js +test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-3.js +test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-4.js + +# ES6 != ES5: Date.prototype is no longer an instance of Date +test262o/test/suite/ch15/15.9/15.9.5/15.9.5.40/15.9.5.40_1.js + +# ES6 != ES5: Object.getPrototypeOf converts argument to object +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1-3.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1-4.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1.js + +# ES6 != ES5: Object.getPrototypeOf(NativeError) +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-12.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-13.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-14.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-15.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-16.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-17.js + +# ES6 != ES5: Object.getOwnPropertyDescriptor converts argument to object +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1-3.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1-4.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1.js + +# ES6 != ES5: Object.getOwnPropertyNames converts argument to object +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1-4.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1-5.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1.js + +# ES6 != ES5: Object.seal accepts all types +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-1.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-2.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-3.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-4.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1.js + +# ES6 != ES5: Object.freeze accepts all types +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-1.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-2.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-3.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-4.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1.js + +# ES6 != ES5: Object.preventExtensions accepts all types +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-1.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-2.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-3.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-4.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1.js + +# ES6 != ES5: Object.isSealed accepts all types +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.11/15.2.3.11-1.js + +# ES6 != ES5: Object.isFrozen accepts all types +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-1.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-2.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-3.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-4.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1.js + +# ES6 != ES5: Object.isExtensible accepts all types +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-1.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-2.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-3.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-4.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1.js + +# ES6 != ES5: Object.keys converts argument to object +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-1.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-2.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-3.js + +# ES6 != ES5: source and other properties of RegExp.prototype are not own properties +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-212.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-213.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-214.js +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-215.js + +# ES6 != ES5: String numeric object properties are enumerated first +test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-4-44.js + +# ES6: new RegExp(regex, flags) is valid +test262o/test/suite/ch15/15.10/15.10.3/S15.10.3.1_A2_T1.js +test262o/test/suite/ch15/15.10/15.10.3/S15.10.3.1_A2_T2.js +test262o/test/suite/ch15/15.10/15.10.4/15.10.4.1/15.10.4.1-1.js +test262o/test/suite/ch15/15.10/15.10.4/S15.10.4.1_A2_T1.js +test262o/test/suite/ch15/15.10/15.10.4/S15.10.4.1_A2_T2.js + +# ES6 != ES5: RegExp.prototype.test behavior +test262o/test/suite/ch15/15.10/15.10.6/15.10.6.2/S15.10.6.2_A5_T3.js + +# ES6 != ES5: source, global, ignoreCase, multiline, lastIndex are not data properties +# of RegExp objects and RegExp.prototype is not a RegExp object +test262o/test/suite/ch15/15.10/15.10.6/15.10.6.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/15.10.7.1-1.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/15.10.7.1-2.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A8.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A9.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A10.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/15.10.7.2-1.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/15.10.7.2-2.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A8.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A9.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A10.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/15.10.7.3-1.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/15.10.7.3-2.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A8.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A9.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A10.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/15.10.7.4-1.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/15.10.7.4-2.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A8.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A9.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A10.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.5/15.10.7.5-1.js +test262o/test/suite/ch15/15.10/15.10.7/15.10.7.5/15.10.7.5-2.js + +# ES6 != ES5: Error.prototype is a normal object +test262o/test/suite/ch15/15.11/15.11.4/S15.11.4_A2.js + +# ES6 different ToLength() semantics +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.5/S15.4.4.5_A4_T3.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A2_T2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T1.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T3.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A2_T2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A4_T1.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A4_T3.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.8/S15.4.4.8_A3_T3.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.9/S15.4.4.9_A3_T3.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T1.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T3.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.11/S15.4.4.11_A4_T3.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A3_T1.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A3_T3.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.13/S15.4.4.13_A3_T2.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-8.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-14.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-25.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-28.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-29.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-25.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-28.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-8.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-14.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-25.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-29.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-8.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-14.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-25.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-28.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-29.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-25.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-8.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-14.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-25.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-28.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-29.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-25.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-25.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-7.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-12.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-25.js + +# ES6 different ToLength() semantics causes near infinite runtime +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-14.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-14.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-14.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-14.js +test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-14.js + +# ES6 arguments/caller changes +test262o/test/suite/ch10/10.6/10.6-13-b-1-s.js +test262o/test/suite/ch10/10.6/10.6-13-b-2-s.js +test262o/test/suite/ch10/10.6/10.6-13-b-3-s.js +test262o/test/suite/ch10/10.6/10.6-14-1-s.js +test262o/test/suite/ch10/10.6/10.6-14-b-1-s.js +test262o/test/suite/ch10/10.6/10.6-14-b-4-s.js +test262o/test/suite/ch13/13.2/13.2-29-s.js +test262o/test/suite/ch13/13.2/13.2-30-s.js +test262o/test/suite/ch13/13.2/13.2-31-s.js +test262o/test/suite/ch13/13.2/13.2-32-s.js +test262o/test/suite/ch13/13.2/13.2-33-s.js +test262o/test/suite/ch13/13.2/13.2-34-s.js +test262o/test/suite/ch13/13.2/13.2-35-s.js +test262o/test/suite/ch13/13.2/13.2-36-s.js +test262o/test/suite/ch13/13.2/S13.2.3_A1.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-1.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-4.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-5.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-1.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-4.js +test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-5.js + +# u180e is no longer considered as a space +test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A2.js +test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A3_T1.js +test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A3_T2.js +test262o/test/suite/ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A2_T10.js +test262o/test/suite/ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A2_T10.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-2.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-3.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-4.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-5.js +test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-6.js +test262o/test/suite/ch15/15.10/15.10.2/15.10.2.12/S15.10.2.12_A1_T1.js +test262o/test/suite/ch15/15.10/15.10.2/15.10.2.12/S15.10.2.12_A2_T1.js + +# E6 eval return value is different +test262o/test/suite/ch12/12.6/12.6.3/S12.6.3_A9.js +test262o/test/suite/ch12/12.6/12.6.3/S12.6.3_A9.1.js + +# ECMA 2019 optional-catch-binding feature allows try{}catch{} +test262o/test/suite/ch12/12.14/S12.14_A16_T4.js + +# Syntax error instead of ReferenceError in ES2020 +test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-1.js +test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-2.js +test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-3.js +test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-4.js + +[tests] +# list test files or use config.testdir diff --git a/test262o_errors.txt b/test262o_errors.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/bjson.c b/tests/bjson.c new file mode 100644 index 0000000..9631300 --- /dev/null +++ b/tests/bjson.c @@ -0,0 +1,88 @@ +/* + * QuickJS: binary JSON module (test only) + * + * Copyright (c) 2017-2019 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 "../quickjs-libc.h" +#include "../cutils.h" + +static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint8_t *buf; + uint64_t pos, len; + JSValue obj; + size_t size; + + if (JS_ToIndex(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[0]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "array buffer overflow"); + obj = JS_ReadObject(ctx, buf + pos, len, 0); + return obj; +} + +static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + size_t len; + uint8_t *buf; + JSValue array; + + buf = JS_WriteObject(ctx, &len, argv[0], 0); + if (!buf) + return JS_EXCEPTION; + array = JS_NewArrayBufferCopy(ctx, buf, len); + js_free(ctx, buf); + return array; +} + +static const JSCFunctionListEntry js_bjson_funcs[] = { + JS_CFUNC_DEF("read", 3, js_bjson_read ), + JS_CFUNC_DEF("write", 1, js_bjson_write ), +}; + +static int js_bjson_init(JSContext *ctx, JSModuleDef *m) +{ + return JS_SetModuleExportList(ctx, m, js_bjson_funcs, + countof(js_bjson_funcs)); +} + +#ifdef JS_SHARED_LIBRARY +#define JS_INIT_MODULE js_init_module +#else +#define JS_INIT_MODULE js_init_module_bjson +#endif + +JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_bjson_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_bjson_funcs, countof(js_bjson_funcs)); + return m; +} diff --git a/tests/microbench.js b/tests/microbench.js new file mode 100644 index 0000000..b79444d --- /dev/null +++ b/tests/microbench.js @@ -0,0 +1,1053 @@ +/* + * Javascript Micro benchmark + * + * Copyright (c) 2017-2019 Fabrice Bellard + * Copyright (c) 2017-2019 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. + */ +import * as std from "std"; + +function pad(str, n) { + str += ""; + while (str.length < n) + str += " "; + return str; +} + +function pad_left(str, n) { + str += ""; + while (str.length < n) + str = " " + str; + return str; +} + +function pad_center(str, n) { + str += ""; + while (str.length < n) { + if ((n - str.length) & 1) + str = str + " "; + else + str = " " + str; + } + return str; +} + +function toPrec(n, prec) { + var i, s; + for (i = 0; i < prec; i++) + n *= 10; + s = "" + Math.round(n); + for (i = s.length - prec; i <= 0; i++) + s = "0" + s; + if (prec > 0) + s = s.substring(0, i) + "." + s.substring(i); + return s; +} + +var ref_data; +var log_data; + +var heads = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (%)" ]; +var widths = [ 22, 10, 9, 9, 9 ]; +var precs = [ 0, 0, 2, 2, 2 ]; +var total = [ 0, 0, 0, 0, 0 ]; +var total_score = 0; +var total_scale = 0; + +if (typeof console == "undefined") { + var console = { log: print }; +} + +function log_line() { + var i, n, s, a; + s = ""; + for (i = 0, n = arguments.length; i < n; i++) { + if (i > 0) + s += " "; + a = arguments[i]; + if (typeof a == "number") { + total[i] += a; + a = toPrec(a, precs[i]); + s += pad_left(a, widths[i]); + } else { + s += pad_left(a, widths[i]); + } + } + console.log(s); +} + +var clocks_per_sec = 1000000; +var max_iterations = 100; +var clock_threshold = 2000; /* favoring short measuring spans */ +var min_n_argument = 1; +var get_clock; + +if (typeof globalThis.__date_clock != "function") { + console.log("using fallback millisecond clock"); + clocks_per_sec = 1000; + max_iterations = 10; + clock_threshold = 100; + get_clock = Date.now; +} else { + get_clock = globalThis.__date_clock; +} + +function log_one(text, n, ti) { + var ref; + + if (ref_data) + ref = ref_data[text]; + else + ref = null; + + ti = Math.round(ti * 100) / 100; + log_data[text] = ti; + if (typeof ref === "number") { + log_line(text, n, ti, ref, ti * 100 / ref); + total_score += ti * 100 / ref; + total_scale += 100; + } else { + log_line(text, n, ti); + total_score += 100; + total_scale += 100; + } +} + +function bench(f, text) +{ + var i, j, n, t, t1, ti, nb_its, ref, ti_n, ti_n1, min_ti; + + nb_its = n = 1; + if (f.bench) { + ti_n = f(text); + } else { + ti_n = 1000000000; + min_ti = clock_threshold / 10; + for(i = 0; i < 30; i++) { + ti = 1000000000; + for (j = 0; j < max_iterations; j++) { + t = get_clock(); + while ((t1 = get_clock()) == t) + continue; + nb_its = f(n); + if (nb_its < 0) + return; // test failure + t1 = get_clock() - t1; + if (ti > t1) + ti = t1; + } + if (ti >= min_ti) { + ti_n1 = ti / nb_its; + if (ti_n > ti_n1) + ti_n = ti_n1; + } + if (ti >= clock_threshold && n >= min_n_argument) + break; + + n = n * [ 2, 2.5, 2 ][i % 3]; + } + // to use only the best timing from the last loop, uncomment below + //ti_n = ti / nb_its; + } + /* nano seconds per iteration */ + log_one(text, n, ti_n * 1e9 / clocks_per_sec); +} + +var global_res; /* to be sure the code is not optimized */ + +function empty_loop(n) { + var j; + for(j = 0; j < n; j++) { + } + return n; +} + +function date_now(n) { + var j; + for(j = 0; j < n; j++) { + Date.now(); + } + return n; +} + +function prop_read(n) +{ + var obj, sum, j; + obj = {a: 1, b: 2, c:3, d:4 }; + sum = 0; + for(j = 0; j < n; j++) { + sum += obj.a; + sum += obj.b; + sum += obj.c; + sum += obj.d; + } + global_res = sum; + return n * 4; +} + +function prop_write(n) +{ + var obj, j; + obj = {a: 1, b: 2, c:3, d:4 }; + for(j = 0; j < n; j++) { + obj.a = j; + obj.b = j; + obj.c = j; + obj.d = j; + } + return n * 4; +} + +function prop_create(n) +{ + var obj, j; + for(j = 0; j < n; j++) { + obj = new Object(); + obj.a = 1; + obj.b = 2; + obj.c = 3; + obj.d = 4; + } + return n * 4; +} + +function array_read(n) +{ + var tab, len, sum, i, j; + tab = []; + len = 10; + for(i = 0; i < len; i++) + tab[i] = i; + sum = 0; + for(j = 0; j < n; j++) { + sum += tab[0]; + sum += tab[1]; + sum += tab[2]; + sum += tab[3]; + sum += tab[4]; + sum += tab[5]; + sum += tab[6]; + sum += tab[7]; + sum += tab[8]; + sum += tab[9]; + } + global_res = sum; + return len * n; +} + +function array_write(n) +{ + var tab, len, i, j; + tab = []; + len = 10; + for(i = 0; i < len; i++) + tab[i] = i; + for(j = 0; j < n; j++) { + tab[0] = j; + tab[1] = j; + tab[2] = j; + tab[3] = j; + tab[4] = j; + tab[5] = j; + tab[6] = j; + tab[7] = j; + tab[8] = j; + tab[9] = j; + } + return len * n; +} + +function array_prop_create(n) +{ + var tab, i, j, len; + len = 1000; + for(j = 0; j < n; j++) { + tab = []; + for(i = 0; i < len; i++) + tab[i] = i; + } + return len * n; +} + +function array_length_decr(n) +{ + var tab, i, j, len; + len = 1000; + tab = []; + for(i = 0; i < len; i++) + tab[i] = i; + for(j = 0; j < n; j++) { + for(i = len - 1; i >= 0; i--) + tab.length = i; + } + return len * n; +} + +function array_hole_length_decr(n) +{ + var tab, i, j, len; + len = 1000; + tab = []; + for(i = 0; i < len; i++) { + if (i != 3) + tab[i] = i; + } + for(j = 0; j < n; j++) { + for(i = len - 1; i >= 0; i--) + tab.length = i; + } + return len * n; +} + +function array_push(n) +{ + var tab, i, j, len; + len = 500; + for(j = 0; j < n; j++) { + tab = []; + for(i = 0; i < len; i++) + tab.push(i); + } + return len * n; +} + +function array_pop(n) +{ + var tab, i, j, len, sum; + len = 500; + for(j = 0; j < n; j++) { + tab = []; + for(i = 0; i < len; i++) + tab[i] = i; + sum = 0; + for(i = 0; i < len; i++) + sum += tab.pop(); + global_res = sum; + } + return len * n; +} + +function typed_array_read(n) +{ + var tab, len, sum, i, j; + len = 10; + tab = new Int32Array(len); + for(i = 0; i < len; i++) + tab[i] = i; + sum = 0; + for(j = 0; j < n; j++) { + sum += tab[0]; + sum += tab[1]; + sum += tab[2]; + sum += tab[3]; + sum += tab[4]; + sum += tab[5]; + sum += tab[6]; + sum += tab[7]; + sum += tab[8]; + sum += tab[9]; + } + global_res = sum; + return len * n; +} + +function typed_array_write(n) +{ + var tab, len, i, j; + len = 10; + tab = new Int32Array(len); + for(i = 0; i < len; i++) + tab[i] = i; + for(j = 0; j < n; j++) { + tab[0] = j; + tab[1] = j; + tab[2] = j; + tab[3] = j; + tab[4] = j; + tab[5] = j; + tab[6] = j; + tab[7] = j; + tab[8] = j; + tab[9] = j; + } + return len * n; +} + +var global_var0; + +function global_read(n) +{ + var sum, j; + global_var0 = 0; + sum = 0; + for(j = 0; j < n; j++) { + sum += global_var0; + sum += global_var0; + sum += global_var0; + sum += global_var0; + } + global_res = sum; + return n * 4; +} + +var global_write = + (1, eval)(`(function global_write(n) + { + var j; + for(j = 0; j < n; j++) { + global_var0 = j; + global_var0 = j; + global_var0 = j; + global_var0 = j; + } + return n * 4; + })`); + +function global_write_strict(n) +{ + var j; + for(j = 0; j < n; j++) { + global_var0 = j; + global_var0 = j; + global_var0 = j; + global_var0 = j; + } + return n * 4; +} + +function local_destruct(n) +{ + var j, v1, v2, v3, v4; + var array = [ 1, 2, 3, 4, 5]; + var o = { a:1, b:2, c:3, d:4 }; + var a, b, c, d; + for(j = 0; j < n; j++) { + [ v1, v2,, v3, ...v4] = array; + ({ a, b, c, d } = o); + ({ a: a, b: b, c: c, d: d } = o); + } + return n * 12; +} + +var global_v1, global_v2, global_v3, global_v4; +var global_a, global_b, global_c, global_d; + +var global_destruct = + (1, eval)(`(function global_destruct(n) + { + var j, v1, v2, v3, v4; + var array = [ 1, 2, 3, 4, 5 ]; + var o = { a:1, b:2, c:3, d:4 }; + var a, b, c, d; + for(j = 0; j < n; j++) { + [ global_v1, global_v2,, global_v3, ...global_v4] = array; + ({ a: global_a, b: global_b, c: global_c, d: global_d } = o); + } + return n * 8; + })`); + +function global_destruct_strict(n) +{ + var j, v1, v2, v3, v4; + var array = [ 1, 2, 3, 4, 5 ]; + var o = { a:1, b:2, c:3, d:4 }; + var a, b, c, d; + for(j = 0; j < n; j++) { + [ global_v1, global_v2,, global_v3, ...global_v4] = array; + ({ a: global_a, b: global_b, c: global_c, d: global_d } = o); + } + return n * 8; +} + +function func_call(n) +{ + function f(a) + { + return 1; + } + + var j, sum; + sum = 0; + for(j = 0; j < n; j++) { + sum += f(j); + sum += f(j); + sum += f(j); + sum += f(j); + } + global_res = sum; + return n * 4; +} + +function closure_var(n) +{ + function f(a) + { + sum++; + } + + var j, sum; + sum = 0; + for(j = 0; j < n; j++) { + f(j); + f(j); + f(j); + f(j); + } + global_res = sum; + return n * 4; +} + +function int_arith(n) +{ + var i, j, sum; + global_res = 0; + for(j = 0; j < n; j++) { + sum = 0; + for(i = 0; i < 1000; i++) { + sum += i * i; + } + global_res += sum; + } + return n * 1000; +} + +function float_arith(n) +{ + var i, j, sum, a, incr, a0; + global_res = 0; + a0 = 0.1; + incr = 1.1; + for(j = 0; j < n; j++) { + sum = 0; + a = a0; + for(i = 0; i < 1000; i++) { + sum += a * a; + a += incr; + } + global_res += sum; + } + return n * 1000; +} + +function bigfloat_arith(n) +{ + var i, j, sum, a, incr, a0; + global_res = 0; + a0 = BigFloat("0.1"); + incr = BigFloat("1.1"); + for(j = 0; j < n; j++) { + sum = 0; + a = a0; + for(i = 0; i < 1000; i++) { + sum += a * a; + a += incr; + } + global_res += sum; + } + return n * 1000; +} + +function float256_arith(n) +{ + return BigFloatEnv.setPrec(bigfloat_arith.bind(null, n), 237, 19); +} + +function bigint_arith(n, bits) +{ + var i, j, sum, a, incr, a0, sum0; + sum0 = global_res = BigInt(0); + a0 = BigInt(1) << BigInt(Math.floor((bits - 10) * 0.5)); + incr = BigInt(1); + for(j = 0; j < n; j++) { + sum = sum0; + a = a0; + for(i = 0; i < 1000; i++) { + sum += a * a; + a += incr; + } + global_res += sum; + } + return n * 1000; +} + +function bigint64_arith(n) +{ + return bigint_arith(n, 64); +} + +function bigint256_arith(n) +{ + return bigint_arith(n, 256); +} + +function set_collection_add(n) +{ + var s, i, j, len = 100; + s = new Set(); + for(j = 0; j < n; j++) { + for(i = 0; i < len; i++) { + s.add(String(i), i); + } + for(i = 0; i < len; i++) { + if (!s.has(String(i))) + throw Error("bug in Set"); + } + } + return n * len; +} + +function array_for(n) +{ + var r, i, j, sum; + r = []; + for(i = 0; i < 100; i++) + r[i] = i; + for(j = 0; j < n; j++) { + sum = 0; + for(i = 0; i < 100; i++) { + sum += r[i]; + } + global_res = sum; + } + return n * 100; +} + +function array_for_in(n) +{ + var r, i, j, sum; + r = []; + for(i = 0; i < 100; i++) + r[i] = i; + for(j = 0; j < n; j++) { + sum = 0; + for(i in r) { + sum += r[i]; + } + global_res = sum; + } + return n * 100; +} + +function array_for_of(n) +{ + var r, i, j, sum; + r = []; + for(i = 0; i < 100; i++) + r[i] = i; + for(j = 0; j < n; j++) { + sum = 0; + for(i of r) { + sum += i; + } + global_res = sum; + } + return n * 100; +} + +function math_min(n) +{ + var i, j, r; + r = 0; + for(j = 0; j < n; j++) { + for(i = 0; i < 1000; i++) + r = Math.min(i, 500); + global_res = r; + } + return n * 1000; +} + +/* incremental string contruction as local var */ +function string_build1(n) +{ + var i, j, r; + r = ""; + for(j = 0; j < n; j++) { + for(i = 0; i < 100; i++) + r += "x"; + global_res = r; + } + return n * 100; +} + +/* incremental string contruction as arg */ +function string_build2(n, r) +{ + var i, j; + r = ""; + for(j = 0; j < n; j++) { + for(i = 0; i < 100; i++) + r += "x"; + global_res = r; + } + return n * 100; +} + +/* incremental string contruction by prepending */ +function string_build3(n, r) +{ + var i, j; + r = ""; + for(j = 0; j < n; j++) { + for(i = 0; i < 100; i++) + r = "x" + r; + global_res = r; + } + return n * 100; +} + +/* incremental string contruction with multiple reference */ +function string_build4(n) +{ + var i, j, r, s; + r = ""; + for(j = 0; j < n; j++) { + for(i = 0; i < 100; i++) { + s = r; + r += "x"; + } + global_res = r; + } + return n * 100; +} + +/* sort bench */ + +function sort_bench(text) { + function random(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[(Math.random() * n) >> 0]; + } + function random8(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[(Math.random() * 256) >> 0]; + } + function random1(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[(Math.random() * 2) >> 0]; + } + function hill(arr, n, def) { + var mid = n >> 1; + for (var i = 0; i < mid; i++) + arr[i] = def[i]; + for (var i = mid; i < n; i++) + arr[i] = def[n - i]; + } + function comb(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[(i & 1) * i]; + } + function crisscross(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[(i & 1) ? n - i : i]; + } + function zero(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[0]; + } + function increasing(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[i]; + } + function decreasing(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[n - 1 - i]; + } + function alternate(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[i ^ 1]; + } + function jigsaw(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[i % (n >> 4)]; + } + function incbutone(arr, n, def) { + for (var i = 0; i < n; i++) + arr[i] = def[i]; + if (n > 0) + arr[n >> 2] = def[n]; + } + function incbutfirst(arr, n, def) { + if (n > 0) + arr[0] = def[n]; + for (var i = 1; i < n; i++) + arr[i] = def[i]; + } + function incbutlast(arr, n, def) { + for (var i = 0; i < n - 1; i++) + arr[i] = def[i + 1]; + if (n > 0) + arr[n - 1] = def[0]; + } + + var sort_cases = [ random, random8, random1, jigsaw, hill, comb, + crisscross, zero, increasing, decreasing, alternate, + incbutone, incbutlast, incbutfirst ]; + + var n = sort_bench.array_size || 10000; + var array_type = sort_bench.array_type || Array; + var def, arr; + var i, j, x, y; + var total = 0; + + var save_total_score = total_score; + var save_total_scale = total_scale; + + // initialize default sorted array (n + 1 elements) + def = new array_type(n + 1); + if (array_type == Array) { + for (i = 0; i <= n; i++) { + def[i] = i + ""; + } + } else { + for (i = 0; i <= n; i++) { + def[i] = i; + } + } + def.sort(); + for (var f of sort_cases) { + var ti = 0, tx = 0; + for (j = 0; j < 100; j++) { + arr = new array_type(n); + f(arr, n, def); + var t1 = get_clock(); + arr.sort(); + t1 = get_clock() - t1; + tx += t1; + if (!ti || ti > t1) + ti = t1; + if (tx >= clocks_per_sec) + break; + } + total += ti; + + i = 0; + x = arr[0]; + if (x !== void 0) { + for (i = 1; i < n; i++) { + y = arr[i]; + if (y === void 0) + break; + if (x > y) + break; + x = y; + } + } + while (i < n && arr[i] === void 0) + i++; + if (i < n) { + console.log("sort_bench: out of order error for " + f.name + + " at offset " + (i - 1) + + ": " + arr[i - 1] + " > " + arr[i]); + } + if (sort_bench.verbose) + log_one("sort_" + f.name, n, ti, n * 100); + } + total_score = save_total_score; + total_scale = save_total_scale; + return total / n / 1000; +} +sort_bench.bench = true; +sort_bench.verbose = false; + +function int_to_string(n) +{ + var s, r, j; + r = 0; + for(j = 0; j < n; j++) { + s = (j + 1).toString(); + } + return n; +} + +function float_to_string(n) +{ + var s, r, j; + r = 0; + for(j = 0; j < n; j++) { + s = (j + 0.1).toString(); + } + return n; +} + +function string_to_int(n) +{ + var s, r, j; + r = 0; + s = "12345"; + r = 0; + for(j = 0; j < n; j++) { + r += (s | 0); + } + global_res = r; + return n; +} + +function string_to_float(n) +{ + var s, r, j; + r = 0; + s = "12345.6"; + r = 0; + for(j = 0; j < n; j++) { + r -= s; + } + global_res = r; + return n; +} + +function load_result(filename) +{ + var f, str, res; + if (typeof std === "undefined") + return null; + try { + f = std.open(filename, "r"); + } catch(e) { + return null; + } + str = f.readAsString(); + res = JSON.parse(str); + f.close(); + return res; +} + +function save_result(filename, obj) +{ + var f; + if (typeof std === "undefined") + return; + f = std.open(filename, "w"); + f.puts(JSON.stringify(obj, null, 2)); + f.puts("\n"); + f.close(); +} + +function main(argc, argv, g) +{ + var test_list = [ + empty_loop, + date_now, + prop_read, + prop_write, + prop_create, + array_read, + array_write, + array_prop_create, + array_length_decr, + array_hole_length_decr, + array_push, + array_pop, + typed_array_read, + typed_array_write, + global_read, + global_write, + global_write_strict, + local_destruct, + global_destruct, + global_destruct_strict, + func_call, + closure_var, + int_arith, + float_arith, + set_collection_add, + array_for, + array_for_in, + array_for_of, + math_min, + string_build1, + string_build2, + //string_build3, + //string_build4, + sort_bench, + int_to_string, + float_to_string, + string_to_int, + string_to_float, + ]; + var tests = []; + var i, j, n, f, name; + + if (typeof BigInt == "function") { + /* BigInt test */ + test_list.push(bigint64_arith); + test_list.push(bigint256_arith); + } + if (typeof BigFloat == "function") { + /* BigFloat test */ + test_list.push(float256_arith); + } + + for (i = 1; i < argc;) { + name = argv[i++]; + if (name == "-a") { + sort_bench.verbose = true; + continue; + } + if (name == "-t") { + name = argv[i++]; + sort_bench.array_type = g[name]; + if (typeof sort_bench.array_type != "function") { + console.log("unknown array type: " + name); + return 1; + } + continue; + } + if (name == "-n") { + sort_bench.array_size = +argv[i++]; + continue; + } + for (j = 0; j < test_list.length; j++) { + f = test_list[j]; + if (name === f.name) { + tests.push(f); + break; + } + } + if (j == test_list.length) { + console.log("unknown benchmark: " + name); + return 1; + } + } + if (tests.length == 0) + tests = test_list; + + ref_data = load_result("microbench.txt"); + log_data = {}; + log_line.apply(null, heads); + n = 0; + + for(i = 0; i < tests.length; i++) { + f = tests[i]; + bench(f, f.name, ref_data, log_data); + if (ref_data && ref_data[f.name]) + n++; + } + if (ref_data) + log_line("total", "", total[2], total[3], total_score * 100 / total_scale); + else + log_line("total", "", total[2]); + + if (tests == test_list) + save_result("microbench-new.txt", log_data); +} + +if (!scriptArgs) + scriptArgs = []; +main(scriptArgs.length, scriptArgs, this); diff --git a/tests/test262.patch b/tests/test262.patch new file mode 100644 index 0000000..6576bdc --- /dev/null +++ b/tests/test262.patch @@ -0,0 +1,71 @@ +diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js +index 9c1217351e..3c24755558 100644 +--- a/harness/atomicsHelper.js ++++ b/harness/atomicsHelper.js +@@ -227,10 +227,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) { + * } + */ + $262.agent.timeouts = { +- yield: 100, +- small: 200, +- long: 1000, +- huge: 10000, ++// yield: 100, ++// small: 200, ++// long: 1000, ++// huge: 10000, ++ yield: 20, ++ small: 20, ++ long: 100, ++ huge: 1000, + }; + + /** +diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js +index be7039fda0..7b38abf8df 100644 +--- a/harness/regExpUtils.js ++++ b/harness/regExpUtils.js +@@ -6,24 +6,27 @@ description: | + defines: [buildString, testPropertyEscapes, matchValidator] + ---*/ + ++if ($262 && typeof $262.codePointRange === "function") { ++ /* use C function to build the codePointRange (much faster with ++ slow JS engines) */ ++ codePointRange = $262.codePointRange; ++} else { ++ codePointRange = function codePointRange(start, end) { ++ const codePoints = []; ++ let length = 0; ++ for (codePoint = start; codePoint < end; codePoint++) { ++ codePoints[length++] = codePoint; ++ } ++ return String.fromCodePoint.apply(null, codePoints); ++ } ++} ++ + function buildString({ loneCodePoints, ranges }) { +- const CHUNK_SIZE = 10000; +- let result = Reflect.apply(String.fromCodePoint, null, loneCodePoints); +- for (let i = 0; i < ranges.length; i++) { +- const range = ranges[i]; +- const start = range[0]; +- const end = range[1]; +- const codePoints = []; +- for (let length = 0, codePoint = start; codePoint <= end; codePoint++) { +- codePoints[length++] = codePoint; +- if (length === CHUNK_SIZE) { +- result += Reflect.apply(String.fromCodePoint, null, codePoints); +- codePoints.length = length = 0; +- } ++ let result = String.fromCodePoint.apply(null, loneCodePoints); ++ for (const [start, end] of ranges) { ++ result += codePointRange(start, end + 1); + } +- result += Reflect.apply(String.fromCodePoint, null, codePoints); +- } +- return result; ++ return result; + } + + function testPropertyEscapes(regex, string, expression) { diff --git a/tests/test_bignum.js b/tests/test_bignum.js new file mode 100644 index 0000000..0745929 --- /dev/null +++ b/tests/test_bignum.js @@ -0,0 +1,241 @@ +"use strict"; + +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +function assertThrows(err, func) +{ + var ex; + ex = false; + try { + func(); + } catch(e) { + ex = true; + assert(e instanceof err); + } + assert(ex, true, "exception expected"); +} + +// load more elaborate version of assert if available +try { __loadScript("test_assert.js"); } catch(e) {} + +/*----------------*/ + +function bigint_pow(a, n) +{ + var r, i; + r = 1n; + for(i = 0n; i < n; i++) + r *= a; + return r; +} + +/* a must be < b */ +function test_less(a, b) +{ + assert(a < b); + assert(!(b < a)); + assert(a <= b); + assert(!(b <= a)); + assert(b > a); + assert(!(a > b)); + assert(b >= a); + assert(!(a >= b)); + assert(a != b); + assert(!(a == b)); +} + +/* a must be numerically equal to b */ +function test_eq(a, b) +{ + assert(a == b); + assert(b == a); + assert(!(a != b)); + assert(!(b != a)); + assert(a <= b); + assert(b <= a); + assert(!(a < b)); + assert(a >= b); + assert(b >= a); + assert(!(a > b)); +} + +function test_bigint1() +{ + var a, r; + + test_less(2n, 3n); + test_eq(3n, 3n); + + test_less(2, 3n); + test_eq(3, 3n); + + test_less(2.1, 3n); + test_eq(Math.sqrt(4), 2n); + + a = bigint_pow(3n, 100n); + assert((a - 1n) != a); + assert(a == 515377520732011331036461129765621272702107522001n); + assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1n); + + r = 1n << 31n; + assert(r, 2147483648n, "1 << 31n === 2147483648n"); + + r = 1n << 32n; + assert(r, 4294967296n, "1 << 32n === 4294967296n"); +} + +function test_bigint2() +{ + assert(BigInt(""), 0n); + assert(BigInt(" 123"), 123n); + assert(BigInt(" 123 "), 123n); + assertThrows(SyntaxError, () => { BigInt("+") } ); + assertThrows(SyntaxError, () => { BigInt("-") } ); + assertThrows(SyntaxError, () => { BigInt("\x00a") } ); + assertThrows(SyntaxError, () => { BigInt(" 123 r") } ); +} + +function test_divrem(div1, a, b, q) +{ + var div, divrem, t; + div = BigInt[div1]; + divrem = BigInt[div1 + "rem"]; + assert(div(a, b) == q); + t = divrem(a, b); + assert(t[0] == q); + assert(a == b * q + t[1]); +} + +function test_idiv1(div, a, b, r) +{ + test_divrem(div, a, b, r[0]); + test_divrem(div, -a, b, r[1]); + test_divrem(div, a, -b, r[2]); + test_divrem(div, -a, -b, r[3]); +} + +/* QuickJS BigInt extensions */ +function test_bigint_ext() +{ + var r; + assert(BigInt.floorLog2(0n) === -1n); + assert(BigInt.floorLog2(7n) === 2n); + + assert(BigInt.sqrt(0xffffffc000000000000000n) === 17592185913343n); + r = BigInt.sqrtrem(0xffffffc000000000000000n); + assert(r[0] === 17592185913343n); + assert(r[1] === 35167191957503n); + + test_idiv1("tdiv", 3n, 2n, [1n, -1n, -1n, 1n]); + test_idiv1("fdiv", 3n, 2n, [1n, -2n, -2n, 1n]); + test_idiv1("cdiv", 3n, 2n, [2n, -1n, -1n, 2n]); + test_idiv1("ediv", 3n, 2n, [1n, -2n, -1n, 2n]); +} + +function test_bigfloat() +{ + var e, a, b, sqrt2; + + assert(typeof 1n === "bigint"); + assert(typeof 1l === "bigfloat"); + assert(1 == 1.0l); + assert(1 !== 1.0l); + + test_less(2l, 3l); + test_eq(3l, 3l); + + test_less(2, 3l); + test_eq(3, 3l); + + test_less(2.1, 3l); + test_eq(Math.sqrt(9), 3l); + + test_less(2n, 3l); + test_eq(3n, 3l); + + e = new BigFloatEnv(128); + assert(e.prec == 128); + a = BigFloat.sqrt(2l, e); + assert(a == BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e)); + assert(e.inexact === true); + assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcc908b2fb1366ea95l); + + b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128); + assert(a == b); +} + +function test_bigdecimal() +{ + assert(1d === 1d); + assert(1d !== 2d); + test_less(1d, 2d); + test_eq(2d, 2d); + + test_less(1, 2d); + test_eq(2, 2d); + + test_less(1.1, 2d); + test_eq(Math.sqrt(4), 2d); + + test_less(2n, 3d); + test_eq(3n, 3d); + + assert(BigDecimal("1234.1") === 1234.1d); + assert(BigDecimal(" 1234.1") === 1234.1d); + assert(BigDecimal(" 1234.1 ") === 1234.1d); + + assert(BigDecimal(0.1) === 0.1d); + assert(BigDecimal(123) === 123d); + assert(BigDecimal(true) === 1d); + + assert(123d + 1d === 124d); + assert(123d - 1d === 122d); + + assert(3.2d * 3d === 9.6d); + assert(10d / 2d === 5d); + assertThrows(RangeError, () => { 10d / 3d } ); + assert(BigDecimal.div(20d, 3d, + { roundingMode: "half-even", + maximumSignificantDigits: 3 }) === 6.67d); + assert(BigDecimal.div(20d, 3d, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 6.667d); + + assert(10d % 3d === 1d); + assert(-10d % 3d === -1d); + + assert(-10d % 3d === -1d); + + assert(1234.5d ** 3d === 1881365963.625d); + assertThrows(RangeError, () => { 2d ** 3.1d } ); + assertThrows(RangeError, () => { 2d ** -3d } ); + + assert(BigDecimal.sqrt(2d, + { roundingMode: "half-even", + maximumSignificantDigits: 4 }) === 1.414d); + + assert(BigDecimal.round(3.14159d, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 3.142d); +} + +test_bigint1(); +test_bigint2(); +test_bigint_ext(); +test_bigfloat(); +test_bigdecimal(); diff --git a/tests/test_bjson.js b/tests/test_bjson.js new file mode 100644 index 0000000..a6196df --- /dev/null +++ b/tests/test_bjson.js @@ -0,0 +1,125 @@ +import * as bjson from "./bjson.so"; + +function assert(b, str) +{ + if (b) { + return; + } else { + throw Error("assertion failed: " + str); + } +} + +function toHex(a) +{ + var i, s = "", tab, v; + tab = new Uint8Array(a); + for(i = 0; i < tab.length; i++) { + v = tab[i].toString(16); + if (v.length < 2) + v = "0" + v; + if (i !== 0) + s += " "; + s += v; + } + return s; +} + +function toStr(a) +{ + var s, i, props, prop; + + switch(typeof(a)) { + case "object": + if (a === null) + return "null"; + if (Array.isArray(a)) { + s = "["; + for(i = 0; i < a.length; i++) { + if (i != 0) + s += ","; + s += toStr(a[i]); + } + s += "]"; + } else { + props = Object.keys(a); + s = "{"; + for(i = 0; i < props.length; i++) { + if (i != 0) + s += ","; + prop = props[i]; + s += prop + ":" + toStr(a[prop]); + } + s += "}"; + } + return s; + case "undefined": + return "undefined"; + case "string": + return a.__quote(); + case "number": + case "bigfloat": + if (a == 0 && 1 / a < 0) + return "-0"; + else + return a.toString(); + break; + default: + return a.toString(); + } +} + +function bjson_test(a) +{ + var buf, r, a_str, r_str; + a_str = toStr(a); + buf = bjson.write(a); + if (0) { + print(a_str, "->", toHex(buf)); + } + r = bjson.read(buf, 0, buf.byteLength); + r_str = toStr(r); + if (a_str != r_str) { + print(a_str); + print(r_str); + assert(false); + } +} + +function bjson_test_all() +{ + var obj; + + bjson_test({x:1, y:2, if:3}); + bjson_test([1, 2, 3]); + bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]); + if (typeof BigInt !== "undefined") { + bjson_test([BigInt("1"), -BigInt("0x123456789"), + BigInt("0x123456789abcdef123456789abcdef")]); + } + if (typeof BigFloat !== "undefined") { + BigFloatEnv.setPrec(function () { + bjson_test([BigFloat("0.1"), BigFloat("-1e30"), BigFloat("0"), + BigFloat("-0"), BigFloat("Infinity"), BigFloat("-Infinity"), + 0.0 / BigFloat("0"), BigFloat.MAX_VALUE, + BigFloat.MIN_VALUE]); + }, 113, 15); + } + if (typeof BigDecimal !== "undefined") { + bjson_test([BigDecimal("0"), + BigDecimal("0.8"), BigDecimal("123321312321321e100"), + BigDecimal("-1233213123213214332333223332e100"), + BigDecimal("1.233e-1000")]); + } + + /* tested with a circular reference */ + obj = {}; + obj.x = obj; + try { + bjson.write(obj); + assert(false); + } catch(e) { + assert(e instanceof TypeError); + } +} + +bjson_test_all(); diff --git a/tests/test_builtin.js b/tests/test_builtin.js new file mode 100644 index 0000000..edd5df3 --- /dev/null +++ b/tests/test_builtin.js @@ -0,0 +1,647 @@ +"use strict"; + +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +// load more elaborate version of assert if available +try { __loadScript("test_assert.js"); } catch(e) {} + +/*----------------*/ + +function my_func(a, b) +{ + return a + b; +} + +function test_function() +{ + function f(a, b) { + var i, tab = []; + tab.push(this); + for(i = 0; i < arguments.length; i++) + tab.push(arguments[i]); + return tab; + } + function constructor1(a) { + this.x = a; + } + + var r, g; + + r = my_func.call(null, 1, 2); + assert(r, 3, "call"); + + r = my_func.apply(null, [1, 2]); + assert(r, 3, "apply"); + + r = new Function("a", "b", "return a + b;"); + assert(r(2,3), 5, "function"); + + g = f.bind(1, 2); + assert(g.length, 1); + assert(g.name, "bound f"); + assert(g(3), [1,2,3]); + + g = constructor1.bind(null, 1); + r = new g(); + assert(r.x, 1); +} + +function test() +{ + var r, a, b, c, err; + + r = Error("hello"); + assert(r.message, "hello", "Error"); + + a = new Object(); + a.x = 1; + assert(a.x, 1, "Object"); + + assert(Object.getPrototypeOf(a), Object.prototype, "getPrototypeOf"); + Object.defineProperty(a, "y", { value: 3, writable: true, configurable: true, enumerable: true }); + assert(a.y, 3, "defineProperty"); + + Object.defineProperty(a, "z", { get: function () { return 4; }, set: function(val) { this.z_val = val; }, configurable: true, enumerable: true }); + assert(a.z, 4, "get"); + a.z = 5; + assert(a.z_val, 5, "set"); + + a = { get z() { return 4; }, set z(val) { this.z_val = val; } }; + assert(a.z, 4, "get"); + a.z = 5; + assert(a.z_val, 5, "set"); + + b = Object.create(a); + assert(Object.getPrototypeOf(b), a, "create"); + c = {u:2}; + /* XXX: refcount bug in 'b' instead of 'a' */ + Object.setPrototypeOf(a, c); + assert(Object.getPrototypeOf(a), c, "setPrototypeOf"); + + a = {}; + assert(a.toString(), "[object Object]", "toString"); + + a = {x:1}; + assert(Object.isExtensible(a), true, "extensible"); + Object.preventExtensions(a); + + err = false; + try { + a.y = 2; + } catch(e) { + err = true; + } + assert(Object.isExtensible(a), false, "extensible"); + assert(typeof a.y, "undefined", "extensible"); + assert(err, true, "extensible"); +} + +function test_enum() +{ + var a, tab; + a = {x:1, + "18014398509481984": 1, + "9007199254740992": 1, + "9007199254740991": 1, + "4294967296": 1, + "4294967295": 1, + y:1, + "4294967294": 1, + "1": 2}; + tab = Object.keys(a); +// console.log("tab=" + tab.toString()); + assert(tab, ["1","4294967294","x","18014398509481984","9007199254740992","9007199254740991","4294967296","4294967295","y"], "keys"); +} + +function test_array() +{ + var a, err; + + a = [1, 2, 3]; + assert(a.length, 3, "array"); + assert(a[2], 3, "array1"); + + a = new Array(10); + assert(a.length, 10, "array2"); + + a = new Array(1, 2); + assert(a.length === 2 && a[0] === 1 && a[1] === 2, true, "array3"); + + a = [1, 2, 3]; + a.length = 2; + assert(a.length === 2 && a[0] === 1 && a[1] === 2, true, "array4"); + + a = []; + a[1] = 10; + a[4] = 3; + assert(a.length, 5); + + a = [1,2]; + a.length = 5; + a[4] = 1; + a.length = 4; + assert(a[4] !== 1, true, "array5"); + + a = [1,2]; + a.push(3,4); + assert(a.join(), "1,2,3,4", "join"); + + a = [1,2,3,4,5]; + Object.defineProperty(a, "3", { configurable: false }); + err = false; + try { + a.length = 2; + } catch(e) { + err = true; + } + assert(err && a.toString() === "1,2,3,4"); +} + +function test_string() +{ + var a; + a = String("abc"); + assert(a.length, 3, "string"); + assert(a[1], "b", "string"); + assert(a.charCodeAt(1), 0x62, "string"); + assert(String.fromCharCode(65), "A", "string"); + assert(String.fromCharCode.apply(null, [65, 66, 67]), "ABC", "string"); + assert(a.charAt(1), "b"); + assert(a.charAt(-1), ""); + assert(a.charAt(3), ""); + + a = "abcd"; + assert(a.substring(1, 3), "bc", "substring"); + a = String.fromCharCode(0x20ac); + assert(a.charCodeAt(0), 0x20ac, "unicode"); + assert(a, "€", "unicode"); + assert(a, "\u20ac", "unicode"); + assert(a, "\u{20ac}", "unicode"); + assert("a", "\x61", "unicode"); + + a = "\u{10ffff}"; + assert(a.length, 2, "unicode"); + assert(a, "\u{dbff}\u{dfff}", "unicode"); + assert(a.codePointAt(0), 0x10ffff); + assert(String.fromCodePoint(0x10ffff), a); + + assert("a".concat("b", "c"), "abc"); + + assert("abcabc".indexOf("cab"), 2); + assert("abcabc".indexOf("cab2"), -1); + assert("abc".indexOf("c"), 2); + + assert("aaa".indexOf("a"), 0); + assert("aaa".indexOf("a", NaN), 0); + assert("aaa".indexOf("a", -Infinity), 0); + assert("aaa".indexOf("a", -1), 0); + assert("aaa".indexOf("a", -0), 0); + assert("aaa".indexOf("a", 0), 0); + assert("aaa".indexOf("a", 1), 1); + assert("aaa".indexOf("a", 2), 2); + assert("aaa".indexOf("a", 3), -1); + assert("aaa".indexOf("a", 4), -1); + assert("aaa".indexOf("a", Infinity), -1); + + assert("aaa".indexOf(""), 0); + assert("aaa".indexOf("", NaN), 0); + assert("aaa".indexOf("", -Infinity), 0); + assert("aaa".indexOf("", -1), 0); + assert("aaa".indexOf("", -0), 0); + assert("aaa".indexOf("", 0), 0); + assert("aaa".indexOf("", 1), 1); + assert("aaa".indexOf("", 2), 2); + assert("aaa".indexOf("", 3), 3); + assert("aaa".indexOf("", 4), 3); + assert("aaa".indexOf("", Infinity), 3); + + assert("aaa".lastIndexOf("a"), 2); + assert("aaa".lastIndexOf("a", NaN), 2); + assert("aaa".lastIndexOf("a", -Infinity), 0); + assert("aaa".lastIndexOf("a", -1), 0); + assert("aaa".lastIndexOf("a", -0), 0); + assert("aaa".lastIndexOf("a", 0), 0); + assert("aaa".lastIndexOf("a", 1), 1); + assert("aaa".lastIndexOf("a", 2), 2); + assert("aaa".lastIndexOf("a", 3), 2); + assert("aaa".lastIndexOf("a", 4), 2); + assert("aaa".lastIndexOf("a", Infinity), 2); + + assert("aaa".lastIndexOf(""), 3); + assert("aaa".lastIndexOf("", NaN), 3); + assert("aaa".lastIndexOf("", -Infinity), 0); + assert("aaa".lastIndexOf("", -1), 0); + assert("aaa".lastIndexOf("", -0), 0); + assert("aaa".lastIndexOf("", 0), 0); + assert("aaa".lastIndexOf("", 1), 1); + assert("aaa".lastIndexOf("", 2), 2); + assert("aaa".lastIndexOf("", 3), 3); + assert("aaa".lastIndexOf("", 4), 3); + assert("aaa".lastIndexOf("", Infinity), 3); + + assert("a,b,c".split(","), ["a","b","c"]); + assert(",b,c".split(","), ["","b","c"]); + assert("a,b,".split(","), ["a","b",""]); + + assert("aaaa".split(), [ "aaaa" ]); + assert("aaaa".split(undefined, 0), [ ]); + assert("aaaa".split(""), [ "a", "a", "a", "a" ]); + assert("aaaa".split("", 0), [ ]); + assert("aaaa".split("", 1), [ "a" ]); + assert("aaaa".split("", 2), [ "a", "a" ]); + assert("aaaa".split("a"), [ "", "", "", "", "" ]); + assert("aaaa".split("a", 2), [ "", "" ]); + assert("aaaa".split("aa"), [ "", "", "" ]); + assert("aaaa".split("aa", 0), [ ]); + assert("aaaa".split("aa", 1), [ "" ]); + assert("aaaa".split("aa", 2), [ "", "" ]); + assert("aaaa".split("aaa"), [ "", "a" ]); + assert("aaaa".split("aaaa"), [ "", "" ]); + assert("aaaa".split("aaaaa"), [ "aaaa" ]); + assert("aaaa".split("aaaaa", 0), [ ]); + assert("aaaa".split("aaaaa", 1), [ "aaaa" ]); + + assert(eval('"\0"'), "\0"); +} + +function test_math() +{ + var a; + a = 1.4; + assert(Math.floor(a), 1); + assert(Math.ceil(a), 2); + assert(Math.imul(0x12345678, 123), -1088058456); + assert(Math.fround(0.1), 0.10000000149011612); +} + +function test_number() +{ + assert(parseInt("123"), 123); + assert(parseInt(" 123r"), 123); + assert(parseInt("0x123"), 0x123); + assert(parseInt("0o123"), 0); + assert(+" 123 ", 123); + assert(+"0b111", 7); + assert(+"0o123", 83); + assert(parseFloat("0x1234"), 0); + assert(parseFloat("Infinity"), Infinity); + assert(parseFloat("-Infinity"), -Infinity); + assert(parseFloat("123.2"), 123.2); + assert(parseFloat("123.2e3"), 123200); + assert(Number.isNaN(Number("+"))); + assert(Number.isNaN(Number("-"))); + assert(Number.isNaN(Number("\x00a"))); + + assert((25).toExponential(0), "3e+1"); + assert((-25).toExponential(0), "-3e+1"); + assert((2.5).toPrecision(1), "3"); + assert((-2.5).toPrecision(1), "-3"); + assert((1.125).toFixed(2), "1.13"); + assert((-1.125).toFixed(2), "-1.13"); +} + +function test_eval2() +{ + var g_call_count = 0; + /* force non strict mode for f1 and f2 */ + var f1 = new Function("eval", "eval(1, 2)"); + var f2 = new Function("eval", "eval(...[1, 2])"); + function g(a, b) { + assert(a, 1); + assert(b, 2); + g_call_count++; + } + f1(g); + f2(g); + assert(g_call_count, 2); +} + +function test_eval() +{ + function f(b) { + var x = 1; + return eval(b); + } + var r, a; + + r = eval("1+1;"); + assert(r, 2, "eval"); + + r = eval("var my_var=2; my_var;"); + assert(r, 2, "eval"); + assert(typeof my_var, "undefined"); + + assert(eval("if (1) 2; else 3;"), 2); + assert(eval("if (0) 2; else 3;"), 3); + + assert(f.call(1, "this"), 1); + + a = 2; + assert(eval("a"), 2); + + eval("a = 3"); + assert(a, 3); + + assert(f("arguments.length", 1), 2); + assert(f("arguments[1]", 1), 1); + + a = 4; + assert(f("a"), 4); + f("a=3"); + assert(a, 3); + + test_eval2(); +} + +function test_typed_array() +{ + var buffer, a, i; + + a = new Uint8Array(4); + assert(a.length, 4); + for(i = 0; i < a.length; i++) + a[i] = i; + assert(a.join(","), "0,1,2,3"); + a[0] = -1; + assert(a[0], 255); + + a = new Int8Array(3); + a[0] = 255; + assert(a[0], -1); + + a = new Int32Array(3); + a[0] = Math.pow(2, 32) - 1; + assert(a[0], -1); + assert(a.BYTES_PER_ELEMENT, 4); + + a = new Uint8ClampedArray(4); + a[0] = -100; + a[1] = 1.5; + a[2] = 0.5; + a[3] = 1233.5; + assert(a.toString(), "0,2,0,255"); + + buffer = new ArrayBuffer(16); + assert(buffer.byteLength, 16); + a = new Uint32Array(buffer, 12, 1); + assert(a.length, 1); + a[0] = -1; + + a = new Uint16Array(buffer, 2); + a[0] = -1; + + a = new Float32Array(buffer, 8, 1); + a[0] = 1; + + a = new Uint8Array(buffer); + + assert(a.toString(), "0,0,255,255,0,0,0,0,0,0,128,63,255,255,255,255"); + + assert(a.buffer, buffer); + + a = new Uint8Array([1, 2, 3, 4]); + assert(a.toString(), "1,2,3,4"); + a.set([10, 11], 2); + assert(a.toString(), "1,2,10,11"); +} + +function test_json() +{ + var a, s; + s = '{"x":1,"y":true,"z":null,"a":[1,2,3],"s":"str"}'; + a = JSON.parse(s); + assert(a.x, 1); + assert(a.y, true); + assert(a.z, null); + assert(JSON.stringify(a), s); + + /* indentation test */ + assert(JSON.stringify([[{x:1,y:{},z:[]},2,3]],undefined,1), +`[ + [ + { + "x": 1, + "y": {}, + "z": [] + }, + 2, + 3 + ] +]`); +} + +function test_date() +{ + var d = new Date(1506098258091), a, s; + assert(d.toISOString(), "2017-09-22T16:37:38.091Z"); + d.setUTCHours(18, 10, 11); + assert(d.toISOString(), "2017-09-22T18:10:11.091Z"); + a = Date.parse(d.toISOString()); + assert((new Date(a)).toISOString(), d.toISOString()); + s = new Date("2020-01-01T01:01:01.1Z").toISOString(); + assert(s == "2020-01-01T01:01:01.100Z"); + s = new Date("2020-01-01T01:01:01.12Z").toISOString(); + assert(s == "2020-01-01T01:01:01.120Z"); + s = new Date("2020-01-01T01:01:01.123Z").toISOString(); + assert(s == "2020-01-01T01:01:01.123Z"); + s = new Date("2020-01-01T01:01:01.1234Z").toISOString(); + assert(s == "2020-01-01T01:01:01.123Z"); + s = new Date("2020-01-01T01:01:01.12345Z").toISOString(); + assert(s == "2020-01-01T01:01:01.123Z"); + s = new Date("2020-01-01T01:01:01.1235Z").toISOString(); + assert(s == "2020-01-01T01:01:01.124Z"); + s = new Date("2020-01-01T01:01:01.9999Z").toISOString(); + assert(s == "2020-01-01T01:01:02.000Z"); +} + +function test_regexp() +{ + var a, str; + str = "abbbbbc"; + a = /(b+)c/.exec(str); + assert(a[0], "bbbbbc"); + assert(a[1], "bbbbb"); + assert(a.index, 1); + assert(a.input, str); + a = /(b+)c/.test(str); + assert(a, true); + assert(/\x61/.exec("a")[0], "a"); + assert(/\u0061/.exec("a")[0], "a"); + assert(/\ca/.exec("\x01")[0], "\x01"); + assert(/\\a/.exec("\\a")[0], "\\a"); + assert(/\c0/.exec("\\c0")[0], "\\c0"); + + a = /(\.(?=com|org)|\/)/.exec("ah.com"); + assert(a.index === 2 && a[0] === "."); + + a = /(\.(?!com|org)|\/)/.exec("ah.com"); + assert(a, null); + + a = /(?=(a+))/.exec("baaabac"); + assert(a.index === 1 && a[0] === "" && a[1] === "aaa"); + + a = /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac"); + assert(a, ["zaacbbbcac","z","ac","a",,"c"]); + + a = eval("/\0a/"); + assert(a.toString(), "/\0a/"); + assert(a.exec("\0a")[0], "\0a"); +} + +function test_symbol() +{ + var a, b, obj, c; + a = Symbol("abc"); + obj = {}; + obj[a] = 2; + assert(obj[a], 2); + assert(typeof obj["abc"], "undefined"); + assert(String(a), "Symbol(abc)"); + b = Symbol("abc"); + assert(a == a); + assert(a === a); + assert(a != b); + assert(a !== b); + + b = Symbol.for("abc"); + c = Symbol.for("abc"); + assert(b === c); + assert(b !== a); + + assert(Symbol.keyFor(b), "abc"); + assert(Symbol.keyFor(a), undefined); + + a = Symbol("aaa"); + assert(a.valueOf(), a); + assert(a.toString(), "Symbol(aaa)"); + + b = Object(a); + assert(b.valueOf(), a); + assert(b.toString(), "Symbol(aaa)"); +} + +function test_map() +{ + var a, i, n, tab, o, v; + n = 1000; + a = new Map(); + tab = []; + for(i = 0; i < n; i++) { + v = { }; + o = { id: i }; + tab[i] = [o, v]; + a.set(o, v); + } + + assert(a.size, n); + for(i = 0; i < n; i++) { + assert(a.get(tab[i][0]), tab[i][1]); + } + + i = 0; + a.forEach(function (v, o) { + assert(o, tab[i++][0]); + assert(a.has(o)); + assert(a.delete(o)); + assert(!a.has(o)); + }); + + assert(a.size, 0); +} + +function test_weak_map() +{ + var a, i, n, tab, o, v, n2; + a = new WeakMap(); + n = 10; + tab = []; + for(i = 0; i < n; i++) { + v = { }; + o = { id: i }; + tab[i] = [o, v]; + a.set(o, v); + } + o = null; + + n2 = n >> 1; + for(i = 0; i < n2; i++) { + a.delete(tab[i][0]); + } + for(i = n2; i < n; i++) { + tab[i][0] = null; /* should remove the object from the WeakMap too */ + } + /* the WeakMap should be empty here */ +} + +function test_generator() +{ + function *f() { + var ret; + yield 1; + ret = yield 2; + assert(ret, "next_arg"); + return 3; + } + function *f2() { + yield 1; + yield 2; + return "ret_val"; + } + function *f1() { + var ret = yield *f2(); + assert(ret, "ret_val"); + return 3; + } + var g, v; + g = f(); + v = g.next(); + assert(v.value === 1 && v.done === false); + v = g.next(); + assert(v.value === 2 && v.done === false); + v = g.next("next_arg"); + assert(v.value === 3 && v.done === true); + v = g.next(); + assert(v.value === undefined && v.done === true); + + g = f1(); + v = g.next(); + assert(v.value === 1 && v.done === false); + v = g.next(); + assert(v.value === 2 && v.done === false); + v = g.next(); + assert(v.value === 3 && v.done === true); + v = g.next(); + assert(v.value === undefined && v.done === true); +} + +test(); +test_function(); +test_enum(); +test_array(); +test_string(); +test_math(); +test_number(); +test_eval(); +test_typed_array(); +test_json(); +test_date(); +test_regexp(); +test_symbol(); +test_map(); +test_weak_map(); +test_generator(); diff --git a/tests/test_closure.js b/tests/test_closure.js new file mode 100644 index 0000000..aa1d17e --- /dev/null +++ b/tests/test_closure.js @@ -0,0 +1,221 @@ +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +// load more elaborate version of assert if available +try { __loadScript("test_assert.js"); } catch(e) {} + +/*----------------*/ + +var log_str = ""; + +function log(str) +{ + log_str += str + ","; +} + +function f(a, b, c) +{ + var x = 10; + log("a="+a); + function g(d) { + function h() { + log("d=" + d); + log("x=" + x); + } + log("b=" + b); + log("c=" + c); + h(); + } + g(4); + return g; +} + +var g1 = f(1, 2, 3); +g1(5); + +assert(log_str, "a=1,b=2,c=3,d=4,x=10,b=2,c=3,d=5,x=10,", "closure1"); + +function test_closure1() +{ + function f2() + { + var val = 1; + + function set(a) { + val = a; + } + function get(a) { + return val; + } + return { "set": set, "get": get }; + } + + var obj = f2(); + obj.set(10); + var r; + r = obj.get(); + assert(r, 10, "closure2"); +} + +function test_closure2() +{ + var expr_func = function myfunc1(n) { + function myfunc2(n) { + return myfunc1(n - 1); + } + if (n == 0) + return 0; + else + return myfunc2(n); + }; + var r; + r = expr_func(1); + assert(r, 0, "expr_func"); +} + +function test_closure3() +{ + function fib(n) + { + if (n <= 0) + return 0; + else if (n == 1) + return 1; + else + return fib(n - 1) + fib(n - 2); + } + + var fib_func = function fib1(n) + { + if (n <= 0) + return 0; + else if (n == 1) + return 1; + else + return fib1(n - 1) + fib1(n - 2); + }; + + assert(fib(6), 8, "fib"); + assert(fib_func(6), 8, "fib_func"); +} + +function test_arrow_function() +{ + "use strict"; + + function f1() { + return (() => arguments)(); + } + function f2() { + return (() => this)(); + } + function f3() { + return (() => eval("this"))(); + } + function f4() { + return (() => eval("new.target"))(); + } + var a; + + a = f1(1, 2); + assert(a.length, 2); + assert(a[0] === 1 && a[1] === 2); + + assert(f2.call("this_val") === "this_val"); + assert(f3.call("this_val") === "this_val"); + assert(new f4() === f4); + + var o1 = { f() { return this; } }; + var o2 = { f() { + return (() => eval("super.f()"))(); + } }; + o2.__proto__ = o1; + + assert(o2.f() === o2); +} + +function test_with() +{ + var o1 = { x: "o1", y: "o1" }; + var x = "local"; + eval('var z="var_obj";'); + assert(z === "var_obj"); + with (o1) { + assert(x === "o1"); + assert(eval("x") === "o1"); + var f = function () { + o2 = { x: "o2" }; + with (o2) { + assert(x === "o2"); + assert(y === "o1"); + assert(z === "var_obj"); + assert(eval("x") === "o2"); + assert(eval("y") === "o1"); + assert(eval("z") === "var_obj"); + assert(eval('eval("x")') === "o2"); + } + }; + f(); + } +} + +function test_eval_closure() +{ + var tab; + + tab = []; + for(let i = 0; i < 3; i++) { + eval("tab.push(function g1() { return i; })"); + } + for(let i = 0; i < 3; i++) { + assert(tab[i]() === i); + } + + tab = []; + for(let i = 0; i < 3; i++) { + let f = function f() { + eval("tab.push(function g2() { return i; })"); + }; + f(); + } + for(let i = 0; i < 3; i++) { + assert(tab[i]() === i); + } +} + +function test_eval_const() +{ + const a = 1; + var success = false; + var f = function () { + eval("a = 1"); + }; + try { + f(); + } catch(e) { + success = (e instanceof TypeError); + } + assert(success); +} + +test_closure1(); +test_closure2(); +test_closure3(); +test_arrow_function(); +test_with(); +test_eval_closure(); +test_eval_const(); diff --git a/tests/test_loop.js b/tests/test_loop.js new file mode 100644 index 0000000..5fda9d8 --- /dev/null +++ b/tests/test_loop.js @@ -0,0 +1,368 @@ +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +// load more elaborate version of assert if available +try { __loadScript("test_assert.js"); } catch(e) {} + +/*----------------*/ + +function test_while() +{ + var i, c; + i = 0; + c = 0; + while (i < 3) { + c++; + i++; + } + assert(c === 3); +} + +function test_while_break() +{ + var i, c; + i = 0; + c = 0; + while (i < 3) { + c++; + if (i == 1) + break; + i++; + } + assert(c === 2 && i === 1); +} + +function test_do_while() +{ + var i, c; + i = 0; + c = 0; + do { + c++; + i++; + } while (i < 3); + assert(c === 3 && i === 3); +} + +function test_for() +{ + var i, c; + c = 0; + for(i = 0; i < 3; i++) { + c++; + } + assert(c === 3 && i === 3); + + c = 0; + for(var j = 0; j < 3; j++) { + c++; + } + assert(c === 3 && j === 3); +} + +function test_for_in() +{ + var i, tab, a, b; + + tab = []; + for(i in {x:1, y: 2}) { + tab.push(i); + } + assert(tab.toString(), "x,y", "for_in"); + + /* prototype chain test */ + a = {x:2, y: 2, "1": 3}; + b = {"4" : 3 }; + Object.setPrototypeOf(a, b); + tab = []; + for(i in a) { + tab.push(i); + } + assert(tab.toString(), "1,x,y,4", "for_in"); + + /* non enumerable properties hide enumerables ones in the + prototype chain */ + a = {y: 2, "1": 3}; + Object.defineProperty(a, "x", { value: 1 }); + b = {"x" : 3 }; + Object.setPrototypeOf(a, b); + tab = []; + for(i in a) { + tab.push(i); + } + assert(tab.toString(), "1,y", "for_in"); + + /* array optimization */ + a = []; + for(i = 0; i < 10; i++) + a.push(i); + tab = []; + for(i in a) { + tab.push(i); + } + assert(tab.toString(), "0,1,2,3,4,5,6,7,8,9", "for_in"); + + /* iterate with a field */ + a={x:0}; + tab = []; + for(a.x in {x:1, y: 2}) { + tab.push(a.x); + } + assert(tab.toString(), "x,y", "for_in"); + + /* iterate with a variable field */ + a=[0]; + tab = []; + for(a[0] in {x:1, y: 2}) { + tab.push(a[0]); + } + assert(tab.toString(), "x,y", "for_in"); + + /* variable definition in the for in */ + tab = []; + for(var j in {x:1, y: 2}) { + tab.push(j); + } + assert(tab.toString(), "x,y", "for_in"); + + /* variable assigment in the for in */ + tab = []; + for(var k = 2 in {x:1, y: 2}) { + tab.push(k); + } + assert(tab.toString(), "x,y", "for_in"); +} + +function test_for_in2() +{ + var i; + tab = []; + for(i in {x:1, y: 2, z:3}) { + if (i === "y") + continue; + tab.push(i); + } + assert(tab.toString() == "x,z"); + + tab = []; + for(i in {x:1, y: 2, z:3}) { + if (i === "z") + break; + tab.push(i); + } + assert(tab.toString() == "x,y"); +} + +function test_for_break() +{ + var i, c; + c = 0; + L1: for(i = 0; i < 3; i++) { + c++; + if (i == 0) + continue; + while (1) { + break L1; + } + } + assert(c === 2 && i === 1); +} + +function test_switch1() +{ + var i, a, s; + s = ""; + for(i = 0; i < 3; i++) { + a = "?"; + switch(i) { + case 0: + a = "a"; + break; + case 1: + a = "b"; + break; + default: + a = "c"; + break; + } + s += a; + } + assert(s === "abc" && i === 3); +} + +function test_switch2() +{ + var i, a, s; + s = ""; + for(i = 0; i < 4; i++) { + a = "?"; + switch(i) { + case 0: + a = "a"; + break; + case 1: + a = "b"; + break; + case 2: + continue; + default: + a = "" + i; + break; + } + s += a; + } + assert(s === "ab3" && i === 4); +} + +function test_try_catch1() +{ + try { + throw "hello"; + } catch (e) { + assert(e, "hello", "catch"); + return; + } + assert(false, "catch"); +} + +function test_try_catch2() +{ + var a; + try { + a = 1; + } catch (e) { + a = 2; + } + assert(a, 1, "catch"); +} + +function test_try_catch3() +{ + var s; + s = ""; + try { + s += "t"; + } catch (e) { + s += "c"; + } finally { + s += "f"; + } + assert(s, "tf", "catch"); +} + +function test_try_catch4() +{ + var s; + s = ""; + try { + s += "t"; + throw "c"; + } catch (e) { + s += e; + } finally { + s += "f"; + } + assert(s, "tcf", "catch"); +} + +function test_try_catch5() +{ + var s; + s = ""; + for(;;) { + try { + s += "t"; + break; + s += "b"; + } finally { + s += "f"; + } + } + assert(s, "tf", "catch"); +} + +function test_try_catch6() +{ + function f() { + try { + s += 't'; + return 1; + } finally { + s += "f"; + } + } + var s = ""; + assert(f() === 1); + assert(s, "tf", "catch6"); +} + +function test_try_catch7() +{ + var s; + s = ""; + + try { + try { + s += "t"; + throw "a"; + } finally { + s += "f"; + } + } catch(e) { + s += e; + } finally { + s += "g"; + } + assert(s, "tfag", "catch"); +} + +function test_try_catch8() +{ + var i, s; + + s = ""; + for(var i in {x:1, y:2}) { + try { + s += i; + throw "a"; + } catch (e) { + s += e; + } finally { + s += "f"; + } + } + assert(s === "xafyaf"); +} + +test_while(); +test_while_break(); +test_do_while(); +test_for(); +test_for_break(); +test_switch1(); +test_switch2(); +test_for_in(); +test_for_in2(); + +test_try_catch1(); +test_try_catch2(); +test_try_catch3(); +test_try_catch4(); +test_try_catch5(); +test_try_catch6(); +test_try_catch7(); +test_try_catch8(); diff --git a/tests/test_op.js b/tests/test_op.js new file mode 100644 index 0000000..08656f2 --- /dev/null +++ b/tests/test_op.js @@ -0,0 +1,363 @@ +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +// load more elaborate version of assert if available +try { __loadScript("test_assert.js"); } catch(e) {} + +/*----------------*/ + +function test_op1() +{ + var r, a; + r = 1 + 2; + assert(r, 3, "1 + 2 === 3"); + + r = 1 - 2; + assert(r, -1, "1 - 2 === -1"); + + r = -1; + assert(r, -1, "-1 === -1"); + + r = +2; + assert(r, 2, "+2 === 2"); + + r = 2 * 3; + assert(r, 6, "2 * 3 === 6"); + + r = 4 / 2; + assert(r, 2, "4 / 2 === 2"); + + r = 4 % 3; + assert(r, 1, "4 % 3 === 3"); + + r = 4 << 2; + assert(r, 16, "4 << 2 === 16"); + + r = 1 << 0; + assert(r, 1, "1 << 0 === 1"); + + r = 1 << 31; + assert(r, -2147483648, "1 << 31 === -2147483648"); + + r = 1 << 32; + assert(r, 1, "1 << 32 === 1"); + + r = (1 << 31) < 0; + assert(r, true, "(1 << 31) < 0 === true"); + + r = -4 >> 1; + assert(r, -2, "-4 >> 1 === -2"); + + r = -4 >>> 1; + assert(r, 0x7ffffffe, "-4 >>> 1 === 0x7ffffffe"); + + r = 1 & 1; + assert(r, 1, "1 & 1 === 1"); + + r = 0 | 1; + assert(r, 1, "0 | 1 === 1"); + + r = 1 ^ 1; + assert(r, 0, "1 ^ 1 === 0"); + + r = ~1; + assert(r, -2, "~1 === -2"); + + r = !1; + assert(r, false, "!1 === false"); + + assert((1 < 2), true, "(1 < 2) === true"); + + assert((2 > 1), true, "(2 > 1) === true"); + + assert(('b' > 'a'), true, "('b' > 'a') === true"); + + assert(2 ** 8, 256, "2 ** 8 === 256"); +} + +function test_cvt() +{ + assert((NaN | 0) === 0); + assert((Infinity | 0) === 0); + assert(((-Infinity) | 0) === 0); + assert(("12345" | 0) === 12345); + assert(("0x12345" | 0) === 0x12345); + assert(((4294967296 * 3 - 4) | 0) === -4); + + assert(("12345" >>> 0) === 12345); + assert(("0x12345" >>> 0) === 0x12345); + assert((NaN >>> 0) === 0); + assert((Infinity >>> 0) === 0); + assert(((-Infinity) >>> 0) === 0); + assert(((4294967296 * 3 - 4) >>> 0) === (4294967296 - 4)); +} + +function test_eq() +{ + assert(null == undefined); + assert(undefined == null); + assert(true == 1); + assert(0 == false); + assert("" == 0); + assert("123" == 123); + assert("122" != 123); + assert((new Number(1)) == 1); + assert(2 == (new Number(2))); + assert((new String("abc")) == "abc"); + assert({} != "abc"); +} + +function test_inc_dec() +{ + var a, r; + + a = 1; + r = a++; + assert(r === 1 && a === 2, true, "++"); + + a = 1; + r = ++a; + assert(r === 2 && a === 2, true, "++"); + + a = 1; + r = a--; + assert(r === 1 && a === 0, true, "--"); + + a = 1; + r = --a; + assert(r === 0 && a === 0, true, "--"); + + a = {x:true}; + a.x++; + assert(a.x, 2, "++"); + + a = {x:true}; + a.x--; + assert(a.x, 0, "--"); + + a = [true]; + a[0]++; + assert(a[0], 2, "++"); + + a = {x:true}; + r = a.x++; + assert(r === 1 && a.x === 2, true, "++"); + + a = {x:true}; + r = a.x--; + assert(r === 1 && a.x === 0, true, "--"); + + a = [true]; + r = a[0]++; + assert(r === 1 && a[0] === 2, true, "++"); + + a = [true]; + r = a[0]--; + assert(r === 1 && a[0] === 0, true, "--"); +} + +function F(x) +{ + this.x = x; +} + +function test_op2() +{ + var a, b; + a = new Object; + a.x = 1; + assert(a.x, 1, "new"); + b = new F(2); + assert(b.x, 2, "new"); + + a = {x : 2}; + assert(("x" in a), true, "in"); + assert(("y" in a), false, "in"); + + a = {}; + assert((a instanceof Object), true, "instanceof"); + assert((a instanceof String), false, "instanceof"); + + assert((typeof 1), "number", "typeof"); + assert((typeof Object), "function", "typeof"); + assert((typeof null), "object", "typeof"); + assert((typeof unknown_var), "undefined", "typeof"); + + a = {x: 1, if: 2, async: 3}; + assert(a.if === 2); + assert(a.async === 3); +} + +function test_delete() +{ + var a, err; + + a = {x: 1, y: 1}; + assert((delete a.x), true, "delete"); + assert(("x" in a), false, "delete"); + + /* the following are not tested by test262 */ + assert(delete "abc"[100], true); + + err = false; + try { + delete null.a; + } catch(e) { + err = (e instanceof TypeError); + } + assert(err, true, "delete"); + + err = false; + try { + a = { f() { delete super.a; } }; + a.f(); + } catch(e) { + err = (e instanceof ReferenceError); + } + assert(err, true, "delete"); +} + +function test_prototype() +{ + function f() { } + assert(f.prototype.constructor, f, "prototype"); +} + +function test_arguments() +{ + function f2() { + assert(arguments.length, 2, "arguments"); + assert(arguments[0], 1, "arguments"); + assert(arguments[1], 3, "arguments"); + } + f2(1, 3); +} + +function test_class() +{ + var o; + class C { + constructor() { + this.x = 10; + } + f() { + return 1; + } + static F() { + return -1; + } + get y() { + return 12; + } + }; + class D extends C { + constructor() { + super(); + this.z = 20; + } + g() { + return 2; + } + static G() { + return -2; + } + h() { + return super.f(); + } + static H() { + return super["F"](); + } + } + + assert(C.F() === -1); + assert(Object.getOwnPropertyDescriptor(C.prototype, "y").get.name === "get y"); + + o = new C(); + assert(o.f() === 1); + assert(o.x === 10); + + assert(D.F() === -1); + assert(D.G() === -2); + assert(D.H() === -1); + + o = new D(); + assert(o.f() === 1); + assert(o.g() === 2); + assert(o.x === 10); + assert(o.z === 20); + assert(o.h() === 1); + + /* test class name scope */ + var E1 = class E { static F() { return E; } }; + assert(E1 === E1.F()); +}; + +function test_template() +{ + var a, b; + b = 123; + a = `abc${b}d`; + assert(a === "abc123d"); + + a = String.raw `abc${b}d`; + assert(a === "abc123d"); +} + +function test_object_literal() +{ + var x = 0, get = 1, set = 2; async = 3; + a = { get: 2, set: 3, async: 4 }; + assert(JSON.stringify(a), '{"get":2,"set":3,"async":4}'); + + a = { x, get, set, async }; + assert(JSON.stringify(a), '{"x":0,"get":1,"set":2,"async":3}'); +} + +function test_regexp_skip() +{ + var a, b; + [a, b = /abc\(/] = [1]; + assert(a === 1); + + [a, b =/abc\(/] = [2]; + assert(a === 2); +} + +function test_labels() +{ + do x: { break x; } while(0); + if (1) + x: { break x; } + else + x: { break x; } + with ({}) x: { break x; }; + while (0) x: { break x; }; +} + +test_op1(); +test_cvt(); +test_eq(); +test_inc_dec(); +test_op2(); +test_delete(); +test_prototype(); +test_arguments(); +test_class(); +test_template(); +test_object_literal(); +test_regexp_skip(); +test_labels(); diff --git a/tests/test_qjscalc.js b/tests/test_qjscalc.js new file mode 100644 index 0000000..f7bf804 --- /dev/null +++ b/tests/test_qjscalc.js @@ -0,0 +1,244 @@ +"use math"; +"use strict"; + +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +function assertThrows(err, func) +{ + var ex; + ex = false; + try { + func(); + } catch(e) { + ex = true; + assert(e instanceof err); + } + assert(ex, true, "exception expected"); +} + +// load more elaborate version of assert if available +try { __loadScript("test_assert.js"); } catch(e) {} + +/*----------------*/ + +function pow(a, n) +{ + var r, i; + r = 1; + for(i = 0; i < n; i++) + r *= a; + return r; +} + +function test_integer() +{ + var a, r; + a = pow(3, 100); + assert((a - 1) != a); + assert(a == 515377520732011331036461129765621272702107522001); + assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1); + assert(Integer.isInteger(1) === true); + assert(Integer.isInteger(1.0) === false); + + assert(Integer.floorLog2(0) === -1); + assert(Integer.floorLog2(7) === 2); + + r = 1 << 31; + assert(r, 2147483648, "1 << 31 === 2147483648"); + + r = 1 << 32; + assert(r, 4294967296, "1 << 32 === 4294967296"); + + r = (1 << 31) < 0; + assert(r, false, "(1 << 31) < 0 === false"); +} + +function test_float() +{ + var e, a, b, sqrt2; + + assert(typeof 1 === "bigint"); + assert(typeof 1.0 === "bigfloat"); + assert(1 == 1.0); + assert(1 !== 1.0); +} + +/* jscalc tests */ + +function test_modulo() +{ + var i, p, a, b; + + /* Euclidian modulo operator */ + assert((-3) % 2 == 1); + assert(3 % (-2) == 1); + + p = 101; + for(i = 1; i < p; i++) { + a = Integer.invmod(i, p); + assert(a >= 0 && a < p); + assert((i * a) % p == 1); + } + + assert(Integer.isPrime(2^107-1)); + assert(!Integer.isPrime((2^107-1) * (2^89-1))); + a = Integer.factor((2^89-1)*2^3*11*13^2*1009); + assert(a == [ 2,2,2,11,13,13,1009,618970019642690137449562111 ]); +} + +function test_mod() +{ + var a, b, p; + + a = Mod(3, 101); + b = Mod(-1, 101); + assert((a + b) == Mod(2, 101)); + assert(a ^ 100 == Mod(1, 101)); + + p = 2 ^ 607 - 1; /* mersenne prime */ + a = Mod(3, p) ^ (p - 1); + assert(a == Mod(1, p)); +} + +function test_polynomial() +{ + var a, b, q, r, t, i; + a = (1 + X) ^ 4; + assert(a == X^4+4*X^3+6*X^2+4*X+1); + + r = (1 + X); + q = (1+X+X^2); + b = (1 - X^2); + a = q * b + r; + t = Polynomial.divrem(a, b); + assert(t[0] == q); + assert(t[1] == r); + + a = 1 + 2*X + 3*X^2; + assert(a.apply(0.1) == 1.23); + + a = 1-2*X^2+2*X^3; + assert(deriv(a) == (6*X^2-4*X)); + assert(deriv(integ(a)) == a); + + a = (X-1)*(X-2)*(X-3)*(X-4)*(X-0.1); + r = polroots(a); + for(i = 0; i < r.length; i++) { + b = abs(a.apply(r[i])); + assert(b <= 1e-13); + } +} + +function test_poly_mod() +{ + var a, p; + + /* modulo using polynomials */ + p = X^2 + X + 1; + a = PolyMod(3+X, p) ^ 10; + assert(a == PolyMod(-3725*X-18357, p)); + + a = PolyMod(1/X, 1+X^2); + assert(a == PolyMod(-X, X^2+1)); +} + +function test_rfunc() +{ + var a; + a = (X+1)/((X+1)*(X-1)); + assert(a == 1/(X-1)); + a = (X + 2) / (X - 2); + assert(a.apply(1/3) == -7/5); + + assert(deriv((X^2-X+1)/(X-1)) == (X^2-2*X)/(X^2-2*X+1)); +} + +function test_series() +{ + var a, b; + a = 1+X+O(X^5); + b = a.inverse(); + assert(b == 1-X+X^2-X^3+X^4+O(X^5)); + assert(deriv(b) == -1+2*X-3*X^2+4*X^3+O(X^4)); + assert(deriv(integ(b)) == b); + + a = Series(1/(1-X), 5); + assert(a == 1+X+X^2+X^3+X^4+O(X^5)); + b = a.apply(0.1); + assert(b == 1.1111); + + assert(exp(3*X^2+O(X^10)) == 1+3*X^2+9/2*X^4+9/2*X^6+27/8*X^8+O(X^10)); + assert(sin(X+O(X^6)) == X-1/6*X^3+1/120*X^5+O(X^6)); + assert(cos(X+O(X^6)) == 1-1/2*X^2+1/24*X^4+O(X^6)); + assert(tan(X+O(X^8)) == X+1/3*X^3+2/15*X^5+17/315*X^7+O(X^8)); + assert((1+X+O(X^6))^(2+X) == 1+2*X+2*X^2+3/2*X^3+5/6*X^4+5/12*X^5+O(X^6)); +} + +function test_matrix() +{ + var a, b, r; + a = [[1, 2],[3, 4]]; + b = [3, 4]; + r = a * b; + assert(r == [11, 25]); + r = (a^-1) * 2; + assert(r == [[-4, 2],[3, -1]]); + + assert(norm2([1,2,3]) == 14); + + assert(diag([1,2,3]) == [ [ 1, 0, 0 ], [ 0, 2, 0 ], [ 0, 0, 3 ] ]); + assert(trans(a) == [ [ 1, 3 ], [ 2, 4 ] ]); + assert(trans([1,2,3]) == [[1,2,3]]); + assert(trace(a) == 5); + + assert(charpoly(Matrix.hilbert(4)) == X^4-176/105*X^3+3341/12600*X^2-41/23625*X+1/6048000); + assert(det(Matrix.hilbert(4)) == 1/6048000); + + a = [[1,2,1],[-2,-3,1],[3,5,0]]; + assert(rank(a) == 2); + assert(ker(a) == [ [ 5 ], [ -3 ], [ 1 ] ]); + + assert(dp([1, 2, 3], [3, -4, -7]) === -26); + assert(cp([1, 2, 3], [3, -4, -7]) == [ -2, 16, -10 ]); +} + +function assert_eq(a, ref) +{ + assert(abs(a / ref - 1.0) <= 1e-15); +} + +function test_trig() +{ + assert_eq(sin(1/2), 0.479425538604203); + assert_eq(sin(2+3*I), 9.154499146911428-4.168906959966565*I); + assert_eq(cos(2+3*I), -4.189625690968807-9.109227893755337*I); + assert_eq((2+0.5*I)^(1.1-0.5*I), 2.494363021357619-0.23076804554558092*I); + assert_eq(sqrt(2*I), 1 + I); +} + +test_integer(); +test_float(); + +test_modulo(); +test_mod(); +test_polynomial(); +test_poly_mod(); +test_rfunc(); +test_series(); +test_matrix(); +test_trig(); diff --git a/tests/test_std.js b/tests/test_std.js new file mode 100644 index 0000000..b5b1bd0 --- /dev/null +++ b/tests/test_std.js @@ -0,0 +1,247 @@ +import * as std from "std"; +import * as os from "os"; + +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) + return; + + if (actual !== null && expected !== null + && typeof actual == 'object' && typeof expected == 'object' + && actual.toString() === expected.toString()) + return; + + throw Error("assertion failed: got |" + actual + "|" + + ", expected |" + expected + "|" + + (message ? " (" + message + ")" : "")); +} + +// load more elaborate version of assert if available +try { std.loadScript("test_assert.js"); } catch(e) {} + +/*----------------*/ + +function test_printf() +{ + assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc"); +} + +function test_file1() +{ + var f, len, str, size, buf, ret, i, str1; + + f = std.tmpfile(); + str = "hello world\n"; + f.puts(str); + + f.seek(0, std.SEEK_SET); + str1 = f.readAsString(); + assert(str1 === str); + + f.seek(0, std.SEEK_END); + size = f.tell(); + assert(size === str.length); + + f.seek(0, std.SEEK_SET); + + buf = new Uint8Array(size); + ret = f.read(buf.buffer, 0, size); + assert(ret === size); + for(i = 0; i < size; i++) + assert(buf[i] === str.charCodeAt(i)); + + f.close(); +} + +function test_file2() +{ + var f, str, i, size; + f = std.tmpfile(); + str = "hello world\n"; + size = str.length; + for(i = 0; i < size; i++) + f.putByte(str.charCodeAt(i)); + f.seek(0, std.SEEK_SET); + for(i = 0; i < size; i++) { + assert(str.charCodeAt(i) === f.getByte()); + } + assert(f.getByte() === -1); + f.close(); +} + +function test_getline() +{ + var f, line, line_count, lines, i; + + lines = ["hello world", "line 1", "line 2" ]; + f = std.tmpfile(); + for(i = 0; i < lines.length; i++) { + f.puts(lines[i], "\n"); + } + + f.seek(0, std.SEEK_SET); + assert(!f.eof()); + line_count = 0; + for(;;) { + line = f.getline(); + if (line === null) + break; + assert(line == lines[line_count]); + line_count++; + } + assert(f.eof()); + assert(line_count === lines.length); + + f.close(); +} + +function test_popen() +{ + var str, f, fname = "tmp_file.txt"; + var content = "hello world"; + + f = std.open(fname, "w"); + f.puts(content); + f.close(); + + /* execute the 'cat' shell command */ + f = std.popen("cat " + fname, "r"); + str = f.readAsString(); + f.close(); + + assert(str, content); + + os.remove(fname); +} + +function test_os() +{ + var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path; + + assert(os.isatty(0)); + + fdir = "test_tmp_dir"; + fname = "tmp_file.txt"; + fpath = fdir + "/" + fname; + link_path = fdir + "/test_link"; + + os.remove(link_path); + os.remove(fpath); + os.remove(fdir); + + err = os.mkdir(fdir, 0o755); + assert(err === 0); + + fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC); + assert(fd >= 0); + + buf = new Uint8Array(10); + for(i = 0; i < buf.length; i++) + buf[i] = i; + assert(os.write(fd, buf.buffer, 0, buf.length) === buf.length); + + assert(os.seek(fd, 0, os.SEEK_SET) === 0); + buf2 = new Uint8Array(buf.length); + assert(os.read(fd, buf2.buffer, 0, buf2.length) === buf2.length); + + for(i = 0; i < buf.length; i++) + assert(buf[i] == buf2[i]); + + assert(os.close(fd) === 0); + + [files, err] = os.readdir(fdir); + assert(err, 0); + assert(files.indexOf(fname) >= 0); + + fdate = 10000; + + err = os.utimes(fpath, fdate, fdate); + assert(err, 0); + + [st, err] = os.stat(fpath); + assert(err, 0); + assert(st.mode & os.S_IFMT, os.S_IFREG); + assert(st.mtime, fdate); + + err = os.symlink(fname, link_path); + assert(err === 0); + + [st, err] = os.lstat(link_path); + assert(err, 0); + assert(st.mode & os.S_IFMT, os.S_IFLNK); + + [buf, err] = os.readlink(link_path); + assert(err, 0); + assert(buf, fname); + + assert(os.remove(link_path) === 0); + + [buf, err] = os.getcwd(); + assert(err, 0); + + [buf2, err] = os.realpath("."); + assert(err, 0); + + assert(buf, buf2); + + assert(os.remove(fpath) === 0); + + fd = os.open(fpath, os.O_RDONLY); + assert(fd < 0); + + assert(os.remove(fdir) === 0); +} + +function test_os_exec() +{ + var ret, fds, pid, f, status; + + ret = os.exec(["true"]); + assert(ret, 0); + + ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false }); + assert(ret, 1); + + fds = os.pipe(); + pid = os.exec(["echo", "hello"], { stdout: fds[1], block: false } ); + assert(pid >= 0); + os.close(fds[1]); /* close the write end (as it is only in the child) */ + f = std.fdopen(fds[0], "r"); + assert(f.getline(), "hello"); + assert(f.getline(), null); + f.close(); + [ret, status] = os.waitpid(pid, 0); + assert(ret, pid); + assert(status & 0x7f, 0); /* exited */ + assert(status >> 8, 0); /* exit code */ + + pid = os.exec(["cat"], { block: false } ); + assert(pid >= 0); + os.kill(pid, os.SIGQUIT); + [ret, status] = os.waitpid(pid, 0); + assert(ret, pid); + assert(status & 0x7f, os.SIGQUIT); +} + +function test_timer() +{ + var th, i; + + /* just test that a timer can be inserted and removed */ + th = []; + for(i = 0; i < 3; i++) + th[i] = os.setTimeout(function () { }, 1000); + for(i = 0; i < 3; i++) + os.clearTimeout(th[i]); +} + +test_printf(); +test_file1(); +test_file2(); +test_getline(); +test_popen(); +test_os(); +test_os_exec(); +test_timer(); diff --git a/unicode_download.sh b/unicode_download.sh new file mode 100755 index 0000000..a72d9ae --- /dev/null +++ b/unicode_download.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -e + +url="ftp://ftp.unicode.org/Public/12.1.0/ucd" +emoji_url="ftp://ftp.unicode.org/Public/emoji/12.0/emoji-data.txt" + +files="CaseFolding.txt DerivedNormalizationProps.txt PropList.txt \ +SpecialCasing.txt CompositionExclusions.txt ScriptExtensions.txt \ +UnicodeData.txt DerivedCoreProperties.txt NormalizationTest.txt Scripts.txt \ +PropertyValueAliases.txt" + +mkdir -p unicode + +for f in $files; do + g="${url}/${f}" + wget $g -O unicode/$f +done + +wget $emoji_url -O unicode/emoji-data.txt diff --git a/unicode_gen.c b/unicode_gen.c new file mode 100644 index 0000000..f18aaa0 --- /dev/null +++ b/unicode_gen.c @@ -0,0 +1,3057 @@ +/* + * Generation of Unicode tables + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" + +/* define it to be able to test unicode.c */ +//#define USE_TEST +/* profile tests */ +//#define PROFILE + +//#define DUMP_CASE_CONV_TABLE +//#define DUMP_TABLE_SIZE +//#define DUMP_CC_TABLE +//#define DUMP_DECOMP_TABLE + +/* Ideas: + - Generalize run length encoding + index for all tables + - remove redundant tables for ID_start, ID_continue, Case_Ignorable, Cased + + Case conversion: + - use a single entry for consecutive U/LF runs + - allow EXT runs of length > 1 + + Decomposition: + - Greek lower case (+1f10/1f10) ? + - allow holes in B runs + - suppress more upper / lower case redundancy +*/ + +#ifdef USE_TEST +#include "libunicode.c" +#endif + +#define CHARCODE_MAX 0x10ffff +#define CC_LEN_MAX 3 + +void *mallocz(size_t size) +{ + void *ptr; + ptr = malloc(size); + memset(ptr, 0, size); + return ptr; +} + +const char *get_field(const char *p, int n) +{ + int i; + for(i = 0; i < n; i++) { + while (*p != ';' && *p != '\0') + p++; + if (*p == '\0') + return NULL; + p++; + } + return p; +} + +const char *get_field_buf(char *buf, size_t buf_size, const char *p, int n) +{ + char *q; + p = get_field(p, n); + q = buf; + while (*p != ';' && *p != '\0') { + if ((q - buf) < buf_size - 1) + *q++ = *p; + p++; + } + *q = '\0'; + return buf; +} + +void add_char(int **pbuf, int *psize, int *plen, int c) +{ + int len, size, *buf; + buf = *pbuf; + size = *psize; + len = *plen; + if (len >= size) { + size = *psize; + size = max_int(len + 1, size * 3 / 2); + buf = realloc(buf, sizeof(buf[0]) * size); + *pbuf = buf; + *psize = size; + } + buf[len++] = c; + *plen = len; +} + +int *get_field_str(int *plen, const char *str, int n) +{ + const char *p; + int *buf, len, size; + p = get_field(str, n); + if (!p) { + *plen = 0; + return NULL; + } + len = 0; + size = 0; + buf = NULL; + for(;;) { + while (isspace(*p)) + p++; + if (!isxdigit(*p)) + break; + add_char(&buf, &size, &len, strtoul(p, (char **)&p, 16)); + } + *plen = len; + return buf; +} + +char *get_line(char *buf, int buf_size, FILE *f) +{ + int len; + if (!fgets(buf, buf_size, f)) + return NULL; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + return buf; +} + +#define UNICODE_GENERAL_CATEGORY + +typedef enum { +#define DEF(id, str) GCAT_ ## id, +#include "unicode_gen_def.h" +#undef DEF + GCAT_COUNT, +} UnicodeGCEnum1; + +static const char *unicode_gc_name[] = { +#define DEF(id, str) #id, +#include "unicode_gen_def.h" +#undef DEF +}; + +static const char *unicode_gc_short_name[] = { +#define DEF(id, str) str, +#include "unicode_gen_def.h" +#undef DEF +}; + +#undef UNICODE_GENERAL_CATEGORY + +#define UNICODE_SCRIPT + +typedef enum { +#define DEF(id, str) SCRIPT_ ## id, +#include "unicode_gen_def.h" +#undef DEF + SCRIPT_COUNT, +} UnicodeScriptEnum1; + +static const char *unicode_script_name[] = { +#define DEF(id, str) #id, +#include "unicode_gen_def.h" +#undef DEF +}; + +const char *unicode_script_short_name[] = { +#define DEF(id, str) str, +#include "unicode_gen_def.h" +#undef DEF +}; + +#undef UNICODE_SCRIPT + +#define UNICODE_PROP_LIST + +typedef enum { +#define DEF(id, str) PROP_ ## id, +#include "unicode_gen_def.h" +#undef DEF + PROP_COUNT, +} UnicodePropEnum1; + +static const char *unicode_prop_name[] = { +#define DEF(id, str) #id, +#include "unicode_gen_def.h" +#undef DEF +}; + +static const char *unicode_prop_short_name[] = { +#define DEF(id, str) str, +#include "unicode_gen_def.h" +#undef DEF +}; + +#undef UNICODE_SPROP_LIST + +typedef struct { + /* case conv */ + uint8_t u_len; + uint8_t l_len; + int u_data[CC_LEN_MAX]; + int l_data[CC_LEN_MAX]; + int f_code; + + uint8_t combining_class; + uint8_t is_compat:1; + uint8_t is_excluded:1; + uint8_t general_category; + uint8_t script; + uint8_t script_ext_len; + uint8_t *script_ext; + uint32_t prop_bitmap_tab[3]; + /* decomposition */ + int decomp_len; + int *decomp_data; +} CCInfo; + +CCInfo *unicode_db; + +int find_name(const char **tab, int tab_len, const char *name) +{ + int i, len, name_len; + const char *p, *r; + + name_len = strlen(name); + for(i = 0; i < tab_len; i++) { + p = tab[i]; + for(;;) { + r = strchr(p, ','); + if (!r) + len = strlen(p); + else + len = r - p; + if (len == name_len && memcmp(p, name, len) == 0) + return i; + if (!r) + break; + p = r + 1; + } + } + return -1; +} + +static int get_prop(uint32_t c, int prop_idx) +{ + return (unicode_db[c].prop_bitmap_tab[prop_idx >> 5] >> (prop_idx & 0x1f)) & 1; +} + +static void set_prop(uint32_t c, int prop_idx, int val) +{ + uint32_t mask; + mask = 1U << (prop_idx & 0x1f); + if (val) + unicode_db[c].prop_bitmap_tab[prop_idx >> 5] |= mask; + else + unicode_db[c].prop_bitmap_tab[prop_idx >> 5] &= ~mask; +} + +void parse_unicode_data(const char *filename) +{ + FILE *f; + char line[1024]; + char buf1[256]; + const char *p; + int code, lc, uc, last_code; + CCInfo *ci, *tab = unicode_db; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + last_code = 0; + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#') + continue; + + p = get_field(line, 0); + if (!p) + continue; + code = strtoul(p, NULL, 16); + lc = 0; + uc = 0; + + p = get_field(line, 12); + if (p && *p != ';') { + uc = strtoul(p, NULL, 16); + } + + p = get_field(line, 13); + if (p && *p != ';') { + lc = strtoul(p, NULL, 16); + } + ci = &tab[code]; + if (uc > 0 || lc > 0) { + assert(code <= CHARCODE_MAX); + if (uc > 0) { + assert(ci->u_len == 0); + ci->u_len = 1; + ci->u_data[0] = uc; + } + if (lc > 0) { + assert(ci->l_len == 0); + ci->l_len = 1; + ci->l_data[0] = lc; + } + } + + { + int i; + get_field_buf(buf1, sizeof(buf1), line, 2); + i = find_name(unicode_gc_name, countof(unicode_gc_name), buf1); + if (i < 0) { + fprintf(stderr, "General category '%s' not found\n", + buf1); + exit(1); + } + ci->general_category = i; + } + + p = get_field(line, 3); + if (p && *p != ';' && *p != '\0') { + int cc; + cc = strtoul(p, NULL, 0); + if (cc != 0) { + assert(code <= CHARCODE_MAX); + ci->combining_class = cc; + // printf("%05x: %d\n", code, ci->combining_class); + } + } + + p = get_field(line, 5); + if (p && *p != ';' && *p != '\0') { + int size; + assert(code <= CHARCODE_MAX); + ci->is_compat = 0; + if (*p == '<') { + while (*p != '\0' && *p != '>') + p++; + if (*p == '>') + p++; + ci->is_compat = 1; + } + size = 0; + for(;;) { + while (isspace(*p)) + p++; + if (!isxdigit(*p)) + break; + add_char(&ci->decomp_data, &size, &ci->decomp_len, strtoul(p, (char **)&p, 16)); + } +#if 0 + { + int i; + static int count, d_count; + + printf("%05x: %c", code, ci->is_compat ? 'C': ' '); + for(i = 0; i < ci->decomp_len; i++) + printf(" %05x", ci->decomp_data[i]); + printf("\n"); + count++; + d_count += ci->decomp_len; + // printf("%d %d\n", count, d_count); + } +#endif + } + + p = get_field(line, 9); + if (p && *p == 'Y') { + set_prop(code, PROP_Bidi_Mirrored, 1); + } + + /* handle ranges */ + get_field_buf(buf1, sizeof(buf1), line, 1); + if (strstr(buf1, " Last>")) { + int i; + // printf("range: 0x%x-%0x\n", last_code, code); + assert(ci->decomp_len == 0); + assert(ci->script_ext_len == 0); + for(i = last_code + 1; i < code; i++) { + unicode_db[i] = *ci; + } + } + last_code = code; + } + + fclose(f); +} + +void parse_special_casing(CCInfo *tab, const char *filename) +{ + FILE *f; + char line[1024]; + const char *p; + int code; + CCInfo *ci; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#') + continue; + + p = get_field(line, 0); + if (!p) + continue; + code = strtoul(p, NULL, 16); + assert(code <= CHARCODE_MAX); + ci = &tab[code]; + + p = get_field(line, 4); + if (p) { + /* locale dependent casing */ + while (isspace(*p)) + p++; + if (*p != '#' && *p != '\0') + continue; + } + + + p = get_field(line, 1); + if (p && *p != ';') { + ci->l_len = 0; + for(;;) { + while (isspace(*p)) + p++; + if (*p == ';') + break; + assert(ci->l_len < CC_LEN_MAX); + ci->l_data[ci->l_len++] = strtoul(p, (char **)&p, 16); + } + + if (ci->l_len == 1 && ci->l_data[0] == code) + ci->l_len = 0; + } + + p = get_field(line, 3); + if (p && *p != ';') { + ci->u_len = 0; + for(;;) { + while (isspace(*p)) + p++; + if (*p == ';') + break; + assert(ci->u_len < CC_LEN_MAX); + ci->u_data[ci->u_len++] = strtoul(p, (char **)&p, 16); + } + + if (ci->u_len == 1 && ci->u_data[0] == code) + ci->u_len = 0; + } + } + + fclose(f); +} + +void parse_case_folding(CCInfo *tab, const char *filename) +{ + FILE *f; + char line[1024]; + const char *p; + int code; + CCInfo *ci; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#') + continue; + + p = get_field(line, 0); + if (!p) + continue; + code = strtoul(p, NULL, 16); + assert(code <= CHARCODE_MAX); + ci = &tab[code]; + + p = get_field(line, 1); + if (!p) + continue; + /* locale dependent casing */ + while (isspace(*p)) + p++; + if (*p != 'C' && *p != 'S') + continue; + + p = get_field(line, 2); + assert(p != 0); + assert(ci->f_code == 0); + ci->f_code = strtoul(p, NULL, 16); + assert(ci->f_code != 0 && ci->f_code != code); + } + + fclose(f); +} + +void parse_composition_exclusions(const char *filename) +{ + FILE *f; + char line[4096], *p; + uint32_t c0; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + assert(c0 > 0 && c0 <= CHARCODE_MAX); + unicode_db[c0].is_excluded = TRUE; + } + fclose(f); +} + +void parse_derived_core_properties(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + int i; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + if (*p == ';') { + p++; + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + i = find_name(unicode_prop_name, + countof(unicode_prop_name), buf); + if (i < 0) { + if (!strcmp(buf, "Grapheme_Link")) + goto next; + fprintf(stderr, "Property not found: %s\n", buf); + exit(1); + } + for(c = c0; c <= c1; c++) { + set_prop(c, i, 1); + } +next: ; + } + } + fclose(f); +} + +void parse_derived_norm_properties(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + if (*p == ';') { + p++; + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (!strcmp(buf, "Changes_When_NFKC_Casefolded")) { + for(c = c0; c <= c1; c++) { + set_prop(c, PROP_Changes_When_NFKC_Casefolded, 1); + } + } + } + } + fclose(f); +} + +void parse_prop_list(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + int i; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + if (*p == ';') { + p++; + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + i = find_name(unicode_prop_name, + countof(unicode_prop_name), buf); + if (i < 0) { + fprintf(stderr, "Property not found: %s\n", buf); + exit(1); + } + for(c = c0; c <= c1; c++) { + set_prop(c, i, 1); + } + } + } + fclose(f); +} + +void parse_scripts(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + int i; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + if (*p == ';') { + p++; + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + i = find_name(unicode_script_name, + countof(unicode_script_name), buf); + if (i < 0) { + fprintf(stderr, "Unknown script: '%s'\n", buf); + exit(1); + } + for(c = c0; c <= c1; c++) + unicode_db[c].script = i; + } + } + fclose(f); +} + +void parse_script_extensions(const char *filename) +{ + FILE *f; + char line[4096], *p, buf[256], *q; + uint32_t c0, c1, c; + int i; + uint8_t script_ext[255]; + int script_ext_len; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@' || *p == '\0') + continue; + c0 = strtoul(p, (char **)&p, 16); + if (*p == '.' && p[1] == '.') { + p += 2; + c1 = strtoul(p, (char **)&p, 16); + } else { + c1 = c0; + } + assert(c1 <= CHARCODE_MAX); + p += strspn(p, " \t"); + script_ext_len = 0; + if (*p == ';') { + p++; + for(;;) { + p += strspn(p, " \t"); + q = buf; + while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (buf[0] == '\0') + break; + i = find_name(unicode_script_short_name, + countof(unicode_script_short_name), buf); + if (i < 0) { + fprintf(stderr, "Script not found: %s\n", buf); + exit(1); + } + assert(script_ext_len < sizeof(script_ext)); + script_ext[script_ext_len++] = i; + } + for(c = c0; c <= c1; c++) { + CCInfo *ci = &unicode_db[c]; + ci->script_ext_len = script_ext_len; + ci->script_ext = malloc(sizeof(ci->script_ext[0]) * script_ext_len); + for(i = 0; i < script_ext_len; i++) + ci->script_ext[i] = script_ext[i]; + } + } + } + fclose(f); +} + +void dump_cc_info(CCInfo *ci, int i) +{ + int j; + printf("%05x:", i); + if (ci->u_len != 0) { + printf(" U:"); + for(j = 0; j < ci->u_len; j++) + printf(" %05x", ci->u_data[j]); + } + if (ci->l_len != 0) { + printf(" L:"); + for(j = 0; j < ci->l_len; j++) + printf(" %05x", ci->l_data[j]); + } + if (ci->f_code != 0) { + printf(" F: %05x", ci->f_code); + } + printf("\n"); +} + +void dump_data(CCInfo *tab) +{ + int i; + CCInfo *ci; + for(i = 0; i <= CHARCODE_MAX; i++) { + ci = &tab[i]; + if (ci->u_len != 0 || ci->l_len != 0 || ci->f_code != 0) { + dump_cc_info(ci, i); + } + } +} + +BOOL is_complicated_case(const CCInfo *ci) +{ + return (ci->u_len > 1 || ci->l_len > 1 || + (ci->u_len > 0 && ci->l_len > 0) || + (ci->f_code != 0) != ci->l_len || + (ci->f_code != 0 && ci->l_data[0] != ci->f_code)); +} + +#ifndef USE_TEST +enum { + RUN_TYPE_U, + RUN_TYPE_L, + RUN_TYPE_UF, + RUN_TYPE_LF, + RUN_TYPE_UL, + RUN_TYPE_LSU, + RUN_TYPE_U2L_399_EXT2, + RUN_TYPE_UF_D20, + RUN_TYPE_UF_D1_EXT, + RUN_TYPE_U_EXT, + RUN_TYPE_LF_EXT, + RUN_TYPE_U_EXT2, + RUN_TYPE_L_EXT2, + RUN_TYPE_U_EXT3, +}; +#endif + +const char *run_type_str[] = { + "U", + "L", + "UF", + "LF", + "UL", + "LSU", + "U2L_399_EXT2", + "UF_D20", + "UF_D1_EXT", + "U_EXT", + "LF_EXT", + "U_EXT2", + "L_EXT2", + "U_EXT3", +}; + +typedef struct { + int code; + int len; + int type; + int data; + int ext_len; + int ext_data[3]; + int data_index; /* 'data' coming from the table */ +} TableEntry; + +/* code (17), len (7), type (4) */ + +void find_run_type(TableEntry *te, CCInfo *tab, int code) +{ + int is_lower, len; + CCInfo *ci, *ci1, *ci2; + + ci = &tab[code]; + ci1 = &tab[code + 1]; + ci2 = &tab[code + 2]; + te->code = code; + + if (ci->l_len == 1 && ci->l_data[0] == code + 2 && + ci->f_code == ci->l_data[0] && + ci->u_len == 0 && + + ci1->l_len == 1 && ci1->l_data[0] == code + 2 && + ci1->f_code == ci1->l_data[0] && + ci1->u_len == 1 && ci1->u_data[0] == code && + + ci2->l_len == 0 && + ci2->f_code == 0 && + ci2->u_len == 1 && ci2->u_data[0] == code) { + te->len = 3; + te->data = 0; + te->type = RUN_TYPE_LSU; + return; + } + + if (is_complicated_case(ci)) { + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (ci1->u_len != 1 || + ci1->u_data[0] != ci->u_data[0] + len || + ci1->l_len != 0 || + ci1->f_code != ci1->u_data[0]) + break; + len++; + } + if (len > 1) { + te->len = len; + te->type = RUN_TYPE_UF; + te->data = ci->u_data[0]; + return; + } + + if (ci->u_len == 2 && ci->u_data[1] == 0x399 && + ci->f_code == 0 && ci->l_len == 0) { + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (!(ci1->u_len == 2 && + ci1->u_data[1] == 0x399 && + ci1->u_data[0] == ci->u_data[0] + len && + ci1->f_code == 0 && + ci1->l_len == 0)) + break; + len++; + } + te->len = len; + te->type = RUN_TYPE_U_EXT2; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->u_data[1]; + te->ext_len = 2; + return; + } + + if (ci->u_len == 2 && ci->u_data[1] == 0x399 && + ci->l_len == 1 && ci->f_code == ci->l_data[0]) { + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (!(ci1->u_len == 2 && + ci1->u_data[1] == 0x399 && + ci1->u_data[0] == ci->u_data[0] + len && + ci1->l_len == 1 && + ci1->l_data[0] == ci->l_data[0] + len && + ci1->f_code == ci1->l_data[0])) + break; + len++; + } + te->len = len; + te->type = RUN_TYPE_U2L_399_EXT2; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->l_data[0]; + te->ext_len = 2; + return; + } + + if (ci->l_len == 1 && ci->u_len == 0 && ci->f_code == 0) { + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (!(ci1->l_len == 1 && + ci1->l_data[0] == ci->l_data[0] + len && + ci1->u_len == 0 && ci1->f_code == 0)) + break; + len++; + } + te->len = len; + te->type = RUN_TYPE_L; + te->data = ci->l_data[0]; + return; + } + + if (ci->l_len == 0 && + ci->u_len == 1 && + ci->u_data[0] < 0x1000 && + ci->f_code == ci->u_data[0] + 0x20) { + te->len = 1; + te->type = RUN_TYPE_UF_D20; + te->data = ci->u_data[0]; + } else if (ci->l_len == 0 && + ci->u_len == 1 && + ci->f_code == ci->u_data[0] + 1) { + te->len = 1; + te->type = RUN_TYPE_UF_D1_EXT; + te->ext_data[0] = ci->u_data[0]; + te->ext_len = 1; + } else if (ci->l_len == 2 && ci->u_len == 0 && ci->f_code == 0) { + te->len = 1; + te->type = RUN_TYPE_L_EXT2; + te->ext_data[0] = ci->l_data[0]; + te->ext_data[1] = ci->l_data[1]; + te->ext_len = 2; + } else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_code == 0) { + te->len = 1; + te->type = RUN_TYPE_U_EXT2; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->u_data[1]; + te->ext_len = 2; + } else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_code == 0) { + te->len = 1; + te->type = RUN_TYPE_U_EXT3; + te->ext_data[0] = ci->u_data[0]; + te->ext_data[1] = ci->u_data[1]; + te->ext_data[2] = ci->u_data[2]; + te->ext_len = 3; + } else { + printf("unsupported encoding case:\n"); + dump_cc_info(ci, code); + abort(); + } + } else { + /* look for a run of identical conversions */ + len = 0; + for(;;) { + if (code >= CHARCODE_MAX || len >= 126) + break; + ci = &tab[code + len]; + ci1 = &tab[code + len + 1]; + if (is_complicated_case(ci) || is_complicated_case(ci1)) { + break; + } + if (ci->l_len != 1 || ci->l_data[0] != code + len + 1) + break; + if (ci1->u_len != 1 || ci1->u_data[0] != code + len) + break; + len += 2; + } + if (len > 0) { + te->len = len; + te->type = RUN_TYPE_UL; + te->data = 0; + return; + } + + ci = &tab[code]; + is_lower = ci->l_len > 0; + len = 1; + while (code + len <= CHARCODE_MAX) { + ci1 = &tab[code + len]; + if (is_complicated_case(ci1)) + break; + if (is_lower) { + if (ci1->l_len != 1 || + ci1->l_data[0] != ci->l_data[0] + len) + break; + } else { + if (ci1->u_len != 1 || + ci1->u_data[0] != ci->u_data[0] + len) + break; + } + len++; + } + te->len = len; + if (is_lower) { + te->type = RUN_TYPE_LF; + te->data = ci->l_data[0]; + } else { + te->type = RUN_TYPE_U; + te->data = ci->u_data[0]; + } + } +} + +TableEntry conv_table[1000]; +int conv_table_len; +int ext_data[1000]; +int ext_data_len; + +void dump_case_conv_table1(void) +{ + int i, j; + const TableEntry *te; + + for(i = 0; i < conv_table_len; i++) { + te = &conv_table[i]; + printf("%05x %02x %-10s %05x", + te->code, te->len, run_type_str[te->type], te->data); + for(j = 0; j < te->ext_len; j++) { + printf(" %05x", te->ext_data[j]); + } + printf("\n"); + } + printf("table_len=%d ext_len=%d\n", conv_table_len, ext_data_len); +} + +int find_data_index(const TableEntry *conv_table, int len, int data) +{ + int i; + const TableEntry *te; + for(i = 0; i < len; i++) { + te = &conv_table[i]; + if (te->code == data) + return i; + } + return -1; +} + +int find_ext_data_index(int data) +{ + int i; + for(i = 0; i < ext_data_len; i++) { + if (ext_data[i] == data) + return i; + } + assert(ext_data_len < countof(ext_data)); + ext_data[ext_data_len++] = data; + return ext_data_len - 1; +} + +void build_conv_table(CCInfo *tab) +{ + int code, i, j; + CCInfo *ci; + TableEntry *te; + + te = conv_table; + for(code = 0; code <= CHARCODE_MAX; code++) { + ci = &tab[code]; + if (ci->u_len == 0 && ci->l_len == 0 && ci->f_code == 0) + continue; + assert(te - conv_table < countof(conv_table)); + find_run_type(te, tab, code); +#if 0 + if (te->type == RUN_TYPE_TODO) { + printf("TODO: "); + dump_cc_info(ci, code); + } +#endif + assert(te->len <= 127); + code += te->len - 1; + te++; + } + conv_table_len = te - conv_table; + + /* find the data index */ + for(i = 0; i < conv_table_len; i++) { + int data_index; + te = &conv_table[i]; + + switch(te->type) { + case RUN_TYPE_U: + case RUN_TYPE_L: + case RUN_TYPE_UF: + case RUN_TYPE_LF: + data_index = find_data_index(conv_table, conv_table_len, te->data); + if (data_index < 0) { + switch(te->type) { + case RUN_TYPE_U: + te->type = RUN_TYPE_U_EXT; + te->ext_len = 1; + te->ext_data[0] = te->data; + break; + case RUN_TYPE_LF: + te->type = RUN_TYPE_LF_EXT; + te->ext_len = 1; + te->ext_data[0] = te->data; + break; + default: + printf("%05x: index not found\n", te->code); + exit(1); + } + } else { + te->data_index = data_index; + } + break; + case RUN_TYPE_UF_D20: + te->data_index = te->data; + break; + } + } + + /* find the data index for ext_data */ + for(i = 0; i < conv_table_len; i++) { + te = &conv_table[i]; + if (te->type == RUN_TYPE_U_EXT3) { + int p, v; + v = 0; + for(j = 0; j < 3; j++) { + p = find_ext_data_index(te->ext_data[j]); + assert(p < 16); + v = (v << 4) | p; + } + te->data_index = v; + } + } + + for(i = 0; i < conv_table_len; i++) { + te = &conv_table[i]; + if (te->type == RUN_TYPE_L_EXT2 || + te->type == RUN_TYPE_U_EXT2 || + te->type == RUN_TYPE_U2L_399_EXT2) { + int p, v; + v = 0; + for(j = 0; j < 2; j++) { + p = find_ext_data_index(te->ext_data[j]); + assert(p < 64); + v = (v << 6) | p; + } + te->data_index = v; + } + } + + for(i = 0; i < conv_table_len; i++) { + te = &conv_table[i]; + if (te->type == RUN_TYPE_UF_D1_EXT || + te->type == RUN_TYPE_U_EXT || + te->type == RUN_TYPE_LF_EXT) { + te->data_index = find_ext_data_index(te->ext_data[0]); + } + } +#ifdef DUMP_CASE_CONV_TABLE + dump_case_conv_table1(); +#endif +} + +void dump_case_conv_table(FILE *f) +{ + int i; + uint32_t v; + const TableEntry *te; + + fprintf(f, "static const uint32_t case_conv_table1[%u] = {", conv_table_len); + for(i = 0; i < conv_table_len; i++) { + if (i % 4 == 0) + fprintf(f, "\n "); + te = &conv_table[i]; + v = te->code << (32 - 17); + v |= te->len << (32 - 17 - 7); + v |= te->type << (32 - 17 - 7 - 4); + v |= te->data_index >> 8; + fprintf(f, " 0x%08x,", v); + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const uint8_t case_conv_table2[%u] = {", conv_table_len); + for(i = 0; i < conv_table_len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + te = &conv_table[i]; + fprintf(f, " 0x%02x,", te->data_index & 0xff); + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const uint16_t case_conv_ext[%u] = {", ext_data_len); + for(i = 0; i < ext_data_len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + fprintf(f, " 0x%04x,", ext_data[i]); + } + fprintf(f, "\n};\n\n"); +} + +int tabcmp(const int *tab1, const int *tab2, int n) +{ + int i; + for(i = 0; i < n; i++) { + if (tab1[i] != tab2[i]) + return -1; + } + return 0; +} + +void dump_str(const char *str, const int *buf, int len) +{ + int i; + printf("%s=", str); + for(i = 0; i < len; i++) + printf(" %05x", buf[i]); + printf("\n"); +} + +void compute_internal_props(void) +{ + int i; + BOOL has_ul; + + for(i = 0; i <= CHARCODE_MAX; i++) { + CCInfo *ci = &unicode_db[i]; + has_ul = (ci->u_len != 0 || ci->l_len != 0 || ci->f_code != 0); + if (has_ul) { + assert(get_prop(i, PROP_Cased)); + } else { + set_prop(i, PROP_Cased1, get_prop(i, PROP_Cased)); + } + set_prop(i, PROP_ID_Continue1, + get_prop(i, PROP_ID_Continue) & (get_prop(i, PROP_ID_Start) ^ 1)); + set_prop(i, PROP_XID_Start1, + get_prop(i, PROP_ID_Start) ^ get_prop(i, PROP_XID_Start)); + set_prop(i, PROP_XID_Continue1, + get_prop(i, PROP_ID_Continue) ^ get_prop(i, PROP_XID_Continue)); + set_prop(i, PROP_Changes_When_Titlecased1, + get_prop(i, PROP_Changes_When_Titlecased) ^ (ci->u_len != 0)); + set_prop(i, PROP_Changes_When_Casefolded1, + get_prop(i, PROP_Changes_When_Casefolded) ^ (ci->f_code != 0)); + /* XXX: reduce table size (438 bytes) */ + set_prop(i, PROP_Changes_When_NFKC_Casefolded1, + get_prop(i, PROP_Changes_When_NFKC_Casefolded) ^ (ci->f_code != 0)); +#if 0 + /* TEST */ +#define M(x) (1U << GCAT_ ## x) + { + int b; + b = ((M(Mn) | M(Cf) | M(Lm) | M(Sk)) >> + unicode_db[i].general_category) & 1; + set_prop(i, PROP_Cased1, + get_prop(i, PROP_Case_Ignorable) ^ b); + } +#undef M +#endif + } +} + +void dump_byte_table(FILE *f, const char *cname, const uint8_t *tab, int len) +{ + int i; + fprintf(f, "static const uint8_t %s[%d] = {", cname, len); + for(i = 0; i < len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + fprintf(f, " 0x%02x,", tab[i]); + } + fprintf(f, "\n};\n\n"); +} + +#define PROP_BLOCK_LEN 32 + +void build_prop_table(FILE *f, int prop_index, BOOL add_index) +{ + int i, j, n, v, offset, code; + DynBuf dbuf_s, *dbuf = &dbuf_s; + DynBuf dbuf1_s, *dbuf1 = &dbuf1_s; + DynBuf dbuf2_s, *dbuf2 = &dbuf2_s; + const uint32_t *buf; + int buf_len, block_end_pos, bit; + char cname[128]; + + dbuf_init(dbuf1); + + for(i = 0; i <= CHARCODE_MAX;) { + v = get_prop(i, prop_index); + j = i + 1; + while (j <= CHARCODE_MAX && get_prop(j, prop_index) == v) { + j++; + } + n = j - i; + if (j == (CHARCODE_MAX + 1) && v == 0) + break; /* no need to encode last zero run */ + //printf("%05x: %d %d\n", i, n, v); + dbuf_put_u32(dbuf1, n - 1); + i += n; + } + + dbuf_init(dbuf); + dbuf_init(dbuf2); + buf = (uint32_t *)dbuf1->buf; + buf_len = dbuf1->size / sizeof(buf[0]); + + /* the first value is assumed to be 0 */ + assert(get_prop(0, prop_index) == 0); + + block_end_pos = PROP_BLOCK_LEN; + i = 0; + code = 0; + bit = 0; + while (i < buf_len) { + if (add_index && dbuf->size >= block_end_pos && bit == 0) { + offset = (dbuf->size - block_end_pos); + /* XXX: offset could be larger in case of runs of small + lengths. Could add code to change the encoding to + prevent it at the expense of one byte loss */ + assert(offset <= 7); + v = code | (offset << 21); + dbuf_putc(dbuf2, v); + dbuf_putc(dbuf2, v >> 8); + dbuf_putc(dbuf2, v >> 16); + block_end_pos += PROP_BLOCK_LEN; + } + + v = buf[i]; + code += v + 1; + bit ^= 1; + if (v < 8 && (i + 1) < buf_len && buf[i + 1] < 8) { + code += buf[i + 1] + 1; + bit ^= 1; + dbuf_putc(dbuf, (v << 3) | buf[i + 1]); + i += 2; + } else if (v < 128) { + dbuf_putc(dbuf, 0x80 + v); + i++; + } else if (v < (1 << 13)) { + dbuf_putc(dbuf, 0x40 + (v >> 8)); + dbuf_putc(dbuf, v); + i++; + } else { + assert(v < (1 << 21)); + dbuf_putc(dbuf, 0x60 + (v >> 16)); + dbuf_putc(dbuf, v >> 8); + dbuf_putc(dbuf, v); + i++; + } + } + + if (add_index) { + /* last index entry */ + v = code; + dbuf_putc(dbuf2, v); + dbuf_putc(dbuf2, v >> 8); + dbuf_putc(dbuf2, v >> 16); + } + +#ifdef DUMP_TABLE_SIZE + printf("prop %s: length=%d bytes\n", unicode_prop_name[prop_index], + (int)(dbuf->size + dbuf2->size)); +#endif + snprintf(cname, sizeof(cname), "unicode_prop_%s_table", unicode_prop_name[prop_index]); + dump_byte_table(f, cname, dbuf->buf, dbuf->size); + if (add_index) { + snprintf(cname, sizeof(cname), "unicode_prop_%s_index", unicode_prop_name[prop_index]); + dump_byte_table(f, cname, dbuf2->buf, dbuf2->size); + } + + dbuf_free(dbuf); + dbuf_free(dbuf1); + dbuf_free(dbuf2); +} + +void build_flags_tables(FILE *f) +{ + build_prop_table(f, PROP_Cased1, TRUE); + build_prop_table(f, PROP_Case_Ignorable, TRUE); + build_prop_table(f, PROP_ID_Start, TRUE); + build_prop_table(f, PROP_ID_Continue1, TRUE); +} + +void dump_name_table(FILE *f, const char *cname, const char **tab_name, int len, + const char **tab_short_name) +{ + int i, w, maxw; + + maxw = 0; + for(i = 0; i < len; i++) { + w = strlen(tab_name[i]); + if (tab_short_name[i][0] != '\0') { + w += 1 + strlen(tab_short_name[i]); + } + if (maxw < w) + maxw = w; + } + + /* generate a sequence of strings terminated by an empty string */ + fprintf(f, "static const char %s[] =\n", cname); + for(i = 0; i < len; i++) { + fprintf(f, " \""); + w = fprintf(f, "%s", tab_name[i]); + if (tab_short_name[i][0] != '\0') { + w += fprintf(f, ",%s", tab_short_name[i]); + } + fprintf(f, "\"%*s\"\\0\"\n", 1 + maxw - w, ""); + } + fprintf(f, ";\n\n"); +} + +void build_general_category_table(FILE *f) +{ + int i, v, j, n, n1; + DynBuf dbuf_s, *dbuf = &dbuf_s; + int cw_count, cw_len_count[4], cw_start; + + fprintf(f, "typedef enum {\n"); + for(i = 0; i < GCAT_COUNT; i++) + fprintf(f, " UNICODE_GC_%s,\n", unicode_gc_name[i]); + fprintf(f, " UNICODE_GC_COUNT,\n"); + fprintf(f, "} UnicodeGCEnum;\n\n"); + + dump_name_table(f, "unicode_gc_name_table", + unicode_gc_name, GCAT_COUNT, + unicode_gc_short_name); + + + dbuf_init(dbuf); + cw_count = 0; + for(i = 0; i < 4; i++) + cw_len_count[i] = 0; + for(i = 0; i <= CHARCODE_MAX;) { + v = unicode_db[i].general_category; + j = i + 1; + while (j <= CHARCODE_MAX && unicode_db[j].general_category == v) + j++; + n = j - i; + /* compress Lu/Ll runs */ + if (v == GCAT_Lu) { + n1 = 1; + while ((i + n1) <= CHARCODE_MAX && unicode_db[i + n1].general_category == (v + (n1 & 1))) { + n1++; + } + if (n1 > n) { + v = 31; + n = n1; + } + } + // printf("%05x %05x %d\n", i, n, v); + cw_count++; + n--; + cw_start = dbuf->size; + if (n < 7) { + dbuf_putc(dbuf, (n << 5) | v); + } else if (n < 7 + 128) { + n1 = n - 7; + assert(n1 < 128); + dbuf_putc(dbuf, (0xf << 5) | v); + dbuf_putc(dbuf, n1); + } else if (n < 7 + 128 + (1 << 14)) { + n1 = n - (7 + 128); + assert(n1 < (1 << 14)); + dbuf_putc(dbuf, (0xf << 5) | v); + dbuf_putc(dbuf, (n1 >> 8) + 128); + dbuf_putc(dbuf, n1); + } else { + n1 = n - (7 + 128 + (1 << 14)); + assert(n1 < (1 << 22)); + dbuf_putc(dbuf, (0xf << 5) | v); + dbuf_putc(dbuf, (n1 >> 16) + 128 + 64); + dbuf_putc(dbuf, n1 >> 8); + dbuf_putc(dbuf, n1); + } + cw_len_count[dbuf->size - cw_start - 1]++; + i += n + 1; + } +#ifdef DUMP_TABLE_SIZE + printf("general category: %d entries [", + cw_count); + for(i = 0; i < 4; i++) + printf(" %d", cw_len_count[i]); + printf(" ], length=%d bytes\n", (int)dbuf->size); +#endif + + dump_byte_table(f, "unicode_gc_table", dbuf->buf, dbuf->size); + + dbuf_free(dbuf); +} + +void build_script_table(FILE *f) +{ + int i, v, j, n, n1, type; + DynBuf dbuf_s, *dbuf = &dbuf_s; + int cw_count, cw_len_count[4], cw_start; + + fprintf(f, "typedef enum {\n"); + for(i = 0; i < SCRIPT_COUNT; i++) + fprintf(f, " UNICODE_SCRIPT_%s,\n", unicode_script_name[i]); + fprintf(f, " UNICODE_SCRIPT_COUNT,\n"); + fprintf(f, "} UnicodeScriptEnum;\n\n"); + + i = 1; + dump_name_table(f, "unicode_script_name_table", + unicode_script_name + i, SCRIPT_COUNT - i, + unicode_script_short_name + i); + + dbuf_init(dbuf); + cw_count = 0; + for(i = 0; i < 4; i++) + cw_len_count[i] = 0; + for(i = 0; i <= CHARCODE_MAX;) { + v = unicode_db[i].script; + j = i + 1; + while (j <= CHARCODE_MAX && unicode_db[j].script == v) + j++; + n = j - i; + if (v == 0 && j == (CHARCODE_MAX + 1)) + break; + // printf("%05x %05x %d\n", i, n, v); + cw_count++; + n--; + cw_start = dbuf->size; + if (v == 0) + type = 0; + else + type = 1; + if (n < 96) { + dbuf_putc(dbuf, n | (type << 7)); + } else if (n < 96 + (1 << 12)) { + n1 = n - 96; + assert(n1 < (1 << 12)); + dbuf_putc(dbuf, ((n1 >> 8) + 96) | (type << 7)); + dbuf_putc(dbuf, n1); + } else { + n1 = n - (96 + (1 << 12)); + assert(n1 < (1 << 20)); + dbuf_putc(dbuf, ((n1 >> 16) + 112) | (type << 7)); + dbuf_putc(dbuf, n1 >> 8); + dbuf_putc(dbuf, n1); + } + if (type != 0) + dbuf_putc(dbuf, v); + + cw_len_count[dbuf->size - cw_start - 1]++; + i += n + 1; + } +#if defined(DUMP_TABLE_SIZE) + printf("script: %d entries [", + cw_count); + for(i = 0; i < 4; i++) + printf(" %d", cw_len_count[i]); + printf(" ], length=%d bytes\n", (int)dbuf->size); +#endif + + dump_byte_table(f, "unicode_script_table", dbuf->buf, dbuf->size); + + dbuf_free(dbuf); +} + +void build_script_ext_table(FILE *f) +{ + int i, j, n, n1, script_ext_len; + DynBuf dbuf_s, *dbuf = &dbuf_s; + int cw_count; + + dbuf_init(dbuf); + cw_count = 0; + for(i = 0; i <= CHARCODE_MAX;) { + script_ext_len = unicode_db[i].script_ext_len; + j = i + 1; + while (j <= CHARCODE_MAX && + unicode_db[j].script_ext_len == script_ext_len && + !memcmp(unicode_db[j].script_ext, unicode_db[i].script_ext, + script_ext_len)) { + j++; + } + n = j - i; + cw_count++; + n--; + if (n < 128) { + dbuf_putc(dbuf, n); + } else if (n < 128 + (1 << 14)) { + n1 = n - 128; + assert(n1 < (1 << 14)); + dbuf_putc(dbuf, (n1 >> 8) + 128); + dbuf_putc(dbuf, n1); + } else { + n1 = n - (128 + (1 << 14)); + assert(n1 < (1 << 22)); + dbuf_putc(dbuf, (n1 >> 16) + 128 + 64); + dbuf_putc(dbuf, n1 >> 8); + dbuf_putc(dbuf, n1); + } + dbuf_putc(dbuf, script_ext_len); + for(j = 0; j < script_ext_len; j++) + dbuf_putc(dbuf, unicode_db[i].script_ext[j]); + i += n + 1; + } +#ifdef DUMP_TABLE_SIZE + printf("script_ext: %d entries", + cw_count); + printf(", length=%d bytes\n", (int)dbuf->size); +#endif + + dump_byte_table(f, "unicode_script_ext_table", dbuf->buf, dbuf->size); + + dbuf_free(dbuf); +} + +/* the following properties are synthetized so no table is necessary */ +#define PROP_TABLE_COUNT PROP_ASCII + +void build_prop_list_table(FILE *f) +{ + int i; + + for(i = 0; i < PROP_TABLE_COUNT; i++) { + if (i == PROP_ID_Start || + i == PROP_Case_Ignorable || + i == PROP_ID_Continue1) { + /* already generated */ + } else { + build_prop_table(f, i, FALSE); + } + } + + fprintf(f, "typedef enum {\n"); + for(i = 0; i < PROP_COUNT; i++) + fprintf(f, " UNICODE_PROP_%s,\n", unicode_prop_name[i]); + fprintf(f, " UNICODE_PROP_COUNT,\n"); + fprintf(f, "} UnicodePropertyEnum;\n\n"); + + i = PROP_ASCII_Hex_Digit; + dump_name_table(f, "unicode_prop_name_table", + unicode_prop_name + i, PROP_XID_Start - i + 1, + unicode_prop_short_name + i); + + fprintf(f, "static const uint8_t * const unicode_prop_table[] = {\n"); + for(i = 0; i < PROP_TABLE_COUNT; i++) { + fprintf(f, " unicode_prop_%s_table,\n", unicode_prop_name[i]); + } + fprintf(f, "};\n\n"); + + fprintf(f, "static const uint16_t unicode_prop_len_table[] = {\n"); + for(i = 0; i < PROP_TABLE_COUNT; i++) { + fprintf(f, " countof(unicode_prop_%s_table),\n", unicode_prop_name[i]); + } + fprintf(f, "};\n\n"); +} + +#ifdef USE_TEST +int check_conv(uint32_t *res, uint32_t c, int conv_type) +{ + return lre_case_conv(res, c, conv_type); +} + +void check_case_conv(void) +{ + CCInfo *tab = unicode_db; + uint32_t res[3]; + int l, error; + CCInfo ci_s, *ci1, *ci = &ci_s; + int code; + + for(code = 0; code <= CHARCODE_MAX; code++) { + ci1 = &tab[code]; + *ci = *ci1; + if (ci->l_len == 0) { + ci->l_len = 1; + ci->l_data[0] = code; + } + if (ci->u_len == 0) { + ci->u_len = 1; + ci->u_data[0] = code; + } + if (ci->f_code == 0) + ci->f_code = code; + + error = 0; + l = check_conv(res, code, 0); + if (l != ci->u_len || tabcmp((int *)res, ci->u_data, l)) { + printf("ERROR: L\n"); + error++; + } + l = check_conv(res, code, 1); + if (l != ci->l_len || tabcmp((int *)res, ci->l_data, l)) { + printf("ERROR: U\n"); + error++; + } + l = check_conv(res, code, 2); + if (l != 1 || res[0] != ci->f_code) { + printf("ERROR: F\n"); + error++; + } + if (error) { + dump_cc_info(ci, code); + exit(1); + } + } +} + +#ifdef PROFILE +static int64_t get_time_ns(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (int64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; +} +#endif + + +void check_flags(void) +{ + int c; + BOOL flag_ref, flag; + for(c = 0; c <= CHARCODE_MAX; c++) { + flag_ref = get_prop(c, PROP_Cased); + flag = lre_is_cased(c); + if (flag != flag_ref) { + printf("ERROR: c=%05x cased=%d ref=%d\n", + c, flag, flag_ref); + exit(1); + } + + flag_ref = get_prop(c, PROP_Case_Ignorable); + flag = lre_is_case_ignorable(c); + if (flag != flag_ref) { + printf("ERROR: c=%05x case_ignorable=%d ref=%d\n", + c, flag, flag_ref); + exit(1); + } + + flag_ref = get_prop(c, PROP_ID_Start); + flag = lre_is_id_start(c); + if (flag != flag_ref) { + printf("ERROR: c=%05x id_start=%d ref=%d\n", + c, flag, flag_ref); + exit(1); + } + + flag_ref = get_prop(c, PROP_ID_Continue); + flag = lre_is_id_continue(c); + if (flag != flag_ref) { + printf("ERROR: c=%05x id_cont=%d ref=%d\n", + c, flag, flag_ref); + exit(1); + } + } +#ifdef PROFILE + { + int64_t ti, count; + ti = get_time_ns(); + count = 0; + for(c = 0x20; c <= 0xffff; c++) { + flag_ref = get_prop(c, PROP_ID_Start); + flag = lre_is_id_start(c); + assert(flag == flag_ref); + count++; + } + ti = get_time_ns() - ti; + printf("flags time=%0.1f ns/char\n", + (double)ti / count); + } +#endif +} + +#endif + +#define CC_BLOCK_LEN 32 + +void build_cc_table(FILE *f) +{ + int i, cc, n, cc_table_len, type, n1; + DynBuf dbuf_s, *dbuf = &dbuf_s; + DynBuf dbuf1_s, *dbuf1 = &dbuf1_s; + int cw_len_tab[3], cw_start, block_end_pos; + uint32_t v; + + dbuf_init(dbuf); + dbuf_init(dbuf1); + cc_table_len = 0; + for(i = 0; i < countof(cw_len_tab); i++) + cw_len_tab[i] = 0; + block_end_pos = CC_BLOCK_LEN; + for(i = 0; i <= CHARCODE_MAX;) { + cc = unicode_db[i].combining_class; + assert(cc <= 255); + /* check increasing values */ + n = 1; + while ((i + n) <= CHARCODE_MAX && + unicode_db[i + n].combining_class == (cc + n)) + n++; + if (n >= 2) { + type = 1; + } else { + type = 0; + n = 1; + while ((i + n) <= CHARCODE_MAX && + unicode_db[i + n].combining_class == cc) + n++; + } + /* no need to encode the last run */ + if (cc == 0 && (i + n - 1) == CHARCODE_MAX) + break; +#ifdef DUMP_CC_TABLE + printf("%05x %6d %d %d\n", i, n, type, cc); +#endif + if (type == 0) { + if (cc == 0) + type = 2; + else if (cc == 230) + type = 3; + } + n1 = n - 1; + + /* add an entry to the index if necessary */ + if (dbuf->size >= block_end_pos) { + v = i | ((dbuf->size - block_end_pos) << 21); + dbuf_putc(dbuf1, v); + dbuf_putc(dbuf1, v >> 8); + dbuf_putc(dbuf1, v >> 16); + block_end_pos += CC_BLOCK_LEN; + } + cw_start = dbuf->size; + if (n1 < 48) { + dbuf_putc(dbuf, n1 | (type << 6)); + } else if (n1 < 48 + (1 << 11)) { + n1 -= 48; + dbuf_putc(dbuf, ((n1 >> 8) + 48) | (type << 6)); + dbuf_putc(dbuf, n1); + } else { + n1 -= 48 + (1 << 11); + assert(n1 < (1 << 20)); + dbuf_putc(dbuf, ((n1 >> 16) + 56) | (type << 6)); + dbuf_putc(dbuf, n1 >> 8); + dbuf_putc(dbuf, n1); + } + cw_len_tab[dbuf->size - cw_start - 1]++; + if (type == 0 || type == 1) + dbuf_putc(dbuf, cc); + cc_table_len++; + i += n; + } + + /* last index entry */ + v = i; + dbuf_putc(dbuf1, v); + dbuf_putc(dbuf1, v >> 8); + dbuf_putc(dbuf1, v >> 16); + + dump_byte_table(f, "unicode_cc_table", dbuf->buf, dbuf->size); + dump_byte_table(f, "unicode_cc_index", dbuf1->buf, dbuf1->size); + +#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) + printf("CC table: size=%d (%d entries) [", + (int)(dbuf->size + dbuf1->size), + cc_table_len); + for(i = 0; i < countof(cw_len_tab); i++) + printf(" %d", cw_len_tab[i]); + printf(" ]\n"); +#endif + dbuf_free(dbuf); + dbuf_free(dbuf1); +} + +/* maximum length of decomposition: 18 chars (1), then 8 */ +#ifndef USE_TEST +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; +#endif + +const char *decomp_type_str[] = { + "C1", + "L1", + "L2", + "L3", + "L4", + "L5", + "L6", + "L7", + "LL1", + "LL2", + "S1", + "S2", + "S3", + "S4", + "S5", + "I1", + "I2_0", + "I2_1", + "I3_1", + "I3_2", + "I4_1", + "I4_2", + "B1", + "B2", + "B3", + "B4", + "B5", + "B6", + "B7", + "B8", + "B18", + "LS2", + "PAT3", + "S2_UL", + "LS2_UL", +}; + +const int decomp_incr_tab[4][4] = { + { DECOMP_TYPE_I1, 0, -1 }, + { DECOMP_TYPE_I2_0, 0, 1, -1 }, + { DECOMP_TYPE_I3_1, 1, 2, -1 }, + { DECOMP_TYPE_I4_1, 1, 2, -1 }, +}; + +/* + entry size: + type bits + code 18 + len 7 + compat 1 + type 5 + index 16 + total 47 +*/ + +typedef struct { + int code; + uint8_t len; + uint8_t type; + uint8_t c_len; + uint16_t c_min; + uint16_t data_index; + int cost; /* size in bytes from this entry to the end */ +} DecompEntry; + +int get_decomp_run_size(const DecompEntry *de) +{ + int s; + s = 6; + if (de->type <= DECOMP_TYPE_C1) { + /* nothing more */ + } else if (de->type <= DECOMP_TYPE_L7) { + s += de->len * de->c_len * 2; + } else if (de->type <= DECOMP_TYPE_LL2) { + /* 18 bits per char */ + s += (de->len * de->c_len * 18 + 7) / 8; + } else if (de->type <= DECOMP_TYPE_S5) { + s += de->len * de->c_len; + } else if (de->type <= DECOMP_TYPE_I4_2) { + s += de->c_len * 2; + } else if (de->type <= DECOMP_TYPE_B18) { + s += 2 + de->len * de->c_len; + } else if (de->type <= DECOMP_TYPE_LS2) { + s += de->len * 3; + } else if (de->type <= DECOMP_TYPE_PAT3) { + s += 4 + de->len * 2; + } else if (de->type <= DECOMP_TYPE_S2_UL) { + s += de->len; + } else if (de->type <= DECOMP_TYPE_LS2_UL) { + s += (de->len / 2) * 3; + } else { + abort(); + } + return s; +} + +static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; + +/* return -1 if not found */ +int get_short_code(int c) +{ + int i; + if (c < 0x80) { + return c; + } else if (c >= 0x300 && c < 0x350) { + return c - 0x300 + 0x80; + } else { + for(i = 0; i < countof(unicode_short_table); i++) { + if (c == unicode_short_table[i]) + return i + 0x80 + 0x50; + } + return -1; + } +} + +static BOOL is_short(int code) +{ + return get_short_code(code) >= 0; +} + +static BOOL is_short_tab(const int *tab, int len) +{ + int i; + for(i = 0; i < len; i++) { + if (!is_short(tab[i])) + return FALSE; + } + return TRUE; +} + +static BOOL is_16bit(const int *tab, int len) +{ + int i; + for(i = 0; i < len; i++) { + if (tab[i] > 0xffff) + return FALSE; + } + return TRUE; +} + +static uint32_t to_lower_simple(uint32_t c) +{ + /* Latin1 and Cyrillic */ + if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) + c += 0x20; + else + c++; + return c; +} + +/* select best encoding with dynamic programming */ +void find_decomp_run(DecompEntry *tab_de, int i) +{ + DecompEntry de_s, *de = &de_s; + CCInfo *ci, *ci1, *ci2; + int l, j, n, len_max; + + ci = &unicode_db[i]; + l = ci->decomp_len; + if (l == 0) { + tab_de[i].cost = tab_de[i + 1].cost; + return; + } + + /* the offset for the compose table has only 6 bits, so we must + limit if it can be used by the compose table */ + if (!ci->is_compat && !ci->is_excluded && l == 2) + len_max = 64; + else + len_max = 127; + + tab_de[i].cost = 0x7fffffff; + + if (!is_16bit(ci->decomp_data, l)) { + assert(l <= 2); + + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_LL1 + l - 1; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + /* Note: we accept a hole */ + if (!(ci1->decomp_len == 0 || + (ci1->decomp_len == l && + ci1->is_compat == ci->is_compat))) + break; + n++; + } + return; + } + + if (l <= 7) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + if (l == 1 && n == 1) { + de->type = DECOMP_TYPE_C1; + } else { + assert(l <= 8); + de->type = DECOMP_TYPE_L1 + l - 1; + } + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + /* Note: we accept a hole */ + if (!(ci1->decomp_len == 0 || + (ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + is_16bit(ci1->decomp_data, l)))) + break; + n++; + } + } + + if (l <= 8 || l == 18) { + int c_min, c_max, c; + c_min = c_max = -1; + n = 1; + for(;;) { + ci1 = &unicode_db[i + n - 1]; + for(j = 0; j < l; j++) { + c = ci1->decomp_data[j]; + if (c == 0x20) { + /* we accept space for Arabic */ + } else if (c_min == -1) { + c_min = c_max = c; + } else { + c_min = min_int(c_min, c); + c_max = max_int(c_max, c); + } + } + if ((c_max - c_min) > 254) + break; + de->code = i; + de->len = n; + if (l == 18) + de->type = DECOMP_TYPE_B18; + else + de->type = DECOMP_TYPE_B1 + l - 1; + de->c_len = l; + de->c_min = c_min; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == l && + ci1->is_compat == ci->is_compat)) + break; + n++; + } + } + + /* find an ascii run */ + if (l <= 5 && is_short_tab(ci->decomp_data, l)) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_S1 + l - 1; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + /* Note: we accept a hole */ + if (!(ci1->decomp_len == 0 || + (ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + is_short_tab(ci1->decomp_data, l)))) + break; + n++; + } + } + + /* check if a single char is increasing */ + if (l <= 4) { + int idx1, idx; + + for(idx1 = 1; (idx = decomp_incr_tab[l - 1][idx1]) >= 0; idx1++) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = decomp_incr_tab[l - 1][0] + idx1 - 1; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == l && + ci1->is_compat == ci->is_compat)) + goto next1; + for(j = 0; j < l; j++) { + if (j == idx) { + if (ci1->decomp_data[j] != ci->decomp_data[j] + n) + goto next1; + } else { + if (ci1->decomp_data[j] != ci->decomp_data[j]) + goto next1; + } + } + n++; + } + next1: ; + } + } + + if (l == 3) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_PAT3; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + ci1->decomp_data[1] <= 0xffff && + ci1->decomp_data[0] == ci->decomp_data[0] && + ci1->decomp_data[l - 1] == ci->decomp_data[l - 1])) + break; + n++; + } + } + + if (l == 2 && is_short(ci->decomp_data[1])) { + n = 1; + for(;;) { + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_LS2; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + if (!((i + n) <= CHARCODE_MAX && n < len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == 0 || + (ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + ci1->decomp_data[0] <= 0xffff && + is_short(ci1->decomp_data[1])))) + break; + n++; + } + } + + if (l == 2) { + BOOL is_16bit; + + n = 0; + is_16bit = FALSE; + for(;;) { + if (!((i + n + 1) <= CHARCODE_MAX && n + 2 <= len_max)) + break; + ci1 = &unicode_db[i + n]; + if (!(ci1->decomp_len == l && + ci1->is_compat == ci->is_compat && + is_short(ci1->decomp_data[1]))) + break; + if (!is_16bit && !is_short(ci1->decomp_data[0])) + is_16bit = TRUE; + ci2 = &unicode_db[i + n + 1]; + if (!(ci2->decomp_len == l && + ci2->is_compat == ci->is_compat && + ci2->decomp_data[0] == to_lower_simple(ci1->decomp_data[0]) && + ci2->decomp_data[1] == ci1->decomp_data[1])) + break; + n += 2; + de->code = i; + de->len = n; + de->type = DECOMP_TYPE_S2_UL + is_16bit; + de->c_len = l; + de->cost = get_decomp_run_size(de) + tab_de[i + n].cost; + if (de->cost < tab_de[i].cost) { + tab_de[i] = *de; + } + } + } +} + +void put16(uint8_t *data_buf, int *pidx, uint16_t c) +{ + int idx; + idx = *pidx; + data_buf[idx++] = c; + data_buf[idx++] = c >> 8; + *pidx = idx; +} + +void add_decomp_data(uint8_t *data_buf, int *pidx, DecompEntry *de) +{ + int i, j, idx, c; + CCInfo *ci; + + idx = *pidx; + de->data_index = idx; + if (de->type <= DECOMP_TYPE_C1) { + ci = &unicode_db[de->code]; + assert(ci->decomp_len == 1); + de->data_index = ci->decomp_data[0]; + } else if (de->type <= DECOMP_TYPE_L7) { + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + for(j = 0; j < de->c_len; j++) { + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[j]; + put16(data_buf, &idx, c); + } + } + } else if (de->type <= DECOMP_TYPE_LL2) { + int n, p, k; + n = (de->len * de->c_len * 18 + 7) / 8; + p = de->len * de->c_len * 2; + memset(data_buf + idx, 0, n); + k = 0; + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + for(j = 0; j < de->c_len; j++) { + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[j]; + data_buf[idx + k * 2] = c; + data_buf[idx + k * 2 + 1] = c >> 8; + data_buf[idx + p + (k / 4)] |= (c >> 16) << ((k % 4) * 2); + k++; + } + } + idx += n; + } else if (de->type <= DECOMP_TYPE_S5) { + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + for(j = 0; j < de->c_len; j++) { + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[j]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + } + } + } else if (de->type <= DECOMP_TYPE_I4_2) { + ci = &unicode_db[de->code]; + assert(ci->decomp_len == de->c_len); + for(j = 0; j < de->c_len; j++) + put16(data_buf, &idx, ci->decomp_data[j]); + } else if (de->type <= DECOMP_TYPE_B18) { + c = de->c_min; + data_buf[idx++] = c; + data_buf[idx++] = c >> 8; + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + for(j = 0; j < de->c_len; j++) { + assert(ci->decomp_len == de->c_len); + c = ci->decomp_data[j]; + if (c == 0x20) { + c = 0xff; + } else { + c -= de->c_min; + assert((uint32_t)c <= 254); + } + data_buf[idx++] = c; + } + } + } else if (de->type <= DECOMP_TYPE_LS2) { + assert(de->c_len == 2); + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[0]; + put16(data_buf, &idx, c); + + if (ci->decomp_len == 0) + c = 0; + else + c = ci->decomp_data[1]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + } + } else if (de->type <= DECOMP_TYPE_PAT3) { + ci = &unicode_db[de->code]; + assert(ci->decomp_len == 3); + put16(data_buf, &idx, ci->decomp_data[0]); + put16(data_buf, &idx, ci->decomp_data[2]); + for(i = 0; i < de->len; i++) { + ci = &unicode_db[de->code + i]; + assert(ci->decomp_len == 3); + put16(data_buf, &idx, ci->decomp_data[1]); + } + } else if (de->type <= DECOMP_TYPE_S2_UL) { + for(i = 0; i < de->len; i += 2) { + ci = &unicode_db[de->code + i]; + c = ci->decomp_data[0]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + c = ci->decomp_data[1]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + } + } else if (de->type <= DECOMP_TYPE_LS2_UL) { + for(i = 0; i < de->len; i += 2) { + ci = &unicode_db[de->code + i]; + c = ci->decomp_data[0]; + put16(data_buf, &idx, c); + c = ci->decomp_data[1]; + c = get_short_code(c); + assert(c >= 0); + data_buf[idx++] = c; + } + } else { + abort(); + } + *pidx = idx; +} + +#if 0 +void dump_large_char(void) +{ + int i, j; + for(i = 0; i <= CHARCODE_MAX; i++) { + CCInfo *ci = &unicode_db[i]; + for(j = 0; j < ci->decomp_len; j++) { + if (ci->decomp_data[j] > 0xffff) + printf("%05x\n", ci->decomp_data[j]); + } + } +} +#endif + +void build_compose_table(FILE *f, const DecompEntry *tab_de); + +void build_decompose_table(FILE *f) +{ + int i, array_len, code_max, data_len, count; + DecompEntry *tab_de, de_s, *de = &de_s; + uint8_t *data_buf; + + code_max = CHARCODE_MAX; + + tab_de = mallocz((code_max + 2) * sizeof(*tab_de)); + + for(i = code_max; i >= 0; i--) { + find_decomp_run(tab_de, i); + } + + /* build the data buffer */ + data_buf = malloc(100000); + data_len = 0; + array_len = 0; + for(i = 0; i <= code_max; i++) { + de = &tab_de[i]; + if (de->len != 0) { + add_decomp_data(data_buf, &data_len, de); + i += de->len - 1; + array_len++; + } + } + +#ifdef DUMP_DECOMP_TABLE + /* dump */ + { + int size, size1; + + printf("START LEN TYPE L C SIZE\n"); + size = 0; + for(i = 0; i <= code_max; i++) { + de = &tab_de[i]; + if (de->len != 0) { + size1 = get_decomp_run_size(de); + printf("%05x %3d %6s %2d %1d %4d\n", i, de->len, + decomp_type_str[de->type], de->c_len, + unicode_db[i].is_compat, size1); + i += de->len - 1; + size += size1; + } + } + + printf("array_len=%d estimated size=%d bytes actual=%d bytes\n", + array_len, size, array_len * 6 + data_len); + } +#endif + + fprintf(f, "static const uint32_t unicode_decomp_table1[%u] = {", + array_len); + count = 0; + for(i = 0; i <= code_max; i++) { + de = &tab_de[i]; + if (de->len != 0) { + uint32_t v; + if (count++ % 4 == 0) + fprintf(f, "\n "); + v = (de->code << (32 - 18)) | + (de->len << (32 - 18 - 7)) | + (de->type << (32 - 18 - 7 - 6)) | + unicode_db[de->code].is_compat; + fprintf(f, " 0x%08x,", v); + i += de->len - 1; + } + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const uint16_t unicode_decomp_table2[%u] = {", + array_len); + count = 0; + for(i = 0; i <= code_max; i++) { + de = &tab_de[i]; + if (de->len != 0) { + if (count++ % 8 == 0) + fprintf(f, "\n "); + fprintf(f, " 0x%04x,", de->data_index); + i += de->len - 1; + } + } + fprintf(f, "\n};\n\n"); + + fprintf(f, "static const uint8_t unicode_decomp_data[%u] = {", + data_len); + for(i = 0; i < data_len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + fprintf(f, " 0x%02x,", data_buf[i]); + } + fprintf(f, "\n};\n\n"); + + build_compose_table(f, tab_de); + + free(data_buf); + + free(tab_de); +} + +typedef struct { + uint32_t c[2]; + uint32_t p; +} ComposeEntry; + +#define COMPOSE_LEN_MAX 10000 + +static int ce_cmp(const void *p1, const void *p2) +{ + const ComposeEntry *ce1 = p1; + const ComposeEntry *ce2 = p2; + int i; + + for(i = 0; i < 2; i++) { + if (ce1->c[i] < ce2->c[i]) + return -1; + else if (ce1->c[i] > ce2->c[i]) + return 1; + } + return 0; +} + + +static int get_decomp_pos(const DecompEntry *tab_de, int c) +{ + int i, v, k; + const DecompEntry *de; + + k = 0; + for(i = 0; i <= CHARCODE_MAX; i++) { + de = &tab_de[i]; + if (de->len != 0) { + if (c >= de->code && c < de->code + de->len) { + v = c - de->code; + assert(v < 64); + v |= k << 6; + assert(v < 65536); + return v; + } + i += de->len - 1; + k++; + } + } + return -1; +} + +void build_compose_table(FILE *f, const DecompEntry *tab_de) +{ + int i, v, tab_ce_len; + ComposeEntry *ce, *tab_ce; + + tab_ce = malloc(sizeof(*tab_ce) * COMPOSE_LEN_MAX); + tab_ce_len = 0; + for(i = 0; i <= CHARCODE_MAX; i++) { + CCInfo *ci = &unicode_db[i]; + if (ci->decomp_len == 2 && !ci->is_compat && + !ci->is_excluded) { + assert(tab_ce_len < COMPOSE_LEN_MAX); + ce = &tab_ce[tab_ce_len++]; + ce->c[0] = ci->decomp_data[0]; + ce->c[1] = ci->decomp_data[1]; + ce->p = i; + } + } + qsort(tab_ce, tab_ce_len, sizeof(*tab_ce), ce_cmp); + +#if 0 + { + printf("tab_ce_len=%d\n", tab_ce_len); + for(i = 0; i < tab_ce_len; i++) { + ce = &tab_ce[i]; + printf("%05x %05x %05x\n", ce->c[0], ce->c[1], ce->p); + } + } +#endif + + fprintf(f, "static const uint16_t unicode_comp_table[%u] = {", + tab_ce_len); + for(i = 0; i < tab_ce_len; i++) { + if (i % 8 == 0) + fprintf(f, "\n "); + v = get_decomp_pos(tab_de, tab_ce[i].p); + if (v < 0) { + printf("ERROR: entry for c=%04x not found\n", + tab_ce[i].p); + exit(1); + } + fprintf(f, " 0x%04x,", v); + } + fprintf(f, "\n};\n\n"); + + free(tab_ce); +} + +#ifdef USE_TEST +void check_decompose_table(void) +{ + int c; + CCInfo *ci; + int res[UNICODE_DECOMP_LEN_MAX], *ref; + int len, ref_len, is_compat; + + for(is_compat = 0; is_compat <= 1; is_compat++) { + for(c = 0; c < CHARCODE_MAX; c++) { + ci = &unicode_db[c]; + ref_len = ci->decomp_len; + ref = ci->decomp_data; + if (!is_compat && ci->is_compat) { + ref_len = 0; + } + len = unicode_decomp_char((uint32_t *)res, c, is_compat); + if (len != ref_len || + tabcmp(res, ref, ref_len) != 0) { + printf("ERROR c=%05x compat=%d\n", c, is_compat); + dump_str("res", res, len); + dump_str("ref", ref, ref_len); + exit(1); + } + } + } +} + +void check_compose_table(void) +{ + int i, p; + /* XXX: we don't test all the cases */ + + for(i = 0; i <= CHARCODE_MAX; i++) { + CCInfo *ci = &unicode_db[i]; + if (ci->decomp_len == 2 && !ci->is_compat && + !ci->is_excluded) { + p = unicode_compose_pair(ci->decomp_data[0], ci->decomp_data[1]); + if (p != i) { + printf("ERROR compose: c=%05x %05x -> %05x ref=%05x\n", + ci->decomp_data[0], ci->decomp_data[1], p, i); + exit(1); + } + } + } + + + +} + +#endif + + + +#ifdef USE_TEST + +void check_str(const char *msg, int num, const int *in_buf, int in_len, + const int *buf1, int len1, + const int *buf2, int len2) +{ + if (len1 != len2 || tabcmp(buf1, buf2, len1) != 0) { + printf("%d: ERROR %s:\n", num, msg); + dump_str(" in", in_buf, in_len); + dump_str("res", buf1, len1); + dump_str("ref", buf2, len2); + exit(1); + } +} + +void check_cc_table(void) +{ + int cc, cc_ref, c; + + for(c = 0; c <= CHARCODE_MAX; c++) { + cc_ref = unicode_db[c].combining_class; + cc = unicode_get_cc(c); + if (cc != cc_ref) { + printf("ERROR: c=%04x cc=%d cc_ref=%d\n", + c, cc, cc_ref); + exit(1); + } + } +#ifdef PROFILE + { + int64_t ti, count; + + ti = get_time_ns(); + count = 0; + /* only do it on meaningful chars */ + for(c = 0x20; c <= 0xffff; c++) { + cc_ref = unicode_db[c].combining_class; + cc = unicode_get_cc(c); + count++; + } + ti = get_time_ns() - ti; + printf("cc time=%0.1f ns/char\n", + (double)ti / count); + } +#endif +} + +void normalization_test(const char *filename) +{ + FILE *f; + char line[4096], *p; + int *in_str, *nfc_str, *nfd_str, *nfkc_str, *nfkd_str; + int in_len, nfc_len, nfd_len, nfkc_len, nfkd_len; + int *buf, buf_len, pos; + + f = fopen(filename, "rb"); + if (!f) { + perror(filename); + exit(1); + } + pos = 0; + for(;;) { + if (!get_line(line, sizeof(line), f)) + break; + pos++; + p = line; + while (isspace(*p)) + p++; + if (*p == '#' || *p == '@') + continue; + in_str = get_field_str(&in_len, p, 0); + nfc_str = get_field_str(&nfc_len, p, 1); + nfd_str = get_field_str(&nfd_len, p, 2); + nfkc_str = get_field_str(&nfkc_len, p, 3); + nfkd_str = get_field_str(&nfkd_len, p, 4); + + // dump_str("in", in_str, in_len); + + buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFD, NULL, NULL); + check_str("nfd", pos, in_str, in_len, buf, buf_len, nfd_str, nfd_len); + free(buf); + + buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFKD, NULL, NULL); + check_str("nfkd", pos, in_str, in_len, buf, buf_len, nfkd_str, nfkd_len); + free(buf); + + buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFC, NULL, NULL); + check_str("nfc", pos, in_str, in_len, buf, buf_len, nfc_str, nfc_len); + free(buf); + + buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFKC, NULL, NULL); + check_str("nfkc", pos, in_str, in_len, buf, buf_len, nfkc_str, nfkc_len); + free(buf); + + free(in_str); + free(nfc_str); + free(nfd_str); + free(nfkc_str); + free(nfkd_str); + } + fclose(f); +} +#endif + +int main(int argc, char **argv) +{ + const char *unicode_db_path, *outfilename; + char filename[1024]; + + if (argc < 2) { + printf("usage: %s unicode_db_path [output_file]\n" + "\n" + "If no output_file is given, a self test is done using the current unicode library\n", + argv[0]); + exit(1); + } + unicode_db_path = argv[1]; + outfilename = NULL; + if (argc >= 3) + outfilename = argv[2]; + + unicode_db = mallocz(sizeof(unicode_db[0]) * (CHARCODE_MAX + 1)); + + snprintf(filename, sizeof(filename), "%s/UnicodeData.txt", unicode_db_path); + + parse_unicode_data(filename); + + snprintf(filename, sizeof(filename), "%s/SpecialCasing.txt", unicode_db_path); + parse_special_casing(unicode_db, filename); + + snprintf(filename, sizeof(filename), "%s/CaseFolding.txt", unicode_db_path); + parse_case_folding(unicode_db, filename); + + snprintf(filename, sizeof(filename), "%s/CompositionExclusions.txt", unicode_db_path); + parse_composition_exclusions(filename); + + snprintf(filename, sizeof(filename), "%s/DerivedCoreProperties.txt", unicode_db_path); + parse_derived_core_properties(filename); + + snprintf(filename, sizeof(filename), "%s/DerivedNormalizationProps.txt", unicode_db_path); + parse_derived_norm_properties(filename); + + snprintf(filename, sizeof(filename), "%s/PropList.txt", unicode_db_path); + parse_prop_list(filename); + + snprintf(filename, sizeof(filename), "%s/Scripts.txt", unicode_db_path); + parse_scripts(filename); + + snprintf(filename, sizeof(filename), "%s/ScriptExtensions.txt", + unicode_db_path); + parse_script_extensions(filename); + + snprintf(filename, sizeof(filename), "%s/emoji-data.txt", + unicode_db_path); + parse_prop_list(filename); + + // dump_data(unicode_db); + + build_conv_table(unicode_db); + + // dump_table(); + + if (!outfilename) { +#ifdef USE_TEST + check_case_conv(); + check_flags(); + check_decompose_table(); + check_compose_table(); + check_cc_table(); + snprintf(filename, sizeof(filename), "%s/NormalizationTest.txt", unicode_db_path); + normalization_test(filename); +#else + fprintf(stderr, "Tests are not compiled\n"); + exit(1); +#endif + } else + { + FILE *fo = fopen(outfilename, "wb"); + + if (!fo) { + perror(outfilename); + exit(1); + } + fprintf(fo, + "/* Compressed unicode tables */\n" + "/* Automatically generated file - do not edit */\n" + "\n" + "#include \n" + "\n"); + dump_case_conv_table(fo); + compute_internal_props(); + build_flags_tables(fo); + fprintf(fo, "#ifdef CONFIG_ALL_UNICODE\n\n"); + build_cc_table(fo); + build_decompose_table(fo); + build_general_category_table(fo); + build_script_table(fo); + build_script_ext_table(fo); + build_prop_list_table(fo); + fprintf(fo, "#endif /* CONFIG_ALL_UNICODE */\n"); + fclose(fo); + } + return 0; +} diff --git a/unicode_gen_def.h b/unicode_gen_def.h new file mode 100644 index 0000000..08dd8d7 --- /dev/null +++ b/unicode_gen_def.h @@ -0,0 +1,280 @@ +#ifdef UNICODE_GENERAL_CATEGORY +DEF(Cn, "Unassigned") /* must be zero */ +DEF(Lu, "Uppercase_Letter") +DEF(Ll, "Lowercase_Letter") +DEF(Lt, "Titlecase_Letter") +DEF(Lm, "Modifier_Letter") +DEF(Lo, "Other_Letter") +DEF(Mn, "Nonspacing_Mark") +DEF(Mc, "Spacing_Mark") +DEF(Me, "Enclosing_Mark") +DEF(Nd, "Decimal_Number,digit") +DEF(Nl, "Letter_Number") +DEF(No, "Other_Number") +DEF(Sm, "Math_Symbol") +DEF(Sc, "Currency_Symbol") +DEF(Sk, "Modifier_Symbol") +DEF(So, "Other_Symbol") +DEF(Pc, "Connector_Punctuation") +DEF(Pd, "Dash_Punctuation") +DEF(Ps, "Open_Punctuation") +DEF(Pe, "Close_Punctuation") +DEF(Pi, "Initial_Punctuation") +DEF(Pf, "Final_Punctuation") +DEF(Po, "Other_Punctuation") +DEF(Zs, "Space_Separator") +DEF(Zl, "Line_Separator") +DEF(Zp, "Paragraph_Separator") +DEF(Cc, "Control,cntrl") +DEF(Cf, "Format") +DEF(Cs, "Surrogate") +DEF(Co, "Private_Use") +/* synthetic properties */ +DEF(LC, "Cased_Letter") +DEF(L, "Letter") +DEF(M, "Mark,Combining_Mark") +DEF(N, "Number") +DEF(S, "Symbol") +DEF(P, "Punctuation,punct") +DEF(Z, "Separator") +DEF(C, "Other") +#endif + +#ifdef UNICODE_SCRIPT +/* scripts aliases names in PropertyValueAliases.txt */ +DEF(Unknown, "Zzzz") +DEF(Adlam, "Adlm") +DEF(Ahom, "Ahom") +DEF(Anatolian_Hieroglyphs, "Hluw") +DEF(Arabic, "Arab") +DEF(Armenian, "Armn") +DEF(Avestan, "Avst") +DEF(Balinese, "Bali") +DEF(Bamum, "Bamu") +DEF(Bassa_Vah, "Bass") +DEF(Batak, "Batk") +DEF(Bengali, "Beng") +DEF(Bhaiksuki, "Bhks") +DEF(Bopomofo, "Bopo") +DEF(Brahmi, "Brah") +DEF(Braille, "Brai") +DEF(Buginese, "Bugi") +DEF(Buhid, "Buhd") +DEF(Canadian_Aboriginal, "Cans") +DEF(Carian, "Cari") +DEF(Caucasian_Albanian, "Aghb") +DEF(Chakma, "Cakm") +DEF(Cham, "Cham") +DEF(Cherokee, "Cher") +DEF(Common, "Zyyy") +DEF(Coptic, "Copt,Qaac") +DEF(Cuneiform, "Xsux") +DEF(Cypriot, "Cprt") +DEF(Cyrillic, "Cyrl") +DEF(Deseret, "Dsrt") +DEF(Devanagari, "Deva") +DEF(Dogra, "Dogr") +DEF(Duployan, "Dupl") +DEF(Egyptian_Hieroglyphs, "Egyp") +DEF(Elbasan, "Elba") +DEF(Elymaic, "Elym") +DEF(Ethiopic, "Ethi") +DEF(Georgian, "Geor") +DEF(Glagolitic, "Glag") +DEF(Gothic, "Goth") +DEF(Grantha, "Gran") +DEF(Greek, "Grek") +DEF(Gujarati, "Gujr") +DEF(Gunjala_Gondi, "Gong") +DEF(Gurmukhi, "Guru") +DEF(Han, "Hani") +DEF(Hangul, "Hang") +DEF(Hanifi_Rohingya, "Rohg") +DEF(Hanunoo, "Hano") +DEF(Hatran, "Hatr") +DEF(Hebrew, "Hebr") +DEF(Hiragana, "Hira") +DEF(Imperial_Aramaic, "Armi") +DEF(Inherited, "Zinh,Qaai") +DEF(Inscriptional_Pahlavi, "Phli") +DEF(Inscriptional_Parthian, "Prti") +DEF(Javanese, "Java") +DEF(Kaithi, "Kthi") +DEF(Kannada, "Knda") +DEF(Katakana, "Kana") +DEF(Kayah_Li, "Kali") +DEF(Kharoshthi, "Khar") +DEF(Khmer, "Khmr") +DEF(Khojki, "Khoj") +DEF(Khudawadi, "Sind") +DEF(Lao, "Laoo") +DEF(Latin, "Latn") +DEF(Lepcha, "Lepc") +DEF(Limbu, "Limb") +DEF(Linear_A, "Lina") +DEF(Linear_B, "Linb") +DEF(Lisu, "Lisu") +DEF(Lycian, "Lyci") +DEF(Lydian, "Lydi") +DEF(Makasar, "Maka") +DEF(Mahajani, "Mahj") +DEF(Malayalam, "Mlym") +DEF(Mandaic, "Mand") +DEF(Manichaean, "Mani") +DEF(Marchen, "Marc") +DEF(Masaram_Gondi, "Gonm") +DEF(Medefaidrin, "Medf") +DEF(Meetei_Mayek, "Mtei") +DEF(Mende_Kikakui, "Mend") +DEF(Meroitic_Cursive, "Merc") +DEF(Meroitic_Hieroglyphs, "Mero") +DEF(Miao, "Plrd") +DEF(Modi, "Modi") +DEF(Mongolian, "Mong") +DEF(Mro, "Mroo") +DEF(Multani, "Mult") +DEF(Myanmar, "Mymr") +DEF(Nabataean, "Nbat") +DEF(Nandinagari, "Nand") +DEF(New_Tai_Lue, "Talu") +DEF(Newa, "Newa") +DEF(Nko, "Nkoo") +DEF(Nushu, "Nshu") +DEF(Nyiakeng_Puachue_Hmong, "Hmnp") +DEF(Ogham, "Ogam") +DEF(Ol_Chiki, "Olck") +DEF(Old_Hungarian, "Hung") +DEF(Old_Italic, "Ital") +DEF(Old_North_Arabian, "Narb") +DEF(Old_Permic, "Perm") +DEF(Old_Persian, "Xpeo") +DEF(Old_Sogdian, "Sogo") +DEF(Old_South_Arabian, "Sarb") +DEF(Old_Turkic, "Orkh") +DEF(Oriya, "Orya") +DEF(Osage, "Osge") +DEF(Osmanya, "Osma") +DEF(Pahawh_Hmong, "Hmng") +DEF(Palmyrene, "Palm") +DEF(Pau_Cin_Hau, "Pauc") +DEF(Phags_Pa, "Phag") +DEF(Phoenician, "Phnx") +DEF(Psalter_Pahlavi, "Phlp") +DEF(Rejang, "Rjng") +DEF(Runic, "Runr") +DEF(Samaritan, "Samr") +DEF(Saurashtra, "Saur") +DEF(Sharada, "Shrd") +DEF(Shavian, "Shaw") +DEF(Siddham, "Sidd") +DEF(SignWriting, "Sgnw") +DEF(Sinhala, "Sinh") +DEF(Sogdian, "Sogd") +DEF(Sora_Sompeng, "Sora") +DEF(Soyombo, "Soyo") +DEF(Sundanese, "Sund") +DEF(Syloti_Nagri, "Sylo") +DEF(Syriac, "Syrc") +DEF(Tagalog, "Tglg") +DEF(Tagbanwa, "Tagb") +DEF(Tai_Le, "Tale") +DEF(Tai_Tham, "Lana") +DEF(Tai_Viet, "Tavt") +DEF(Takri, "Takr") +DEF(Tamil, "Taml") +DEF(Tangut, "Tang") +DEF(Telugu, "Telu") +DEF(Thaana, "Thaa") +DEF(Thai, "Thai") +DEF(Tibetan, "Tibt") +DEF(Tifinagh, "Tfng") +DEF(Tirhuta, "Tirh") +DEF(Ugaritic, "Ugar") +DEF(Vai, "Vaii") +DEF(Wancho, "Wcho") +DEF(Warang_Citi, "Wara") +DEF(Yi, "Yiii") +DEF(Zanabazar_Square, "Zanb") +#endif + +#ifdef UNICODE_PROP_LIST +/* Prop list not exported to regexp */ +DEF(Hyphen, "") +DEF(Other_Math, "") +DEF(Other_Alphabetic, "") +DEF(Other_Lowercase, "") +DEF(Other_Uppercase, "") +DEF(Other_Grapheme_Extend, "") +DEF(Other_Default_Ignorable_Code_Point, "") +DEF(Other_ID_Start, "") +DEF(Other_ID_Continue, "") +DEF(Prepended_Concatenation_Mark, "") +/* additional computed properties for smaller tables */ +DEF(ID_Continue1, "") +DEF(XID_Start1, "") +DEF(XID_Continue1, "") +DEF(Changes_When_Titlecased1, "") +DEF(Changes_When_Casefolded1, "") +DEF(Changes_When_NFKC_Casefolded1, "") + +/* Prop list exported to JS */ +DEF(ASCII_Hex_Digit, "AHex") +DEF(Bidi_Control, "Bidi_C") +DEF(Dash, "") +DEF(Deprecated, "Dep") +DEF(Diacritic, "Dia") +DEF(Extender, "Ext") +DEF(Hex_Digit, "Hex") +DEF(IDS_Binary_Operator, "IDSB") +DEF(IDS_Trinary_Operator, "IDST") +DEF(Ideographic, "Ideo") +DEF(Join_Control, "Join_C") +DEF(Logical_Order_Exception, "LOE") +DEF(Noncharacter_Code_Point, "NChar") +DEF(Pattern_Syntax, "Pat_Syn") +DEF(Pattern_White_Space, "Pat_WS") +DEF(Quotation_Mark, "QMark") +DEF(Radical, "") +DEF(Regional_Indicator, "RI") +DEF(Sentence_Terminal, "STerm") +DEF(Soft_Dotted, "SD") +DEF(Terminal_Punctuation, "Term") +DEF(Unified_Ideograph, "UIdeo") +DEF(Variation_Selector, "VS") +DEF(White_Space, "space") +DEF(Bidi_Mirrored, "Bidi_M") +DEF(Emoji, "") +DEF(Emoji_Component, "") +DEF(Emoji_Modifier, "") +DEF(Emoji_Modifier_Base, "") +DEF(Emoji_Presentation, "") +DEF(Extended_Pictographic, "") +DEF(Default_Ignorable_Code_Point, "DI") +DEF(ID_Start, "IDS") +DEF(Case_Ignorable, "CI") + +/* other binary properties */ +DEF(ASCII,"") +DEF(Alphabetic, "Alpha") +DEF(Any, "") +DEF(Assigned,"") +DEF(Cased, "") +DEF(Changes_When_Casefolded, "CWCF") +DEF(Changes_When_Casemapped, "CWCM") +DEF(Changes_When_Lowercased, "CWL") +DEF(Changes_When_NFKC_Casefolded, "CWKCF") +DEF(Changes_When_Titlecased, "CWT") +DEF(Changes_When_Uppercased, "CWU") +DEF(Grapheme_Base, "Gr_Base") +DEF(Grapheme_Extend, "Gr_Ext") +DEF(ID_Continue, "IDC") +DEF(Lowercase, "Lower") +DEF(Math, "") +DEF(Uppercase, "Upper") +DEF(XID_Continue, "XIDC") +DEF(XID_Start, "XIDS") + +/* internal tables with index */ +DEF(Cased1, "") + +#endif