From a655816f89bb6423d2dfd59a705c8e9cad61411f Mon Sep 17 00:00:00 2001 From: wupengyong Date: Wed, 18 Aug 2021 17:23:46 +0800 Subject: [PATCH] Signed-off-by: wupengyong Change-Id: Icc8727a8fbc4a6c4935a79eb2f09dd071a236b35 Change-Id: I7608f6237968bea69d980ebbee6d3032aaf7378a --- Changelog | 80 + Makefile | 235 +- TODO | 74 +- VERSION | 2 +- cutils.c | 21 +- cutils.h | 4 + debugger.c | 8 +- doc/jsbignum.texi | 656 +- doc/quickjs.texi | 371 +- examples/pi_bigdecimal.js | 68 + examples/pi_bigfloat.js | 66 + examples/pi_bigint.js | 118 + examples/point.c | 151 + examples/test_fib.js | 6 + examples/test_point.js | 40 + libbf.c | 4777 +++++++--- libbf.h | 343 +- libregexp.c | 187 +- libregexp.h | 1 + libunicode-table.h | 3183 +++---- libunicode.c | 26 +- qjs.c | 107 +- qjsc.c | 128 +- qjscalc.js | 1454 ++-- quickjs-atom.h | 33 +- quickjs-libc.c | 2034 ++++- quickjs-libc.h | 13 + quickjs-opcode.h | 21 +- quickjs.c | 15504 ++++++++++++++++++++------------- quickjs.h | 220 +- release.sh | 136 +- repl.js | 145 +- run-test262.c | 44 +- test262.conf | 36 +- test262_errors.txt | 49 + tests/bjson.c | 96 + tests/microbench.js | 93 +- tests/test262.patch | 10 +- tests/test_bignum.js | 435 +- tests/test_bjson.js | 88 +- tests/test_builtin.js | 38 +- tests/test_language.js | 547 ++ tests/test_op_overloading.js | 207 + tests/test_qjscalc.js | 256 + tests/test_std.js | 38 +- tests/test_worker.js | 62 + tests/test_worker_module.js | 31 + unicode_download.sh | 12 +- unicode_gen.c | 1 + unicode_gen_def.h | 14 +- 50 files changed, 21216 insertions(+), 11053 deletions(-) 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 tests/bjson.c create mode 100644 tests/test_language.js create mode 100644 tests/test_op_overloading.js create mode 100644 tests/test_qjscalc.js create mode 100644 tests/test_worker.js create mode 100644 tests/test_worker_module.js diff --git a/Changelog b/Changelog index 18b5e54..0c25b96 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,83 @@ +2020-11-08: + +- improved function parameter initializers +- added std.setenv(), std.unsetenv() and std.getenviron() +- added JS_EvalThis() +- misc bug fixes + +2020-09-06: + +- added logical assignment operators +- added IsHTMLDDA support +- faster for-of loops +- os.Worker now takes a module filename as parameter +- qjsc: added -D option to compile dynamically loaded modules or workers +- misc bug fixes + +2020-07-05: + +- modified JS_GetPrototype() to return a live value +- REPL: support unicode characters larger than 16 bits +- added os.Worker +- improved object serialization +- added std.parseExtJSON +- misc bug fixes + +2020-04-12: + +- added cross realm support +- added AggregateError and Promise.any +- added env, uid and gid options in os.exec() +- misc bug fixes + +2020-03-16: + +- reworked error handling in std and os libraries: suppressed I/O + exceptions in std FILE functions and return a positive errno value + when it is explicit +- output exception messages to stderr +- added std.loadFile(), std.strerror(), std.FILE.prototype.tello() +- added JS_GetRuntimeOpaque(), JS_SetRuntimeOpaque(), JS_NewUint32() +- updated to Unicode 13.0.0 +- misc bug fixes + +2020-01-19: + +- keep CONFIG_BIGNUM in the makefile +- added os.chdir() +- qjs: added -I option +- more memory checks in the bignum operations +- modified operator overloading semantics to be closer to the TC39 + proposal +- suppressed "use bigint" mode. Simplified "use math" mode +- BigDecimal: changed suffix from 'd' to 'm' +- misc bug fixes + +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 diff --git a/Makefile b/Makefile index 377043c..766a7e1 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # # QuickJS Javascript Engine # -# Copyright (c) 2017-2019 Fabrice Bellard -# Copyright (c) 2017-2019 Charlie Gordon +# 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 @@ -28,9 +28,9 @@ endif # Windows cross compilation from Linux #CONFIG_WIN32=y # use link time optimization (smaller and faster executables but slower build) -CONFIG_LTO=y +#CONFIG_LTO=y # consider warnings as errors (for development) -#CONFIG_WERROR=y +CONFIG_WERROR=y # force 32 bit build for some utilities #CONFIG_M32=y @@ -47,11 +47,17 @@ prefix=/usr/local #CONFIG_PROFILE=y # use address sanitizer #CONFIG_ASAN=y +# include the code for BigInt/BigFloat/BigDecimal and math mode +CONFIG_BIGNUM=y OBJDIR=.obj ifdef CONFIG_WIN32 - CROSS_PREFIX=i686-w64-mingw32- + ifdef CONFIG_M32 + CROSS_PREFIX=i686-w64-mingw32- + else + CROSS_PREFIX=x86_64-w64-mingw32- + endif EXE=.exe else CROSS_PREFIX= @@ -94,6 +100,13 @@ ifdef CONFIG_WERROR CFLAGS+=-Werror endif DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\" +ifdef CONFIG_BIGNUM +DEFINES+=-DCONFIG_BIGNUM +endif +ifdef CONFIG_WIN32 +DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior +endif + CFLAGS+=$(DEFINES) CFLAGS_DEBUG=$(CFLAGS) -O0 CFLAGS_SMALL=$(CFLAGS) -Os @@ -110,8 +123,8 @@ CFLAGS+=-p LDFLAGS+=-p endif ifdef CONFIG_ASAN -CFLAGS+=-fsanitize=address -LDFLAGS+=-fsanitize=address +CFLAGS+=-fsanitize=address -fno-omit-frame-pointer +LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer endif ifdef CONFIG_WIN32 LDEXPORT= @@ -119,26 +132,24 @@ else LDEXPORT=-rdynamic endif -PROGS=qjs$(EXE) qjsbn$(EXE) qjsc$(EXE) qjsbnc$(EXE) run-test262 run-test262-bn +PROGS=qjs$(EXE) qjsc$(EXE) run-test262 ifneq ($(CROSS_PREFIX),) QJSC_CC=gcc QJSC=./host-qjsc -QJSBNC=./host-qjsbnc -PROGS+=$(QJSC) $(QJSBNC) +PROGS+=$(QJSC) else QJSC_CC=$(CC) QJSC=./qjsc$(EXE) -QJSBNC=./qjsbnc$(EXE) endif ifndef CONFIG_WIN32 PROGS+=qjscalc endif ifdef CONFIG_M32 -PROGS+=qjs32 qjs32_s qjsbn32 +PROGS+=qjs32 qjs32_s endif -PROGS+=libquickjs.a libquickjs.bn.a +PROGS+=libquickjs.a ifdef CONFIG_LTO -PROGS+=libquickjs.lto.a libquickjs.bn.lto.a +PROGS+=libquickjs.lto.a endif # examples @@ -146,7 +157,10 @@ ifeq ($(CROSS_PREFIX),) ifdef CONFIG_ASAN PROGS+= else -PROGS+=examples/hello examples/hello_module examples/c_module +PROGS+=examples/hello examples/hello_module examples/test_fib +ifndef CONFIG_DARWIN +PROGS+=examples/fib.so examples/point.so +endif endif endif @@ -154,19 +168,20 @@ all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o -QJSBN_LIB_OBJS=$(patsubst %.o, %.bn.o, $(QJS_LIB_OBJS)) $(OBJDIR)/libbf.bn.o - QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS) +ifdef CONFIG_BIGNUM +QJS_LIB_OBJS+=$(OBJDIR)/libbf.o +QJS_OBJS+=$(OBJDIR)/qjscalc.o +endif -QJSBN_OBJS=$(OBJDIR)/qjs.bn.o $(OBJDIR)/repl-bn.bn.o $(OBJDIR)/qjscalc.bn.o $(QJSBN_LIB_OBJS) - +HOST_LIBS=-lm -ldl -lpthread LIBS=-lm ifndef CONFIG_WIN32 -LIBS+=-ldl +LIBS+=-ldl -lpthread endif $(OBJDIR): - mkdir -p $(OBJDIR) + mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests qjs$(EXE): $(QJS_OBJS) $(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) @@ -177,18 +192,11 @@ qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS)) qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -qjsbnc$(EXE): $(OBJDIR)/qjsc.bn.o $(QJSBN_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) - -$(QJSBNC): $(OBJDIR)/qjsc.bn.host.o \ - $(patsubst %.o, %.host.o, $(QJSBN_LIB_OBJS)) - $(HOST_CC) $(LDFLAGS) -o $@ $^ $(LIBS) + $(HOST_CC) $(LDFLAGS) -o $@ $^ $(HOST_LIBS) endif #CROSS_PREFIX @@ -198,8 +206,8 @@ QJSC_DEFINES+=-DCONFIG_LTO endif QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(prefix)\" -$(OBJDIR)/qjsc.o $(OBJDIR)/qjsc.bn.o: CFLAGS+=$(QJSC_DEFINES) -$(OBJDIR)/qjsc.host.o $(OBJDIR)/qjsc.bn.host.o: CFLAGS+=$(QJSC_HOST_DEFINES) +$(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) @@ -208,18 +216,9 @@ qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS)) $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) @size $@ -qjsbn$(EXE): $(QJSBN_OBJS) - $(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) - -qjsbn32: $(patsubst %.o, %.m32.o, $(QJSBN_OBJS)) - $(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) - -qjscalc: qjsbn +qjscalc: qjs ln -sf $< $@ -qjsbn-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJSBN_OBJS)) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - ifdef CONFIG_LTO LTOEXT=.lto else @@ -229,50 +228,35 @@ endif libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS) $(AR) rcs $@ $^ -libquickjs.bn$(LTOEXT).a: $(QJSBN_LIB_OBJS) - $(AR) rcs $@ $^ - ifdef CONFIG_LTO libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS)) $(AR) rcs $@ $^ - -libquickjs.bn.a: $(patsubst %.o, %.nolto.o, $(QJSBN_LIB_OBJS)) - $(AR) rcs $@ $^ endif # CONFIG_LTO -repl.c: $(QJSC) repl.js +repl.c: $(QJSC) repl.js $(QJSC) -c -o $@ -m repl.js -repl-bn.c: $(QJSBNC) repl.js - $(QJSBNC) -c -o $@ -m repl.js - -qjscalc.c: $(QJSBNC) qjscalc.js - $(QJSBNC) -c -o $@ qjscalc.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.bn.o $(OBJDIR)/libunicode.bn.m32.o \ - $(OBJDIR)/libunicode.nolto.o $(OBJDIR)/libunicode.bn.nolto.o: libunicode-table.h +$(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-bn: $(OBJDIR)/run-test262.bn.o $(QJSBN_LIB_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) - $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread + $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -run-test262-bn32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.bn.o $(QJSBN_LIB_OBJS)) - $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread - -# object suffix order: bn, nolto, [m32|m32s] +# object suffix order: nolto, [m32|m32s] $(OBJDIR)/%.o: %.c | $(OBJDIR) $(CC) $(CFLAGS_OPT) -c -o $@ $< @@ -283,61 +267,44 @@ $(OBJDIR)/%.host.o: %.c | $(OBJDIR) $(OBJDIR)/%.pic.o: %.c | $(OBJDIR) $(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $< -$(OBJDIR)/%.bn.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_OPT) -DCONFIG_BIGNUM -c -o $@ $< - -$(OBJDIR)/%.bn.host.o: %.c | $(OBJDIR) - $(HOST_CC) $(CFLAGS_OPT) -DCONFIG_BIGNUM -c -o $@ $< - $(OBJDIR)/%.nolto.o: %.c | $(OBJDIR) $(CC) $(CFLAGS_NOLTO) -c -o $@ $< -$(OBJDIR)/%.bn.nolto.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_NOLTO) -DCONFIG_BIGNUM -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)/%.bn.m32.o: %.c | $(OBJDIR) - $(CC) -m32 $(CFLAGS_OPT) -DCONFIG_BIGNUM -c -o $@ $< - $(OBJDIR)/%.debug.o: %.c | $(OBJDIR) $(CC) $(CFLAGS_DEBUG) -c -o $@ $< -$(OBJDIR)/%.bn.debug.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_DEBUG) -DCONFIG_BIGNUM -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 repl-bn.c qjscalc.c out.c - rm -f *.a *.so *.o *.d *~ jscompress unicode_gen regexp_test $(PROGS) - rm -f hello.c hello_module.c c_module.c - rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug qjsbn-debug - rm -rf run-test262-debug run-test262-32 run-test262-bn32 + rm -f repl.c qjscalc.c out.c + rm -f *.a *.o *.d *~ unicode_gen regexp_test $(PROGS) + rm -f hello.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 qjsbn qjsc qjsbnc - install -m755 qjs qjsbn qjsc qjsbnc "$(DESTDIR)$(prefix)/bin" - ln -sf qjsbn "$(DESTDIR)$(prefix)/bin/qjscalc" + $(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 libquickjs.bn.a "$(DESTDIR)$(prefix)/lib/quickjs" + install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs" ifdef CONFIG_LTO - install -m644 libquickjs.lto.a libquickjs.bn.lto.a "$(DESTDIR)$(prefix)/lib/quickjs" + 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" @@ -350,6 +317,9 @@ 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 +ifdef CONFIG_BIGNUM +HELLO_OPTS+=-fno-bigint +endif hello.c: $(QJSC) $(HELLO_SRCS) $(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS) @@ -372,14 +342,17 @@ examples/hello_module: $(QJSC) libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS) # use of an external C module (static compilation) -c_module.c: $(QJSC) examples/c_module.js - $(QJSC) -e -M examples/fib.so,fib -m -o $@ examples/c_module.js +test_fib.c: $(QJSC) examples/test_fib.js + $(QJSC) -e -M examples/fib.so,fib -m -o $@ examples/test_fib.js -examples/c_module: $(OBJDIR)/c_module.o $(OBJDIR)/fib.o libquickjs$(LTOEXT).a +examples/test_fib: $(OBJDIR)/test_fib.o $(OBJDIR)/examples/fib.o libquickjs$(LTOEXT).a $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -$(OBJDIR)/fib.o: examples/fib.c - $(CC) $(CFLAGS_OPT) -c -o $@ $< +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 @@ -404,47 +377,55 @@ doc/%.html: doc/%.html.pre # tests ifndef CONFIG_DARWIN -test: bjson.so +test: tests/bjson.so examples/point.so +endif +ifdef CONFIG_M32 +test: qjs32 endif -test: qjs qjsbn +test: qjs ./qjs tests/test_closure.js - ./qjs tests/test_op.js + ./qjs tests/test_language.js ./qjs tests/test_builtin.js ./qjs tests/test_loop.js ./qjs tests/test_std.js + ./qjs tests/test_worker.js ifndef CONFIG_DARWIN +ifdef CONFIG_BIGNUM + ./qjs --bignum tests/test_bjson.js +else ./qjs tests/test_bjson.js endif - ./qjsbn tests/test_closure.js - ./qjsbn tests/test_op.js - ./qjsbn tests/test_builtin.js - ./qjsbn tests/test_loop.js - ./qjsbn tests/test_std.js - ./qjsbn --qjscalc tests/test_bignum.js - -test-32: qjs32 qjsbn32 + ./qjs examples/test_point.js +endif +ifdef CONFIG_BIGNUM + ./qjs --bignum tests/test_op_overloading.js + ./qjs --bignum tests/test_bignum.js + ./qjs --qjscalc tests/test_qjscalc.js +endif +ifdef CONFIG_M32 ./qjs32 tests/test_closure.js - ./qjs32 tests/test_op.js + ./qjs32 tests/test_language.js ./qjs32 tests/test_builtin.js ./qjs32 tests/test_loop.js ./qjs32 tests/test_std.js - ./qjsbn32 tests/test_closure.js - ./qjsbn32 tests/test_op.js - ./qjsbn32 tests/test_builtin.js - ./qjsbn32 tests/test_loop.js - ./qjsbn32 tests/test_std.js - ./qjsbn32 --qjscalc tests/test_bignum.js + ./qjs32 tests/test_worker.js +ifdef CONFIG_BIGNUM + ./qjs32 --bignum tests/test_op_overloading.js + ./qjs32 --bignum tests/test_bignum.js + ./qjs32 --qjscalc tests/test_qjscalc.js +endif +endif stats: qjs qjs32 ./qjs -qd ./qjs32 -qd microbench: qjs - ./qjs --std tests/microbench.js + ./qjs tests/microbench.js microbench-32: qjs32 - ./qjs32 --std tests/microbench.js + ./qjs32 tests/microbench.js # ES5 tests (obsolete) test2o: run-test262 @@ -472,27 +453,17 @@ test2-update: run-test262 test2-check: run-test262 time ./run-test262 -m -c test262.conf -E -a -# Test262 + BigInt tests -test2bn-default: run-test262-bn - time ./run-test262-bn -m -c test262bn.conf +testall: all test microbench test2o test2 -test2bn: run-test262-bn - time ./run-test262-bn -m -c test262bn.conf -a - -test2bn-32: run-test262-bn32 - time ./run-test262-bn32 -m -c test262bn.conf -a - -testall: all test microbench test2o test2 test2bn - -testall-32: all test-32 microbench-32 test2o-32 test2-32 test2bn-32 +testall-32: all test-32 microbench-32 test2o-32 test2-32 testall-complete: testall testall-32 -bench-v8: qjs qjs32 +bench-v8: qjs make -C tests/bench-v8 ./qjs -d tests/bench-v8/combined.js -bjson.so: $(OBJDIR)/bjson.pic.o +tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) -include $(wildcard $(OBJDIR)/*.d) diff --git a/TODO b/TODO index 9edcedd..b5500c9 100644 --- a/TODO +++ b/TODO @@ -1,29 +1,48 @@ -- 64-bit atoms in 64-bit mode? -- rename CONFIG_ALL_UNICODE, CONFIG_BIGNUM, CONFIG_ATOMICS, CONFIG_CHECK_JSVALUE ? +Bugs: +- modules: better error handling with cyclic module references + +Misc ideas: +- use custom printf to avoid compatibility issues with floating point numbers +- consistent naming for preprocessor defines - 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: +- use memory pools for objects, etc? - 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: -- use auto-init properties for more global objects +Built-in standard library: +- BSD sockets +- modules: use realpath in module name normalizer and put it in quickjs-libc +- modules: if no ".", use a well known module loading path ? +- get rid of __loadScript, use more common name + +REPL: +- debugger +- readline: support MS Windows terminal +- readline: handle dynamic terminal resizing +- readline: handle double width unicode characters +- 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 + +Optimization ideas: +- 64-bit atoms in 64-bit mode ? +- 64-bit small bigint in 64-bit mode ? - 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 @@ -32,49 +51,20 @@ Optimizations: - 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 -- debugger keyword support - 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 -- limited support for web assembly -- get rid of __loadScript, use more common name -- BSD sockets -- Process or thread control -- use custom printf to avoid C library compatibility issues -- use custom timezone support to avoid C library compatibility issues - -REPL: -- strip internal functions from stack trace -- readline: support MS Windows terminal -- readline: handle dynamic terminal resizing -- multiline editing -- debugger -- 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: 2/67351 errors, 839 excluded, 1370 skipped -Test262bn: 2/69452 errors, 772 excluded, 383 skipped -test262 commit: d65b9b35be091147edf31ec527a47cb95a327217 +Result: 51/75119 errors, 899 excluded, 570 skipped +Test262 commit: 1c33fdb0ca60fb9d7392403be769ed0d26209132 diff --git a/VERSION b/VERSION index 80c17d9..78749b0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2019-09-18 +2020-11-08 diff --git a/cutils.c b/cutils.c index 64a32d3..a02fb76 100644 --- a/cutils.c +++ b/cutils.c @@ -258,19 +258,30 @@ int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp) return c; } switch(c) { - case 0xc0 ... 0xdf: + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: l = 1; break; - case 0xe0 ... 0xef: + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: l = 2; break; - case 0xf0 ... 0xf7: + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: l = 3; break; - case 0xf8 ... 0xfb: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: l = 4; break; - case 0xfc ... 0xfd: + case 0xfc: case 0xfd: l = 5; break; default: diff --git a/cutils.h b/cutils.h index 26c68ee..31f7cd8 100644 --- a/cutils.h +++ b/cutils.h @@ -268,6 +268,10 @@ void dbuf_free(DynBuf *s); static inline BOOL dbuf_error(DynBuf *s) { return s->error; } +static inline void dbuf_set_error(DynBuf *s) +{ + s->error = TRUE; +} #define UTF8_CHAR_LEN_MAX 6 diff --git a/debugger.c b/debugger.c index 2f3d168..0d6a28f 100644 --- a/debugger.c +++ b/debugger.c @@ -185,7 +185,6 @@ static void DBG_SendStopMsg(DebuggerInfo *debuggerInfo, const char *stopReason) JS_SetPropertyStr(cx, msgBody, "type", JS_NewString(cx, "event")); JS_SetPropertyStr(cx, msgBody, "event", stopEvent); DBG_SendMsg(debuggerInfo, msgBody); - JS_FreeValue(cx, stopEvent); JS_FreeValue(cx, msgBody); return; @@ -271,7 +270,6 @@ static void DBG_StackTraceProcess(DebuggerInfo *debuggerInfo, JSValue msg, const } JSValue stackTrace = JS_BuildStackTrace(debuggerInfo->cx, curPc); DBG_SendResponseMsg(debuggerInfo, msg, stackTrace); - JS_FreeValue(debuggerInfo->cx, stackTrace); return; } @@ -669,7 +667,6 @@ static void DBG_VariablesProcess(DebuggerInfo *debuggerInfo, properties = DBG_VariablesUnFilteredProcess(cx, variable, properties, state); } JS_FreeValue(cx, variable); - JS_FreeValue(cx, filter); JS_FreeValue(cx, args); DBG_SendResponseMsg(debuggerInfo, request, properties); JS_FreeValue(cx, properties); @@ -708,7 +705,6 @@ static void DBG_EvaluateProcess(DebuggerInfo *debuggerInfo, JSValue body = JS_NewObject(cx); DBG_SetVariableProperty(cx, ret, body, "result"); DBG_SetVariableType(cx, state, body, ret); - JS_FreeValue(cx, ret); DBG_SendResponseMsg(debuggerInfo, msg, body); JS_FreeValue(cx, body); @@ -772,7 +768,6 @@ static int DBG_RequestProcess(DebuggerInfo *debuggerInfo, isNeedReadMsg = DBG_NEED_READ_MSG; } JS_FreeCString(cx, command); - JS_FreeValue(cx, jsCommand); JS_FreeValue(cx, jsRequestMsg); return isNeedReadMsg; @@ -989,7 +984,8 @@ void DBG_CallDebugger(JSContext *cx, const uint8_t *pc) // must check breakpotint first, then process step operation if (JS_HitBreakpoint(cx, pc) && JS_JudgeConditionBreakPoint(cx, pc)) { LocInfo loc = JS_GetCurrentLocation(cx, pc); - DEBUGGER_LOGI("DBG_CallDebugger hit breakpoint at line: %d", loc.line); + DEBUGGER_LOGI("DBG_CallDebugger hit breakpoint at line %d", loc.line); + debuggerInfo->stepOperation = NO_STEP_OPERATION; debuggerInfo->depth = JS_GetStackDepth(cx); DBG_SendStopMsg(debuggerInfo, "breakpoint"); diff --git a/doc/jsbignum.texi b/doc/jsbignum.texi index 9525387..079d920 100644 --- a/doc/jsbignum.texi +++ b/doc/jsbignum.texi @@ -10,12 +10,12 @@ @sp 7 @center @titlefont{Javascript Bignum Extensions} @sp 3 -@center Version 2018-06-16 +@center Version 2020-01-11 @sp 3 @center Author: Fabrice Bellard @end titlepage -@setfilename spec.info +@setfilename jsbignum.info @settitle Javascript Bignum Extensions @contents @@ -27,347 +27,51 @@ language while being 100% backward compatible: @itemize -@item Overloading of the standard operators -to support new types such as complex numbers, fractions or matrixes. - -@item Bigint mode where arbitrarily large integers are available by default (no @code{n} suffix is necessary as in the TC39 BigInt proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}}). +@item Operator overloading with a dispatch logic inspired from the proposal available at @url{https://github.com/tc39/proposal-operator-overloading/}. @item Arbitrarily large floating point numbers (@code{BigFloat}) in base 2 using the IEEE 754 semantics. -@item Optional @code{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. +@item Arbitrarily large floating point numbers (@code{BigDecimal}) in base 10 based on the proposal available at +@url{https://github.com/littledan/proposal-bigdecimal}. + +@item @code{math} mode: arbitrarily large integers and floating point numbers are available by default. The integer division and power can be overloaded for example to return a fraction. The modulo operator (@code{%}) is defined as the Euclidian +remainder. @code{^} is an alias to the power operator +(@code{**}). @code{^^} is used as the exclusive or operator. @end itemize The extensions are independent from each other except the @code{math} -mode which relies on the bigint mode and the operator overloading. +mode which relies on BigFloat and operator overloading. @chapter Operator overloading -@section Introduction +Operator overloading is inspired from the proposal available at +@url{https://github.com/tc39/proposal-operator-overloading/}. It +implements the same dispatch logic but finds the operator sets by +looking at the @code{Symbol.operatorSet} property in the objects. The +changes were done in order to simplify the implementation. -If the operands of an operator have at least one object type, a custom -operator method is searched before doing the legacy Javascript -@code{ToNumber} conversion. - -For unary operators, the custom function is looked up in the object -and has the following name: - -@table @code -@item unary + -@code{Symbol.operatorPlus} - -@item unary - -@code{Symbol.operatorNeg} - -@item ++ -@code{Symbol.operatorInc} - -@item -- -@code{Symbol.operatorDec} - -@item ~ -@code{Symbol.operatorNot} - -@end table - -For binary operators: +More precisely, the following modifications were made: @itemize -@item -If both operands have the same constructor function, then the operator -is looked up in the constructor. +@item @code{with operators from} is not supported. Operator overloading is always enabled. -@item -Otherwise, the property @code{Symbol.operatorOrder} is looked up in both -constructors and converted to @code{Int32}. The operator is then -looked in the constructor with the larger @code{Symbol.operatorOrder} -value. A @code{TypeError} is raised if both constructors have the same -@code{Symbol.operatorOrder} value. +@item The dispatch is not based on a static @code{[[OperatorSet]]} field in all instances. Instead, a dynamic lookup of the @code{Symbol.operatorSet} property is done. This property is typically added in the prototype of each object. + +@item @code{Operators.create(...dictionaries)} is used to create a new OperatorSet object. The @code{Operators} function is supported as an helper to be closer to the TC39 proposal. + +@item @code{[]} cannot be overloaded. + +@item In math mode, the BigInt division and power operators can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}. @end itemize -The operator is looked up with the following name: +@chapter BigInt extensions + +A few properties are added to the BigInt object: @table @code -@item + -@code{Symbol.operatorAdd} - -@item - -@code{Symbol.operatorSub} - -@item * -@code{Symbol.operatorMul} - -@item / -@code{Symbol.operatorDiv} - -@item % -@code{Symbol.operatorMod} - -@item % (math mode) -@code{Symbol.operatorMathMod} - -@item ** -@code{Symbol.operatorPow} - -@item | -@code{Symbol.operatorOr} - -@item ^ -@code{Symbol.operatorXor} - -@item & -@code{Symbol.operatorAnd} - -@item << -@code{Symbol.operatorShl} - -@item >> -@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 @@ -410,63 +114,12 @@ Return the number of trailing zeros in the two's complement binary representatio @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 environement. 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 +@chapter BigFloat @section Introduction This extension adds the @code{BigFloat} primitive type. The -@code{BigFloat} type represents floating point numbers are in base 2 +@code{BigFloat} type represents floating point numbers 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} @@ -490,14 +143,13 @@ 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}; +environment is @code{BigFloatEnv.expBits}. The global environment +subnormal flag is set to @code{true}. -For example, @code{prec = 53} and @code{ expBits = 11} give exactly -the same precision as the IEEE 754 64 bit floating point type. It is -the default floating point precision. +For example, @code{prec = 53} and @code{ expBits = 11} exactly give +the same precision as the IEEE 754 64 bit floating point format. The +default precision is @code{prec = 113} and @code{ expBits = 15} (IEEE +754 128 bit floating point format). The global floating point environment can only be modified temporarily when calling a function (see @code{BigFloatEnv.setPrec}). Hence a @@ -568,6 +220,12 @@ 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]) @@ -577,12 +235,14 @@ 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[, e]) -@item ceil(x[, e]) -@item round(x[, e]) -@item trunc(x[, e]) -Round to integer. A rounded @code{BigFloat} is returned. @code{e} is an -optional floating point environment. +@item floor(x) +@item ceil(x) +@item round(x) +@item trunc(x) +Round to an integer. No additional rounding is performed. + +@item abs(x) +Return the absolute value of x. No additional rounding is performed. @item fmod(x, y[, e]) @item remainder(x, y[, e]) @@ -614,6 +274,9 @@ number. @code{e} is an optional floating point environment. The following properties are modified: @table @code +@item valueOf() +Return the bigfloat primitive value corresponding to @code{this}. + @item toString(radix) For floating point numbers: @@ -630,13 +293,16 @@ 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]) +The exponent letter is @code{e} for base 10, @code{p} for bases 2, 8, +16 with a binary exponent and @code{@@} for the other bases. + +@item toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10) +@item toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10) +@item toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10) 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}. +rounding mode and radix can be optionally specified. The radix must be +between 2 and 36. @end table @@ -673,13 +339,12 @@ subnormal flags is set to @code{false}. If @code{rndMode} is @item prec Getter. Return the precision in bits of the global floating point -environment. The initial value is @code{53}. +environment. The initial value is @code{113}. @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}. +environment assuming an IEEE 754 representation. The initial value is +@code{15}. @item setPrec(f, p[, e]) Set the precision of the global floating point environment to @code{p} @@ -687,15 +352,13 @@ 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. +is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}. @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. +Read-only integer. Return the maximum allowed precision. Must be at least 113. @item expBitsMin Read-only integer. Return the minimum allowed exponent size in @@ -703,7 +366,7 @@ bits. Must be at least 3. @item expBitsMax Read-only integer. Return the maximum allowed exponent size in -bits. Must be at least 11. +bits. Must be at least 15. @item RNDN Read-only integer. Round to nearest, with ties to even rounding mode. @@ -720,12 +383,12 @@ 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 RNDA +Read-only integer. Round away from zero rounding mode. -@item RNDF@footnote{Could be removed in case a deterministic behvior for floating point operations is required.} +@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-deterministicly rounded to -Infinity or +Infinity. This rounding +non-deterministically rounded to -Infinity or +Infinity. This rounding mode usually gives a faster and deterministic running time for the floating point operations. @@ -761,69 +424,166 @@ Getter and setter (Boolean). Status flags. @end table -@subsection @code{Math} object +@chapter BigDecimal -The following properties are modified: +This extension adds the @code{BigDecimal} primitive type. The +@code{BigDecimal} type represents floating point numbers in base +10. It is inspired from the proposal available at +@url{https://github.com/littledan/proposal-bigdecimal}. + +The @code{BigDecimal} floating point numbers are always normalized and +finite. There is no concept of @code{-0}, @code{Infinity} or +@code{NaN}. By default, all the computations are done with infinite +precision. + +@section Operators + +The following builtin operators support BigDecimal: @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. +@item + +@item - +@item * +Both operands must be BigDecimal. The result is computed with infinite +precision. +@item % +Both operands must be BigDecimal. The result is computed with infinite +precision. A range error is throws in case of division by zero. + +@item / +Both operands must be BigDecimal. A range error is throws in case of +division by zero or if the result cannot be represented with infinite +precision (use @code{BigDecimal.div} to specify the rounding). + +@item ** +Both operands must be BigDecimal. The exponent must be a positive +integer. The result is computed with infinite precision. + +@item === +When one of the operand is a BigDecimal, return true if both operands +are a BigDecimal and if they are equal. + +@item == +@item != +@item <= +@item >= +@item < +@item > + +Numerical comparison. When one of the operand is not a BigDecimal, it is +converted to BigDecimal by using ToString(). Hence comparisons between +Number and BigDecimal do not use the exact mathematical value of the +Number value. + +@end table + +@section BigDecimal literals + +BigDecimal literals are decimal floating point numbers with a trailing +@code{m} suffix. + +@section Builtin Object changes + +@subsection The @code{BigDecimal} function. + +It returns @code{0m} if no parameter is provided. Otherwise the first +parameter is converted to a bigdecimal by using ToString(). Hence +Number values are not converted to their exact numerical value as +BigDecimal. + +@subsection Properties of the @code{BigDecimal} object + +@table @code + +@item add(a, b[, e]) +@item sub(a, b[, e]) +@item mul(a, b[, e]) +@item div(a, b[, e]) +@item mod(a, b[, e]) +@item sqrt(a, e) +@item round(a, e) +Perform the specified floating point operation and round the floating +point result according to the rounding object @code{e}. If the +rounding object is not present, the operation is executed with +infinite precision. + +For @code{div}, a @code{RangeError} exception is thrown in case of +division by zero or if the result cannot be represented with infinite +precision if no rounding object is present. + +For @code{sqrt}, a range error is thrown if @code{a} is less than +zero. + +The rounding object must contain the following properties: +@code{roundingMode} is a string specifying the rounding mode +(@code{"floor"}, @code{"ceiling"}, @code{"down"}, @code{"up"}, +@code{"half-even"}, @code{"half-up"}). Either +@code{maximumSignificantDigits} or @code{maximumFractionDigits} must +be present to specify respectively the number of significant digits +(must be >= 1) or the number of digits after the decimal point (must +be >= 0). + +@end table + +@subsection Properties of the @code{BigDecimal.prototype} object + +@table @code +@item valueOf() +Return the bigdecimal primitive value corresponding to @code{this}. + +@item toString() +Convert @code{this} to a string with infinite precision in base 10. + +@item toPrecision(p, rnd_mode = "half-up") +@item toFixed(p, rnd_mode = "half-up") +@item toExponential(p, rnd_mode = "half-up") +Convert the BigDecimal @code{this} to string with the specified +precision @code{p}. There is no limit on the accepted precision +@code{p}. The rounding mode can be optionally +specified. @code{toPrecision} outputs either in decimal fixed notation +or in decimal exponential notation with a @code{p} digits of +precision. @code{toExponential} outputs in decimal exponential +notation with @code{p} digits after the decimal point. @code{toFixed} +outputs in decimal notation with @code{p} digits after the decimal +point. @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. +directive. It propagates the same way as the @emph{strict mode}. It is +designed so that arbitrarily large integers and floating point numbers +are available by default. In order to minimize the number of changes +in the Javascript semantics, integers are represented either as Number +or BigInt depending on their magnitude. Floating point numbers are +always represented as BigFloat. -It propagates the same way as the @emph{strict mode}. In -this mode: +The following changes are made to the Javascript semantics: @itemize -@item The @code{^} operator is a similar to the power operator (@code{**}). +@item Floating point literals (i.e. number with a decimal point or an exponent) are @code{BigFloat} by default (i.e. a @code{l} suffix is implied). Hence @code{typeof 1.0 === "bigfloat"}. + +@item Integer literals (i.e. numbers without a decimal point or an exponent) with or without the @code{n} suffix are @code{BigInt} if their value cannot be represented as a safe integer. A safe integer is defined as a integer whose absolute value is smaller or equal to @code{2**53-1}. Hence @code{typeof 1 === "number "}, @code{typeof 1n === "number"} but @code{typeof 9007199254740992 === "bigint" }. + +@item All the bigint builtin operators and functions are modified so that their result is returned as a Number if it is a safe integer. Otherwise the result stays a BigInt. + +@item The builtin operators are modified so that they return an exact result (which can be a BigInt) if their operands are safe integers. Operands between Number and BigInt are accepted provided the Number operand is a safe integer. The integer power with a negative exponent returns a BigFloat as result. The integer division returns a BigFloat as result. + +@item The @code{^} operator is an alias 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 modulo operator (@code{%}) returns the Euclidian remainder (always positive) instead of the truncated remainder. -@item The power operator invokes @code{BigInt[Symbol.operatorPow]} in case both operands are integers and the exponent is strictly negative. +@item The integer division operator can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}. -@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). +@item The integer power operator with a non zero negative exponent can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}. @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.texi b/doc/quickjs.texi index 952e6f0..40c0bb5 100644 --- a/doc/quickjs.texi +++ b/doc/quickjs.texi @@ -20,34 +20,34 @@ @chapter Introduction QuickJS is a small and embeddable Javascript engine. It supports the -ES2019 specification -@footnote{@url{https://www.ecma-international.org/ecma-262/10.0}} -including modules, asynchronous generators and proxies. +ES2020 specification +@footnote{@url{https://tc39.es/ecma262/}} +including modules, asynchronous generators, proxies and BigInt. -It optionally supports mathematical extensions such as big integers -(BigInt), big floating point numbers (BigFloat) and operator -overloading. +It supports mathematical extensions such as big decimal float float +numbers (BigDecimal), big binary floating point numbers (BigFloat), +and operator overloading. @section Main Features @itemize -@item Small and easily embeddable: just a few C files, no external dependency, 180 KiB of x86 code for a simple ``hello world'' program. +@item Small and easily embeddable: just a few C files, no external dependency, 210 KiB of x86 code for a simple ``hello world'' program. @item Fast interpreter with very low startup time: runs the 69000 tests of the ECMAScript Test Suite@footnote{@url{https://github.com/tc39/test262}} 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. -@item Almost complete ES2019 support including modules, asynchronous +@item Almost complete ES2020 support including modules, asynchronous generators and full Annex B support (legacy web compatibility). Many -features from the upcoming ES2020 specification +features from the upcoming ES2021 specification @footnote{@url{https://tc39.github.io/ecma262/}} are also supported. -@item Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2019 features. +@item Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2020 features. -@item Can compile Javascript sources to executables with no external dependency. +@item Compile Javascript sources to executables with no external dependency. @item Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal. -@item Mathematical extensions: BigInt, BigFloat, operator overloading, bigint mode, math mode. +@item Mathematical extensions: BigDecimal, BigFloat, operator overloading, bigint mode, math mode. @item Command line interpreter with contextual colorization and completion implemented in Javascript. @@ -87,28 +87,12 @@ Javascript files and/or expressions as arguments to execute them: generates a @code{hello} executable with no external dependency. -@code{qjsbn} and @code{qjscbn} are the corresponding interpreter and -compiler with the mathematical extensions: - -@example -./qjsbn examples/pi.js 1000 -@end example - -displays 1000 digits of PI. - -@example -./qjsbnc -o pi examples/pi.js -./pi 1000 -@end example - -compiles and executes the PI program. - @section Command line options @subsection @code{qjs} interpreter @verbatim -usage: qjs [options] [files] +usage: qjs [options] [file [args]] @end verbatim Options are: @@ -127,16 +111,30 @@ Go to interactive mode (it is not the default when files are provided on the com @item -m @item --module -Load as ES6 module (default=autodetect). +Load as ES6 module (default=autodetect). A module is autodetected if +the filename extension is @code{.mjs} or if the first keyword of the +source is @code{import}. @item --script Load as ES6 script (default=autodetect). +@item --bignum +Enable the bignum extensions: BigDecimal object, BigFloat object and +the @code{"use math"} directive. + +@item -I file +@item --include file +Include an additional file. + @end table Advanced options are: @table @code +@item --std +Make the @code{std} and @code{os} modules available to the loaded +script even if it is not a module. + @item -d @item --dump Dump the memory usage stats. @@ -169,6 +167,12 @@ Set the C name of the generated data. @item -m Compile as Javascript module (default=autodetect). +@item -D module_name +Compile a dynamically loaded module and its dependencies. This option +is needed when your code uses the @code{import} keyword or the +@code{os.Worker} constructor because the compiler cannot statically +find the name of the dynamically loaded modules. + @item -M module_name[,cname] Add initialization code for an external C module. See the @code{c_module} example. @@ -181,14 +185,18 @@ Use link time optimization. The compilation is slower but the executable is smaller and faster. This option is automatically set when the @code{-fno-x} options are used. -@item -fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise] +@item -fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint] Disable selected language features to produce a smaller executable file. +@item -fbignum +Enable the bignum extensions: BigDecimal object, BigFloat object and +the @code{"use math"} directive. + @end table @section @code{qjscalc} application -The @code{qjscalc} application is a superset of the @code{qjsbn} +The @code{qjscalc} application is a superset of the @code{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 @@ -202,13 +210,8 @@ QuickJS archive. @section Test262 (ECMAScript Test Suite) -A test262 runner is included in the QuickJS archive. - -For reference, the full test262 tests are provided in the archive -@file{qjs-tests-yyyy-mm-dd.tar.xz}. You just need to untar it into the -QuickJS source code directory. - -Alternatively, the test262 tests can be installed with: +A test262 runner is included in the QuickJS archive. The test262 tests +can be installed in the QuickJS source directory with: @example git clone https://github.com/tc39/test262.git test262 @@ -227,13 +230,12 @@ The tests can be run with make test2 @end example -The configuration files @code{test262.conf} (resp -@code{test262bn.conf} for the bignum version, @code{test262o.conf} for -the old ES5.1 tests@footnote{The old ES5.1 tests can be extracted with -@code{git clone --single-branch --branch es5-tests -https://github.com/tc39/test262.git test262o}})) contain the options -to run the various tests. Tests can be excluded based on features or -filename. +The configuration files @code{test262.conf} +(resp. @code{test262o.conf} for the old ES5.1 tests@footnote{The old +ES5.1 tests can be extracted with @code{git clone --single-branch +--branch es5-tests https://github.com/tc39/test262.git test262o}})) +contain the options to run the various tests. Tests can be excluded +based on features or filename. The file @code{test262_errors.txt} contains the current list of errors. The runner displays a message when a new error appears or when @@ -263,25 +265,19 @@ about 100 seconds). @section Language support -@subsection ES2019 support +@subsection ES2020 support -The ES2019 specification is almost fully supported including the Annex +The ES2020 specification is almost fully supported including the Annex B (legacy web compatibility) and the Unicode related features. The following features are not supported yet: @itemize -@item Realms (althougth the C API supports different runtimes and contexts) - @item Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.} @end itemize -@subsection JSON - -The JSON parser is currently more tolerant than the specification. - @subsection ECMA402 ECMA402 (Internationalization API) is not supported. @@ -298,13 +294,12 @@ ECMA402 (Internationalization API) is not supported. @subsection Mathematical extensions -The mathematical extensions are available in the @code{qjsbn} version and are fully -backward compatible with standard Javascript. See @code{jsbignum.pdf} -for more information. +The mathematical extensions are fully backward compatible with +standard Javascript. See @code{jsbignum.pdf} for more information. @itemize -@item The @code{BigInt} (big integers) TC39 proposal is supported. +@item @code{BigDecimal} support: arbitrary large floating point numbers in base 10. @item @code{BigFloat} support: arbitrary large floating point numbers in base 2. @@ -364,56 +359,55 @@ Available exports: @item exit(n) Exit the process. -@item evalScript(str) -Evaluate the string @code{str} as a script (global eval). +@item evalScript(str, options = undefined) +Evaluate the string @code{str} as a script (global +eval). @code{options} is an optional object containing the following +optional properties: + + @table @code + @item backtrace_barrier + Boolean (default = false). If true, error backtraces do not list the + stack frames below the evalScript. + @end table @item loadScript(filename) Evaluate the file @code{filename} as a script (global eval). -@item Error(errno) +@item loadFile(filename) +Load the file @code{filename} and return it as a string assuming UTF-8 +encoding. Return @code{null} in case of I/O error. -@code{std.Error} constructor. Error instances contain the field -@code{errno} (error code) and @code{message} (result of -@code{std.Error.strerror(errno)}). +@item open(filename, flags, errorObj = undefined) +Open a file (wrapper to the libc @code{fopen()}). Return the FILE +object or @code{null} in case of I/O error. If @code{errorObj} is not +undefined, set its @code{errno} property to the error code or to 0 if +no error occured. -The constructor contains the following fields: +@item popen(command, flags, errorObj = undefined) +Open a process by creating a pipe (wrapper to the libc +@code{popen()}). Return the FILE +object or @code{null} in case of I/O error. If @code{errorObj} is not +undefined, set its @code{errno} property to the error code or to 0 if +no error occured. - @table @code - @item EINVAL - @item EIO - @item EACCES - @item EEXIST - @item ENOSPC - @item ENOSYS - @item EBUSY - @item ENOENT - @item EPERM - @item EPIPE - Integer value of common errors (additional error codes may be defined). - @item strerror(errno) - Return a string that describes the error @code{errno}. - @end table - -@item open(filename, flags) -Open a file (wrapper to the libc @code{fopen()}). Throws -@code{std.Error} in case of I/O error. - -@item popen(command, flags) -Open a process by creating a pipe (wrapper to the libc @code{popen()}). Throws -@code{std.Error} in case of I/O error. - -@item fdopen(fd, flags) +@item fdopen(fd, flags, errorObj = undefined) Open a file from a file handle (wrapper to the libc -@code{fdopen()}). Throws @code{std.Error} in case of I/O error. +@code{fdopen()}). Return the FILE +object or @code{null} in case of I/O error. If @code{errorObj} is not +undefined, set its @code{errno} property to the error code or to 0 if +no error occured. -@item tmpfile() -Open a temporary file. Throws @code{std.Error} in case of I/O error. +@item tmpfile(errorObj = undefined) +Open a temporary file. Return the FILE +object or @code{null} in case of I/O error. If @code{errorObj} is not +undefined, set its @code{errno} property to the error code or to 0 if +no error occured. @item puts(str) Equivalent to @code{std.out.puts(str)}. @item printf(fmt, ...args) -Equivalent to @code{std.out.printf(fmt, ...args)} +Equivalent to @code{std.out.printf(fmt, ...args)}. @item sprintf(fmt, ...args) Equivalent to the libc sprintf(). @@ -428,6 +422,27 @@ Wrappers to the libc file @code{stdin}, @code{stdout}, @code{stderr}. @item SEEK_END Constants for seek(). +@item Error + +Enumeration object containing the integer value of common errors +(additional error codes may be defined): + + @table @code + @item EINVAL + @item EIO + @item EACCES + @item EEXIST + @item ENOSPC + @item ENOSYS + @item EBUSY + @item ENOENT + @item EPERM + @item EPIPE + @end table + +@item strerror(errno) +Return a string that describes the error @code{errno}. + @item gc() Manually invoke the cycle removal algorithm. The cycle removal algorithm is automatically started when needed, so this function is @@ -437,6 +452,16 @@ useful in case of specific memory constraints or for testing. Return the value of the environment variable @code{name} or @code{undefined} if it is not defined. +@item setenv(name, value) +Set the value of the environment variable @code{name} to the string +@code{value}. + +@item unsetenv(name) +Delete the environment variable @code{name}. + +@item getenviron() +Return an object containing the environment variables as key-value pairs. + @item urlGet(url, options = undefined) Download @code{url} using the @file{curl} command line @@ -450,37 +475,66 @@ optional properties: to be UTF-8 encoded. @item full + Boolean (default = false). If true, return the an object contains the properties @code{response} (response content), @code{responseHeaders} (headers separated by CRLF), @code{status} - (status code). If @code{full} is false, only the response is - returned if the status is between 200 and 299. Otherwise an - @code{std.Error} exception is raised. + (status code). @code{response} is @code{null} is case of protocol or + network error. If @code{full} is false, only the response is + returned if the status is between 200 and 299. Otherwise @code{null} + is returned. @end table +@item parseExtJSON(str) + + Parse @code{str} using a superset of @code{JSON.parse}. The + following extensions are accepted: + + @itemize + @item Single line and multiline comments + @item unquoted properties (ASCII-only Javascript identifiers) + @item trailing comma in array and object definitions + @item single quoted strings + @item @code{\f} and @code{\v} are accepted as space characters + @item leading plus in numbers + @item octal (@code{0o} prefix) and hexadecimal (@code{0x} prefix) numbers + @end itemize @end table FILE prototype: @table @code @item close() -Close the file. +Close the file. Return 0 if OK or @code{-errno} in case of I/O error. @item puts(str) Outputs the string with the UTF-8 encoding. @item printf(fmt, ...args) -Formatted printf, same formats as the libc printf. +Formatted printf. + +The same formats as the standard C library @code{printf} are +supported. Integer format types (e.g. @code{%d}) truncate the Numbers +or BigInts to 32 bits. Use the @code{l} modifier (e.g. @code{%ld}) to +truncate to 64 bits. + @item flush() Flush the buffered file. @item seek(offset, whence) -Seek to a give file position (whence is @code{std.SEEK_*}). Throws a -@code{std.Error} in case of I/O error. +Seek to a give file position (whence is +@code{std.SEEK_*}). @code{offset} can be a number or a bigint. Return +0 if OK or @code{-errno} in case of I/O error. @item tell() Return the current file position. +@item tello() +Return the current file position as a bigint. @item eof() Return true if end of file. @item fileno() Return the associated OS handle. +@item error() +Return true if there was an error. +@item clearerr() +Clear the error indication. @item read(buffer, position, length) Read @code{length} bytes from the file to the ArrayBuffer @code{buffer} at byte @@ -488,14 +542,19 @@ position @code{position} (wrapper to the libc @code{fread}). @item write(buffer, position, length) Write @code{length} bytes to the file from the ArrayBuffer @code{buffer} at byte -position @code{position} (wrapper to the libc @code{fread}). +position @code{position} (wrapper to the libc @code{fwrite}). @item getline() Return the next line from the file, assuming UTF-8 encoding, excluding the trailing line feed. +@item readAsString(max_size = undefined) +Read @code{max_size} bytes from the file and return them as a string +assuming UTF-8 encoding. If @code{max_size} is not present, the file +is read up its end. + @item getByte() -Return the next byte from the file. +Return the next byte from the file. Return -1 if the end of file is reached. @item putByte(c) Write one byte to the file. @@ -510,6 +569,7 @@ The @code{os} module provides Operating System specific functions: @item signals @item timers @item asynchronous I/O +@item workers (threads) @end itemize The OS functions usually return 0 if OK or an OS specific negative @@ -537,7 +597,9 @@ POSIX open flags. Close the file handle @code{fd}. @item seek(fd, offset, whence) -Seek in the file. Use @code{std.SEEK_*} for @code{whence}. +Seek in the file. Use @code{std.SEEK_*} for +@code{whence}. @code{offset} is either a number or a bigint. If +@code{offset} is a bigint, a bigint is returned too. @item read(fd, buffer, offset, length) Read @code{length} bytes from the file handle @code{fd} to the @@ -559,10 +621,10 @@ Return the TTY size as @code{[width, height]} or @code{null} if not available. Set the TTY in raw mode. @item remove(filename) -Remove a file. Return 0 if OK or < 0 if error. +Remove a file. Return 0 if OK or @code{-errno}. @item rename(oldname, newname) -Rename a file. Return 0 if OK or < 0 if error. +Rename a file. Return 0 if OK or @code{-errno}. @item realpath(path) Return @code{[str, err]} where @code{str} is the canonicalized absolute @@ -572,8 +634,11 @@ pathname of @code{path} and @code{err} the error code. Return @code{[str, err]} where @code{str} is the current working directory and @code{err} the error code. +@item chdir(path) +Change the current directory. Return 0 if OK or @code{-errno}. + @item mkdir(path, mode = 0o777) -Create a directory at @code{path}. Return the error code. +Create a directory at @code{path}. Return 0 if OK or @code{-errno}. @item stat(path) @item lstat(path) @@ -602,10 +667,10 @@ Constants to interpret the @code{mode} property returned by @item utimes(path, atime, mtime) Change the access and modification times of the file @code{path}. The -times are specified in milliseconds since 1970. +times are specified in milliseconds since 1970. Return 0 if OK or @code{-errno}. @item symlink(target, linkpath) -Create a link at @code{linkpath} containing the string @code{target}. +Create a link at @code{linkpath} containing the string @code{target}. Return 0 if OK or @code{-errno}. @item readlink(path) Return @code{[str, err]} where @code{str} is the link target and @code{err} @@ -620,19 +685,19 @@ the error code. Add a read handler to the file handle @code{fd}. @code{func} is called each time there is data pending for @code{fd}. A single read handler per file handle is supported. Use @code{func = null} to remove the -hander. +handler. @item setWriteHandler(fd, func) Add a write handler to the file handle @code{fd}. @code{func} is called each time data can be written to @code{fd}. A single write handler per file handle is supported. Use @code{func = null} to remove -the hander. +the handler. @item signal(signal, func) Call the function @code{func} when the signal @code{signal} happens. Only a single handler per signal number is supported. Use @code{null} to set the default handler or @code{undefined} to ignore -the signal. +the signal. Signal handlers can only be defined in the main thread. @item SIGINT @item SIGABRT @@ -652,7 +717,7 @@ object containing optional parameters: @table @code @item block Boolean (default = true). If true, wait until the process is - termined. In this case, @code{exec} return the exit code if positive + terminated. In this case, @code{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. @@ -670,11 +735,23 @@ object containing optional parameters: @item stdout @item stderr If present, set the handle in the child for stdin, stdout or stderr. - + + @item env + Object. If present, set the process environment from the object + key-value pairs. Otherwise use the same environment as the current + process. + + @item uid + Integer. If present, the process uid with @code{setuid}. + + @item gid + Integer. If present, the process gid with @code{setgid}. + @end table @item waitpid(pid, options) -@code{waitpid} Unix system call. Return the array @code{[ret, status]}. +@code{waitpid} Unix system call. Return the array @code{[ret, +status]}. @code{ret} contains @code{-errno} in case of error. @item WNOHANG Constant for the @code{options} argument of @code{waitpid}. @@ -703,6 +780,46 @@ Cancel a timer. Return a string representing the platform: @code{"linux"}, @code{"darwin"}, @code{"win32"} or @code{"js"}. +@item Worker(module_filename) +Constructor to create a new thread (worker) with an API close to the +@code{WebWorkers}. @code{module_filename} is a string specifying the +module filename which is executed in the newly created thread. As for +dynamically imported module, it is relative to the current script or +module path. Threads normally don't share any data and communicate +between each other with messages. Nested workers are not supported. An +example is available in @file{tests/test_worker.js}. + +The worker class has the following static properties: + + @table @code + @item parent + In the created worker, @code{Worker.parent} represents the parent + worker and is used to send or receive messages. + @end table + +The worker instances have the following properties: + + @table @code + @item postMessage(msg) + + Send a message to the corresponding worker. @code{msg} is cloned in + the destination worker using an algorithm similar to the @code{HTML} + structured clone algorithm. @code{SharedArrayBuffer} are shared + between workers. + + Current limitations: @code{Map} and @code{Set} are not supported + yet. + + @item onmessage + + Getter and setter. Set a function which is called each time a + message is received. The function is called with a single + argument. It is an object with a @code{data} property containing the + received message. The thread is not terminated if there is at least + one non @code{null} @code{onmessage} handler. + + @end table + @end table @section QuickJS C API @@ -719,7 +836,7 @@ supported. @code{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, similary +several JSContexts per JSRuntime and they can share objects, similar to frames of the same origin sharing Javascript objects in a web browser. @@ -727,7 +844,7 @@ web browser. @code{JSValue} represents a Javascript value which can be a primitive type or an object. Reference counting is used, so it is important to -explicitely duplicate (@code{JS_DupValue()}, increment the reference +explicitly duplicate (@code{JS_DupValue()}, increment the reference count) or free (@code{JS_FreeValue()}, decrement the reference count) JSValues. @@ -747,9 +864,9 @@ general rule, C functions take constant @code{JSValue}s as parameters @subsection Exceptions Exceptions: most C functions can return a Javascript exception. It -must be explicitely tested and handled by the C code. The specific +must be explicitly tested and handled by the C code. The specific @code{JSValue} @code{JS_EXCEPTION} indicates that an exception -occured. The actual exception object is stored in the +occurred. The actual exception object is stored in the @code{JSContext} and can be retrieved with @code{JS_GetException()}. @subsection Script evaluation @@ -934,16 +1051,16 @@ stack holds the Javascript parameters and local variables. @section RegExp -A specific regular expression engine was developped. It is both small +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 quantizers are specifically +recursion on the system stack. Simple quantifiers are specifically optimized to avoid recursions. -Infinite recursions coming from quantizers with empty terms are +Infinite recursions coming from quantifiers with empty terms are avoided. The full regexp library weights about 15 KiB (x86 code), excluding the @@ -951,9 +1068,9 @@ Unicode library. @section Unicode -A specific Unicode library was developped so that there is no +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 reasonnable access +Unicode tables are compressed while keeping a reasonable access speed. The library supports case conversion, Unicode normalization, Unicode @@ -962,10 +1079,10 @@ binary properties. The full Unicode library weights about 45 KiB (x86 code). -@section BigInt and BigFloat +@section BigInt, BigFloat, BigDecimal -BigInt and BigFloat are implemented with the @code{libbf} -library@footnote{@url{https://bellard.org/libbf}}. It weights about 60 +BigInt, BigFloat and BigDecimal are implemented with the @code{libbf} +library@footnote{@url{https://bellard.org/libbf}}. It weights about 90 KiB (x86 code) and provides arbitrary precision IEEE 754 floating point operations and transcendental functions with exact rounding. diff --git a/examples/pi_bigdecimal.js b/examples/pi_bigdecimal.js new file mode 100644 index 0000000..6a416b7 --- /dev/null +++ b/examples/pi_bigdecimal.js @@ -0,0 +1,68 @@ +/* + * PI computation in Javascript using the QuickJS bigdecimal type + * (decimal floating point) + */ +"use strict"; + +/* compute PI with a precision of 'prec' digits */ +function calc_pi(prec) { + const CHUD_A = 13591409m; + const CHUD_B = 545140134m; + const CHUD_C = 640320m; + const CHUD_C3 = 10939058860032000m; /* C^3/24 */ + const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(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, b1; + if (a == (b - 1n)) { + b1 = BigDecimal(b); + G = (2m * b1 - 1m) * (6m * b1 - 1m) * (6m * b1 - 5m); + P = G * (CHUD_B * b1 + CHUD_A); + if (b & 1n) + P = -P; + G = G; + Q = b1 * b1 * b1 * 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 = 0m; + } + 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 / 12m) * 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); + print(r.toFixed(n_digits, "down")); +})(); 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..fbe2ce1 --- /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)); + + 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_SetClassProto(ctx, js_point_class_id, 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/libbf.c b/libbf.c index 1741809..3bf257a 100644 --- a/libbf.c +++ b/libbf.c @@ -1,7 +1,7 @@ /* * Tiny arbitrary precision floating point library * - * Copyright (c) 2017-2018 Fabrice Bellard + * 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 @@ -39,6 +39,8 @@ //#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)) @@ -49,8 +51,8 @@ #endif /* XXX: adjust */ -#define BASECASE_DIV_THRESHOLD_B 300 -#define BASECASE_DIV_THRESHOLD_Q 300 +#define DIVNORM_LARGE_THRESHOLD 50 +#define UDIV1NORM_THRESHOLD 3 #if LIMB_BITS == 64 #define FMT_LIMB1 "%" PRIx64 @@ -67,6 +69,8 @@ #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); @@ -74,11 +78,17 @@ typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, #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 void fft_mul(bf_t *res, limb_t *a_tab, limb_t a_len, - limb_t *b_tab, limb_t b_len, int mul_flags); +static no_inline int fft_mul(bf_context_t *s, + bf_t *res, limb_t *a_tab, limb_t a_len, + limb_t *b_tab, limb_t b_len, int mul_flags); static void fft_clear_cache(bf_context_t *s); #endif +#ifdef USE_BF_DEC +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos); +#endif + /* could leading zeros */ static inline int clz(limb_t a) @@ -115,24 +125,44 @@ static inline int ceil_log2(limb_t a) return LIMB_BITS - clz(a - 1); } -#if 0 -static inline slimb_t ceil_div(slimb_t a, limb_t b) +/* 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 / (slimb_t)b; + return a / b; } -static inline slimb_t floor_div(slimb_t a, limb_t 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) / (slimb_t)b; + return (a - b + 1) / b; } } -#endif + +/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ +static inline limb_t smod(slimb_t a, slimb_t b) +{ + a = a % (slimb_t)b; + if (a < 0) + a += b; + return a; +} + +/* signed addition with saturation */ +static inline slimb_t sat_add(slimb_t a, slimb_t b) +{ + slimb_t r; + r = a + b; + /* overflow ? */ + if (((a ^ r) & (b ^ r)) < 0) + r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1); + return r; +} #define malloc(s) malloc_is_forbidden(s) #define free(p) free_is_forbidden(p) @@ -151,17 +181,6 @@ void bf_context_end(bf_context_t *s) bf_clear_cache(s); } -/* 'size' must be > 0 */ -static void *bf_malloc(bf_context_t *s, size_t size) -{ - return bf_realloc(s, NULL, size); -} - -static void bf_free(bf_context_t *s, void *ptr) -{ - bf_realloc(s, ptr, 0); -} - void bf_init(bf_context_t *s, bf_t *r) { r->ctx = s; @@ -171,20 +190,28 @@ void bf_init(bf_context_t *s, bf_t *r) r->tab = NULL; } -void bf_resize(bf_t *r, limb_t len) +/* return 0 if OK, -1 if alloc error */ +int bf_resize(bf_t *r, limb_t len) { + limb_t *tab; + if (len != r->len) { - r->tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t)); + 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; } -void bf_set_ui(bf_t *r, uint64_t a) +/* 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); + bf_resize(r, 0); /* cannot fail */ } #if LIMB_BITS == 32 else if (a <= 0xffffffff) @@ -193,7 +220,8 @@ void bf_set_ui(bf_t *r, uint64_t a) #endif { int shift; - bf_resize(r, 1); + if (bf_resize(r, 1)) + goto fail; shift = clz(a); r->tab[0] = a << shift; r->expn = LIMB_BITS - shift; @@ -202,7 +230,8 @@ void bf_set_ui(bf_t *r, uint64_t a) else { uint32_t a1, a0; int shift; - bf_resize(r, 2); + if (bf_resize(r, 2)) + goto fail; a0 = a; a1 = a >> 32; shift = clz(a1); @@ -211,50 +240,63 @@ void bf_set_ui(bf_t *r, uint64_t a) r->expn = 2 * LIMB_BITS - shift; } #endif + return 0; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; } -void bf_set_si(bf_t *r, int64_t a) +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_si(bf_t *r, int64_t a) { + int ret; + if (a < 0) { - bf_set_ui(r, -a); + ret = bf_set_ui(r, -a); r->sign = 1; } else { - bf_set_ui(r, a); + ret = bf_set_ui(r, a); } + return ret; } void bf_set_nan(bf_t *r) { - bf_resize(r, 0); + 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); + 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); + bf_resize(r, 0); /* cannot fail */ r->expn = BF_EXP_INF; r->sign = is_neg; } -void bf_set(bf_t *r, const bf_t *a) +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set(bf_t *r, const bf_t *a) { if (r == a) - return; + 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; - bf_resize(r, a->len); memcpy(r->tab, a->tab, a->len * sizeof(limb_t)); + return 0; } -/* equivalent to bf_set(r, a); bf_delete(a) */ +/* equivalent to bf_set(r, a); bf_delete(a) */ void bf_move(bf_t *r, bf_t *a) { bf_context_t *s = r->ctx; @@ -317,6 +359,16 @@ static inline limb_t limb_mask(int start, int last) 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) { @@ -338,7 +390,8 @@ static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos) return 0; } -/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */ +/* 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) { @@ -376,18 +429,13 @@ static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, if (r->sign == (rnd_mode == BF_RNDD)) add_one = inexact; break; + case BF_RNDA: + add_one = inexact; + break; case BF_RNDNA: case BF_RNDF: add_one = bit1; break; - case BF_RNDNU: - if (bit1) { - if (r->sign) - add_one = bit0; - else - add_one = 1; - } - break; default: abort(); } @@ -406,14 +454,17 @@ static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) if (prec == BF_PREC_INF || rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDNU || + rnd_mode == BF_RNDA || (rnd_mode == BF_RNDD && sign == 1) || (rnd_mode == BF_RNDU && sign == 0)) { bf_set_inf(r, sign); } else { /* set to maximum finite number */ l = (prec + LIMB_BITS - 1) / LIMB_BITS; - bf_resize(r, l); + 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++) @@ -426,12 +477,15 @@ static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) } /* round to prec1 bits assuming 'r' is non zero and finite. 'r' is - assumed to have length 'l'. Note: 'prec1' can be negative or - infinite (BF_PREC_INF). */ -static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l) + assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be + infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result + is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of + overflow not returning infinity. */ +static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l, + int ret) { limb_t v, a; - int shift, add_one, ret, rnd_mode; + int shift, add_one, rnd_mode; slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; /* e_min and e_max are computed to match the IEEE 754 conventions */ @@ -439,22 +493,28 @@ static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l) e_min = -e_range + 3; e_max = e_range; - if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + 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); + 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; @@ -524,11 +584,11 @@ static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l) l -= i; memmove(r->tab, r->tab + i, l * sizeof(limb_t)); } - bf_resize(r, l); + bf_resize(r, l); /* cannot fail */ return ret; } -/* 'r' must be a finite number */ +/* '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; @@ -542,7 +602,7 @@ int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) if (l == 0) { /* zero */ r->expn = BF_EXP_ZERO; - bf_resize(r, 0); + bf_resize(r, 0); /* cannot fail */ ret = 0; } else { r->expn -= (r->len - l) * LIMB_BITS; @@ -558,7 +618,7 @@ int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) } r->expn -= shift; } - ret = __bf_round(r, prec1, flags, l); + ret = __bf_round(r, prec1, flags, l, 0); } // bf_print_str("r_final", r); return ret; @@ -581,8 +641,7 @@ int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) } if (a->expn == BF_EXP_ZERO) return FALSE; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDNU); + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); if (k < (prec + 2)) return FALSE; bit_pos = a->len * LIMB_BITS - 1 - prec; @@ -604,11 +663,12 @@ int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) 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); + return __bf_round(r, prec, flags, r->len, 0); } /* for debugging */ @@ -622,6 +682,32 @@ static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t } } +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) { @@ -696,51 +782,26 @@ int bf_cmp_full(const bf_t *a, const bf_t *b) 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) +/* Standard floating point comparison: return 2 if one of the operands + is NaN (unordered) or -1, 0, 1 depending on the ordering assuming + -0 == +0 */ +int bf_cmp(const bf_t *a, const bf_t *b) { - 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; - } + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + res = 2; + } else if (a->sign != b->sign) { + if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) + res = 0; + else + res = 1 - 2 * a->sign; } else { res = bf_cmpu(a, b); - if (res == 0) { - return op & BF_CMP_EQ; - } else if (op & BF_CMP_LT) { - return (res < 0) ^ a->sign; - } else { - return FALSE; - } + if (a->sign) + res = -res; } -} - -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); + return res; } /* Compute the number of bits 'n' matching the pattern: @@ -866,7 +927,8 @@ static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, 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); - bf_resize(r, r_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; @@ -933,7 +995,8 @@ static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, /* carry is only possible in add case */ if (!is_sub && carry) { - bf_resize(r, r_len + 1); + if (bf_resize(r, r_len + 1)) + goto fail; r->tab[r_len] = 1; r->expn += LIMB_BITS; } @@ -941,6 +1004,9 @@ static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, 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, @@ -955,8 +1021,8 @@ static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, return bf_add_internal(r, a, b, prec, flags, 1); } -static 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(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; @@ -973,9 +1039,111 @@ static limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, return k; } +limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n) +{ + size_t i; + limb_t k, a; + + k=b; + for(i=0;i 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 b) { limb_t i, l; dlimb_t t; @@ -989,6 +1157,42 @@ static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n, 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, @@ -1043,7 +1247,7 @@ static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, { slimb_t i; - if (n >= 3) { + if (n >= UDIV1NORM_THRESHOLD) { limb_t b_inv; b_inv = udiv1norm_init(b); for(i = n - 1; i >= 0; i--) { @@ -1060,31 +1264,50 @@ static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, 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. taba[na] is modified. */ -static void mp_divnorm(limb_t *tabq, - limb_t *taba, limb_t na, - const limb_t *tabb, limb_t 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; + slimb_t i, j; b1 = tabb[nb - 1]; if (nb == 1) { taba[0] = mp_div1norm(tabq, taba, na, b1, 0); - return; + return 0; } - taba[na] = 0; n = na - nb; - if (n >= 3) + 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; - /* XXX: could simplify the first iteration */ - for(i = n; i >= 0; i--) { + /* 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) { @@ -1117,12 +1340,190 @@ static void mp_divnorm(limb_t *tabq, } 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) { - limb_t i; int ret, r_sign; if (a->len < b->len) { @@ -1173,7 +1574,8 @@ int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, mul_flags |= FFT_MUL_R_OVERLAP_A; if (r == b) mul_flags |= FFT_MUL_R_OVERLAP_B; - fft_mul(r, a_tab, a_len, b_tab, b_len, mul_flags); + if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags)) + goto fail; } else #endif { @@ -1182,16 +1584,18 @@ int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, r1 = r; r = &tmp; } - bf_resize(r, a_len + b_len); - memset(r->tab, 0, sizeof(limb_t) * a_len); - for(i = 0; i < b_len; i++) { - r->tab[i + a_len] = mp_add_mul1(r->tab + i, a_tab, a_len, - b_tab[i]); + 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); } @@ -1204,88 +1608,11 @@ 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_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1; e = bf_max(e, -e_max); e = bf_min(e, e_max); r->expn += e; - return __bf_round(r, prec, flags, r->len); -} - -static void bf_recip_rec(bf_t *a, const bf_t *x, limb_t prec1) -{ - bf_t t0; - limb_t prec; - - bf_init(a->ctx, &t0); - - if (prec1 <= LIMB_BITS - 3) { - limb_t v; - /* initial approximation */ - v = x->tab[x->len - 1]; - /* 2^(L-1) <= v <= 2^L-1 (L=LIMB_BITS) */ - v = ((dlimb_t)1 << (2 * LIMB_BITS - 2)) / v; - /* 2^(L-2) <= v <= 2^(L-1) */ - bf_resize(a, 1); - a->sign = x->sign; - a->expn = 2 - x->expn; - if (v == ((limb_t)1 << (LIMB_BITS - 1))) { - a->tab[0] = v; - } else { - a->tab[0] = v << 1; - a->expn--; - } - a->tab[0] &= limb_mask(LIMB_BITS - prec1, LIMB_BITS - 1); - } else { - /* XXX: prove the added precision */ - bf_recip_rec(a, x, (prec1 / 2) + 8); - prec = prec1 + 8; - - /* a = a + a * (1 - x * a) */ - bf_mul(&t0, x, a, prec, BF_RNDF); - t0.sign ^= 1; - bf_add_si(&t0, &t0, 1, prec, BF_RNDF); - bf_mul(&t0, &t0, a, prec, BF_RNDF); - bf_add(a, a, &t0, prec1, BF_RNDF); - } - // bf_print_str("r", a); - bf_delete(&t0); -} - -/* Note: only faithful rounding is supported */ -void bf_recip(bf_t *r, const bf_t *a, limb_t prec) -{ - assert(r != a); - if (a->len == 0) { - if (a->expn == BF_EXP_NAN) { - bf_set_nan(r); - } else if (a->expn == BF_EXP_INF) { - bf_set_zero(r, a->sign); - } else { - bf_set_inf(r, a->sign); - } - } else { - // bf_print_str("a", a); - bf_recip_rec(r, a, prec); - } -} - -/* add zero limbs if necessary to have at least precl limbs */ -static void bf_add_zero_limbs(bf_t *r, limb_t precl) -{ - limb_t l = r->len; - if (l < precl) { - bf_resize(r, precl); - memmove(r->tab + precl - l, r->tab, - l * sizeof(limb_t)); - memset(r->tab, 0, (precl - l) * sizeof(limb_t)); - } -} - -/* set a bit to 1 at bit position >= (precl * LIMB_BITS - 1) */ -static void bf_or_one(bf_t *r, limb_t precl) -{ - bf_add_zero_limbs(r, precl); - r->tab[0] |= 1; + return __bf_round(r, prec, flags, r->len, 0); } /* Return e such as a=m*2^e with m odd integer. return 0 if a is zero, @@ -1311,26 +1638,21 @@ slimb_t bf_get_exp_min(const bf_t *a) static void bf_tdivremu(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b) { - if (a->expn < b->expn) { + if (bf_cmpu(a, b) < 0) { bf_set_ui(q, 0); bf_set(r, a); } else { - /* for large numbers, use the floating point division in - faithful mode */ - bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDF); - bf_rint(q, BF_PREC_INF, BF_RNDZ); - bf_mul(r, q, b, BF_PREC_INF, BF_RNDN); - bf_sub(r, a, r, BF_PREC_INF, BF_RNDN); - if (r->len != 0 && r->sign) { - bf_add_si(q, q, -1, BF_PREC_INF, BF_RNDZ); - bf_add(r, r, b, BF_PREC_INF, BF_RNDZ); - } + 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; @@ -1367,65 +1689,36 @@ static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, nb = b->len; n = bf_max(a->len, precl); - if (nb <= BASECASE_DIV_THRESHOLD_B || - (slimb_t)n <= (BASECASE_DIV_THRESHOLD_Q - 1)) { - limb_t *taba, na, i; + { + limb_t *taba, na; slimb_t d; na = n + nb; - taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t)); + 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)); - bf_resize(r, n + 1); - mp_divnorm(r->tab, taba, na, b->tab, nb); - - /* see if non zero remainder */ - for(i = 0; i < nb; i++) { - if (taba[i] != 0) { - r->tab[0] |= 1; - break; - } + if (bf_resize(r, n + 1)) + goto fail1; + if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) { + fail1: + bf_free(s, taba); + goto fail; } + /* see if non zero remainder */ + if (mp_scan_nz(taba, nb)) + r->tab[0] |= 1; bf_free(r->ctx, taba); r->expn = a->expn - b->expn + LIMB_BITS; r->sign = r_sign; ret = bf_normalize_and_round(r, prec, flags); - } else if ((flags & BF_RND_MASK) == BF_RNDF) { - bf_t b_inv; - bf_init(r->ctx, &b_inv); - bf_recip(&b_inv, b, prec + 3); - ret = bf_mul(r, a, &b_inv, prec, flags); - bf_delete(&b_inv); - } else { - bf_t a1_s, *a1 = &a1_s; - bf_t b1_s, *b1 = &b1_s; - bf_t rem_s, *rem = &rem_s; - - /* convert the mantissa of 'a' and 'b' to integers and generate - a quotient with at least prec + 2 bits */ - a1->expn = (n + nb) * LIMB_BITS; - a1->tab = a->tab; - a1->len = a->len; - a1->sign = 0; - - b1->expn = nb * LIMB_BITS; - b1->tab = b->tab; - b1->len = nb; - b1->sign = 0; - - bf_init(r->ctx, rem); - bf_tdivremu(r, rem, a1, b1); - /* the remainder is not zero: put it in the rounding bits */ - if (rem->len != 0) { - bf_or_one(r, precl); - } - bf_delete(rem); - r->expn += a->expn - b->expn - n * LIMB_BITS; - r->sign = r_sign; - ret = bf_round(r, prec, flags); } return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; } /* division and remainder. @@ -1441,7 +1734,7 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, { bf_t a1_s, *a1 = &a1_s; bf_t b1_s, *b1 = &b1_s; - int q_sign; + int q_sign, ret; BOOL is_ceil, is_rndn; assert(q != a && q != b); @@ -1463,8 +1756,7 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, } q_sign = a->sign ^ b->sign; - is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA || - rnd_mode == BF_RNDNU); + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); switch(rnd_mode) { default: case BF_RNDZ: @@ -1478,12 +1770,12 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, case BF_RNDU: is_ceil = q_sign ^ 1; break; + case BF_RNDA: + is_ceil = TRUE; + break; case BF_DIVREM_EUCLIDIAN: is_ceil = a->sign; break; - case BF_RNDNU: - /* XXX: unsupported yet */ - abort(); } a1->expn = a->expn; @@ -1498,6 +1790,8 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, /* 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) { @@ -1513,36 +1807,30 @@ int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, } } else if (is_ceil) { do_sub_r: - bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); - bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); + 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) +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) { bf_t q_s, *q = &q_s; int ret; bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, 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); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); bf_delete(q); return ret; } @@ -1557,13 +1845,13 @@ static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int 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) + bf_flags_t flags, int rnd_mode) { bf_t q_s, *q = &q_s; int ret; bf_init(r->ctx, q); - ret = bf_divrem(q, r, a, b, prec, flags, BF_RNDN); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); bf_get_limb(pq, q, BF_GET_INT_MOD); bf_delete(q); return ret; @@ -1590,138 +1878,195 @@ static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r) } #endif -/* (128.0 / sqrt((i + 64) / 256)) & 0xff */ -static const uint8_t rsqrt_table[192] = { - 0,254,252,250,248,247,245,243,241,240,238,236,235,233,232,230, -229,228,226,225,223,222,221,220,218,217,216,215,214,212,211,210, -209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194, -194,193,192,191,190,189,189,188,187,186,185,185,184,183,182,182, -181,180,180,179,178,178,177,176,176,175,174,174,173,172,172,171, -171,170,169,169,168,168,167,167,166,166,165,164,164,163,163,162, -162,161,161,160,160,159,159,158,158,158,157,157,156,156,155,155, -154,154,154,153,153,152,152,151,151,151,150,150,149,149,149,148, -148,147,147,147,146,146,146,145,145,144,144,144,143,143,143,142, -142,142,141,141,141,140,140,140,139,139,139,138,138,138,137,137, -137,137,136,136,136,135,135,135,134,134,134,134,133,133,133,132, -132,132,132,131,131,131,131,130,130,130,130,129,129,129,129,128, +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, }; -static void __bf_rsqrt(bf_t *a, const bf_t *x, limb_t prec1) +/* 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) { - bf_t t0; - limb_t prec; - - if (prec1 <= 7) { - slimb_t e; - limb_t v; - /* initial approximation using 8 mantissa bits */ - v = x->tab[x->len - 1]; - e = x->expn; - if (e & 1) { - v >>= 1; - e++; - } - v = rsqrt_table[(v >> (LIMB_BITS - 8)) - 64]; - e = 1 - (e / 2); - if (v == 0) { - v = 128; /* real table value is 256 */ - e++; - } - bf_resize(a, 1); - a->tab[0] = (v << (LIMB_BITS - 8)) & - limb_mask(LIMB_BITS - prec1, LIMB_BITS - 1); - a->expn = e; - a->sign = 0; - } else { - /* XXX: prove rounding */ - __bf_rsqrt(a, x, (prec1 / 2) + 2); - - prec = prec1 + 3; - - /* x' = x + (x/2) * (1 - a * x^2) */ - bf_init(a->ctx, &t0); - - bf_mul(&t0, a, a, prec, BF_RNDF); - bf_mul(&t0, &t0, x, prec, BF_RNDF); - t0.sign ^= 1; - bf_add_si(&t0, &t0, 1, prec, BF_RNDF); - bf_mul(&t0, &t0, a, prec, BF_RNDF); - if (t0.len != 0) - t0.expn--; - bf_add(a, a, &t0, prec1, BF_RNDF); - - bf_delete(&t0); + 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++; } -} - -static int __bf_sqrt(bf_t *x, const bf_t *a, limb_t prec1, bf_flags_t flags) -{ - bf_t t0, t1; - limb_t prec; - int ret; - /* XXX: prove rounding */ - __bf_rsqrt(x, a, (prec1 / 2) + 2); - prec = prec1 + 3; - - /* x' = a * x + (x/2) * (a - (a * x)^2) */ - - bf_init(x->ctx, &t0); - bf_init(x->ctx, &t1); - bf_mul(&t1, x, a, prec, BF_RNDF); - bf_mul(&t0, &t1, &t1, prec, BF_RNDF); - t0.sign ^= 1; - bf_add(&t0, &t0, a, prec, BF_RNDF); - bf_mul(&t0, &t0, x, prec, BF_RNDF); - if (t0.len != 0) - t0.expn--; - ret = bf_add(x, &t1, &t0, prec1, flags); - - bf_delete(&t0); - bf_delete(&t1); - return ret; -} - -/* Note: only faithful rounding is supported */ -void bf_rsqrt(bf_t *r, const bf_t *a, limb_t prec) -{ - if (a->len == 0) { - if (a->expn == BF_EXP_NAN || - (a->sign && a->expn != BF_EXP_ZERO)) { - bf_set_nan(r); - } else if (a->expn == BF_EXP_INF) { - bf_set_zero(r, a->sign); - } else { - bf_set_inf(r, 0); - } - } else if (a->sign) { - bf_set_nan(r); - } else { - // bf_print_str("a", a); - __bf_rsqrt(r, a, prec); + /* 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)) */ -static limb_t bf_isqrt(limb_t a) +limb_t bf_isqrt(limb_t a) { - unsigned int l; - limb_t u, s; - + limb_t s, r; + int k; + if (a == 0) return 0; - l = ceil_log2(a); - u = (limb_t)1 << ((l + 1) / 2); - /* u >= floor(sqrt(a)) */ - for(;;) { - s = u; - u = ((a / s) + s) / 2; - if (u >= s) - break; - } + 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. */ @@ -1748,10 +2093,9 @@ int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a) ret = BF_ST_INVALID_OP; } else { bf_t rem_s, *rem; - int res; - bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDF); - bf_rint(r, BF_PREC_INF, BF_RNDZ); + 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; @@ -1759,43 +2103,30 @@ int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a) rem = &rem_s; bf_init(r->ctx, rem); } + /* XXX: could avoid recomputing the remainder */ bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ); - ret = 0; - if (rem1) { - bf_neg(rem); - bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ); - if (rem->len != 0) { - ret = BF_ST_INEXACT; - if (rem->sign) { - bf_t a1_s, *a1 = &a1_s; - bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ); - a1->tab = a->tab; - a1->len = a->len; - a1->sign = a->sign; - a1->expn = a->expn + 1; - bf_add(rem, rem, r, BF_PREC_INF, BF_RNDZ); - bf_add_si(rem, rem, 1, BF_PREC_INF, BF_RNDZ); - } - } else { - ret = 0; - } - } else { - res = bf_cmpu(rem, a); - bf_delete(rem); - // printf("res2=%d\n", res2); - if (res > 0) { - /* need to correct the result */ - bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ); - } - ret = (res != 0 ? BF_ST_INEXACT : 0); + 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) { - int rnd_mode, ret; + bf_context_t *s = a->ctx; + int ret; assert(r != a); @@ -1813,34 +2144,47 @@ int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) bf_set_nan(r); ret = BF_ST_INVALID_OP; } else { - rnd_mode = flags & BF_RND_MASK; - if (rnd_mode == BF_RNDF) { - ret = __bf_sqrt(r, a, prec, flags); + 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 { - bf_t a1_s, *a1 = &a1_s; - slimb_t d, prec2; - int res1, res2; - - bf_init(r->ctx, a1); - bf_set(a1, a); - /* convert the mantissa to an integer with at most 2 * - prec + 4 bits */ - prec2 = prec + 2; - /* make '-a->expn + d' divisible by two */ - d = prec2 * 2 - (a->expn & 1); - a1->expn = d; - res1 = bf_rint(a1, BF_PREC_INF, BF_RNDZ); - res2 = bf_sqrtrem(r, NULL, a1); - bf_delete(a1); - if ((res2 | res1) != 0) { - bf_or_one(r, (prec2 + LIMB_BITS - 1) / LIMB_BITS); - } - /* update the exponent */ - r->expn -= (-a->expn + d) >> 1; - ret = bf_round(r, prec, flags); + 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, @@ -1883,8 +2227,8 @@ int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_t b; int ret; bf_init(r->ctx, &b); - bf_set_ui(&b, b1); - ret = bf_mul(r, a, &b, prec, flags); + ret = bf_set_ui(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); bf_delete(&b); return ret; } @@ -1895,8 +2239,8 @@ int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_t b; int ret; bf_init(r->ctx, &b); - bf_set_si(&b, b1); - ret = bf_mul(r, a, &b, prec, flags); + ret = bf_set_si(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); bf_delete(&b); return ret; } @@ -1908,24 +2252,21 @@ int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, int ret; bf_init(r->ctx, &b); - bf_set_si(&b, b1); - ret = bf_add(r, a, &b, prec, flags); + ret = bf_set_si(&b, b1); + ret |= bf_add(r, a, &b, prec, flags); bf_delete(&b); return ret; } -int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, - bf_flags_t flags) +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) { - bf_set_ui(r, 1); - return 0; - } - bf_set(r, a); - ret = 0; + 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); @@ -1935,31 +2276,29 @@ int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, return ret; } -int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, limb_t prec, bf_flags_t flags) +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; - bf_init(r->ctx, &a); - bf_set_ui(&a, a1); - ret = bf_pow_ui(r, &a, b, prec, flags); - bf_delete(&a); + 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 (single rounding) */ -int bf_rint(bf_t *r, limb_t prec, bf_flags_t flags) +/* convert to integer (infinite precision) */ +int bf_rint(bf_t *r, int rnd_mode) { - int ret; - if (r->len == 0) - return 0; - if (r->expn <= 0) { - ret = __bf_round(r, r->expn, flags & ~BF_FLAG_SUBNORMAL, r->len) & - ~BF_ST_UNDERFLOW; - } else { - ret = __bf_round(r, bf_min(r->expn, prec), flags, r->len); - } - return ret; + return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); } /* logical operations */ @@ -1980,12 +2319,13 @@ static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op) } } -static void bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) +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); @@ -2002,7 +2342,10 @@ static void bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) if (a_sign) { a = &a1_s; bf_init(r->ctx, a); - bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ); + if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) { + b = NULL; + goto fail; + } } else { a = (bf_t *)a1; } @@ -2010,7 +2353,8 @@ static void bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) if (b_sign) { b = &b1_s; bf_init(r->ctx, b); - bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ); + if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ)) + goto fail; } else { b = (bf_t *)b1; } @@ -2029,7 +2373,8 @@ static void bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) } /* Note: a or b can be zero */ l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS; - bf_resize(r, l); + 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; @@ -2042,31 +2387,40 @@ static void bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) } r->expn = l * LIMB_BITS; r->sign = r_sign; - bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); - if (r_sign) - bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ); + 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 */ -void bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b) +/* '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) { - bf_logic_op(r, a, b, BF_LOGIC_OR); + return bf_logic_op(r, a, b, BF_LOGIC_OR); } -/* 'a' and 'b' must be integers */ -void bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b) +/* '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) { - bf_logic_op(r, a, b, BF_LOGIC_XOR); + return bf_logic_op(r, a, b, BF_LOGIC_XOR); } -/* 'a' and 'b' must be integers */ -void bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b) +/* '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) { - bf_logic_op(r, a, b, BF_LOGIC_AND); + return bf_logic_op(r, a, b, BF_LOGIC_AND); } /* conversion between fixed size types */ @@ -2125,7 +2479,7 @@ int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode) return ret; } -void bf_set_float64(bf_t *a, double d) +int bf_set_float64(bf_t *a, double d) { Float64Union u; uint64_t m; @@ -2157,25 +2511,31 @@ void bf_set_float64(bf_t *a, double d) norm: a->expn = e - 1023 + 1; #if LIMB_BITS == 32 - bf_resize(a, 2); + if (bf_resize(a, 2)) + goto fail; a->tab[0] = m; a->tab[1] = m >> 32; #else - bf_resize(a, 1); + 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 +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there is an overflow and 0 otherwise. */ int bf_get_int32(int *pres, const bf_t *a, int flags) { uint32_t v; int ret; if (a->expn >= BF_EXP_INF) { - ret = 0; + ret = BF_ST_INVALID_OP; if (flags & BF_GET_INT_MOD) { v = 0; } else if (a->expn == BF_EXP_INF) { @@ -2192,7 +2552,7 @@ int bf_get_int32(int *pres, const bf_t *a, int flags) v = -v; ret = 0; } else if (!(flags & BF_GET_INT_MOD)) { - ret = BF_ST_OVERFLOW; + ret = BF_ST_INVALID_OP; if (a->sign) { v = (uint32_t)INT32_MAX + 1; if (a->expn == 32 && @@ -2212,14 +2572,14 @@ int bf_get_int32(int *pres, const bf_t *a, int flags) return ret; } -/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there is an overflow and 0 otherwise. */ int bf_get_int64(int64_t *pres, const bf_t *a, int flags) { uint64_t v; int ret; if (a->expn >= BF_EXP_INF) { - ret = 0; + ret = BF_ST_INVALID_OP; if (flags & BF_GET_INT_MOD) { v = 0; } else if (a->expn == BF_EXP_INF) { @@ -2244,14 +2604,14 @@ int bf_get_int64(int64_t *pres, const bf_t *a, int flags) v = -v; ret = 0; } else if (!(flags & BF_GET_INT_MOD)) { - ret = BF_ST_OVERFLOW; + ret = BF_ST_INVALID_OP; if (a->sign) { uint64_t v1; v = (uint64_t)INT64_MAX + 1; if (a->expn == 64) { v1 = a->tab[a->len - 1]; #if LIMB_BITS == 32 - v1 |= (v1 << 32) | get_limbz(a, a->len - 2); + v1 = (v1 << 32) | get_limbz(a, a->len - 2); #endif if (v1 == v) ret = 0; @@ -2273,6 +2633,40 @@ int bf_get_int64(int64_t *pres, const bf_t *a, int flags) return ret; } +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_uint64(uint64_t *pres, const bf_t *a) +{ + uint64_t v; + int ret; + if (a->expn == BF_EXP_NAN) { + goto overflow; + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->sign) { + v = 0; + ret = BF_ST_INVALID_OP; + } else if (a->expn <= 64) { +#if LIMB_BITS == 32 + if (a->expn <= 32) + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + else + v = (((uint64_t)a->tab[a->len - 1] << 32) | + get_limbz(a, a->len - 2)) >> (64 - a->expn); +#else + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); +#endif + ret = 0; + } else { + overflow: + v = UINT64_MAX; + ret = BF_ST_INVALID_OP; + } + *pres = v; + return ret; +} + /* base conversion from radix */ static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = { @@ -2295,12 +2689,14 @@ static limb_t get_limb_radix(int radix) return radixl; } -static void 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) +/* 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) { - bf_set_ui(r, tab[0]); + ret = bf_set_ui(r, tab[0]); } else { bf_t T_s, *T = &T_s, *B; limb_t n1, n2; @@ -2310,38 +2706,50 @@ static void bf_integer_from_radix_rec(bf_t *r, const limb_t *tab, // printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2); B = &pow_tab[level]; if (B->len == 0) { - bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ); + ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ); + if (ret) + return ret; } - bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0, - radix, pow_tab); - bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ); + 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); - bf_integer_from_radix_rec(T, tab, n2, level + 1, n0, - radix, pow_tab); - bf_add(r, r, T, BF_PREC_INF, BF_RNDZ); + 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); } -static void bf_integer_from_radix(bf_t *r, const limb_t *tab, +/* 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; + 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]); - bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab); + 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. */ @@ -2353,11 +2761,11 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, bf_t B_s, *B = &B_s; if (T->len == 0) { - bf_set(r, T); - return 0; + return bf_set(r, T); } else if (expn == 0) { - bf_set(r, T); - return bf_round(r, prec, flags); + ret = bf_set(r, T); + ret |= bf_round(r, prec, flags); + return ret; } e = expn; @@ -2369,11 +2777,11 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, bf_init(r->ctx, B); if (prec == BF_PREC_INF) { /* infinite precision: only used if the result is known to be exact */ - bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN); + 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); + ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN); } else { - ret = bf_mul(r, T, B, BF_PREC_INF, BF_RNDN); + ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN); } } else { ziv_extra_bits = 16; @@ -2382,20 +2790,24 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, /* 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); + ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); overflow = !bf_is_finite(B); /* XXX: if bf_pow_ui_ui returns an exact result, can stop after the next operation */ if (expn_sign) - ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN); + ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); else - ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN); + ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + if (ret & BF_ST_MEM_ERROR) + break; if ((ret & BF_ST_INEXACT) && !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) && !overflow) { /* and more precision and retry */ ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); } else { + /* XXX: need to use __bf_round() to pass the inexact + flag for the subnormal case */ ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT); break; } @@ -2417,15 +2829,19 @@ static inline int to_digit(int c) return 36; } -/* add a limb at 'pos' and decrement pos. new space is created if needed */ -static void bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v) +/* 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; + limb_t new_size, d, *new_tab; new_size = bf_max(a->len + 1, a->len * 3 / 2); - a->tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size); + 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; @@ -2433,6 +2849,7 @@ static void bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v) } a->tab[pos--] = v; *ppos = pos; + return 0; } static int bf_tolower(int c) @@ -2458,35 +2875,20 @@ static int strcasestart(const char *str, const char *val, const char **ptr) return 1; } -/* - Return (status, n, exp). 'status' is the floating point status. 'n' - is the parsed number. - If prec = BF_PREC_INF: - If the number is an integer or if the radix is a power of two, - *pexponent = 0. - Otherwise, '*pexponent' is the exponent in radix '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) +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, sep; - int ret_legacy_octal = 0; + 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, is_float; + BOOL has_decpt, is_bin_exp; bf_t a_s, *a; - /* optional separator between digits */ - sep = (flags & BF_ATOF_UNDERSCORE_SEP) ? '_' : 256; - *pexponent = 0; p = str; - if (!(flags & (BF_ATOF_INT_ONLY | BF_ATOF_JS_QUIRKS)) && - radix <= 16 && + if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && strcasestart(p, "nan", &p)) { bf_set_nan(r); ret = 0; @@ -2497,14 +2899,10 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, if (p[0] == '+') { p++; p_start = p; - if (flags & BF_ATOF_NO_PREFIX_AFTER_SIGN) - goto no_radix_prefix; } else if (p[0] == '-') { is_neg = 1; p++; p_start = p; - if (flags & BF_ATOF_NO_PREFIX_AFTER_SIGN) - goto no_radix_prefix; } else { p_start = p; } @@ -2522,28 +2920,7 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, radix == 0 && (flags & BF_ATOF_BIN_OCT)) { p += 2; radix = 2; - } else if ((p[1] >= '0' && p[1] <= '9') && - radix == 0 && (flags & BF_ATOF_LEGACY_OCTAL)) { - int i; - ret_legacy_octal = BF_ATOF_ST_LEGACY_OCTAL; - /* the separator is not allowed in legacy octal literals */ - 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 { - /* 0 cannot be followed by a separator */ - if (p[1] == sep) { - p++; - bf_set_zero(r, 0); - ret = 0; - if (flags & BF_ATOF_INT_PREC_INF) - ret |= BF_ATOF_ST_INTEGER; - goto done; - } goto no_prefix; } /* there must be a digit after the prefix */ @@ -2554,10 +2931,8 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, } no_prefix: ; } else { - no_radix_prefix: - if (!(flags & BF_ATOF_INT_ONLY) && radix <= 16 && - ((!(flags & BF_ATOF_JS_QUIRKS) && strcasestart(p, "inf", &p)) || - ((flags & BF_ATOF_JS_QUIRKS) && strstart(p, "Infinity", &p)))) { + if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && + strcasestart(p, "inf", &p)) { bf_set_inf(r, is_neg); ret = 0; goto done; @@ -2566,7 +2941,11 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, if (radix == 0) radix = 10; - if ((radix & (radix - 1)) != 0) { + 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); @@ -2577,7 +2956,7 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, /* skip leading zeros */ /* XXX: could also skip zeros after the decimal point */ - while (*p == '0' || (*p == sep && to_digit(p[1]) < radix)) + while (*p == '0') p++; if (radix_bits) { @@ -2591,22 +2970,15 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, pos = 0; has_decpt = FALSE; int_len = digit_count = 0; - is_float = FALSE; for(;;) { limb_t c; if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) { - if ((flags & BF_ATOF_INT_ONLY) || - (radix != 10 && (flags & BF_ATOF_ONLY_DEC_FLOAT))) - break; if (has_decpt) break; - is_float = TRUE; has_decpt = TRUE; int_len = digit_count; p++; } - if (*p == sep && to_digit(p[1]) < radix) - p++; c = to_digit(*p); if (c >= radix) break; @@ -2616,7 +2988,8 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, shift -= radix_bits; if (shift <= 0) { cur_limb |= c >> (-shift); - bf_add_limb(a, &pos, cur_limb); + if (bf_add_limb(a, &pos, cur_limb)) + goto mem_error; if (shift < 0) cur_limb = c << (LIMB_BITS + shift); else @@ -2629,7 +3002,8 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, cur_limb = cur_limb * radix + c; shift--; if (shift == 0) { - bf_add_limb(a, &pos, cur_limb); + if (bf_add_limb(a, &pos, cur_limb)) + goto mem_error; shift = digits_per_limb; cur_limb = 0; } @@ -2646,28 +3020,37 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, shift--; } } - bf_add_limb(a, &pos, cur_limb); + 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) - goto error; + 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 (!(flags & BF_ATOF_INT_ONLY) && - !(radix != 10 && (flags & BF_ATOF_ONLY_DEC_FLOAT)) && - ((radix == 10 && (*p == 'e' || *p == 'E')) || + 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++; - is_float = TRUE; exp_is_neg = 0; if (*p == '+') { p++; @@ -2677,12 +3060,10 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, } for(;;) { int c; - if (*p == sep && to_digit(p[1]) < 10) - p++; c = to_digit(*p); if (c >= 10) break; - if (unlikely(expn > ((EXP_MAX - 2 - 9) / 10))) { + if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) { /* exponent overflow */ if (exp_is_neg) { bf_set_zero(r, is_neg); @@ -2698,17 +3079,12 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, } if (exp_is_neg) expn = -expn; - } else if (!is_float) { - if (*p == 'n' && (flags & BF_ATOF_INT_N_SUFFIX)) { - p++; - prec = BF_PREC_INF; - } else if (flags & BF_ATOF_INT_PREC_INF) { - prec = BF_PREC_INF; - } else { - is_float = TRUE; - } } - if (radix_bits) { + 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; @@ -2727,45 +3103,50 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, expn -= l * digits_per_limb - int_len; bf_init(r->ctx, T); - bf_integer_from_radix(T, a->tab + pos, l, radix); - T->sign = is_neg; - if (prec == BF_PREC_INF && is_float) { - /* return the exponent */ - *pexponent = expn; - bf_set(r, T); - ret = 0; + if (bf_integer_from_radix(T, a->tab + pos, l, radix)) { + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; } else { - ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags); + 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); } - if (!is_float) - ret |= BF_ATOF_ST_INTEGER; done: if (pnext) *pnext = p; - return ret | ret_legacy_octal; - error: - if (!radix_bits) - bf_delete(a); - ret = 0; - if (flags & BF_ATOF_NAN_IF_EMPTY) { - bf_set_nan(r); - } else { - bf_set_zero(r, 0); - if (flags & BF_ATOF_INT_PREC_INF) - ret |= BF_ATOF_ST_INTEGER; - } - goto done; + 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_atof2(r, &dummy_exp, str, pnext, radix, prec, flags); + return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, FALSE); } /* base conversion to radix */ @@ -2776,7 +3157,7 @@ int bf_atof(bf_t *r, const char *str, const char **pnext, int radix, #define RADIXL_10 UINT64_C(1000000000) #endif -static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 2 + 1] = { +static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = { #if LIMB_BITS == 32 { 0x80000000, 0x00000000,}, { 0x50c24e60, 0xd4d4f4a7,}, @@ -2989,12 +3370,14 @@ slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, } /* '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) +static int bf_integer_to_radix_rec(bf_t *pow_tab, + limb_t *out, const bf_t *a, limb_t n, + int level, limb_t n0, limb_t radixl, + unsigned int radixl_bits) { limb_t n1, n2, q_prec; + int ret; + assert(n >= 1); if (n == 1) { out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); @@ -3021,71 +3404,90 @@ static void bf_integer_to_radix_rec(bf_t *pow_tab, n1 = n - n2; B = &pow_tab[2 * level]; B_inv = &pow_tab[2 * level + 1]; + ret = 0; if (B->len == 0) { /* compute BASE^n2 */ - bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ); + ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ); /* we use enough bits for the maximum possible 'n1' value, i.e. n2 + 1 */ - bf_recip(B_inv, B, (n2 + 1) * radixl_bits + 2); + ret |= bf_set_ui(&R, 1); + ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN); } // printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2); q_prec = n1 * radixl_bits; - bf_mul(&Q, a, B_inv, q_prec, BF_RNDN); - bf_rint(&Q, BF_PREC_INF, BF_RNDZ); + ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN); + ret |= 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); + ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ); + ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ); + + if (ret & BF_ST_MEM_ERROR) + goto fail; /* adjust if necessary */ q_add = 0; while (R.sign && R.len != 0) { - bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ); + if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ)) + goto fail; q_add--; } while (bf_cmpu(&R, B) >= 0) { - bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ); + if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ)) + goto fail; q_add++; } if (q_add != 0) { - bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ); + if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ)) + goto fail; + } + if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0, + radixl, radixl_bits)) + goto fail; + if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0, + radixl, radixl_bits)) { + fail: + bf_delete(&Q); + bf_delete(&R); + return -1; } - bf_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); } + return 0; } -static void bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl) +/* return 0 if OK != 0 if memory error */ +static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl) { bf_context_t *s = r->ctx; limb_t r_len; bf_t *pow_tab; - int i, pow_tab_len; + int i, pow_tab_len, ret; r_len = r->len; pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */ pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); + if (!pow_tab) + return -1; for(i = 0; i < pow_tab_len; i++) bf_init(r->ctx, &pow_tab[i]); - bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl, - ceil_log2(radixl)); + ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl, + ceil_log2(radixl)); for(i = 0; i < pow_tab_len; i++) { bf_delete(&pow_tab[i]); } bf_free(s, pow_tab); + return ret; } /* a must be >= 0. 'P' is the wanted number of digits in radix 'radix'. 'r' is the mantissa represented as an integer. *pE - contains the exponent. */ -static void 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) + 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; @@ -3094,8 +3496,7 @@ static void bf_convert_to_radix(bf_t *r, slimb_t *pE, if (a->len == 0) { /* zero case */ *pE = 0; - bf_set(r, a); - return; + return bf_set(r, a); } if (is_fixed_exponent) { @@ -3121,11 +3522,16 @@ static void bf_convert_to_radix(bf_t *r, slimb_t *pE, 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); + ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); if (!e_sign) - ret |= bf_mul(r, r, a, prec + extra_bits, BF_RNDN); + ret |= bf_mul(r, r, a, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); else - ret |= bf_div(r, a, r, prec + extra_bits, BF_RNDN); + ret |= bf_div(r, a, r, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + if (ret & BF_ST_MEM_ERROR) + return BF_ST_MEM_ERROR; /* if the result is not exact, check that it can be safely rounded to an integer */ if ((ret & BF_ST_INEXACT) && @@ -3134,16 +3540,22 @@ static void bf_convert_to_radix(bf_t *r, slimb_t *pE, ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); continue; } else { - bf_rint(r, BF_PREC_INF, rnd_mode); + 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 an fast approximate test first ? */ + /* XXX: do a fast approximate test first ? */ bf_init(r->ctx, B); - bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ); + 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) @@ -3152,6 +3564,7 @@ static void bf_convert_to_radix(bf_t *r, slimb_t *pE, E++; } *pE = E; + return 0; } static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) @@ -3196,19 +3609,35 @@ static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len) } } -/* 'a' must be an integer. A dot is added before the 'dot_pos' - digit. dot_pos = n_digits does not display the dot. 0 <= dot_pos <= +/* '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) + 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 ((radix & (radix - 1)) != 0) { + + if (is_dec) { + digits_per_limb = LIMB_DIGITS; + a = (bf_t *)a1; + radix_bits = 0; + pos = a->len; + pos_incr = 1; + first_buf_pos = 0; + } else if ((radix & (radix - 1)) == 0) { + a = (bf_t *)a1; + radix_bits = ceil_log2(radix); + digits_per_limb = LIMB_BITS / radix_bits; + pos_incr = digits_per_limb * radix_bits; + /* digits are aligned relative to the radix point */ + pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits); + first_buf_pos = 0; + } else { limb_t n, radixl; digits_per_limb = digits_per_limb_table[radix - 2]; @@ -3216,19 +3645,18 @@ static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, 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); + if (bf_resize(a, n)) { + dbuf_set_error(s); + goto done; + } + if (bf_integer_to_radix(a, a1, radixl)) { + dbuf_set_error(s); + goto done; + } radix_bits = 0; pos = n; pos_incr = 1; first_buf_pos = pos * digits_per_limb - n_digits; - } 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; @@ -3257,6 +3685,7 @@ static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, buf_pos += l; i += l; } + done: if (a != a1) bf_delete(a); } @@ -3268,15 +3697,16 @@ static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size) } /* return the length in bytes. A trailing '\0' is added */ -size_t bf_ftoa(char **pbuf, const bf_t *a2, int radix, limb_t prec, - bf_flags_t flags) +static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, + limb_t prec, bf_flags_t flags, BOOL is_dec) { + bf_context_t *ctx = a2->ctx; DynBuf s_s, *s = &s_s; int radix_bits; // bf_print_str("ftoa", a2); // printf("radix=%d\n", radix); - dbuf_init2(s, a2->ctx, bf_dbuf_realloc); + dbuf_init2(s, ctx, bf_dbuf_realloc); if (a2->expn == BF_EXP_NAN) { dbuf_putstr(s, "NaN"); } else { @@ -3288,90 +3718,209 @@ size_t bf_ftoa(char **pbuf, const bf_t *a2, int radix, limb_t prec, else dbuf_putstr(s, "Inf"); } else { - int fmt; + int fmt, ret; slimb_t n_digits, n, i, n_max, n1; - bf_t a1_s, *a1; - bf_t a_s, *a = &a_s; + bf_t a1_s, *a1 = &a1_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); + bf_init(ctx, a1); if (fmt == BF_FTOA_FORMAT_FRAC) { - size_t pos, start; - /* 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; - bf_convert_to_radix(a1, &n1, a, radix, n_digits, - flags & BF_RND_MASK, TRUE); - start = s->size; - output_digits(s, a1, radix, n_digits, n); - /* 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 { - 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; + if (is_dec || radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; +#ifdef USE_BF_DEC + if (is_dec) { + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = a1->expn; + } else +#endif + { + if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = ceil_div(a1->expn, radix_bits); } - 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. */ - 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; - bf_convert_to_radix(a1, &n, a, radix, n_digits, - flags & BF_RND_MASK, FALSE); - /* convert back to a number and compare */ - bf_mul_pow_radix(b, a1, radix, n - n_digits, - prec, - (flags & ~BF_RND_MASK) | - BF_RNDN); - if (bf_cmpu(b, a) == 0) { - n_digits_max = n_digits; - } else { - n_digits_min = n_digits + 1; + if (flags & BF_FTOA_ADD_PREFIX) { + if (radix == 16) + dbuf_putstr(s, "0x"); + else if (radix == 8) + dbuf_putstr(s, "0o"); + else if (radix == 2) + dbuf_putstr(s, "0b"); + } + if (a1->expn == BF_EXP_ZERO) { + dbuf_putstr(s, "0"); + if (prec > 0) { + dbuf_putstr(s, "."); + for(i = 0; i < prec; i++) { + dbuf_putc(s, '0'); } } - bf_delete(b); - n_digits = n_digits_max; + } else { + n_digits = prec + n; + if (n <= 0) { + /* 0.x */ + dbuf_putstr(s, "0."); + for(i = 0; i < -n; i++) { + dbuf_putc(s, '0'); + } + if (n_digits > 0) { + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + } + } else { + output_digits(s, a1, radix, n_digits, n, is_dec); + } + } + } else { + size_t pos, start; + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + /* one more digit for the rounding */ + n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE); + n_digits = n + prec; + n1 = n; + if (bf_convert_to_radix(a1, &n1, a, radix, n_digits, + flags & BF_RND_MASK, TRUE)) + goto fail1; + start = s->size; + output_digits(s, a1, radix, n_digits, n, is_dec); + /* remove leading zeros because we allocated one more digit */ + pos = start; + while ((pos + 1) < s->size && s->buf[pos] == '0' && + s->buf[pos + 1] != '.') + pos++; + if (pos > start) { + memmove(s->buf + start, s->buf + pos, s->size - pos); + s->size -= (pos - start); + } + } + } else { +#ifdef USE_BF_DEC + if (is_dec) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + /* prec is ignored */ + prec = n_digits = a1->len * LIMB_DIGITS; + /* remove the trailing zero digits */ + while (n_digits > 1 && + get_digit(a1->tab, a1->len, prec - n_digits) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = a1->expn; + } else +#endif + if (radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + slimb_t prec_bits; + n_digits = prec; + n_max = n_digits; + /* align to the radix point */ + prec_bits = prec * radix_bits - + smod(-a1->expn, radix_bits); + if (bf_round(a1, prec_bits, + (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + limb_t digit_mask; + slimb_t pos; + /* position of the digit before the most + significant digit in bits */ + pos = a1->len * LIMB_BITS + + smod(-a1->expn, radix_bits); + n_digits = ceil_div(pos, radix_bits); + /* remove the trailing zero digits */ + digit_mask = ((limb_t)1 << radix_bits) - 1; + while (n_digits > 1 && + (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = ceil_div(a1->expn, radix_bits); + } else { + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + } else { + slimb_t n_digits_max, n_digits_min; + + assert(prec != BF_PREC_INF); + n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE); + /* max number of digits for non exponential + notation. The rational is to have the same rule + as JS i.e. n_max = 21 for 64 bit float in base 10. */ + n_max = n_digits + 4; + if (fmt == BF_FTOA_FORMAT_FREE_MIN) { + bf_t b_s, *b = &b_s; + + /* find the minimum number of digits by + dichotomy. */ + /* XXX: inefficient */ + n_digits_max = n_digits; + n_digits_min = 1; + bf_init(ctx, b); + while (n_digits_min < n_digits_max) { + n_digits = (n_digits_min + n_digits_max) / 2; + if (bf_convert_to_radix(a1, &n, a, radix, n_digits, + flags & BF_RND_MASK, FALSE)) { + bf_delete(b); + goto fail1; + } + /* convert back to a number and compare */ + ret = bf_mul_pow_radix(b, a1, radix, n - n_digits, + prec, + (flags & ~BF_RND_MASK) | + BF_RNDN); + if (ret & BF_ST_MEM_ERROR) { + bf_delete(b); + goto fail1; + } + if (bf_cmpu(b, a) == 0) { + n_digits_max = n_digits; + } else { + n_digits_min = n_digits + 1; + } + } + bf_delete(b); + n_digits = n_digits_max; + } + } + if (bf_convert_to_radix(a1, &n, a, radix, n_digits, + flags & BF_RND_MASK, FALSE)) { + fail1: + bf_delete(a1); + goto fail; } } - bf_convert_to_radix(a1, &n, a, radix, n_digits, - flags & BF_RND_MASK, FALSE); if (a1->expn == BF_EXP_ZERO && fmt != BF_FTOA_FORMAT_FIXED && !(flags & BF_FTOA_FORCE_EXP)) { @@ -3392,7 +3941,7 @@ size_t bf_ftoa(char **pbuf, const bf_t *a2, int radix, limb_t prec, n <= -6 || n > n_max) { const char *fmt; /* exponential notation */ - output_digits(s, a1, radix, n_digits, 1); + 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; @@ -3413,15 +3962,15 @@ size_t bf_ftoa(char **pbuf, const bf_t *a2, int radix, limb_t prec, for(i = 0; i < -n; i++) { dbuf_putc(s, '0'); } - output_digits(s, a1, radix, n_digits, n_digits); + 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); + 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); + output_digits(s, a1, radix, n_digits, n, is_dec); } } } @@ -3430,8 +3979,22 @@ size_t bf_ftoa(char **pbuf, const bf_t *a2, int radix, limb_t prec, } } dbuf_putc(s, '\0'); - *pbuf = (char *)s->buf; - return s->size - 1; + if (dbuf_error(s)) + goto fail; + if (plen) + *plen = s->size - 1; + return (char *)s->buf; + fail: + bf_free(ctx, s->buf); + if (plen) + *plen = 0; + return NULL; +} + +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags) +{ + return bf_ftoa_internal(plen, a, radix, prec, flags, FALSE); } /***************************************************************/ @@ -3578,9 +4141,9 @@ static void bf_const_pi_internal(bf_t *Q, limb_t prec) bf_add(&P, &G, &P, prec1, BF_RNDN); bf_div(Q, Q, &P, prec1, BF_RNDF); - bf_set_ui(&P, CHUD_C / 64); - bf_rsqrt(&G, &P, prec1); - bf_mul_ui(&G, &G, (uint64_t)CHUD_C * CHUD_C / (8 * 12), 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); @@ -3588,7 +4151,7 @@ static void bf_const_pi_internal(bf_t *Q, limb_t prec) 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)) + void (*func)(bf_t *res, limb_t prec), int sign) { limb_t ziv_extra_bits, prec1; @@ -3604,6 +4167,7 @@ static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags, prec1 = c->prec; } bf_set(T, &c->val); + T->sign = sign; if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) { /* and more precision and retry */ ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); @@ -3623,13 +4187,20 @@ static void bf_const_free(BFConstCache *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); + return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0); +} + +/* return rounded pi * (1 - 2 * sign) */ +static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = T->ctx; + return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal, + sign); } int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags) { - bf_context_t *s = T->ctx; - return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal); + return bf_const_pi_signed(T, 0, prec, flags); } void bf_clear_cache(bf_context_t *s) @@ -3663,10 +4234,10 @@ static int bf_ziv_rounding(bf_t *r, const bf_t *a, for(;;) { prec1 = prec + ziv_extra_bits; ret = f(r, a, prec1, opaque); - if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW)) { - /* should never happen because it indicates the - rounding cannot be done correctly, but we do not - catch all the cases */ + 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 */ @@ -3679,9 +4250,29 @@ static int bf_ziv_rounding(bf_t *r, const bf_t *a, break; } ziv_extra_bits = ziv_extra_bits * 2; + // printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits); } } - return bf_round(r, prec, flags) | ret; + if (r->len == 0) + return ret; + else + return __bf_round(r, prec, flags, r->len, ret); +} + +/* add (1 - 2*e_sign) * 2^e */ +static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign, + limb_t prec, int flags) +{ + bf_t T_s, *T = &T_s; + int ret; + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_init(a->ctx, T); + bf_set_ui(T, 1); + T->sign = e_sign; + T->expn += e; + ret = bf_add(r, r, T, prec, flags); + bf_delete(T); + return ret; } /* Compute the exponential using faithful rounding at precision 'prec'. @@ -3746,18 +4337,70 @@ static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) /* undo the range reduction */ for(i = 0; i < K; i++) { - bf_mul(r, r, r, prec1, BF_RNDN); + bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); } /* undo the argument reduction */ - bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ); + bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP); return BF_ST_INEXACT; } +/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */ +static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r, + const bf_t *a_low, const bf_t *a_high, + limb_t prec, bf_flags_t flags) +{ + bf_t T_s, *T = &T_s; + bf_t log2_s, *log2 = &log2_s; + slimb_t e_min, e_max; + + if (a_high->expn <= 0) + return 0; + + e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_max + 3; + if (flags & BF_FLAG_SUBNORMAL) + e_min -= (prec - 1); + + bf_init(s, T); + bf_init(s, log2); + bf_const_log2(log2, LIMB_BITS, BF_RNDU); + bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU); + /* a_low > e_max * log(2) implies exp(a) > e_max */ + if (bf_cmp_lt(T, a_low) > 0) { + /* overflow */ + bf_delete(T); + bf_delete(log2); + return bf_set_overflow(r, 0, prec, flags); + } + /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */ + bf_const_log2(log2, LIMB_BITS, BF_RNDD); + bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD); + if (bf_cmp_lt(a_high, T)) { + int rnd_mode = flags & BF_RND_MASK; + + /* underflow */ + bf_delete(T); + bf_delete(log2); + if (rnd_mode == BF_RNDU) { + /* set the smallest value */ + bf_set_ui(r, 1); + r->expn = e_min; + } else { + bf_set_zero(r, 0); + } + return BF_ST_UNDERFLOW | BF_ST_INEXACT; + } + bf_delete(log2); + bf_delete(T); + return 0; +} + int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { bf_context_t *s = r->ctx; + int ret; assert(r != a); if (a->len == 0) { if (a->expn == BF_EXP_NAN) { @@ -3773,48 +4416,15 @@ int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) 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); + ret = check_exp_underflow_overflow(s, r, a, a, prec, flags); + if (ret) + return ret; + if (a->expn < 0 && (-a->expn) >= (prec + 2)) { + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags); } - + return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL); } @@ -3957,7 +4567,6 @@ int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) } /* 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; @@ -3968,15 +4577,17 @@ static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque) 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_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + if (bf_is_nan(T)) + bf_set_nan(r); + else + bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */ bf_delete(T); return BF_ST_INEXACT; } /* x and y finite, x > 0, y integer and y fits on one limb */ -/* 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; @@ -3991,11 +4602,11 @@ static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque) 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); + ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP); if (y->sign) { bf_init(s, T); bf_set_ui(T, 1); - ret |= bf_div(r, T, r, prec1, BF_RNDN); + ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); bf_delete(T); } return ret; @@ -4061,7 +4672,7 @@ int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) 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) && + if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) && (y->expn >= BF_EXP_INF)) { bf_set_nan(r); } else if (cmp_x_abs_1 == 0 && @@ -4118,70 +4729,95 @@ int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) 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; - } + /* check the overflow/underflow cases */ + { + bf_t al_s, *al = &al_s; + bf_t ah_s, *ah = &ah_s; + limb_t precl = LIMB_BITS; + + bf_init(s, al); + bf_init(s, ah); + /* compute bounds of log(abs(x)) * y with a low precision */ + /* XXX: compute bf_log() once */ + /* XXX: add a fast test before this slow test */ + bf_log(al, T, precl, BF_RNDD); + bf_log(ah, T, precl, BF_RNDU); + bf_mul(al, al, y, precl, BF_RNDD ^ y->sign); + bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign); + ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags); + bf_delete(al); + bf_delete(ah); + if (ret) + goto done; + } + + if (y_is_int) { + slimb_t T_bits, e; + int_pow: + T_bits = T->expn - bf_get_exp_min(T); + if (T_bits == 1) { + /* pow(2^b, y) = 2^(b*y) */ + bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, T, 0); + bf_set_ui(r, 1); + ret = bf_mul_2exp(r, e, prec, flags); + } else if (prec == BF_PREC_INF) { + slimb_t y1; + /* specific case for infinite precision (integer case) */ + bf_get_limb(&y1, y, 0); + assert(!y->sign); + /* x must be an integer, so abs(x) >= 2 */ + if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) { + bf_delete(T); + return bf_set_overflow(r, 0, BF_PREC_INF, flags); + } + ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ); + } else { + if (y->expn <= 31) { + /* small enough power: use exponentiation in all cases */ + } else if (y->sign) { + /* cannot be exact */ + goto general_case; + } else { + if (rnd_mode == BF_RNDF) + goto general_case; /* no need to track exact results */ + /* see if the result has a chance to be exact: + if x=a*2^b (a odd), x^y=a^y*2^(b*y) + x^y needs a precision of at least floor_log2(a)*y bits + */ + bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, r, 0); + if (prec < e) + goto general_case; + } + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y); + } + } else { + if (rnd_mode != BF_RNDF) { + bf_t *y1; + if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) { + /* the problem is reduced to a power to an integer */ +#if 0 + printf("\nn=%" PRId64 "\n", -(int64_t)y_emin); + bf_print_str("T", T); + bf_print_str("r", r); +#endif + bf_set(T, r); + y1 = &ytmp_s; + y1->tab = y->tab; + y1->len = y->len; + y1->sign = y->sign; + y1->expn = y->expn - y_emin; + y = y1; + goto int_pow; + } + } + general_case: + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); } - general_case: - ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); } + done: bf_delete(T); r->sign = r_sign; return ret; @@ -4202,7 +4838,7 @@ static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1) bf_delete(T); } -int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) +static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) { bf_context_t *s1 = a->ctx; bf_t T_s, *T = &T_s; @@ -4212,27 +4848,6 @@ int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) 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); @@ -4252,10 +4867,10 @@ int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) slimb_t cancel; cancel = 0; for(;;) { - prec2 = prec1 + cancel; + prec2 = prec1 + a->expn + cancel; bf_const_pi(U, prec2, BF_RNDF); bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ); - bf_remquo(&mod, T, a, U, prec2, BF_RNDN); + bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN); // printf("T.expn=%ld prec2=%ld\n", T->expn, prec2); if (mod == 0 || (T->expn != BF_EXP_ZERO && (T->expn + prec2) >= (prec1 - 1))) @@ -4327,6 +4942,30 @@ static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_ui(r, 1); + return 0; + } + } + + /* small argument case: result = 1+r(x) with r(x) = -x^2/2 + + O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = 2 * a->expn - 1; + if (e < -(prec + 2)) { + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, e, 1, prec, flags); + } + } + return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL); } @@ -4337,15 +4976,6 @@ static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) 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); @@ -4359,6 +4989,26 @@ static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) } } + /* small argument case: result = x+r(x) with r(x) = -x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL); +} + +static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + limb_t prec1; + /* XXX: precision analysis */ prec1 = prec + 8; bf_init(s, T); @@ -4370,6 +5020,31 @@ static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + /* small argument case: result = x+r(x) with r(x) = x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL); } @@ -4387,50 +5062,15 @@ static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, 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); + // printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1); - if (cmp_1 > 0) { + bf_init(s, T); + cmp_1 = (a->expn >= 1); /* a >= 1 */ + if (cmp_1) { bf_set_ui(T, 1); bf_div(T, T, a, prec1, BF_RNDN); } else { @@ -4498,6 +5138,47 @@ static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + /* -PI/2 or PI/2 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res == 0) { + /* short cut: abs(a) == 1 -> +/-pi/4 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + + /* small argument case: result = x+r(x) with r(x) = -x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE); } @@ -4551,38 +5232,6 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) 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) */ @@ -4594,6 +5243,7 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) prec2 = BF_PREC_INF; else prec2 = prec1; + bf_init(s, T); bf_mul(T, a, a, prec2, BF_RNDN); bf_neg(T); bf_add_si(T, T, 1, prec2, BF_RNDN); @@ -4609,14 +5259,1980 @@ static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } + + /* small argument case: result = x+r(x) with r(x) = x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE); } int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) { + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_const_pi(r, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (res == 0 && a->sign == 0) { + bf_set_zero(r, 0); + return 0; + } + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE); } +/***************************************************************/ +/* decimal floating point numbers */ + +#ifdef USE_BF_DEC + +#define adddq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 += (a0); \ + r1 += (a1) + (r0 < __t); \ + } while (0) + +#define subdq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 -= (a0); \ + r1 -= (a1) + (r0 > __t); \ + } while (0) + +#if LIMB_BITS == 64 + +/* Note: we assume __int128 is available */ +#define muldq(r1, r0, a, b) \ + do { \ + unsigned __int128 __t; \ + __t = (unsigned __int128)(a) * (unsigned __int128)(b); \ + r0 = __t; \ + r1 = __t >> 64; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + unsigned __int128 __t; \ + limb_t __b = (b); \ + __t = ((unsigned __int128)(a1) << 64) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#else + +#define muldq(r1, r0, a, b) \ + do { \ + uint64_t __t; \ + __t = (uint64_t)(a) * (uint64_t)(b); \ + r0 = __t; \ + r1 = __t >> 32; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + uint64_t __t; \ + limb_t __b = (b); \ + __t = ((uint64_t)(a1) << 32) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#endif /* LIMB_BITS != 64 */ + +static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift) +{ + if (shift != 0) + low = (low >> shift) | (high << (LIMB_BITS - shift)); + return low; +} + +static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift) +{ + if (shift != 0) + return (a1 << shift) | (a0 >> (LIMB_BITS - shift)); + else + return a1; +} + +#if LIMB_DIGITS == 19 + +/* WARNING: hardcoded for b = 1e19. It is assumed that: + 0 <= a1 < 2^63 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \ + __a0 = a0;\ + __a1 = a1;\ + __t0 = __a1;\ + __t0 = shld(__t0, __a0, 1);\ + muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \ + muldq(__t1, __t0, q, __b);\ + subdq(__a1, __a0, __t1, __t0);\ + subdq(__a1, __a0, 1, __b * 2); \ + __t0 = (slimb_t)__a1 >> 1; \ + q += 2 + __t0;\ + adddq(__a1, __a0, 0, __b & __t0);\ + q += __a1; \ + __a0 += __b & __a1; \ + r = __a0;\ +} while(0) + +#elif LIMB_DIGITS == 9 + +/* WARNING: hardcoded for b = 1e9. It is assumed that: + 0 <= a1 < 2^29 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint32_t __t0, __t1, __b = BF_DEC_BASE; \ + __t0 = a1;\ + __t1 = a0;\ + __t0 = (__t0 << 3) | (__t1 >> (32 - 3)); \ + muldq(q, __t1, __t0, 2305843009U);\ + r = a0 - q * __b;\ + __t1 = (r >= __b);\ + q += __t1;\ + if (__t1)\ + r -= __b;\ +} while(0) + +#endif + +/* fast integer division by a fixed constant */ + +typedef struct FastDivData { + limb_t m1; /* multiplier */ + int8_t shift1; + int8_t shift2; +} FastDivData; + +/* From "Division by Invariant Integers using Multiplication" by + Torborn Granlund and Peter L. Montgomery */ +/* d must be != 0 */ +static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d) +{ + int l; + limb_t q, r, m1; + if (d == 1) + l = 0; + else + l = 64 - clz64(d - 1); + divdq(q, r, ((limb_t)1 << l) - d, 0, d); + (void)r; + m1 = q + 1; + // printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1); + s->m1 = m1; + s->shift1 = l; + if (s->shift1 > 1) + s->shift1 = 1; + s->shift2 = l - 1; + if (s->shift2 < 0) + s->shift2 = 0; +} + +static inline limb_t fast_udiv(limb_t a, const FastDivData *s) +{ + limb_t t0, t1; + muldq(t1, t0, s->m1, a); + t0 = (a - t1) >> s->shift1; + return (t1 + t0) >> s->shift2; +} + +/* contains 10^i */ +const limb_t mp_pow_dec[LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +#if LIMB_BITS == 64 + 10000000000U, + 100000000000U, + 1000000000000U, + 10000000000000U, + 100000000000000U, + 1000000000000000U, + 10000000000000000U, + 100000000000000000U, + 1000000000000000000U, + 10000000000000000000U, +#endif +}; + +/* precomputed from fast_udiv_init(10^i) */ +static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = { +#if LIMB_BITS == 32 + { 0x00000001, 0, 0 }, + { 0x9999999a, 1, 3 }, + { 0x47ae147b, 1, 6 }, + { 0x0624dd30, 1, 9 }, + { 0xa36e2eb2, 1, 13 }, + { 0x4f8b588f, 1, 16 }, + { 0x0c6f7a0c, 1, 19 }, + { 0xad7f29ac, 1, 23 }, + { 0x5798ee24, 1, 26 }, + { 0x12e0be83, 1, 29 }, +#else + { 0x0000000000000001, 0, 0 }, + { 0x999999999999999a, 1, 3 }, + { 0x47ae147ae147ae15, 1, 6 }, + { 0x0624dd2f1a9fbe77, 1, 9 }, + { 0xa36e2eb1c432ca58, 1, 13 }, + { 0x4f8b588e368f0847, 1, 16 }, + { 0x0c6f7a0b5ed8d36c, 1, 19 }, + { 0xad7f29abcaf48579, 1, 23 }, + { 0x5798ee2308c39dfa, 1, 26 }, + { 0x12e0be826d694b2f, 1, 29 }, + { 0xb7cdfd9d7bdbab7e, 1, 33 }, + { 0x5fd7fe17964955fe, 1, 36 }, + { 0x19799812dea11198, 1, 39 }, + { 0xc25c268497681c27, 1, 43 }, + { 0x6849b86a12b9b01f, 1, 46 }, + { 0x203af9ee756159b3, 1, 49 }, + { 0xcd2b297d889bc2b7, 1, 53 }, + { 0x70ef54646d496893, 1, 56 }, + { 0x2725dd1d243aba0f, 1, 59 }, + { 0xd83c94fb6d2ac34d, 1, 63 }, +#endif +}; + +/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */ +static inline limb_t fast_shr_dec(limb_t a, int shift) +{ + return fast_udiv(a, &mp_pow_div[shift]); +} + +/* division and remainder by 10^shift */ +#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift] + +limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, a, v; + + k=carry; + for(i=0;i v; + if (k) + a += base; + res[i] = a; + } + return k; +} + +limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, v, a; + + k=b; + for(i=0;i v; + if (k) + a += base; + tab[i]=a; + if (k == 0) + break; + } + return k; +} + +/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */ +limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b, limb_t l) +{ + mp_size_t i; + limb_t t0, t1, r; + + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add + to the high word */ +limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + mp_size_t i; + limb_t l, t0, t1, r; + + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + adddq(t1, t0, 0, tabr[i]); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to + substract to the high word. */ +limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t l, t0, t1, r, a, v, c; + + /* XXX: optimize */ + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + v = tabr[i]; + a = v - r; + c = a > v; + if (c) + a += base; + /* never bigger than base because r = 0 when l = base - 1 */ + l += c; + tabr[i] = a; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +void mp_mul_basecase_dec(limb_t *result, + const limb_t *op1, mp_size_t op1_size, + const limb_t *op2, mp_size_t op2_size) +{ + mp_size_t i; + limb_t r; + + result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0); + + for(i=1;i> 1; + if (r) + r = base_div2; + for(i = na - 1; i >= 0; i--) { + t0 = taba[i]; + tabr[i] = (t0 >> 1) + r; + r = 0; + if (t0 & 1) + r = base_div2; + } + if (r) + r = 1; + } else +#endif + if (na >= UDIV1NORM_THRESHOLD) { + shift = clz(b); + if (shift == 0) { + /* normalized case: b >= 2^(LIMB_BITS-1) */ + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + q = udiv1norm(&r, t1, t0, b, b_inv); + tabr[i] = q; + } + } else { + limb_t b_inv; + b <<= shift; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift)); + t0 <<= shift; + q = udiv1norm(&r, t1, t0, b, b_inv); + r >>= shift; + tabr[i] = q; + } + } + } else { + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + divdq(q, r, t1, t0, b); + tabr[i] = q; + } + } + return r; +} + +static __maybe_unused void mp_print_str_dec(const char *str, + const limb_t *tab, slimb_t n) +{ + slimb_t i; + printf("%s=", str); + for(i = n - 1; i >= 0; i--) { + if (i != n - 1) + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +static __maybe_unused void mp_print_str_h_dec(const char *str, + const limb_t *tab, slimb_t n, + limb_t high) +{ + slimb_t i; + printf("%s=", str); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, high); + for(i = n - 1; i >= 0; i--) { + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +//#define DEBUG_DIV_SLOW + +#define DIV_STATIC_ALLOC_LEN 16 + +/* return q = a / b and r = a % b. + + taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1] + must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1] + >= B / 2. + + The remainder is is returned in taba and contains nb libms. tabq + contains na - nb + 1 limbs. No overlap is permitted. + + Running time of the standard method: (na - nb + 1) * nb + Return 0 if OK, -1 if memory alloc error +*/ +/* XXX: optimize */ +static int mp_div_dec(bf_context_t *s, limb_t *tabq, + limb_t *taba, mp_size_t na, + const limb_t *tabb1, mp_size_t nb) +{ + limb_t base = BF_DEC_BASE; + limb_t r, mult, t0, t1, a, c, q, v, *tabb; + mp_size_t i, j; + limb_t static_tabb[DIV_STATIC_ALLOC_LEN]; + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("a", taba, na); + mp_print_str_dec("b", tabb1, nb); +#endif + + /* normalize tabb */ + r = tabb1[nb - 1]; + assert(r != 0); + i = na - nb; + if (r >= BF_DEC_BASE / 2) { + mult = 1; + tabb = (limb_t *)tabb1; + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[i + j] != tabb[j]) { + if (taba[i + j] < tabb[j]) + q = 0; + break; + } + } + tabq[i] = q; + if (q) { + mp_sub_dec(taba + i, taba + i, tabb, nb, 0); + } + i--; + } else { + mult = base / (r + 1); + if (likely(nb <= DIV_STATIC_ALLOC_LEN)) { + tabb = static_tabb; + } else { + tabb = bf_malloc(s, sizeof(limb_t) * nb); + if (!tabb) + return -1; + } + mp_mul1_dec(tabb, tabb1, nb, mult, 0); + taba[na] = mp_mul1_dec(taba, taba, na, mult, 0); + } + +#ifdef DEBUG_DIV_SLOW + printf("mult=" FMT_LIMB "\n", mult); + mp_print_str_dec("a_norm", taba, na + 1); + mp_print_str_dec("b_norm", tabb, nb); +#endif + + for(; i >= 0; i--) { + if (unlikely(taba[i + nb] >= tabb[nb - 1])) { + /* XXX: check if it is really possible */ + q = base - 1; + } else { + muldq(t1, t0, taba[i + nb], base); + adddq(t1, t0, 0, taba[i + nb - 1]); + divdq(q, r, t1, t0, tabb[nb - 1]); + } + // printf("i=%d q1=%ld\n", i, q); + + r = mp_sub_mul1_dec(taba + i, tabb, nb, q); + // mp_dump("r1", taba + i, nb, bd); + // printf("r2=%ld\n", r); + + v = taba[i + nb]; + a = v - r; + c = a > v; + if (c) + a += base; + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add_dec(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == base) { + break; + } + } + } + } + tabq[i] = q; + } + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("q", tabq, na - nb + 1); + mp_print_str_dec("r", taba, nb); +#endif + + /* remove the normalization */ + if (mult != 1) { + mp_div1_dec(taba, taba, nb, mult, 0); + if (unlikely(tabb != static_tabb)) + bf_free(s, tabb); + } + return 0; +} + +/* divide by 10^shift */ +static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t high) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + fast_shr_rem_dec(q, r, a, shift); + tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift]; + l = r; + } + return l; +} + +/* multiply by 10^shift */ +static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t low) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = low; + for(i = 0; i < n; i++) { + a = tab[i]; + fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift); + tab_r[i] = r * mp_pow_dec[shift] + l; + l = q; + } + return l; +} + +static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba) +{ + int k; + dlimb_t a, b, r; + limb_t taba1[2], s, r0, r1; + + /* convert to binary and normalize */ + a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0]; + k = clz(a >> LIMB_BITS) & ~1; + b = a << k; + taba1[0] = b; + taba1[1] = b >> LIMB_BITS; + mp_sqrtrem2(&s, taba1); + s >>= (k >> 1); + /* convert the remainder back to decimal */ + r = a - (dlimb_t)s * (dlimb_t)s; + divdq_base(r1, r0, r >> LIMB_BITS, r); + taba[0] = r0; + tabs[0] = s; + return r1; +} + +//#define DEBUG_SQRTREM_DEC + +/* tmp_buf must contain (n / 2 + 1 limbs) */ +static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n, + limb_t *tmp_buf) +{ + limb_t l, h, rh, ql, qh, c, i; + + if (n == 1) + return mp_sqrtrem2_dec(tabs, taba); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("a", taba, 2 * n); +#endif + l = n / 2; + h = n - l; + qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("s1", tabs + l, h); + mp_print_str_h_dec("r1", taba + 2 * l, h, qh); + mp_print_str_h_dec("r2", taba + l, n, qh); +#endif + + /* the remainder is in taba + 2 * l. Its high bit is in qh */ + if (qh) { + mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); + } + /* instead of dividing by 2*s, divide by s (which is normalized) + and update q and r */ + mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h); + qh += tmp_buf[l]; + for(i = 0; i < l; i++) + tabs[i] = tmp_buf[i]; + ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1); + qh = qh >> 1; /* 0 or 1 */ + if (ql) + rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0); + else + rh = 0; +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_h_dec("q", tabs, l, qh); + mp_print_str_h_dec("u", taba + l, h, rh); +#endif + + mp_add_ui_dec(tabs + l, qh, h); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("s2", tabs, n); +#endif + + /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ + /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ + if (qh) { + c = qh; + } else { + mp_mul_basecase_dec(taba + n, tabs, l, tabs, l); + c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0); + } + rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l); + if ((slimb_t)rh < 0) { + mp_sub_ui_dec(tabs, 1, n); + rh += mp_add_mul1_dec(taba, tabs, n, 2); + rh += mp_add_ui_dec(taba, 1, n); + } + return rh; +} + +/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s, + r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n + limbs. r is returned in the lower n limbs of taba. Its r[n] is the + returned value of the function. */ +int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) +{ + limb_t tmp_buf1[8]; + limb_t *tmp_buf; + mp_size_t n2; + n2 = n / 2 + 1; + if (n2 <= countof(tmp_buf1)) { + tmp_buf = tmp_buf1; + } else { + tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); + if (!tmp_buf) + return -1; + } + taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf); + if (tmp_buf != tmp_buf1) + bf_free(s, tmp_buf); + return 0; +} + +/* return the number of leading zero digits, from 0 to LIMB_DIGITS */ +static int clz_dec(limb_t a) +{ + if (a == 0) + return LIMB_DIGITS; + switch(LIMB_BITS - 1 - clz(a)) { + case 0: /* 1-1 */ + return LIMB_DIGITS - 1; + case 1: /* 2-3 */ + return LIMB_DIGITS - 1; + case 2: /* 4-7 */ + return LIMB_DIGITS - 1; + case 3: /* 8-15 */ + if (a < 10) + return LIMB_DIGITS - 1; + else + return LIMB_DIGITS - 2; + case 4: /* 16-31 */ + return LIMB_DIGITS - 2; + case 5: /* 32-63 */ + return LIMB_DIGITS - 2; + case 6: /* 64-127 */ + if (a < 100) + return LIMB_DIGITS - 2; + else + return LIMB_DIGITS - 3; + case 7: /* 128-255 */ + return LIMB_DIGITS - 3; + case 8: /* 256-511 */ + return LIMB_DIGITS - 3; + case 9: /* 512-1023 */ + if (a < 1000) + return LIMB_DIGITS - 3; + else + return LIMB_DIGITS - 4; + case 10: /* 1024-2047 */ + return LIMB_DIGITS - 4; + case 11: /* 2048-4095 */ + return LIMB_DIGITS - 4; + case 12: /* 4096-8191 */ + return LIMB_DIGITS - 4; + case 13: /* 8192-16383 */ + if (a < 10000) + return LIMB_DIGITS - 4; + else + return LIMB_DIGITS - 5; + case 14: /* 16384-32767 */ + return LIMB_DIGITS - 5; + case 15: /* 32768-65535 */ + return LIMB_DIGITS - 5; + case 16: /* 65536-131071 */ + if (a < 100000) + return LIMB_DIGITS - 5; + else + return LIMB_DIGITS - 6; + case 17: /* 131072-262143 */ + return LIMB_DIGITS - 6; + case 18: /* 262144-524287 */ + return LIMB_DIGITS - 6; + case 19: /* 524288-1048575 */ + if (a < 1000000) + return LIMB_DIGITS - 6; + else + return LIMB_DIGITS - 7; + case 20: /* 1048576-2097151 */ + return LIMB_DIGITS - 7; + case 21: /* 2097152-4194303 */ + return LIMB_DIGITS - 7; + case 22: /* 4194304-8388607 */ + return LIMB_DIGITS - 7; + case 23: /* 8388608-16777215 */ + if (a < 10000000) + return LIMB_DIGITS - 7; + else + return LIMB_DIGITS - 8; + case 24: /* 16777216-33554431 */ + return LIMB_DIGITS - 8; + case 25: /* 33554432-67108863 */ + return LIMB_DIGITS - 8; + case 26: /* 67108864-134217727 */ + if (a < 100000000) + return LIMB_DIGITS - 8; + else + return LIMB_DIGITS - 9; +#if LIMB_BITS == 64 + case 27: /* 134217728-268435455 */ + return LIMB_DIGITS - 9; + case 28: /* 268435456-536870911 */ + return LIMB_DIGITS - 9; + case 29: /* 536870912-1073741823 */ + if (a < 1000000000) + return LIMB_DIGITS - 9; + else + return LIMB_DIGITS - 10; + case 30: /* 1073741824-2147483647 */ + return LIMB_DIGITS - 10; + case 31: /* 2147483648-4294967295 */ + return LIMB_DIGITS - 10; + case 32: /* 4294967296-8589934591 */ + return LIMB_DIGITS - 10; + case 33: /* 8589934592-17179869183 */ + if (a < 10000000000) + return LIMB_DIGITS - 10; + else + return LIMB_DIGITS - 11; + case 34: /* 17179869184-34359738367 */ + return LIMB_DIGITS - 11; + case 35: /* 34359738368-68719476735 */ + return LIMB_DIGITS - 11; + case 36: /* 68719476736-137438953471 */ + if (a < 100000000000) + return LIMB_DIGITS - 11; + else + return LIMB_DIGITS - 12; + case 37: /* 137438953472-274877906943 */ + return LIMB_DIGITS - 12; + case 38: /* 274877906944-549755813887 */ + return LIMB_DIGITS - 12; + case 39: /* 549755813888-1099511627775 */ + if (a < 1000000000000) + return LIMB_DIGITS - 12; + else + return LIMB_DIGITS - 13; + case 40: /* 1099511627776-2199023255551 */ + return LIMB_DIGITS - 13; + case 41: /* 2199023255552-4398046511103 */ + return LIMB_DIGITS - 13; + case 42: /* 4398046511104-8796093022207 */ + return LIMB_DIGITS - 13; + case 43: /* 8796093022208-17592186044415 */ + if (a < 10000000000000) + return LIMB_DIGITS - 13; + else + return LIMB_DIGITS - 14; + case 44: /* 17592186044416-35184372088831 */ + return LIMB_DIGITS - 14; + case 45: /* 35184372088832-70368744177663 */ + return LIMB_DIGITS - 14; + case 46: /* 70368744177664-140737488355327 */ + if (a < 100000000000000) + return LIMB_DIGITS - 14; + else + return LIMB_DIGITS - 15; + case 47: /* 140737488355328-281474976710655 */ + return LIMB_DIGITS - 15; + case 48: /* 281474976710656-562949953421311 */ + return LIMB_DIGITS - 15; + case 49: /* 562949953421312-1125899906842623 */ + if (a < 1000000000000000) + return LIMB_DIGITS - 15; + else + return LIMB_DIGITS - 16; + case 50: /* 1125899906842624-2251799813685247 */ + return LIMB_DIGITS - 16; + case 51: /* 2251799813685248-4503599627370495 */ + return LIMB_DIGITS - 16; + case 52: /* 4503599627370496-9007199254740991 */ + return LIMB_DIGITS - 16; + case 53: /* 9007199254740992-18014398509481983 */ + if (a < 10000000000000000) + return LIMB_DIGITS - 16; + else + return LIMB_DIGITS - 17; + case 54: /* 18014398509481984-36028797018963967 */ + return LIMB_DIGITS - 17; + case 55: /* 36028797018963968-72057594037927935 */ + return LIMB_DIGITS - 17; + case 56: /* 72057594037927936-144115188075855871 */ + if (a < 100000000000000000) + return LIMB_DIGITS - 17; + else + return LIMB_DIGITS - 18; + case 57: /* 144115188075855872-288230376151711743 */ + return LIMB_DIGITS - 18; + case 58: /* 288230376151711744-576460752303423487 */ + return LIMB_DIGITS - 18; + case 59: /* 576460752303423488-1152921504606846975 */ + if (a < 1000000000000000000) + return LIMB_DIGITS - 18; + else + return LIMB_DIGITS - 19; +#endif + default: + return 0; + } +} + +/* for debugging */ +void bfdec_print_str(const char *str, const bfdec_t *a) +{ + slimb_t i; + printf("%s=", str); + + if (a->expn == BF_EXP_NAN) { + printf("NaN"); + } else { + if (a->sign) + putchar('-'); + if (a->expn == BF_EXP_ZERO) { + putchar('0'); + } else if (a->expn == BF_EXP_INF) { + printf("Inf"); + } else { + printf("0."); + for(i = a->len - 1; i >= 0; i--) + printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]); + printf("e%" PRId_LIMB, a->expn); + } + } + printf("\n"); +} + +/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */ +static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos) +{ + slimb_t pos; + limb_t v, q; + int shift; + + if (bit_pos < 0) + return 0; + pos = (limb_t)bit_pos / LIMB_DIGITS; + shift = (limb_t)bit_pos % LIMB_DIGITS; + fast_shr_rem_dec(q, v, r->tab[pos], shift + 1); + (void)q; + if (v != 0) + return 1; + pos--; + while (pos >= 0) { + if (r->tab[pos] != 0) + return 1; + pos--; + } + return 0; +} + +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos) +{ + slimb_t i; + int shift; + i = floor_div(pos, LIMB_DIGITS); + if (i < 0 || i >= len) + return 0; + shift = pos - i * LIMB_DIGITS; + return fast_shr_dec(tab[i], shift) % 10; +} + +#if 0 +static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos) +{ + limb_t a0, a1; + int shift; + slimb_t i; + + i = floor_div(pos, LIMB_DIGITS); + shift = pos - i * LIMB_DIGITS; + if (i >= 0 && i < len) + a0 = tab[i]; + else + a0 = 0; + if (shift == 0) { + return a0; + } else { + i++; + if (i >= 0 && i < len) + a1 = tab[i]; + else + a1 = 0; + return fast_shr_dec(a0, shift) + + fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) * + mp_pow_dec[shift]; + } +} +#endif + +/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */ +static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l, + slimb_t prec, int rnd_mode) +{ + int add_one, inexact; + limb_t digit1, digit0; + + // bfdec_print_str("get_rnd_add", r); + if (rnd_mode == BF_RNDF) { + digit0 = 1; /* faithful rounding does not honor the INEXACT flag */ + } else { + /* starting limb for bit 'prec + 1' */ + digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1)); + } + + /* get the digit at 'prec' */ + digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec); + inexact = (digit1 | digit0) != 0; + + add_one = 0; + switch(rnd_mode) { + case BF_RNDZ: + break; + case BF_RNDN: + if (digit1 == 5) { + if (digit0) { + add_one = 1; + } else { + /* round to even */ + add_one = + get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1; + } + } else if (digit1 > 5) { + add_one = 1; + } + break; + case BF_RNDD: + case BF_RNDU: + if (r->sign == (rnd_mode == BF_RNDD)) + add_one = inexact; + break; + case BF_RNDNA: + case BF_RNDF: + add_one = (digit1 >= 5); + break; + case BF_RNDA: + add_one = inexact; + break; + default: + abort(); + } + + if (inexact) + *pret |= BF_ST_INEXACT; + return add_one; +} + +/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is + assumed to have length 'l' (1 <= l <= r->len). prec1 can be + BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with + BF_ST_MEM_ERROR. + */ +static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) +{ + int shift, add_one, rnd_mode, ret; + slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; + + /* XXX: align to IEEE 754 2008 for decimal numbers ? */ + e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_range + 3; + e_max = e_range; + + if (flags & BF_FLAG_RADPNT_PREC) { + /* 'prec' is the precision after the decimal point */ + if (prec1 != BF_PREC_INF) + prec = r->expn + prec1; + else + prec = prec1; + } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + /* restrict the precision in case of potentially subnormal + result */ + assert(prec1 != BF_PREC_INF); + prec = prec1 - (e_min - r->expn); + } else { + prec = prec1; + } + + /* round to prec bits */ + rnd_mode = flags & BF_RND_MASK; + ret = 0; + add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode); + + if (prec <= 0) { + if (add_one) { + bfdec_resize(r, 1); /* cannot fail because r is non zero */ + r->tab[0] = BF_DEC_BASE / 10; + r->expn += 1 - prec; + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } else { + goto underflow; + } + } else if (add_one) { + limb_t carry; + + /* add one starting at digit 'prec - 1' */ + bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); + pos = bit_pos / LIMB_DIGITS; + carry = mp_pow_dec[bit_pos % LIMB_DIGITS]; + carry = mp_add_ui_dec(r->tab + pos, carry, l - pos); + if (carry) { + /* shift right by one digit */ + mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1); + r->expn++; + } + } + + /* check underflow */ + if (unlikely(r->expn < e_min)) { + if (flags & BF_FLAG_SUBNORMAL) { + /* if inexact, also set the underflow flag */ + if (ret & BF_ST_INEXACT) + ret |= BF_ST_UNDERFLOW; + } else { + underflow: + bfdec_set_zero(r, r->sign); + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } + } + + /* check overflow */ + if (unlikely(r->expn > e_max)) { + bfdec_set_inf(r, r->sign); + ret |= BF_ST_OVERFLOW | BF_ST_INEXACT; + return ret; + } + + /* keep the bits starting at 'prec - 1' */ + bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); + i = floor_div(bit_pos, LIMB_DIGITS); + if (i >= 0) { + shift = smod(bit_pos, LIMB_DIGITS); + if (shift != 0) { + r->tab[i] = fast_shr_dec(r->tab[i], shift) * + mp_pow_dec[shift]; + } + } else { + i = 0; + } + /* remove trailing zeros */ + while (r->tab[i] == 0) + i++; + if (i > 0) { + l -= i; + memmove(r->tab, r->tab + i, l * sizeof(limb_t)); + } + bfdec_resize(r, l); /* cannot fail */ + return ret; +} + +/* Cannot fail with BF_ST_MEM_ERROR. */ +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags) +{ + if (r->len == 0) + return 0; + return __bfdec_round(r, prec, flags, r->len); +} + +/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR. */ +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags) +{ + limb_t l, v; + int shift, ret; + + // bfdec_print_str("bf_renorm", r); + l = r->len; + while (l > 0 && r->tab[l - 1] == 0) + l--; + if (l == 0) { + /* zero */ + r->expn = BF_EXP_ZERO; + bfdec_resize(r, 0); /* cannot fail */ + ret = 0; + } else { + r->expn -= (r->len - l) * LIMB_DIGITS; + /* shift to have the MSB set to '1' */ + v = r->tab[l - 1]; + shift = clz_dec(v); + if (shift != 0) { + mp_shl_dec(r->tab, r->tab, l, shift, 0); + r->expn -= shift; + } + ret = __bfdec_round(r, prec1, flags, l); + } + // bf_print_str("r_final", r); + return ret; +} + +int bfdec_set_ui(bfdec_t *r, uint64_t v) +{ +#if LIMB_BITS == 32 + if (v >= BF_DEC_BASE * BF_DEC_BASE) { + if (bfdec_resize(r, 3)) + goto fail; + r->tab[0] = v % BF_DEC_BASE; + v /= BF_DEC_BASE; + r->tab[1] = v % BF_DEC_BASE; + r->tab[2] = v / BF_DEC_BASE; + r->expn = 3 * LIMB_DIGITS; + } else +#endif + if (v >= BF_DEC_BASE) { + if (bfdec_resize(r, 2)) + goto fail; + r->tab[0] = v % BF_DEC_BASE; + r->tab[1] = v / BF_DEC_BASE; + r->expn = 2 * LIMB_DIGITS; + } else { + if (bfdec_resize(r, 1)) + goto fail; + r->tab[0] = v; + r->expn = LIMB_DIGITS; + } + r->sign = 0; + return bfdec_normalize_and_round(r, BF_PREC_INF, 0); + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_set_si(bfdec_t *r, int64_t v) +{ + int ret; + if (v < 0) { + ret = bfdec_set_ui(r, -v); + r->sign = 1; + } else { + ret = bfdec_set_ui(r, v); + } + return ret; +} + +static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg) +{ + bf_context_t *s = r->ctx; + int is_sub, cmp_res, a_sign, b_sign, ret; + + a_sign = a->sign; + b_sign = b->sign ^ b_neg; + is_sub = a_sign ^ b_sign; + cmp_res = bfdec_cmpu(a, b); + if (cmp_res < 0) { + const bfdec_t *tmp; + tmp = a; + a = b; + b = tmp; + a_sign = b_sign; /* b_sign is never used later */ + } + /* abs(a) >= abs(b) */ + if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { + /* zero result */ + bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); + ret = 0; + } else if (a->len == 0 || b->len == 0) { + ret = 0; + if (a->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN) { + /* at least one operand is NaN */ + bfdec_set_nan(r); + ret = 0; + } else if (b->expn == BF_EXP_INF && is_sub) { + /* infinities with different signs */ + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bfdec_set_inf(r, a_sign); + } + } else { + /* at least one zero and not subtract */ + if (bfdec_set(r, a)) + return BF_ST_MEM_ERROR; + r->sign = a_sign; + goto renorm; + } + } else { + slimb_t d, a_offset, b_offset, i, r_len; + limb_t carry; + limb_t *b1_tab; + int b_shift; + mp_size_t b1_len; + + d = a->expn - b->expn; + + /* XXX: not efficient in time and memory if the precision is + not infinite */ + r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); + if (bfdec_resize(r, r_len)) + goto fail; + r->sign = a_sign; + r->expn = a->expn; + + a_offset = r_len - a->len; + for(i = 0; i < a_offset; i++) + r->tab[i] = 0; + for(i = 0; i < a->len; i++) + r->tab[a_offset + i] = a->tab[i]; + + b_shift = d % LIMB_DIGITS; + if (b_shift == 0) { + b1_len = b->len; + b1_tab = (limb_t *)b->tab; + } else { + b1_len = b->len + 1; + b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len); + if (!b1_tab) + goto fail; + b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) * + mp_pow_dec[LIMB_DIGITS - b_shift]; + } + b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); + + if (is_sub) { + carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset, + b1_tab, b1_len, 0); + if (carry != 0) { + carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry, + r_len - (b_offset + b1_len)); + assert(carry == 0); + } + } else { + carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset, + b1_tab, b1_len, 0); + if (carry != 0) { + carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry, + r_len - (b_offset + b1_len)); + } + if (carry != 0) { + if (bfdec_resize(r, r_len + 1)) { + if (b_shift != 0) + bf_free(s, b1_tab); + goto fail; + } + r->tab[r_len] = 1; + r->expn += LIMB_DIGITS; + } + } + if (b_shift != 0) + bf_free(s, b1_tab); + renorm: + ret = bfdec_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bfdec_add_internal(r, a, b, prec, flags, 0); +} + +static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bfdec_add_internal(r, a, b, prec, flags, 1); +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_add); +} + +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_sub); +} + +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + int ret, r_sign; + + if (a->len < b->len) { + const bfdec_t *tmp = a; + a = b; + b = tmp; + } + r_sign = a->sign ^ b->sign; + /* here b->len <= a->len */ + if (b->len == 0) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + ret = 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { + if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || + (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bfdec_set_inf(r, r_sign); + ret = 0; + } + } else { + bfdec_set_zero(r, r_sign); + ret = 0; + } + } else { + bfdec_t tmp, *r1 = NULL; + limb_t a_len, b_len; + limb_t *a_tab, *b_tab; + + a_len = a->len; + b_len = b->len; + a_tab = a->tab; + b_tab = b->tab; + + if (r == a || r == b) { + bfdec_init(r->ctx, &tmp); + r1 = r; + r = &tmp; + } + if (bfdec_resize(r, a_len + b_len)) { + bfdec_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; + } + mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len); + r->sign = r_sign; + r->expn = a->expn + b->expn; + ret = bfdec_normalize_and_round(r, prec, flags); + done: + if (r == &tmp) + bfdec_move(r1, &tmp); + } + return ret; +} + +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bfdec_t b; + int ret; + bfdec_init(r->ctx, &b); + ret = bfdec_set_si(&b, b1); + ret |= bfdec_mul(r, a, &b, prec, flags); + bfdec_delete(&b); + return ret; +} + +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bfdec_t b; + int ret; + + bfdec_init(r->ctx, &b); + ret = bfdec_set_si(&b, b1); + ret |= bfdec_add(r, a, &b, prec, flags); + bfdec_delete(&b); + return ret; +} + +static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags) +{ + int ret, r_sign; + limb_t n, nb, precl; + + r_sign = a->sign ^ b->sign; + if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else if (a->expn == BF_EXP_INF) { + bfdec_set_inf(r, r_sign); + return 0; + } else { + bfdec_set_zero(r, r_sign); + return 0; + } + } else if (a->expn == BF_EXP_ZERO) { + if (b->expn == BF_EXP_ZERO) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bfdec_set_zero(r, r_sign); + return 0; + } + } else if (b->expn == BF_EXP_ZERO) { + bfdec_set_inf(r, r_sign); + return BF_ST_DIVIDE_ZERO; + } + + nb = b->len; + if (prec == BF_PREC_INF) { + /* infinite precision: return BF_ST_INVALID_OP if not an exact + result */ + /* XXX: check */ + precl = nb + 1; + } else if (flags & BF_FLAG_RADPNT_PREC) { + /* number of digits after the decimal point */ + /* XXX: check (2 extra digits for rounding + 2 digits) */ + precl = (bf_max(a->expn - b->expn, 0) + 2 + + prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + } else { + /* number of limbs of the quotient (2 extra digits for rounding) */ + precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + } + n = bf_max(a->len, precl); + + { + limb_t *taba, na, i; + slimb_t d; + + na = n + nb; + taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t)); + if (!taba) + goto fail; + d = na - a->len; + memset(taba, 0, d * sizeof(limb_t)); + memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); + if (bfdec_resize(r, n + 1)) + goto fail1; + if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) { + fail1: + bf_free(r->ctx, taba); + goto fail; + } + /* see if non zero remainder */ + for(i = 0; i < nb; i++) { + if (taba[i] != 0) + break; + } + bf_free(r->ctx, taba); + if (i != nb) { + if (prec == BF_PREC_INF) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + r->tab[0] |= 1; + } + } + r->expn = a->expn - b->expn + LIMB_DIGITS; + r->sign = r_sign; + ret = bfdec_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_div); +} + +/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the + integer defined as floor(a/b) and r = a - q * b. */ +static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r, + const bfdec_t *a, const bfdec_t *b) +{ + if (bfdec_cmpu(a, b) < 0) { + bfdec_set_ui(q, 0); + bfdec_set(r, a); + } else { + bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC); + bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ); + bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ); + } +} + +/* division and remainder. + + rnd_mode is the rounding mode for the quotient. The additional + rounding mode BF_RND_EUCLIDIAN is supported. + + 'q' is an integer. 'r' is rounded with prec and flags (prec can be + BF_PREC_INF). +*/ +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode) +{ + bf_context_t *s = q->ctx; + bfdec_t a1_s, *a1 = &a1_s; + bfdec_t b1_s, *b1 = &b1_s; + bfdec_t r1_s, *r1 = &r1_s; + int q_sign, res; + BOOL is_ceil, is_rndn; + + assert(q != a && q != b); + assert(r != a && r != b); + assert(q != r); + + if (a->len == 0 || b->len == 0) { + bfdec_set_zero(q, 0); + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bfdec_set(r, a); + return bfdec_round(r, prec, flags); + } + } + + q_sign = a->sign ^ b->sign; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + switch(rnd_mode) { + default: + case BF_RNDZ: + case BF_RNDN: + case BF_RNDNA: + is_ceil = FALSE; + break; + case BF_RNDD: + is_ceil = q_sign; + break; + case BF_RNDU: + is_ceil = q_sign ^ 1; + break; + case BF_RNDA: + is_ceil = TRUE; + break; + case BF_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + } + + a1->expn = a->expn; + a1->tab = a->tab; + a1->len = a->len; + a1->sign = 0; + + b1->expn = b->expn; + b1->tab = b->tab; + b1->len = b->len; + b1->sign = 0; + + // bfdec_print_str("a1", a1); + // bfdec_print_str("b1", b1); + /* XXX: could improve to avoid having a large 'q' */ + bfdec_tdivremu(s, q, r, a1, b1); + if (bfdec_is_nan(q) || bfdec_is_nan(r)) + goto fail; + // bfdec_print_str("q", q); + // bfdec_print_str("r", r); + + if (r->len != 0) { + if (is_rndn) { + bfdec_init(s, r1); + if (bfdec_set(r1, r)) + goto fail; + if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) { + bfdec_delete(r1); + goto fail; + } + res = bfdec_cmpu(r1, b); + bfdec_delete(r1); + if (res > 0 || + (res == 0 && + (rnd_mode == BF_RNDNA || + (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) { + goto do_sub_r; + } + } else if (is_ceil) { + do_sub_r: + res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); + res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); + if (res & BF_ST_MEM_ERROR) + goto fail; + } + } + + r->sign ^= a->sign; + q->sign = q_sign; + return bfdec_round(r, prec, flags); + fail: + bfdec_set_nan(q); + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bfdec_t q_s, *q = &q_s; + int ret; + + bfdec_init(r->ctx, q); + ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode); + bfdec_delete(q); + return ret; +} + +/* convert to integer (infinite precision) */ +int bfdec_rint(bfdec_t *r, int rnd_mode) +{ + return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); +} + +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = a->ctx; + int ret, k; + limb_t *a1, v; + slimb_t n, n1, prec1; + limb_t res; + + assert(r != a); + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bfdec_set(r, a); + } + ret = 0; + } else if (a->sign || prec == BF_PREC_INF) { + invalid_op: + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + if (flags & BF_FLAG_RADPNT_PREC) { + prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1); + } else { + prec1 = prec; + } + /* convert the mantissa to an integer with at least 2 * + prec + 4 digits */ + n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); + if (bfdec_resize(r, n)) + goto fail; + a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); + if (!a1) + goto fail; + n1 = bf_min(2 * n, a->len); + memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); + memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); + if (a->expn & 1) { + res = mp_shr_dec(a1, a1, 2 * n, 1, 0); + } else { + res = 0; + } + /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1 + because mp_sqrtrem2_dec already does it */ + k = 0; + if (n > 1) { + v = a1[2 * n - 1]; + while (v < BF_DEC_BASE / 4) { + k++; + v *= 4; + } + if (k != 0) + mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0); + } + if (mp_sqrtrem_dec(s, r->tab, a1, n)) { + bf_free(s, a1); + goto fail; + } + if (k != 0) + mp_div1_dec(r->tab, r->tab, n, 1 << k, 0); + if (!res) { + res = mp_scan_nz(a1, n + 1); + } + bf_free(s, a1); + if (!res) { + res = mp_scan_nz(a->tab, a->len - n1); + } + if (res != 0) + r->tab[0] |= 1; + r->sign = 0; + r->expn = (a->expn + 1) >> 1; + ret = bfdec_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there + is an overflow and 0 otherwise. No memory error is possible. */ +int bfdec_get_int32(int *pres, const bfdec_t *a) +{ + uint32_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = 0; + if (a->expn == BF_EXP_INF) { + v = (uint32_t)INT32_MAX + a->sign; + /* XXX: return overflow ? */ + } else { + v = INT32_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 9) { + v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } else if (a->expn == 10) { + uint64_t v1; + uint32_t v_max; +#if LIMB_BITS == 64 + v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); +#else + v1 = (uint64_t)a->tab[a->len - 1] * 10 + + get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1); +#endif + v_max = (uint32_t)INT32_MAX + a->sign; + if (v1 > v_max) { + v = v_max; + ret = BF_ST_OVERFLOW; + } else { + v = v1; + if (a->sign) + v = -v; + ret = 0; + } + } else { + v = (uint32_t)INT32_MAX + a->sign; + ret = BF_ST_OVERFLOW; + } + *pres = v; + return ret; +} + +/* power to an integer with infinite precision */ +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b) +{ + int ret, n_bits, i; + + assert(r != a); + if (b == 0) + return bfdec_set_ui(r, 1); + ret = bfdec_set(r, a); + n_bits = LIMB_BITS - clz(b); + for(i = n_bits - 2; i >= 0; i--) { + ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ); + if ((b >> i) & 1) + ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ); + } + return ret; +} + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags) +{ + return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE); +} + +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags) +{ + slimb_t dummy_exp; + return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec, + flags, TRUE); +} + +#endif /* USE_BF_DEC */ + #ifdef USE_FFT_MUL /***************************************************************/ /* Integer multiplication with FFT */ @@ -4921,10 +7537,10 @@ static void ntt_free(BFNTTState *s, void *ptr) bf_aligned_free(s->ctx, ptr); } -static no_inline void ntt_fft(BFNTTState *s, - NTTLimb *out_buf, NTTLimb *in_buf, - NTTLimb *tmp_buf, int fft_len_log2, - int inverse, int m_idx) +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; @@ -4945,6 +7561,8 @@ static no_inline void ntt_fft(BFNTTState *s, 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]); @@ -4966,6 +7584,8 @@ static no_inline void ntt_fft(BFNTTState *s, 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]); @@ -4993,6 +7613,8 @@ static no_inline void ntt_fft(BFNTTState *s, 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++; @@ -5023,6 +7645,7 @@ static no_inline void ntt_fft(BFNTTState *s, _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, @@ -5095,9 +7718,9 @@ static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m) return a; } -static no_inline void ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, - NTTLimb *tmp_buf, int fft_len_log2, - int inverse, int m_idx) +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; @@ -5117,6 +7740,8 @@ static no_inline void ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, 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]; @@ -5149,6 +7774,7 @@ static no_inline void ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, tab_out[k] = b0; tab_out[k + stride_in] = b1; } + return 0; } static void ntt_vec_mul(BFNTTState *s, @@ -5190,44 +7816,40 @@ static no_inline void mul_trig(NTTLimb *buf, #endif /* !AVX2 */ static no_inline NTTLimb *get_trig(BFNTTState *s, - int k, int inverse1, int m_idx1) + int k, int inverse, int m_idx) { NTTLimb *tab; limb_t i, n2, c, c_mul, m, c_mul_inv; - int m_idx, inverse; if (k > NTT_TRIG_K_MAX) return NULL; - for(;;) { - tab = s->ntt_trig[m_idx1][inverse1][k]; - if (tab) - return tab; - n2 = (limb_t)1 << (k - 1); - for(m_idx = 0; m_idx < NB_MODS; m_idx++) { - m = ntt_mods[m_idx]; - for(inverse = 0; inverse < 2; inverse++) { + 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); + tab = ntt_malloc(s, sizeof(NTTLimb) * n2); #else - tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2); + tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2); #endif - 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++) { + 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); + 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); + 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; - } - } + 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) @@ -5257,20 +7879,25 @@ void fft_clear_cache(bf_context_t *s1) #define STRIP_LEN 16 /* dst = buf1, src = buf2 */ -static void ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, - int k1, int k2, limb_t n1, limb_t n2, int inverse, - limb_t m_idx) +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) { - ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx); + 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]; @@ -5285,7 +7912,8 @@ static void ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, for(l = 0; l < strip_len; l++) { if (inverse) mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); - ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx); + 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); @@ -5300,12 +7928,17 @@ static void ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, 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 void ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, - int k, int k_tot, limb_t m_idx) +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; @@ -5320,8 +7953,10 @@ static void ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, n1 = (limb_t)1 << k1; n2 = (limb_t)1 << k2; - ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx); - ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx); + 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 { @@ -5329,7 +7964,9 @@ static void ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx); } } - ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx); + if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx)) + return -1; + return 0; } @@ -5717,10 +8354,11 @@ int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) return fft_len_log2_found; } -static no_inline void fft_mul(bf_t *res, limb_t *a_tab, limb_t a_len, - limb_t *b_tab, limb_t b_len, int mul_flags) +/* 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) { - bf_context_t *s1 = res->ctx; BFNTTState *s; int dpl, fft_len_log2, j, nb_mods, reduced_mem; slimb_t len, fft_len; @@ -5729,7 +8367,8 @@ static no_inline void fft_mul(bf_t *res, limb_t *a_tab, limb_t a_len, limb_t ha, hb, hr, h_ref; #endif - ntt_static_init(s1); + if (ntt_static_init(s1)) + return -1; s = s1->ntt_state; /* find the optimal number of digits per limb (dpl) */ @@ -5742,7 +8381,8 @@ static no_inline void fft_mul(bf_t *res, limb_t *a_tab, limb_t a_len, 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) { - bf_resize(res, 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 */ @@ -5754,20 +8394,28 @@ static no_inline void fft_mul(bf_t *res, limb_t *a_tab, limb_t a_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) { - bf_resize(res, 0); + 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); - bf_resize(res, 0); /* in case res == b */ + 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) { @@ -5777,12 +8425,18 @@ static no_inline void fft_mul(bf_t *res, limb_t *a_tab, limb_t a_len, } else { ptr = buf2 + fft_len * j; } - ntt_conv(s, buf1 + fft_len * j, ptr, - fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods); + if (ntt_conv(s, buf1 + fft_len * j, ptr, + fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods)) + goto fail; } - bf_resize(res, 0); /* in case res == b and reduced mem */ + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); /* in case res == b and reduced mem */ ntt_free(s, buf2); - bf_resize(res, len); + 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) @@ -5795,6 +8449,11 @@ static no_inline void fft_mul(bf_t *res, limb_t *a_tab, limb_t a_len, exit(1); } #endif + return 0; + fail: + ntt_free(s, buf1); + ntt_free(s, buf2); + return -1; } #else /* USE_FFT_MUL */ diff --git a/libbf.h b/libbf.h index e3ea060..4576bf8 100644 --- a/libbf.h +++ b/libbf.h @@ -1,7 +1,7 @@ /* * Tiny arbitrary precision floating point library * - * Copyright (c) 2017-2018 Fabrice Bellard + * 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 @@ -27,7 +27,7 @@ #include #include -#if defined(__x86_64__) +#if INTPTR_MAX >= INT64_MAX #define LIMB_LOG2_BITS 6 #else #define LIMB_LOG2_BITS 5 @@ -41,24 +41,37 @@ 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 BF_RAW_EXP_MIN INT64_MIN +#define BF_RAW_EXP_MAX INT64_MAX + +#define LIMB_DIGITS 19 +#define BF_DEC_BASE UINT64_C(10000000000000000000) #else typedef int32_t slimb_t; typedef uint32_t limb_t; typedef uint64_t dlimb_t; -#define EXP_MIN INT32_MIN -#define EXP_MAX INT32_MAX +#define BF_RAW_EXP_MIN INT32_MIN +#define BF_RAW_EXP_MAX INT32_MAX + +#define LIMB_DIGITS 9 +#define BF_DEC_BASE 1000000000U #endif /* in bits */ +/* minimum number of bits for the exponent */ #define BF_EXP_BITS_MIN 3 -#define BF_EXP_BITS_MAX (LIMB_BITS - 2) +/* maximum number of bits for the exponent */ +#define BF_EXP_BITS_MAX (LIMB_BITS - 3) +/* extended range for exponent, used internally */ +#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) +/* minimum possible precision */ #define BF_PREC_MIN 2 -#define BF_PREC_MAX (((limb_t)1 << BF_EXP_BITS_MAX) - 2) +/* minimum possible precision */ +#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) +/* some operations support infinite precision */ #define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ #if LIMB_BITS == 64 @@ -67,9 +80,9 @@ typedef uint64_t dlimb_t; #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 +#define BF_EXP_ZERO BF_RAW_EXP_MIN +#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) +#define BF_EXP_NAN BF_RAW_EXP_MAX /* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, +/-infinity is represented with expn = BF_EXP_INF and len = 0, @@ -83,25 +96,41 @@ typedef struct { 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_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ BF_RNDU, /* round to +inf */ BF_RNDNA, /* round to nearest, ties away from zero */ - BF_RNDNU, /* round to nearest, ties to +inf */ + BF_RNDA, /* round away from zero */ BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, inexact flag is always set) */ } bf_rnd_t; -/* allow subnormal numbers (only available if the number of exponent - bits is < BF_EXP_BITS_MAX and prec != BF_PREC_INF) */ +/* allow subnormal numbers. Only available if the number of exponent + bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ #define BF_FLAG_SUBNORMAL (1 << 3) +/* 'prec' is the precision after the radix point instead of the whole + mantissa. Can only be used with bf_round() and + bfdec_[add|sub|mul|div|sqrt|round](). */ +#define BF_FLAG_RADPNT_PREC (1 << 4) #define BF_RND_MASK 0x7 -#define BF_EXP_BITS_SHIFT 4 +#define BF_EXP_BITS_SHIFT 5 #define BF_EXP_BITS_MASK 0x3f +/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ +#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) + /* contains the rounding mode and number of exponents bits */ typedef uint32_t bf_flags_t; @@ -122,12 +151,17 @@ typedef struct 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); + int e; + e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; + if (e == BF_EXP_BITS_MASK) + return BF_EXP_BITS_MAX + 1; + else + return BF_EXP_BITS_MAX - e; } static inline bf_flags_t bf_set_exp_bits(int n) { - return (BF_EXP_BITS_MAX - n) << BF_EXP_BITS_SHIFT; + return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; } /* returned status */ @@ -136,8 +170,7 @@ static inline bf_flags_t bf_set_exp_bits(int n) #define BF_ST_OVERFLOW (1 << 2) #define BF_ST_UNDERFLOW (1 << 3) #define BF_ST_INEXACT (1 << 4) -/* not used yet, indicate that a memory allocation error occured. NaN - is returned */ +/* 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() */ @@ -169,13 +202,26 @@ 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) { + if (s && r->tab) { bf_realloc(s, r->tab, 0); } } @@ -200,21 +246,39 @@ static inline int bf_is_zero(const bf_t *a) return (a->expn == BF_EXP_ZERO); } -void bf_set_ui(bf_t *r, uint64_t a); -void bf_set_si(bf_t *r, int64_t a); +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); -void bf_set(bf_t *r, const bf_t *a); +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); -void bf_set_float64(bf_t *a, double d); +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_cmp(const bf_t *a, const bf_t *b); +static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) == 0; +} + +static inline int bf_cmp_le(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) <= 0; +} + +static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) < 0; +} + int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); @@ -227,54 +291,29 @@ 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_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags); -int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, - bf_flags_t flags); -int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, limb_t prec, bf_flags_t flags); -int bf_rint(bf_t *r, limb_t prec, bf_flags_t flags); + bf_flags_t flags, int rnd_mode); +/* round to integer with infinite precision */ +int bf_rint(bf_t *r, int rnd_mode); int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); slimb_t bf_get_exp_min(const bf_t *a); -void bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); -void bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); -void bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); +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) -/* Only accept integers (no decimal point, no exponent, no infinity nor NaN */ -#define BF_ATOF_INT_ONLY (1 << 18) -/* Do not accept radix prefix after sign */ -#define BF_ATOF_NO_PREFIX_AFTER_SIGN (1 << 19) -/* Do not parse NaN and parse case sensitive 'Infinity' */ -#define BF_ATOF_JS_QUIRKS (1 << 20) -/* Do not round integers to the indicated precision */ -#define BF_ATOF_INT_PREC_INF (1 << 21) -/* Support legacy octal syntax for well formed numbers */ -#define BF_ATOF_LEGACY_OCTAL (1 << 22) -/* accept _ between digits as a digit separator */ -#define BF_ATOF_UNDERSCORE_SEP (1 << 23) -/* if a 'n' suffix is present, force integer parsing (XXX: remove) */ -#define BF_ATOF_INT_N_SUFFIX (1 << 24) -/* if set return NaN if empty number string (instead of 0) */ -#define BF_ATOF_NAN_IF_EMPTY (1 << 25) -/* only accept decimal floating point if radix = 0 */ -#define BF_ATOF_ONLY_DEC_FLOAT (1 << 26) - -/* additional return flags */ -/* indicate that the parsed number is an integer (only set when the - flags BF_ATOF_INT_PREC_INF or BF_ATOF_INT_N_SUFFIX are used) */ -#define BF_ATOF_ST_INTEGER (1 << 5) -/* integer parsed as legacy octal */ -#define BF_ATOF_ST_LEGACY_OCTAL (1 << 6) +/* 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); @@ -286,25 +325,39 @@ int bf_atof2(bf_t *r, slimb_t *pexponent, int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, slimb_t expn, limb_t prec, bf_flags_t flags); + +/* Conversion of floating point number to string. Return a null + terminated string or NULL if memory error. *plen contains its + length if plen != NULL. The exponent letter is "e" for base 10, + "p" for bases 2, 8, 16 with a binary exponent and "@" for the other + bases. */ + #define BF_FTOA_FORMAT_MASK (3 << 16) + /* fixed format: prec significant digits rounded with (flags & BF_RND_MASK). Exponential notation is used if too many zeros are - needed. */ + 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. +/* free format: - Infinite precision (BF_PREC_INF) is supported when the radix is a - power of two. */ + For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum + number of digits to represent 'a'. The precision and the rounding + mode are ignored. + + For the non binary radices with bf_ftoa(): use as many digits as + necessary so that bf_atof() return the same number when using + precision 'prec', rounding to nearest and the subnormal + configuration of 'flags'. The result is meaningful only if 'a' is + already rounded to 'prec' bits. If the subnormal flag is set, the + exponent in 'flags' must also be set to the desired exponent range. +*/ #define BF_FTOA_FORMAT_FREE (2 << 16) /* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits - (takes more computation time). */ + (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for + binary radices with bf_ftoa() and for bfdec_ftoa(). */ #define BF_FTOA_FORMAT_FREE_MIN (3 << 16) /* force exponential notation for fixed or free format */ @@ -312,33 +365,44 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, /* 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) -size_t bf_ftoa(char **pbuf, const bf_t *a, int radix, limb_t prec, - bf_flags_t flags); +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags); /* modulo 2^n instead of saturation. NaN and infinity return 0 */ #define BF_GET_INT_MOD (1 << 0) int bf_get_int32(int *pres, const bf_t *a, int flags); int bf_get_int64(int64_t *pres, const bf_t *a, int flags); +int bf_get_uint64(uint64_t *pres, const bf_t *a); /* the following functions are exported for testing only. */ +void mp_print_str(const char *str, const limb_t *tab, limb_t n); void bf_print_str(const char *str, const bf_t *a); -void bf_resize(bf_t *r, limb_t len); +int bf_resize(bf_t *r, limb_t len); int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); -void bf_recip(bf_t *r, const bf_t *a, limb_t prec); -void bf_rsqrt(bf_t *a, const bf_t *x, limb_t prec); 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) +#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); @@ -349,4 +413,123 @@ int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +/* decimal floating point */ + +static inline void bfdec_init(bf_context_t *s, bfdec_t *r) +{ + bf_init(s, (bf_t *)r); +} +static inline void bfdec_delete(bfdec_t *r) +{ + bf_delete((bf_t *)r); +} + +static inline void bfdec_neg(bfdec_t *r) +{ + r->sign ^= 1; +} + +static inline int bfdec_is_finite(const bfdec_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bfdec_is_nan(const bfdec_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bfdec_is_zero(const bfdec_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) +{ + bf_memcpy((bf_t *)r, (const bf_t *)a); +} + +int bfdec_set_ui(bfdec_t *r, uint64_t a); +int bfdec_set_si(bfdec_t *r, int64_t a); + +static inline void bfdec_set_nan(bfdec_t *r) +{ + bf_set_nan((bf_t *)r); +} +static inline void bfdec_set_zero(bfdec_t *r, int is_neg) +{ + bf_set_zero((bf_t *)r, is_neg); +} +static inline void bfdec_set_inf(bfdec_t *r, int is_neg) +{ + bf_set_inf((bf_t *)r, is_neg); +} +static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) +{ + return bf_set((bf_t *)r, (bf_t *)a); +} +static inline void bfdec_move(bfdec_t *r, bfdec_t *a) +{ + bf_move((bf_t *)r, (bf_t *)a); +} +static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmpu((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp_full((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) == 0; +} +static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) <= 0; +} +static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) < 0; +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bfdec_rint(bfdec_t *r, int rnd_mode); +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); +int bfdec_get_int32(int *pres, const bfdec_t *a); +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags); + +/* the following functions are exported for testing only. */ +extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; +void bfdec_print_str(const char *str, const bfdec_t *a); +static inline int bfdec_resize(bfdec_t *r, limb_t len) +{ + return bf_resize((bf_t *)r, len); +} +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); + #endif /* LIBBF_H */ diff --git a/libregexp.c b/libregexp.c index 4425ce5..379bfc7 100644 --- a/libregexp.c +++ b/libregexp.c @@ -75,7 +75,7 @@ typedef struct { 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; + void *opaque; DynBuf group_names; union { char error_msg[TMP_BUF_SIZE]; @@ -110,12 +110,14 @@ 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) +/* insert 'len' bytes at position 'pos'. Return < 0 if error. */ +static int dbuf_insert(DynBuf *s, int pos, int len) { - dbuf_realloc(s, s->size + len); + if (dbuf_realloc(s, s->size + len)) + return -1; memmove(s->buf + pos + len, s->buf + pos, s->size - pos); s->size += len; + return 0; } /* canonicalize with the specific JS regexp rules */ @@ -228,7 +230,7 @@ static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c) invert = c & 1; c_pt = char_range_table[c >> 1]; len = *c_pt++; - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); for(i = 0; i < len * 2; i++) { if (cr_add_point(cr, c_pt[i])) goto fail; @@ -434,8 +436,14 @@ static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, return -1; } -/* Return -1 in case of overflow */ -static int parse_digits(const uint8_t **pp) +static int re_parse_out_of_memory(REParseState *s) +{ + return re_parse_error(s, "out of memory"); +} + +/* If allow_overflow is false, return -1 in case of + overflow. Otherwise return INT32_MAX. */ +static int parse_digits(const uint8_t **pp, BOOL allow_overflow) { const uint8_t *p; uint64_t v; @@ -448,8 +456,12 @@ static int parse_digits(const uint8_t **pp) if (c < '0' || c > '9') break; v = v * 10 + c - '0'; - if (v >= INT32_MAX) - return -1; + if (v >= INT32_MAX) { + if (allow_overflow) + v = INT32_MAX; + else + return -1; + } p++; } *pp = p; @@ -557,7 +569,8 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16) } } break; - case '0' ... '7': + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': c -= '0'; if (allow_utf16 == 2) { /* only accept \0 not followed by digit */ @@ -612,7 +625,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, p++; q = name; while (is_unicode_char(*p)) { - if ((q - name) > sizeof(name) - 1) + if ((q - name) >= sizeof(name) - 1) goto unknown_property_name; *q++ = *p++; } @@ -621,7 +634,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, if (*p == '=') { p++; while (is_unicode_char(*p)) { - if ((q - value) > sizeof(value) - 1) + if ((q - value) >= sizeof(value) - 1) return re_parse_error(s, "unknown unicode property value"); *q++ = *p++; } @@ -638,7 +651,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { script_ext = TRUE; do_script: - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); ret = unicode_script(cr, value, script_ext); if (ret) { cr_free(cr); @@ -648,7 +661,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, goto out_of_memory; } } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); ret = unicode_general_category(cr, value); if (ret) { cr_free(cr); @@ -658,7 +671,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, goto out_of_memory; } } else if (value[0] == '\0') { - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); ret = unicode_general_category(cr, name); if (ret == -1) { cr_free(cr); @@ -688,7 +701,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr, *pp = p; return 0; out_of_memory: - return re_parse_error(s, "out of memory"); + return re_parse_out_of_memory(s); } #endif /* CONFIG_ALL_UNICODE */ @@ -851,7 +864,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp) CharRange cr1_s, *cr1 = &cr1_s; BOOL invert; - cr_init(cr, s->mem_opaque, lre_realloc); + cr_init(cr, s->opaque, lre_realloc); p = *pp; p++; /* skip '[' */ invert = FALSE; @@ -923,7 +936,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp) *pp = p; return 0; memory_error: - re_parse_error(s, "out of memory"); + re_parse_out_of_memory(s); fail: cr_free(cr); return -1; @@ -1134,9 +1147,13 @@ static int re_parse_captures(REParseState *s, int *phas_named_captures, } } capture_index++; + if (capture_index >= CAPTURE_COUNT_MAX) + goto done; } } else { capture_index++; + if (capture_index >= CAPTURE_COUNT_MAX) + goto done; } break; case '\\': @@ -1150,6 +1167,7 @@ static int re_parse_captures(REParseState *s, int *phas_named_captures, break; } } + done: if (capture_name) return -1; else @@ -1225,14 +1243,27 @@ static int re_parse_term(REParseState *s, BOOL 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; + if (s->is_utf16) { + return re_parse_error(s, "syntax error"); + } else if (!is_digit(p[1])) { + /* Annex B: we accept '{' not followed by digits as a + normal atom */ goto parse_class_atom; + } else { + const uint8_t *p1 = p + 1; + /* Annex B: error if it is like a repetition count */ + parse_digits(&p1, TRUE); + if (*p1 == ',') { + p1++; + if (is_digit(*p1)) { + parse_digits(&p1, TRUE); + } + } + if (*p1 != '}') { + goto parse_class_atom; + } } - /* fall tru */ + /* fall thru */ case '*': case '+': case '?': @@ -1277,6 +1308,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) return -1; re_emit_op(s, REOP_match); /* jump after the 'match' after the lookahead is successful */ + if (dbuf_error(&s->byte_code)) + return -1; put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4)); } else if (p[2] == '<') { p += 3; @@ -1383,11 +1416,13 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) } } goto normal_char; - case '1' ... '9': + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': { const uint8_t *q = ++p; - c = parse_digits(&p); + c = parse_digits(&p, FALSE); if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { if (!s->is_utf16) { /* Annex B.1.4: accept legacy octal */ @@ -1407,7 +1442,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) } goto normal_char; } - return re_parse_error(s, "back reference out of range in reguar expression"); + return re_parse_error(s, "back reference out of range in regular expression"); } emit_back_reference: last_atom_start = s->byte_code.size; @@ -1484,32 +1519,38 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) 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) + { + const uint8_t *p1 = p; + /* As an extension (see ES6 annex B), we accept '{' not + followed by digits as a normal atom */ + if (!is_digit(p[1])) { + if (s->is_utf16) goto invalid_quant_count; - } else { - quant_max = INT32_MAX; /* infinity */ + break; } + p++; + quant_min = parse_digits(&p, TRUE); + quant_max = quant_min; + if (*p == ',') { + p++; + if (is_digit(*p)) { + quant_max = parse_digits(&p, TRUE); + if (quant_max < quant_min) { + invalid_quant_count: + return re_parse_error(s, "invalid repetition count"); + } + } else { + quant_max = INT32_MAX; /* infinity */ + } + } + if (*p != '}' && !s->is_utf16) { + /* Annex B: normal atom if invalid '{' syntax */ + p = p1; + break; + } + if (re_parse_expect(s, &p, '}')) + return -1; } - if (re_parse_expect(s, &p, '}')) - return -1; quantifier: greedy = TRUE; if (*p == '?') { @@ -1524,12 +1565,15 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) if (quant_max > 0) { /* specific optimization for simple quantifiers */ + if (dbuf_error(&s->byte_code)) + goto out_of_memory; len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start, s->byte_code.size - last_atom_start); if (len > 0) { re_emit_op(s, REOP_match); - dbuf_insert(&s->byte_code, last_atom_start, 17); + if (dbuf_insert(&s->byte_code, last_atom_start, 17)) + goto out_of_memory; pos = last_atom_start; s->byte_code.buf[pos++] = REOP_simple_greedy_quant; put_u32(&s->byte_code.buf[pos], @@ -1545,6 +1589,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) } } + if (dbuf_error(&s->byte_code)) + goto out_of_memory; add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start, s->byte_code.size - last_atom_start) == 0); } else { @@ -1558,7 +1604,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) /* 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); + if (dbuf_insert(&s->byte_code, last_atom_start, 3)) + goto out_of_memory; s->byte_code.buf[last_atom_start++] = REOP_save_reset; s->byte_code.buf[last_atom_start++] = last_capture_count; s->byte_code.buf[last_atom_start++] = s->capture_count - 1; @@ -1566,12 +1613,14 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) if (quant_max == 0) { s->byte_code.size = last_atom_start; } else if (quant_max == 1) { - dbuf_insert(&s->byte_code, last_atom_start, 5); + if (dbuf_insert(&s->byte_code, last_atom_start, 5)) + goto out_of_memory; s->byte_code.buf[last_atom_start] = REOP_split_goto_first + greedy; put_u32(s->byte_code.buf + last_atom_start + 1, len); } else if (quant_max == INT32_MAX) { - dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check); + if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check)) + goto out_of_memory; s->byte_code.buf[last_atom_start] = REOP_split_goto_first + greedy; put_u32(s->byte_code.buf + last_atom_start + 1, @@ -1587,7 +1636,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) re_emit_goto(s, REOP_goto, last_atom_start); } } else { - dbuf_insert(&s->byte_code, last_atom_start, 10); + if (dbuf_insert(&s->byte_code, last_atom_start, 10)) + goto out_of_memory; pos = last_atom_start; s->byte_code.buf[pos++] = REOP_push_i32; put_u32(s->byte_code.buf + pos, quant_max); @@ -1605,7 +1655,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) if (quant_min == 1) { /* nothing to add */ } else { - dbuf_insert(&s->byte_code, last_atom_start, 5); + if (dbuf_insert(&s->byte_code, last_atom_start, 5)) + goto out_of_memory; s->byte_code.buf[last_atom_start] = REOP_push_i32; put_u32(s->byte_code.buf + last_atom_start + 1, quant_min); @@ -1646,6 +1697,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) done: s->buf_ptr = p; return 0; + out_of_memory: + return re_parse_out_of_memory(s); } static int re_parse_alternative(REParseState *s, BOOL is_backward_dir) @@ -1686,6 +1739,9 @@ static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) { int start, len, pos; + if (lre_check_stack_overflow(s->opaque, 0)) + return re_parse_error(s, "stack overflow"); + start = s->byte_code.size; if (re_parse_alternative(s, is_backward_dir)) return -1; @@ -1695,7 +1751,9 @@ static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) len = s->byte_code.size - start; /* insert a split before the first alternative */ - dbuf_insert(&s->byte_code, start, 5); + if (dbuf_insert(&s->byte_code, start, 5)) { + return re_parse_out_of_memory(s); + } s->byte_code.buf[start] = REOP_split_next_first; put_u32(s->byte_code.buf + start + 1, len + 5); @@ -1769,7 +1827,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, BOOL is_sticky; memset(s, 0, sizeof(*s)); - s->mem_opaque = opaque; + s->opaque = opaque; s->buf_ptr = (const uint8_t *)buf; s->buf_end = s->buf_ptr + buf_len; s->buf_start = s->buf_ptr; @@ -1820,7 +1878,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, } if (dbuf_error(&s->byte_code)) { - re_parse_error(s, "out of memory"); + re_parse_out_of_memory(s); goto error; } @@ -2486,6 +2544,17 @@ int lre_get_flags(const uint8_t *bc_buf) return bc_buf[RE_HEADER_FLAGS]; } +/* Return NULL if no group names. Otherwise, return a pointer to + 'capture_count - 1' zero terminated UTF-8 strings. */ +const char *lre_get_groupnames(const uint8_t *bc_buf) +{ + uint32_t re_bytecode_len; + if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0) + return NULL; + re_bytecode_len = get_u32(bc_buf + 3); + return (const char *)(bc_buf + 7 + re_bytecode_len); +} + #ifdef TEST BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) diff --git a/libregexp.h b/libregexp.h index cd0b24f..9aedb7e 100644 --- a/libregexp.h +++ b/libregexp.h @@ -44,6 +44,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, void *opaque); int lre_get_capture_count(const uint8_t *bc_buf); int lre_get_flags(const uint8_t *bc_buf); +const char *lre_get_groupnames(const uint8_t *bc_buf); int lre_exec(uint8_t **capture, const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, int cbuf_type, void *opaque); diff --git a/libunicode-table.h b/libunicode-table.h index 521f2f3..0ef2113 100644 --- a/libunicode-table.h +++ b/libunicode-table.h @@ -3,7 +3,7 @@ #include -static const uint32_t case_conv_table1[359] = { +static const uint32_t case_conv_table1[361] = { 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, @@ -86,17 +86,18 @@ static const uint32_t case_conv_table1[359] = { 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, + 0x53e20131, 0x53e28130, 0x53e30130, 0x53e38440, + 0x53fa8240, 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] = { +static const uint8_t case_conv_table2[361] = { 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04, 0x10, 0x00, 0x8f, 0x0b, 0x00, 0x00, 0x11, 0x00, 0x08, 0x00, 0x53, 0x4a, 0x51, 0x00, 0x52, 0x00, @@ -118,7 +119,7 @@ static const uint8_t case_conv_table2[359] = { 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, + 0x4e, 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, @@ -137,11 +138,12 @@ static const uint8_t case_conv_table2[359] = { 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, + 0x60, 0x5e, 0x70, 0x69, 0x6f, 0x4d, 0x00, 0x00, + 0x3b, 0x67, 0xb8, 0x00, 0x00, 0x45, 0xa8, 0x8a, + 0x8b, 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf, 0x94, + 0xb0, 0x6f, 0xb2, 0x5c, 0x5b, 0x5e, 0x5d, 0x60, + 0x5f, 0x62, 0x61, 0x64, 0x63, 0x66, 0x65, 0x68, + 0x67, }; static const uint16_t case_conv_ext[58] = { @@ -170,7 +172,7 @@ static const uint8_t unicode_prop_Cased1_table[172] = { 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, + 0xa2, 0x06, 0x80, 0x8c, 0x61, 0x28, 0x96, 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, @@ -186,107 +188,109 @@ static const uint8_t unicode_prop_Cased1_index[18] = { 0xf1, 0x01, }; -static const uint8_t unicode_prop_Case_Ignorable_table[678] = { +static const uint8_t unicode_prop_Case_Ignorable_table[692] = { 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, + 0xfa, 0x86, 0x40, 0xce, 0x04, 0x80, 0xb0, 0xac, + 0x00, 0x01, 0x01, 0x00, 0xab, 0x80, 0x8a, 0x85, + 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f, + 0x80, 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80, + 0x9d, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0x18, 0x08, + 0x97, 0x97, 0xaa, 0x82, 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, 0x06, 0x81, 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, 0x9c, 0x80, 0xc7, 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, 0x90, 0xbe, 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, 0x0c, 0x80, + 0x40, 0x96, 0x81, 0x99, 0x91, 0x8c, 0x80, 0xa5, + 0x87, 0x98, 0x8a, 0xad, 0x82, 0xaf, 0x01, 0x19, + 0x81, 0x90, 0x80, 0x94, 0x81, 0xc1, 0x29, 0x09, + 0x81, 0x8b, 0x07, 0x80, 0xa2, 0x80, 0x8a, 0x80, + 0xb2, 0x00, 0x11, 0x0c, 0x08, 0x80, 0x9a, 0x80, + 0x8d, 0x0c, 0x08, 0x80, 0xe3, 0x84, 0x88, 0x82, + 0xf8, 0x01, 0x03, 0x80, 0x60, 0x4f, 0x2f, 0x80, + 0x40, 0x92, 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, 0x41, 0x82, 0x81, + 0x40, 0x98, 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, + 0x81, 0xb3, 0x88, 0x89, 0x19, 0x80, 0xde, 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, 0x40, 0xff, 0x08, 0x03, 0x80, + 0x40, 0x8f, 0x19, 0x0b, 0x80, 0x9f, 0x89, 0xa7, + 0x29, 0x1f, 0x80, 0x88, 0x29, 0x82, 0xad, 0x8c, + 0x01, 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95, + 0x0e, 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30, + 0x80, 0xc7, 0x0a, 0x00, 0x80, 0x41, 0x5a, 0x81, + 0x55, 0x3a, 0x88, 0x60, 0x36, 0xb6, 0x84, 0xba, + 0x86, 0x88, 0x83, 0x44, 0x0a, 0x80, 0xbe, 0x90, + 0xbf, 0x08, 0x81, 0x60, 0x4c, 0xb7, 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, + 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, + 0x20, 0x05, 0x0c, 0x20, 0x3b, 0x0e, 0x40, 0x61, + 0x10, 0x40, 0x0f, 0x18, 0x20, 0x43, 0x1b, 0x60, + 0x79, 0x1d, 0x00, 0xf1, 0x20, 0x00, 0x0d, 0xa6, + 0x40, 0x2e, 0xa9, 0x20, 0xde, 0xaa, 0x00, 0x0f, + 0xff, 0x20, 0xe7, 0x0a, 0x41, 0x82, 0x11, 0x21, + 0xc4, 0x14, 0x61, 0x44, 0x19, 0x01, 0x48, 0x1d, + 0x21, 0xa4, 0xbc, 0x01, 0x3e, 0xe1, 0x01, 0xf0, 0x01, 0x0e, }; -static const uint8_t unicode_prop_ID_Start_table[1024] = { +static const uint8_t unicode_prop_ID_Start_table[1045] = { 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83, 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, @@ -297,142 +301,146 @@ static const uint8_t unicode_prop_ID_Start_table[1024] = { 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, + 0xb4, 0x94, 0x80, 0x91, 0xbb, 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, 0x90, 0x88, 0x02, 0x80, 0xa8, 0x08, + 0x8f, 0x04, 0x17, 0x82, 0x97, 0x2c, 0x91, 0x82, + 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9, 0xaf, 0x01, + 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20, 0x97, 0x00, + 0x80, 0x89, 0x01, 0x88, 0x01, 0x20, 0x80, 0x94, + 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3, 0x9a, 0x84, + 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b, 0x1a, 0x02, + 0x0e, 0x13, 0x8c, 0x8b, 0x80, 0x90, 0xa5, 0x00, + 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c, 0x03, 0x0e, + 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81, 0xa0, 0x03, + 0x0e, 0x00, 0x03, 0x81, 0x8e, 0x80, 0xb8, 0x03, + 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5, 0x0d, 0x82, + 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99, 0x84, 0xca, + 0x82, 0x8a, 0x86, 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, + 0x9f, 0xaf, 0x8f, 0x41, 0xff, 0x59, 0xbf, 0xbf, + 0x60, 0x51, 0xfc, 0x82, 0x44, 0x8c, 0xc2, 0xad, + 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89, 0x81, 0x93, + 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6, 0x88, 0x81, + 0xe6, 0x81, 0xb4, 0x81, 0x88, 0xa9, 0x8c, 0x02, + 0x03, 0x80, 0x96, 0x9c, 0xb3, 0x8d, 0xb1, 0xbd, + 0x2a, 0x00, 0x81, 0x8a, 0x9b, 0x89, 0x96, 0x98, + 0x9c, 0x86, 0xae, 0x9b, 0x80, 0x8f, 0x20, 0x89, + 0x89, 0x20, 0xa8, 0x96, 0x10, 0x87, 0x93, 0x96, + 0x10, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x08, 0x00, + 0x97, 0x11, 0x8a, 0x32, 0x8b, 0x29, 0x29, 0x85, + 0x88, 0x30, 0x30, 0xaa, 0x80, 0x8d, 0x85, 0xf2, + 0x9c, 0x60, 0x2b, 0xa3, 0x8b, 0x96, 0x83, 0xb0, + 0x60, 0x21, 0x03, 0x41, 0x6d, 0x81, 0xe9, 0xa5, + 0x86, 0x8b, 0x24, 0x00, 0x89, 0x80, 0x8c, 0x04, + 0x00, 0x01, 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, + 0x91, 0xbf, 0x81, 0xb5, 0xa7, 0x8b, 0xf3, 0x20, + 0x40, 0x86, 0xa3, 0x99, 0x85, 0x99, 0x8a, 0xd8, + 0x15, 0x0d, 0x0d, 0x0a, 0xa2, 0x8b, 0x80, 0x99, + 0x80, 0x92, 0x01, 0x80, 0x8e, 0x81, 0x8d, 0xa1, + 0xfa, 0xc4, 0xb4, 0x41, 0x0a, 0x9c, 0x82, 0xb0, + 0xae, 0x9f, 0x8c, 0x9d, 0x84, 0xa5, 0x89, 0x9d, + 0x81, 0xa3, 0x1f, 0x04, 0xa9, 0x40, 0x9d, 0x91, + 0xa3, 0x83, 0xa3, 0x83, 0xa7, 0x87, 0xb3, 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, 0x5b, 0xa9, 0x29, 0xcd, + 0x9c, 0x89, 0x07, 0x95, 0xe9, 0x94, 0x9a, 0x96, + 0x8b, 0xb4, 0xca, 0xac, 0x9f, 0x98, 0x99, 0xa3, + 0x9c, 0x01, 0x07, 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, 0x82, 0x9d, 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, 0x39, 0x01, + 0x38, 0x08, 0x97, 0x8e, 0x00, 0x80, 0xdd, 0x39, + 0xa6, 0x8f, 0x00, 0x80, 0x9b, 0x80, 0x89, 0xa7, + 0x30, 0x94, 0x80, 0x8a, 0xad, 0x92, 0x80, 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, 0x40, 0xbc, 0x80, 0xce, 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, 0x44, 0xd5, 0xa9, 0x88, + 0x60, 0x22, 0xf6, 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, + 0xdd, 0xa1, 0x50, 0x34, 0x8a, 0x40, 0xdd, 0x81, + 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x4c, 0x1e, 0x42, + 0x1d, 0x45, 0xe1, 0x53, 0x4a, }; -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_Start_index[99] = { + 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09, + 0x00, 0xb4, 0x0a, 0x00, 0xba, 0x0b, 0x00, 0x3e, + 0x0d, 0x00, 0xe0, 0x0e, 0x20, 0x57, 0x12, 0x00, + 0xeb, 0x16, 0x00, 0xca, 0x19, 0x20, 0xc0, 0x1d, + 0x60, 0x80, 0x20, 0x00, 0x2e, 0x2d, 0x00, 0xc0, + 0x31, 0x20, 0x89, 0xa7, 0x20, 0xf0, 0xa9, 0x00, + 0xe3, 0xab, 0x00, 0x3e, 0xfd, 0x00, 0xfb, 0x00, + 0x21, 0x37, 0x07, 0x61, 0x01, 0x0a, 0x01, 0x1d, + 0x0f, 0x21, 0x2c, 0x12, 0x01, 0xc8, 0x14, 0x21, + 0xd1, 0x19, 0x21, 0x47, 0x1d, 0x01, 0x39, 0x6a, + 0x21, 0x09, 0x8d, 0x01, 0xbc, 0xd4, 0x01, 0xa9, + 0xd7, 0x21, 0x3a, 0xee, 0x01, 0xde, 0xa6, 0x22, + 0x4b, 0x13, 0x03, }; -static const uint8_t unicode_prop_ID_Continue1_table[607] = { +static const uint8_t unicode_prop_ID_Continue1_table[626] = { 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47, 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08, 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, @@ -445,14 +453,14 @@ static const uint8_t unicode_prop_ID_Continue1_table[607] = { 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, + 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x16, + 0x82, 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, + 0x10, 0x83, 0x88, 0x80, 0x89, 0x09, 0x89, 0x90, + 0x82, 0xc5, 0x03, 0x28, 0x00, 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, 0x38, 0x89, 0xd6, 0x01, 0x88, 0x8a, 0x29, 0x89, 0xbd, 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, 0xb0, 0x93, 0x01, 0x84, @@ -464,67 +472,70 @@ static const uint8_t unicode_prop_ID_Continue1_table[607] = { 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, + 0x01, 0xbe, 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, 0x23, 0x80, 0xd2, 0x81, 0xb1, 0x91, 0x89, + 0x89, 0x85, 0x91, 0x8c, 0x8a, 0x9b, 0x87, 0x98, + 0x8c, 0xab, 0x83, 0xae, 0x8d, 0x8e, 0x89, 0x8a, + 0x80, 0x89, 0x89, 0xae, 0x8d, 0x8b, 0x07, 0x09, + 0x89, 0xa0, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x08, + 0x80, 0xa8, 0x24, 0x81, 0x40, 0xeb, 0x38, 0x09, + 0x89, 0x60, 0x4f, 0x23, 0x80, 0x42, 0xe0, 0x8f, + 0x8f, 0x8f, 0x11, 0x97, 0x82, 0x40, 0xbf, 0x89, + 0xa4, 0x80, 0x42, 0xbc, 0x80, 0x40, 0xe1, 0x80, + 0x40, 0x94, 0x84, 0x41, 0x24, 0x89, 0x45, 0x56, + 0x10, 0x0c, 0x83, 0xa7, 0x13, 0x80, 0x40, 0xa4, + 0x81, 0x42, 0x3c, 0x1f, 0x89, 0x41, 0x70, 0x81, + 0x40, 0x98, 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, 0x80, 0x8b, 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, 0xc5, 0x28, 0x09, + 0x18, 0x00, 0x81, 0x8b, 0x89, 0xf6, 0x31, 0x32, + 0x80, 0x9b, 0x89, 0xa7, 0x30, 0x1f, 0x80, 0x88, + 0x8a, 0xad, 0x8f, 0x41, 0x94, 0x38, 0x87, 0x8f, + 0x89, 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, + 0x08, 0x30, 0x07, 0x89, 0xaf, 0x20, 0x08, 0x27, + 0x89, 0x41, 0x48, 0x83, 0x60, 0x4b, 0x68, 0x89, + 0x40, 0x85, 0x84, 0xba, 0x86, 0x98, 0x89, 0x43, + 0xf4, 0x00, 0xb6, 0x33, 0xd0, 0x80, 0x8a, 0x81, + 0x60, 0x4c, 0xaa, 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, + 0x34, 0x89, 0x52, 0x95, 0x89, 0x6c, 0x05, 0x05, + 0x40, 0xef, }; -static const uint8_t unicode_prop_ID_Continue1_index[57] = { +static const uint8_t unicode_prop_ID_Continue1_index[60] = { 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, + 0x10, 0x20, 0x1a, 0x18, 0x20, 0x74, 0x1b, 0x20, + 0xdd, 0x20, 0x00, 0x0c, 0xa8, 0x00, 0x5a, 0xaa, + 0x20, 0x1a, 0xff, 0x00, 0xad, 0x0e, 0x01, 0x38, + 0x12, 0x21, 0xc1, 0x15, 0x21, 0xe5, 0x19, 0x21, + 0xaa, 0x1d, 0x21, 0x8c, 0xd1, 0x41, 0x4a, 0xe1, + 0x21, 0xf0, 0x01, 0x0e, }; #ifdef CONFIG_ALL_UNICODE -static const uint8_t unicode_cc_table[831] = { +static const uint8_t unicode_cc_table[851] = { 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03, 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, @@ -574,77 +585,81 @@ static const uint8_t unicode_cc_table[831] = { 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, + 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc, 0x80, + 0x01, 0xdc, 0xb0, 0x42, 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, 0xa4, 0x00, 0x09, + 0xb0, 0x66, 0x00, 0x09, 0x9a, 0xd1, 0xb0, 0x08, + 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, 0x2e, 0x00, + 0x07, 0x8b, 0x00, 0x09, 0xb0, 0xbe, 0xc0, 0x80, + 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, 0xc1, 0x80, + 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, 0xc5, 0x00, + 0x09, 0xb8, 0x46, 0xff, 0x00, 0x1a, 0xb2, 0xd0, + 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, 0x00, 0xdc, + 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, 0xc4, 0xb6, + 0x61, 0x00, 0xdc, 0x80, 0xc0, 0xa7, 0xc0, 0x00, + 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, 0xb0, 0x74, + 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb1, 0x52, + 0xc1, 0xb0, 0x68, 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, 0xb0, 0xd1, 0x01, 0x09, 0x83, 0x00, + 0x07, 0xb0, 0x6b, 0x00, 0x09, 0xb0, 0x22, 0x00, 0x09, 0x91, 0x00, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x74, 0x00, 0x09, 0xb0, 0xd1, 0x00, 0x07, 0x80, 0x01, 0x09, 0xb0, 0x20, 0x00, 0x09, 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, + 0x45, 0x27, 0x04, 0x01, 0xb0, 0x0a, 0xc6, 0xb4, + 0x88, 0x01, 0x06, 0xb8, 0x44, 0x7b, 0x00, 0x01, + 0xb8, 0x0c, 0x95, 0x01, 0xd8, 0x02, 0x01, 0x82, + 0x00, 0xe2, 0x04, 0xd8, 0x87, 0x07, 0xdc, 0x81, + 0xc4, 0x01, 0xdc, 0x9d, 0xc3, 0xb0, 0x63, 0xc2, + 0xb8, 0x05, 0x8a, 0xc6, 0x80, 0xd0, 0x81, 0xc6, + 0x80, 0xc1, 0x80, 0xc4, 0xb0, 0xd4, 0xc6, 0xb1, + 0x84, 0xc3, 0xb5, 0xaf, 0x06, 0xdc, 0xb0, 0x3c, + 0xc5, 0x00, 0x07, }; -static const uint8_t unicode_cc_index[78] = { +static const uint8_t unicode_cc_index[81] = { 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, + 0x20, 0x60, 0x1a, 0x20, 0xaa, 0x1b, 0x00, 0xf4, + 0x1c, 0x00, 0xfe, 0x1d, 0x20, 0x7f, 0x2d, 0x20, + 0xf0, 0xa6, 0x00, 0xb2, 0xaa, 0x00, 0xfe, 0x01, + 0x01, 0xab, 0x0e, 0x01, 0x73, 0x11, 0x21, 0x70, + 0x13, 0x01, 0xb8, 0x16, 0x01, 0x9a, 0x1a, 0x01, + 0x9f, 0xbc, 0x01, 0x22, 0xe0, 0x01, 0x4b, 0xe9, + 0x01, }; -static const uint32_t unicode_decomp_table1[687] = { +static const uint32_t unicode_decomp_table1[690] = { 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, @@ -771,55 +786,56 @@ static const uint32_t unicode_decomp_table1[687] = { 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, + 0x29dc0081, 0x29fe0103, 0x2ad70203, 0x2ada4081, + 0x3e401482, 0x3e4a7f82, 0x3e6a3f82, 0x3e8aa102, + 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590, 0x3ec00197, + 0x3ec0c119, 0x3ec1413f, 0x3ec4c2af, 0x3ec74184, + 0x3ec804ad, 0x3eca4081, 0x3eca8304, 0x3ecc03a0, + 0x3ece02a0, 0x3ecf8084, 0x3ed00120, 0x3ed0c120, + 0x3ed184ae, 0x3ed3c085, 0x3ed4312d, 0x3ef4cbad, + 0x3efa892f, 0x3eff022d, 0x3f002f2f, 0x3f1782a5, + 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf, 0x3f3c81a5, + 0x3f3d64af, 0x3f542031, 0x3f649b31, 0x3f7c0131, + 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd, 0x3f7ec0bb, + 0x3f7f00b3, 0x3f840503, 0x3f8c01ad, 0x3f8cc315, + 0x3f8e462d, 0x3f91cc03, 0x3f97c695, 0x3f9c01af, + 0x3f9d0085, 0x3f9d852f, 0x3fa03aad, 0x3fbd442f, + 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad, 0x3fe80081, + 0x3fe84f1f, 0x3ff0831f, 0x3ff2831f, 0x3ff4831f, + 0x3ff6819f, 0x3ff80783, 0x44268192, 0x442ac092, + 0x444b8112, 0x44d2c112, 0x452ec212, 0x456e8112, + 0x464e0092, 0x74578392, 0x746ec312, 0x75000d1f, + 0x75068d1f, 0x750d0d1f, 0x7513839f, 0x7515891f, + 0x751a0d1f, 0x75208d1f, 0x75271015, 0x752f439f, + 0x7531459f, 0x75340d1f, 0x753a8d1f, 0x75410395, + 0x7543441f, 0x7545839f, 0x75478d1f, 0x754e0795, + 0x7552839f, 0x75548d1f, 0x755b0d1f, 0x75618d1f, + 0x75680d1f, 0x756e8d1f, 0x75750d1f, 0x757b8d1f, + 0x75820d1f, 0x75888d1f, 0x758f0d1f, 0x75958d1f, + 0x759c0d1f, 0x75a28d1f, 0x75a90103, 0x75aa089f, + 0x75ae4081, 0x75ae839f, 0x75b04081, 0x75b08c9f, + 0x75b6c081, 0x75b7032d, 0x75b8889f, 0x75bcc081, + 0x75bd039f, 0x75bec081, 0x75bf0c9f, 0x75c54081, + 0x75c5832d, 0x75c7089f, 0x75cb4081, 0x75cb839f, + 0x75cd4081, 0x75cd8c9f, 0x75d3c081, 0x75d4032d, + 0x75d5889f, 0x75d9c081, 0x75da039f, 0x75dbc081, + 0x75dc0c9f, 0x75e24081, 0x75e2832d, 0x75e4089f, + 0x75e84081, 0x75e8839f, 0x75ea4081, 0x75ea8c9f, + 0x75f0c081, 0x75f1042d, 0x75f3851f, 0x75f6051f, + 0x75f8851f, 0x75fb051f, 0x75fd851f, 0x7b80022d, + 0x7b814dad, 0x7b884203, 0x7b89c081, 0x7b8a452d, + 0x7b8d0403, 0x7b908081, 0x7b91dc03, 0x7ba0052d, + 0x7ba2c8ad, 0x7ba84483, 0x7baac8ad, 0x7c400097, + 0x7c404521, 0x7c440d25, 0x7c4a8087, 0x7c4ac115, + 0x7c4b4117, 0x7c4c0d1f, 0x7c528217, 0x7c538099, + 0x7c53c097, 0x7c5a8197, 0x7c640097, 0x7c80012f, + 0x7c808081, 0x7c841603, 0x7c9004c1, 0x7c940103, + 0x7efc051f, 0xbe0001ac, 0xbe00d110, 0xbe0947ac, + 0xbe0d3910, 0xbe29872c, 0xbe2d022c, 0xbe2e3790, + 0xbe49ff90, 0xbe69bc10, }; -static const uint16_t unicode_decomp_table2[687] = { +static const uint16_t unicode_decomp_table2[690] = { 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, @@ -883,32 +899,33 @@ static const uint16_t unicode_decomp_table2[687] = { 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, + 0xa76f, 0x11af, 0x11b3, 0x028d, 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, 0x1c8a, 0x1caa, 0x1cc5, 0x1cc7, 0x1cc9, 0x1ccb, 0x1ccd, + 0x1ccf, 0x1cd1, 0x1cd3, 0x1cf3, 0x1cf5, 0x1cf7, 0x1cf9, 0x1cfb, + 0x1d02, 0x1d04, 0x1d06, 0x1d08, 0x1d17, 0x1d19, 0x1d1b, 0x1d1d, + 0x1d1f, 0x1d21, 0x1d23, 0x1d25, 0x1d27, 0x1d29, 0x1d2b, 0x1d2d, + 0x1d2f, 0x1d31, 0x1d33, 0x1d37, 0x03f4, 0x1d39, 0x2207, 0x1d3b, + 0x2202, 0x1d3d, 0x1d45, 0x03f4, 0x1d47, 0x2207, 0x1d49, 0x2202, + 0x1d4b, 0x1d53, 0x03f4, 0x1d55, 0x2207, 0x1d57, 0x2202, 0x1d59, + 0x1d61, 0x03f4, 0x1d63, 0x2207, 0x1d65, 0x2202, 0x1d67, 0x1d6f, + 0x03f4, 0x1d71, 0x2207, 0x1d73, 0x2202, 0x1d75, 0x1d7f, 0x1d81, + 0x1d83, 0x1d85, 0x1d87, 0x1d89, 0x1d8f, 0x1dac, 0x062d, 0x1db4, + 0x1dc0, 0x062c, 0x1dd0, 0x1e40, 0x1e4c, 0x1e5f, 0x1e71, 0x1e84, + 0x1e86, 0x1e8a, 0x1e90, 0x1e96, 0x1e98, 0x1e9c, 0x1e9e, 0x1ea6, + 0x1ea9, 0x1eab, 0x1eb1, 0x1eb3, 0x30b5, 0x1eb9, 0x1f11, 0x1f27, + 0x1f2b, 0x1f2d, 0x1f32, 0x1f7f, 0x1f90, 0x2091, 0x20a1, 0x20a7, + 0x21a1, 0x22bf, }; -static const uint8_t unicode_decomp_data[9158] = { +static const uint8_t unicode_decomp_data[9165] = { 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31, 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, @@ -1821,242 +1838,243 @@ static const uint8_t unicode_decomp_data[9158] = { 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, + 0xb9, 0x15, 0xaf, 0x15, 0x55, 0x35, 0x19, 0x30, + 0x19, 0x05, 0x57, 0xd1, 0x65, 0xd1, 0x58, 0xd1, + 0x65, 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, 0xd1, + 0x6f, 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, 0xd1, + 0x71, 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, 0x55, + 0x55, 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, 0xd1, + 0x65, 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, 0xd1, + 0x6e, 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, 0xd1, + 0x6f, 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, 0x61, + 0x00, 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, 0x00, + 0x4e, 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, + 0x64, 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, - 0x46, 0x47, 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, + 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, 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, + 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, + 0x06, 0x2c, 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, + 0x06, 0x32, 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, + 0x06, 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, + 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, + 0x06, 0x0c, 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, + 0x2c, 0x00, 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, + 0x14, 0x30, 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, + 0x43, 0x44, 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, + 0x4d, 0x56, 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, + 0x56, 0x57, 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, + 0x52, 0x44, 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, + 0x68, 0x4b, 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, + 0x30, 0x8c, 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, + 0x59, 0xa4, 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, + 0x65, 0x4d, 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, + 0x65, 0x1d, 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, + 0x8c, 0xf0, 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, + 0x62, 0x55, 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, + 0x90, 0xe6, 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, + 0x63, 0x70, 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, + 0x7a, 0x08, 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, + 0x67, 0x33, 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, + 0x91, 0x14, 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, + 0x4e, 0x8c, 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, + 0x62, 0xd7, 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, + 0x5f, 0xef, 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, + 0x00, 0x09, 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, + 0xbb, 0x4f, 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, + 0xe7, 0x50, 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, + 0x4d, 0x51, 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, + 0x1c, 0x05, 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, + 0x4b, 0x05, 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, + 0xac, 0x51, 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, + 0x03, 0x52, 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, + 0x72, 0x52, 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, + 0x20, 0x80, 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, + 0x52, 0x00, 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, + 0x82, 0x8a, 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, + 0x2c, 0x0a, 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, + 0x63, 0x0b, 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, + 0x9e, 0x54, 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, + 0xa2, 0x54, 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, + 0x63, 0x55, 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, + 0xab, 0x55, 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, + 0x06, 0x56, 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, + 0x07, 0x52, 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, + 0x0d, 0x58, 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, + 0xac, 0x58, 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, + 0x06, 0x59, 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, + 0xa8, 0x16, 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, + 0x27, 0x5a, 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, + 0xfc, 0x36, 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, + 0xc8, 0x19, 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, + 0xf3, 0x5b, 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, + 0x53, 0x5f, 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, + 0x6e, 0x5c, 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, + 0x43, 0x5d, 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, + 0x7c, 0x5d, 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, + 0xfd, 0x5d, 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, + 0x62, 0x38, 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, + 0xb3, 0x5e, 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, + 0xfe, 0x5e, 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, + 0x22, 0x5f, 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, + 0xda, 0x61, 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, + 0x9a, 0x5f, 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, + 0x81, 0x60, 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, + 0xd4, 0x26, 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, + 0x00, 0x00, 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, + 0x00, 0x08, 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, + 0x02, 0x48, 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, + 0x46, 0x6a, 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, + 0xd3, 0x5d, 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, + 0x2b, 0x3d, 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, + 0x63, 0xe4, 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, + 0x63, 0xa9, 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, + 0x64, 0x9d, 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, + 0x65, 0x6c, 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, + 0x66, 0x49, 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, + 0x3b, 0xe4, 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, + 0x67, 0x9c, 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, + 0x67, 0x1b, 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, + 0x67, 0xc3, 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, + 0x67, 0x52, 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, + 0x68, 0x1f, 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, + 0x69, 0xa3, 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, + 0x36, 0xdb, 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, + 0x38, 0x54, 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, + 0x6b, 0xba, 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, + 0x1d, 0xfa, 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, + 0x6c, 0xcd, 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, + 0x6d, 0x77, 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, + 0x6d, 0x85, 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, + 0x6e, 0x6e, 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, + 0x6e, 0xd1, 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, + 0x3f, 0x8e, 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, + 0x70, 0x1b, 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, + 0x70, 0x77, 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, + 0x71, 0x63, 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, + 0x72, 0x35, 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, + 0x72, 0x95, 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, + 0x00, 0x00, 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, + 0x20, 0x00, 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, + 0x20, 0x14, 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, + 0x3e, 0xa5, 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, + 0x74, 0x5c, 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, + 0x74, 0x1b, 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, + 0x75, 0x92, 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, + 0x76, 0xa1, 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, + 0x3f, 0x08, 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, + 0x50, 0x19, 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, + 0x77, 0x1f, 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, + 0x77, 0x46, 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, + 0x78, 0x8c, 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, + 0x56, 0x56, 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, + 0x79, 0xeb, 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, + 0x7a, 0x4f, 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, + 0x5a, 0xee, 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, + 0x7b, 0xc9, 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, + 0x7c, 0xa0, 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, + 0x7d, 0x86, 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, + 0x7d, 0x02, 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, + 0x62, 0x47, 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, + 0x7f, 0x3e, 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, + 0x80, 0xda, 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, + 0x65, 0x70, 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, + 0x80, 0x03, 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, + 0x5a, 0xa7, 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, + 0x33, 0x01, 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, + 0x44, 0x91, 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, + 0x52, 0xb1, 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, + 0x82, 0x3c, 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, + 0x83, 0xad, 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, + 0x83, 0x57, 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, + 0x83, 0xdc, 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, + 0x00, 0x00, 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, + 0x20, 0x80, 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, + 0x02, 0x80, 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, + 0x6c, 0x2b, 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, + 0x85, 0xca, 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, + 0x45, 0x61, 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, + 0x45, 0x50, 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, + 0x86, 0xa9, 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, + 0x86, 0x79, 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, + 0x87, 0xd7, 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, + 0x45, 0x60, 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, + 0x88, 0xde, 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, + 0x34, 0xae, 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, + 0x46, 0xa0, 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, + 0x8c, 0xa8, 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, + 0x8d, 0x77, 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, + 0x8d, 0xbc, 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, + 0x8e, 0x38, 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, + 0x90, 0xf1, 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, + 0x91, 0x38, 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, + 0x92, 0xf9, 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, + 0x95, 0x95, 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, + 0x49, 0xc3, 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, + 0x91, 0x1a, 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, + 0x97, 0x0a, 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, + 0x98, 0x0b, 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, + 0x98, 0x33, 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, + 0x99, 0xfe, 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, + 0x9b, 0x40, 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, + 0x4c, 0x67, 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, + 0xa1, 0x0e, 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, + 0x4d, 0xf9, 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, + 0x9f, 0x16, 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, + 0x88, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x28, 0x00, 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, + 0x80, 0x80, 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, + 0x00, 0x20, 0x2a, 0x00, 0x80, }; -static const uint16_t unicode_comp_table[944] = { +static const uint16_t unicode_comp_table[945] = { 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, @@ -2173,8 +2191,9 @@ static const uint16_t unicode_comp_table[944] = { 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, + 0x5901, 0x5902, 0x5903, 0x5940, 0x8e80, 0x8e82, 0x8ec0, 0x8f00, + 0x8f01, 0x8f40, 0x8f41, 0x8f81, 0x8f80, 0x8f83, 0x8fc0, 0x8fc1, + 0x9000, }; typedef enum { @@ -2260,7 +2279,7 @@ static const char unicode_gc_name_table[] = "C,Other" "\0" ; -static const uint8_t unicode_gc_table[3719] = { +static const uint8_t unicode_gc_table[3790] = { 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, @@ -2304,8 +2323,8 @@ static const uint8_t unicode_gc_table[3719] = { 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, + 0xe0, 0x2d, 0xe5, 0x0d, 0x00, 0xe5, 0x0a, 0xe0, + 0x03, 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, @@ -2328,80 +2347,80 @@ static const uint8_t unicode_gc_table[3719] = { 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, + 0x20, 0x27, 0x20, 0x27, 0x06, 0xc0, 0x26, 0x07, + 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02, + 0x0f, 0x05, 0xab, 0xe0, 0x02, 0x06, 0x05, 0x00, + 0xa5, 0x40, 0x45, 0x00, 0x65, 0x40, 0x25, 0x00, + 0x05, 0x00, 0x25, 0x40, 0x25, 0x40, 0x45, 0x40, + 0xe5, 0x04, 0x60, 0x27, 0x06, 0x27, 0x40, 0x47, + 0x00, 0x47, 0x06, 0x20, 0x05, 0xa0, 0x07, 0xe0, + 0x06, 0xe9, 0x02, 0x4b, 0xaf, 0x0d, 0x0f, 0x80, + 0x06, 0x47, 0x06, 0xe5, 0x00, 0x00, 0x45, 0x00, + 0xe5, 0x0f, 0x00, 0xe5, 0x08, 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, 0xe5, + 0x01, 0x00, 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, + 0x47, 0x66, 0x00, 0x47, 0x00, 0x47, 0x06, 0x05, + 0x0f, 0x60, 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, + 0xe9, 0x02, 0xeb, 0x01, 0x0f, 0xa5, 0x00, 0x06, + 0x27, 0x00, 0xe5, 0x0a, 0x40, 0xe5, 0x10, 0x00, + 0xe5, 0x01, 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, + 0x60, 0x47, 0x46, 0x00, 0x06, 0x00, 0xe7, 0x00, + 0xa0, 0xe9, 0x02, 0x20, 0x27, 0x16, 0xe0, 0x04, + 0xe5, 0x28, 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, + 0x04, 0xe6, 0x00, 0x16, 0xe9, 0x02, 0x36, 0xe0, + 0x1d, 0x25, 0x00, 0x05, 0x00, 0x85, 0x00, 0xe5, + 0x10, 0x00, 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, + 0xe6, 0x01, 0x05, 0x20, 0x85, 0x00, 0x04, 0x00, + 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, 0x26, 0xe0, 0x37, 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, @@ -2461,7 +2480,7 @@ static const uint8_t unicode_gc_table[3719] = { 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, + 0xef, 0x1f, 0x20, 0xef, 0x18, 0x00, 0xef, 0x61, 0xe1, 0x27, 0x00, 0xe2, 0x27, 0x00, 0x5f, 0x21, 0x22, 0xdf, 0x41, 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, 0x41, 0x02, 0xff, 0x5a, 0x02, 0xaf, 0x7f, @@ -2475,257 +2494,266 @@ static const uint8_t unicode_gc_table[3719] = { 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, + 0x16, 0x12, 0xf6, 0x05, 0x2f, 0x16, 0xe0, 0x25, + 0xef, 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, + 0x80, 0x4e, 0xe0, 0x12, 0xef, 0x04, 0x60, 0x17, + 0x56, 0x0f, 0x04, 0x05, 0x0a, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x11, 0x12, 0x33, 0x0f, 0xea, 0x01, 0x66, 0x27, + 0x11, 0x84, 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, + 0x00, 0xe5, 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, + 0x11, 0xe5, 0x52, 0x16, 0x44, 0x05, 0x80, 0xe5, + 0x23, 0x00, 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, + 0x02, 0xe5, 0x18, 0xef, 0x1c, 0xe0, 0x04, 0xe5, + 0x08, 0xef, 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, + 0xeb, 0x00, 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, + 0x02, 0xef, 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, + 0xe5, 0x99, 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, + 0x75, 0x40, 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, 0x41, 0x02, 0x3f, 0xe0, 0x22, + 0x3f, 0x05, 0x24, 0x02, 0xc5, 0x06, 0x45, 0x06, + 0x65, 0x06, 0xe5, 0x0f, 0x27, 0x26, 0x07, 0x6f, + 0x06, 0x40, 0xab, 0x2f, 0x0d, 0x0f, 0xa0, 0xe5, + 0x2c, 0x76, 0xe0, 0x00, 0x27, 0xe5, 0x2a, 0xe7, + 0x08, 0x26, 0xe0, 0x00, 0x36, 0xe9, 0x02, 0xa0, + 0xe6, 0x0a, 0xa5, 0x56, 0x05, 0x16, 0x25, 0x06, + 0xe9, 0x02, 0xe5, 0x14, 0xe6, 0x00, 0x36, 0xe5, + 0x0f, 0xe6, 0x03, 0x27, 0xe0, 0x03, 0x16, 0xe5, + 0x15, 0x40, 0x46, 0x07, 0xe5, 0x27, 0x06, 0x27, + 0x66, 0x27, 0x26, 0x47, 0xf6, 0x05, 0x00, 0x04, + 0xe9, 0x02, 0x60, 0x36, 0x85, 0x06, 0x04, 0xe5, + 0x01, 0xe9, 0x02, 0x85, 0x00, 0xe5, 0x21, 0xa6, + 0x27, 0x26, 0x27, 0x26, 0xe0, 0x01, 0x45, 0x06, + 0xe5, 0x00, 0x06, 0x07, 0x20, 0xe9, 0x02, 0x20, + 0x76, 0xe5, 0x08, 0x04, 0xa5, 0x4f, 0x05, 0x07, + 0x06, 0x07, 0xe5, 0x2a, 0x06, 0x05, 0x46, 0x25, + 0x26, 0x85, 0x26, 0x05, 0x06, 0x05, 0xe0, 0x10, + 0x25, 0x04, 0x36, 0xe5, 0x03, 0x07, 0x26, 0x27, + 0x36, 0x05, 0x24, 0x07, 0x06, 0xe0, 0x02, 0xa5, + 0x20, 0xa5, 0x20, 0xa5, 0xe0, 0x01, 0xc5, 0x00, + 0xc5, 0x00, 0xe2, 0x23, 0x0e, 0x64, 0xe2, 0x01, + 0x04, 0x2e, 0x60, 0xe2, 0x48, 0xe5, 0x1b, 0x27, + 0x06, 0x27, 0x06, 0x27, 0x16, 0x07, 0x06, 0x20, + 0xe9, 0x02, 0xa0, 0xe5, 0xab, 0x1c, 0xe0, 0x04, + 0xe5, 0x0f, 0x60, 0xe5, 0x29, 0x60, 0xfc, 0x87, + 0x78, 0xfd, 0x98, 0x78, 0xe5, 0x80, 0xe6, 0x20, + 0xe5, 0x62, 0xe0, 0x1e, 0xc2, 0xe0, 0x04, 0x82, + 0x80, 0x05, 0x06, 0xe5, 0x02, 0x0c, 0xe5, 0x05, + 0x00, 0x85, 0x00, 0x05, 0x00, 0x25, 0x00, 0x25, + 0x00, 0xe5, 0x64, 0xee, 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, 0x05, 0x40, 0x0f, 0xe0, + 0x27, 0xef, 0x25, 0x06, 0xe0, 0x7a, 0xe5, 0x15, + 0x40, 0xe5, 0x29, 0xe0, 0x07, 0x06, 0xeb, 0x13, + 0x60, 0xe5, 0x18, 0x6b, 0xe0, 0x01, 0xe5, 0x0c, + 0x0a, 0xe5, 0x00, 0x0a, 0x80, 0xe5, 0x1e, 0x86, + 0x80, 0xe5, 0x16, 0x00, 0x16, 0xe5, 0x1c, 0x60, + 0xe5, 0x00, 0x16, 0x8a, 0xe0, 0x22, 0xe1, 0x20, + 0xe2, 0x20, 0xe5, 0x46, 0x20, 0xe9, 0x02, 0xa0, + 0xe1, 0x1c, 0x60, 0xe2, 0x1c, 0x60, 0xe5, 0x20, + 0xe0, 0x00, 0xe5, 0x2c, 0xe0, 0x03, 0x16, 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, 0x00, 0xe5, 0x22, 0x00, 0x26, 0x11, 0x20, + 0x25, 0xe0, 0x46, 0xe5, 0x15, 0xeb, 0x02, 0x05, + 0xe0, 0x00, 0xe5, 0x0e, 0xe6, 0x03, 0x6b, 0x96, + 0xe0, 0x4e, 0xe5, 0x0d, 0xcb, 0xe0, 0x0c, 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, + 0x02, 0x76, 0x05, 0x27, 0x05, 0xe0, 0x00, 0xe5, + 0x1b, 0x06, 0x36, 0x05, 0xe0, 0x01, 0x26, 0x07, + 0xe5, 0x28, 0x47, 0xe6, 0x01, 0x27, 0x65, 0x76, + 0x66, 0x16, 0x07, 0x06, 0xe9, 0x02, 0x05, 0x16, + 0x05, 0x56, 0x00, 0xeb, 0x0c, 0xe0, 0x03, 0xe5, + 0x0a, 0x00, 0xe5, 0x11, 0x47, 0x46, 0x27, 0x06, + 0x07, 0x26, 0xb6, 0x06, 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, 0x36, 0x00, 0x16, 0x06, 0x45, 0xe0, + 0x16, 0xe5, 0x28, 0x47, 0xa6, 0x07, 0x06, 0x67, + 0x26, 0x07, 0x26, 0x25, 0x16, 0x05, 0xe0, 0x00, + 0xe9, 0x02, 0xe0, 0x80, 0x1e, 0xe5, 0x27, 0x47, + 0x66, 0x20, 0x67, 0x26, 0x07, 0x26, 0xf6, 0x0f, + 0x65, 0x26, 0xe0, 0x1a, 0xe5, 0x28, 0x47, 0xe6, + 0x00, 0x27, 0x06, 0x07, 0x26, 0x56, 0x05, 0xe0, + 0x03, 0xe9, 0x02, 0xa0, 0xf6, 0x05, 0xe0, 0x0b, + 0xe5, 0x23, 0x06, 0x07, 0x06, 0x27, 0xa6, 0x07, + 0x06, 0x05, 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, 0xe5, 0x00, 0x20, 0x05, + 0x20, 0xe5, 0x00, 0x00, 0x25, 0x00, 0xe5, 0x10, + 0xa7, 0x00, 0x27, 0x20, 0x26, 0x07, 0x06, 0x05, + 0x07, 0x05, 0x07, 0x06, 0x56, 0xe0, 0x01, 0xe9, + 0x02, 0xe0, 0x3e, 0xe5, 0x00, 0x20, 0xe5, 0x1f, + 0x47, 0x66, 0x20, 0x26, 0x67, 0x06, 0x05, 0x16, + 0x05, 0x07, 0xe0, 0x13, 0x05, 0xe6, 0x02, 0xe5, + 0x20, 0xa6, 0x07, 0x05, 0x66, 0xf6, 0x00, 0x06, + 0xe0, 0x00, 0x05, 0xa6, 0x27, 0x46, 0xe5, 0x26, + 0xe6, 0x05, 0x07, 0x26, 0x56, 0x05, 0x96, 0xe0, + 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, 0x2f, 0x05, 0xe0, 0x07, 0xeb, 0x0d, 0xef, + 0x00, 0x6d, 0xef, 0x09, 0xe0, 0x05, 0x16, 0xe5, + 0x83, 0x12, 0xe0, 0x5e, 0xea, 0x67, 0x00, 0x96, + 0xe0, 0x03, 0xe5, 0x80, 0x3c, 0xe0, 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, 0x06, 0xe0, 0x03, 0x27, 0xe0, + 0x06, 0xe5, 0x97, 0x70, 0xe0, 0x00, 0xe5, 0x84, + 0x4e, 0xe0, 0x22, 0xe5, 0x01, 0xe0, 0xa2, 0x6f, + 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, 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, + 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, 0xef, 0x80, + 0x19, 0xe0, 0x30, 0xef, 0x15, 0xe0, 0x05, 0xef, + 0x24, 0x60, 0xef, 0x01, 0xc0, 0x2f, 0xe0, 0x06, + 0xaf, 0xe0, 0x80, 0x12, 0xef, 0x80, 0x73, 0x8e, + 0xef, 0x82, 0x50, 0xe0, 0x00, 0xef, 0x05, 0x40, + 0xef, 0x05, 0x40, 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, 0x20, 0x2f, + 0xe0, 0x46, 0xef, 0x71, 0x00, 0xef, 0x4a, 0x00, + 0xef, 0x7f, 0xe0, 0x04, 0xef, 0x06, 0x20, 0x8f, + 0x40, 0x4f, 0x80, 0xcf, 0xe0, 0x01, 0xef, 0x11, + 0xc0, 0xcf, 0xe0, 0x01, 0x4f, 0xe0, 0x05, 0xcf, + 0xe0, 0x21, 0xef, 0x80, 0x0b, 0x00, 0xef, 0x2f, + 0xe0, 0x1d, 0xe9, 0x02, 0xe0, 0x83, 0x7e, 0xe5, + 0xc0, 0x66, 0x56, 0xe0, 0x1a, 0xe5, 0x8f, 0xad, + 0xe0, 0x03, 0xe5, 0x80, 0x56, 0x20, 0xe5, 0x95, + 0xfa, 0xe0, 0x06, 0xe5, 0x9c, 0xa9, 0xe0, 0x8b, + 0x97, 0xe5, 0x81, 0x96, 0xe0, 0x85, 0x5a, 0xe5, + 0x92, 0xc3, 0xe0, 0xca, 0xac, 0x2e, 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 { @@ -2753,6 +2781,7 @@ typedef enum { UNICODE_SCRIPT_Chakma, UNICODE_SCRIPT_Cham, UNICODE_SCRIPT_Cherokee, + UNICODE_SCRIPT_Chorasmian, UNICODE_SCRIPT_Common, UNICODE_SCRIPT_Coptic, UNICODE_SCRIPT_Cuneiform, @@ -2760,6 +2789,7 @@ typedef enum { UNICODE_SCRIPT_Cyrillic, UNICODE_SCRIPT_Deseret, UNICODE_SCRIPT_Devanagari, + UNICODE_SCRIPT_Dives_Akuru, UNICODE_SCRIPT_Dogra, UNICODE_SCRIPT_Duployan, UNICODE_SCRIPT_Egyptian_Hieroglyphs, @@ -2793,6 +2823,7 @@ typedef enum { UNICODE_SCRIPT_Kharoshthi, UNICODE_SCRIPT_Khmer, UNICODE_SCRIPT_Khojki, + UNICODE_SCRIPT_Khitan_Small_Script, UNICODE_SCRIPT_Khudawadi, UNICODE_SCRIPT_Lao, UNICODE_SCRIPT_Latin, @@ -2880,6 +2911,7 @@ typedef enum { UNICODE_SCRIPT_Vai, UNICODE_SCRIPT_Wancho, UNICODE_SCRIPT_Warang_Citi, + UNICODE_SCRIPT_Yezidi, UNICODE_SCRIPT_Yi, UNICODE_SCRIPT_Zanabazar_Square, UNICODE_SCRIPT_COUNT, @@ -2909,6 +2941,7 @@ static const char unicode_script_name_table[] = "Chakma,Cakm" "\0" "Cham,Cham" "\0" "Cherokee,Cher" "\0" + "Chorasmian,Chrs" "\0" "Common,Zyyy" "\0" "Coptic,Copt,Qaac" "\0" "Cuneiform,Xsux" "\0" @@ -2916,6 +2949,7 @@ static const char unicode_script_name_table[] = "Cyrillic,Cyrl" "\0" "Deseret,Dsrt" "\0" "Devanagari,Deva" "\0" + "Dives_Akuru,Diak" "\0" "Dogra,Dogr" "\0" "Duployan,Dupl" "\0" "Egyptian_Hieroglyphs,Egyp" "\0" @@ -2949,6 +2983,7 @@ static const char unicode_script_name_table[] = "Kharoshthi,Khar" "\0" "Khmer,Khmr" "\0" "Khojki,Khoj" "\0" + "Khitan_Small_Script,Kits" "\0" "Khudawadi,Sind" "\0" "Lao,Laoo" "\0" "Latin,Latn" "\0" @@ -3036,434 +3071,442 @@ static const char unicode_script_name_table[] = "Vai,Vaii" "\0" "Wancho,Wcho" "\0" "Warang_Citi,Wara" "\0" + "Yezidi,Yezi" "\0" "Yi,Yiii" "\0" "Zanabazar_Square,Zanb" "\0" ; -static const uint8_t unicode_script_table[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_table[2609] = { + 0xc0, 0x19, 0x99, 0x45, 0x85, 0x19, 0x99, 0x45, + 0xae, 0x19, 0x80, 0x45, 0x8e, 0x19, 0x80, 0x45, + 0x84, 0x19, 0x96, 0x45, 0x80, 0x19, 0x9e, 0x45, + 0x80, 0x19, 0xe1, 0x60, 0x45, 0xa6, 0x19, 0x84, + 0x45, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0, + 0x0f, 0x37, 0x83, 0x2b, 0x80, 0x19, 0x82, 0x2b, + 0x01, 0x83, 0x2b, 0x80, 0x19, 0x80, 0x2b, 0x03, + 0x80, 0x2b, 0x80, 0x19, 0x80, 0x2b, 0x80, 0x19, + 0x82, 0x2b, 0x00, 0x80, 0x2b, 0x00, 0x93, 0x2b, + 0x00, 0xbe, 0x2b, 0x8d, 0x1a, 0x8f, 0x2b, 0xe0, + 0x24, 0x1d, 0x81, 0x37, 0xe0, 0x48, 0x1d, 0x00, + 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, + 0x00, 0xb6, 0x34, 0x07, 0x9a, 0x34, 0x03, 0x85, + 0x34, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04, + 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x80, 0x19, 0x9f, 0x04, 0x80, + 0x19, 0x89, 0x04, 0x8a, 0x37, 0x99, 0x04, 0x80, + 0x37, 0xe0, 0x0b, 0x04, 0x80, 0x19, 0xa1, 0x04, + 0x8d, 0x87, 0x00, 0xbb, 0x87, 0x01, 0x82, 0x87, + 0xaf, 0x04, 0xb1, 0x91, 0x0d, 0xba, 0x63, 0x01, + 0x82, 0x63, 0xad, 0x7b, 0x01, 0x8e, 0x7b, 0x00, + 0x9b, 0x50, 0x01, 0x80, 0x50, 0x00, 0x8a, 0x87, + 0x34, 0x94, 0x04, 0x00, 0x91, 0x04, 0x0a, 0x8e, + 0x04, 0x80, 0x19, 0x9c, 0x04, 0xd0, 0x1f, 0x83, + 0x37, 0x8e, 0x1f, 0x81, 0x19, 0x99, 0x1f, 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, 0x2e, 0x00, 0x85, 0x2e, 0x03, 0x81, + 0x2e, 0x01, 0x95, 0x2e, 0x00, 0x86, 0x2e, 0x00, + 0x81, 0x2e, 0x00, 0x81, 0x2e, 0x00, 0x81, 0x2e, + 0x01, 0x80, 0x2e, 0x00, 0x84, 0x2e, 0x03, 0x81, + 0x2e, 0x01, 0x82, 0x2e, 0x02, 0x80, 0x2e, 0x06, + 0x83, 0x2e, 0x00, 0x80, 0x2e, 0x06, 0x90, 0x2e, + 0x09, 0x82, 0x2c, 0x00, 0x88, 0x2c, 0x00, 0x82, + 0x2c, 0x00, 0x95, 0x2c, 0x00, 0x86, 0x2c, 0x00, + 0x81, 0x2c, 0x00, 0x84, 0x2c, 0x01, 0x89, 0x2c, + 0x00, 0x82, 0x2c, 0x00, 0x82, 0x2c, 0x01, 0x80, + 0x2c, 0x0e, 0x83, 0x2c, 0x01, 0x8b, 0x2c, 0x06, + 0x86, 0x2c, 0x00, 0x82, 0x70, 0x00, 0x87, 0x70, + 0x01, 0x81, 0x70, 0x01, 0x95, 0x70, 0x00, 0x86, + 0x70, 0x00, 0x81, 0x70, 0x00, 0x84, 0x70, 0x01, + 0x88, 0x70, 0x01, 0x81, 0x70, 0x01, 0x82, 0x70, + 0x06, 0x82, 0x70, 0x03, 0x81, 0x70, 0x00, 0x84, + 0x70, 0x01, 0x91, 0x70, 0x09, 0x81, 0x8e, 0x00, + 0x85, 0x8e, 0x02, 0x82, 0x8e, 0x00, 0x83, 0x8e, + 0x02, 0x81, 0x8e, 0x00, 0x80, 0x8e, 0x00, 0x81, + 0x8e, 0x02, 0x81, 0x8e, 0x02, 0x82, 0x8e, 0x02, + 0x8b, 0x8e, 0x03, 0x84, 0x8e, 0x02, 0x82, 0x8e, + 0x00, 0x83, 0x8e, 0x01, 0x80, 0x8e, 0x05, 0x80, + 0x8e, 0x0d, 0x94, 0x8e, 0x04, 0x8c, 0x90, 0x00, + 0x82, 0x90, 0x00, 0x96, 0x90, 0x00, 0x8f, 0x90, + 0x02, 0x87, 0x90, 0x00, 0x82, 0x90, 0x00, 0x83, + 0x90, 0x06, 0x81, 0x90, 0x00, 0x82, 0x90, 0x04, + 0x83, 0x90, 0x01, 0x89, 0x90, 0x06, 0x88, 0x90, + 0x8c, 0x3c, 0x00, 0x82, 0x3c, 0x00, 0x96, 0x3c, + 0x00, 0x89, 0x3c, 0x00, 0x84, 0x3c, 0x01, 0x88, + 0x3c, 0x00, 0x82, 0x3c, 0x00, 0x83, 0x3c, 0x06, + 0x81, 0x3c, 0x06, 0x80, 0x3c, 0x00, 0x83, 0x3c, + 0x01, 0x89, 0x3c, 0x00, 0x81, 0x3c, 0x0c, 0x8c, + 0x4f, 0x00, 0x82, 0x4f, 0x00, 0xb2, 0x4f, 0x00, + 0x82, 0x4f, 0x00, 0x85, 0x4f, 0x03, 0x8f, 0x4f, + 0x01, 0x99, 0x4f, 0x00, 0x82, 0x81, 0x00, 0x91, + 0x81, 0x02, 0x97, 0x81, 0x00, 0x88, 0x81, 0x00, + 0x80, 0x81, 0x01, 0x86, 0x81, 0x02, 0x80, 0x81, + 0x03, 0x85, 0x81, 0x00, 0x80, 0x81, 0x00, 0x87, + 0x81, 0x05, 0x89, 0x81, 0x01, 0x82, 0x81, 0x0b, + 0xb9, 0x92, 0x03, 0x80, 0x19, 0x9b, 0x92, 0x24, + 0x81, 0x44, 0x00, 0x80, 0x44, 0x00, 0x84, 0x44, + 0x00, 0x97, 0x44, 0x00, 0x80, 0x44, 0x00, 0x96, + 0x44, 0x01, 0x84, 0x44, 0x00, 0x80, 0x44, 0x00, + 0x85, 0x44, 0x01, 0x89, 0x44, 0x01, 0x83, 0x44, + 0x1f, 0xc7, 0x93, 0x00, 0xa3, 0x93, 0x03, 0xa6, + 0x93, 0x00, 0xa3, 0x93, 0x00, 0x8e, 0x93, 0x00, + 0x86, 0x93, 0x83, 0x19, 0x81, 0x93, 0x24, 0xe0, + 0x3f, 0x5e, 0xa5, 0x27, 0x00, 0x80, 0x27, 0x04, + 0x80, 0x27, 0x01, 0xaa, 0x27, 0x80, 0x19, 0x83, + 0x27, 0xe0, 0x9f, 0x30, 0xc8, 0x26, 0x00, 0x83, + 0x26, 0x01, 0x86, 0x26, 0x00, 0x80, 0x26, 0x00, + 0x83, 0x26, 0x01, 0xa8, 0x26, 0x00, 0x83, 0x26, + 0x01, 0xa0, 0x26, 0x00, 0x83, 0x26, 0x01, 0x86, + 0x26, 0x00, 0x80, 0x26, 0x00, 0x83, 0x26, 0x01, + 0x8e, 0x26, 0x00, 0xb8, 0x26, 0x00, 0x83, 0x26, + 0x01, 0xc2, 0x26, 0x01, 0x9f, 0x26, 0x02, 0x99, + 0x26, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, + 0xe2, 0x1f, 0x12, 0x9c, 0x66, 0x02, 0xca, 0x7a, + 0x82, 0x19, 0x8a, 0x7a, 0x06, 0x8c, 0x88, 0x00, + 0x86, 0x88, 0x0a, 0x94, 0x32, 0x81, 0x19, 0x08, + 0x93, 0x11, 0x0b, 0x8c, 0x89, 0x00, 0x82, 0x89, + 0x00, 0x81, 0x89, 0x0b, 0xdd, 0x40, 0x01, 0x89, + 0x40, 0x05, 0x89, 0x40, 0x05, 0x81, 0x5b, 0x81, + 0x19, 0x80, 0x5b, 0x80, 0x19, 0x88, 0x5b, 0x00, + 0x89, 0x5b, 0x05, 0xd8, 0x5b, 0x06, 0xaa, 0x5b, + 0x04, 0xc5, 0x12, 0x09, 0x9e, 0x47, 0x00, 0x8b, + 0x47, 0x03, 0x8b, 0x47, 0x03, 0x80, 0x47, 0x02, + 0x8b, 0x47, 0x9d, 0x8a, 0x01, 0x84, 0x8a, 0x0a, + 0xab, 0x61, 0x03, 0x99, 0x61, 0x05, 0x8a, 0x61, + 0x02, 0x81, 0x61, 0x9f, 0x40, 0x9b, 0x10, 0x01, + 0x81, 0x10, 0xbe, 0x8b, 0x00, 0x9c, 0x8b, 0x01, + 0x8a, 0x8b, 0x05, 0x89, 0x8b, 0x05, 0x8d, 0x8b, + 0x01, 0x90, 0x37, 0x3e, 0xcb, 0x07, 0x03, 0xac, + 0x07, 0x02, 0xbf, 0x85, 0xb3, 0x0a, 0x07, 0x83, + 0x0a, 0xb7, 0x46, 0x02, 0x8e, 0x46, 0x02, 0x82, + 0x46, 0xaf, 0x67, 0x88, 0x1d, 0x06, 0xaa, 0x27, + 0x01, 0x82, 0x27, 0x87, 0x85, 0x07, 0x82, 0x37, + 0x80, 0x19, 0x8c, 0x37, 0x80, 0x19, 0x86, 0x37, + 0x83, 0x19, 0x80, 0x37, 0x85, 0x19, 0x80, 0x37, + 0x82, 0x19, 0x81, 0x37, 0x80, 0x19, 0x04, 0xa5, + 0x45, 0x84, 0x2b, 0x80, 0x1d, 0xb0, 0x45, 0x84, + 0x2b, 0x83, 0x45, 0x84, 0x2b, 0x8c, 0x45, 0x80, + 0x1d, 0xc5, 0x45, 0x80, 0x2b, 0xb9, 0x37, 0x00, + 0x84, 0x37, 0xe0, 0x9f, 0x45, 0x95, 0x2b, 0x01, + 0x85, 0x2b, 0x01, 0xa5, 0x2b, 0x01, 0x85, 0x2b, + 0x01, 0x87, 0x2b, 0x00, 0x80, 0x2b, 0x00, 0x80, + 0x2b, 0x00, 0x80, 0x2b, 0x00, 0x9e, 0x2b, 0x01, + 0xb4, 0x2b, 0x00, 0x8e, 0x2b, 0x00, 0x8d, 0x2b, + 0x01, 0x85, 0x2b, 0x00, 0x92, 0x2b, 0x01, 0x82, + 0x2b, 0x00, 0x88, 0x2b, 0x00, 0x8b, 0x19, 0x81, + 0x37, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80, 0x45, + 0x01, 0x8a, 0x19, 0x80, 0x45, 0x8e, 0x19, 0x00, + 0x8c, 0x45, 0x02, 0x9f, 0x19, 0x0f, 0xa0, 0x37, + 0x0e, 0xa5, 0x19, 0x80, 0x2b, 0x82, 0x19, 0x81, + 0x45, 0x85, 0x19, 0x80, 0x45, 0x9a, 0x19, 0x80, + 0x45, 0x90, 0x19, 0xa8, 0x45, 0x82, 0x19, 0x03, + 0xe2, 0x36, 0x19, 0x18, 0x8a, 0x19, 0x14, 0xe3, + 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, 0x19, + 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19, 0xae, + 0x28, 0x00, 0xae, 0x28, 0x00, 0x9f, 0x45, 0xe0, + 0x13, 0x1a, 0x04, 0x86, 0x1a, 0xa5, 0x27, 0x00, + 0x80, 0x27, 0x04, 0x80, 0x27, 0x01, 0xb7, 0x94, + 0x06, 0x81, 0x94, 0x0d, 0x80, 0x94, 0x96, 0x26, + 0x08, 0x86, 0x26, 0x00, 0x86, 0x26, 0x00, 0x86, + 0x26, 0x00, 0x86, 0x26, 0x00, 0x86, 0x26, 0x00, + 0x86, 0x26, 0x00, 0x86, 0x26, 0x00, 0x86, 0x26, + 0x00, 0x9f, 0x1d, 0xd2, 0x19, 0x2c, 0x99, 0x2f, + 0x00, 0xd8, 0x2f, 0x0b, 0xe0, 0x75, 0x2f, 0x19, + 0x8b, 0x19, 0x03, 0x84, 0x19, 0x80, 0x2f, 0x80, + 0x19, 0x80, 0x2f, 0x98, 0x19, 0x88, 0x2f, 0x83, + 0x37, 0x81, 0x30, 0x87, 0x19, 0x83, 0x2f, 0x83, + 0x19, 0x00, 0xd5, 0x35, 0x01, 0x81, 0x37, 0x81, + 0x19, 0x82, 0x35, 0x80, 0x19, 0xd9, 0x3d, 0x81, + 0x19, 0x82, 0x3d, 0x04, 0xaa, 0x0d, 0x00, 0xdd, + 0x30, 0x00, 0x8f, 0x19, 0x9f, 0x0d, 0xa3, 0x19, + 0x0b, 0x8f, 0x3d, 0x9e, 0x30, 0x00, 0xbf, 0x19, + 0x9e, 0x30, 0xd0, 0x19, 0xae, 0x3d, 0x80, 0x19, + 0xd7, 0x3d, 0xe0, 0x47, 0x19, 0xf0, 0x09, 0x5f, + 0x2f, 0xbf, 0x19, 0xf0, 0x41, 0x9c, 0x2f, 0x02, + 0xe4, 0x2c, 0x9b, 0x02, 0xb6, 0x9b, 0x08, 0xaf, + 0x4a, 0xe0, 0xcb, 0x97, 0x13, 0xdf, 0x1d, 0xd7, + 0x08, 0x07, 0xa1, 0x19, 0xe0, 0x05, 0x45, 0x82, + 0x19, 0xb4, 0x45, 0x01, 0x88, 0x45, 0x29, 0x8a, + 0x45, 0xac, 0x86, 0x02, 0x89, 0x19, 0x05, 0xb7, + 0x76, 0x07, 0xc5, 0x7c, 0x07, 0x8b, 0x7c, 0x05, + 0x9f, 0x1f, 0xad, 0x3e, 0x80, 0x19, 0x80, 0x3e, + 0xa3, 0x79, 0x0a, 0x80, 0x79, 0x9c, 0x30, 0x02, + 0xcd, 0x3a, 0x00, 0x80, 0x19, 0x89, 0x3a, 0x03, + 0x81, 0x3a, 0x9e, 0x5e, 0x00, 0xb6, 0x16, 0x08, + 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, 0x83, 0x16, + 0x9f, 0x5e, 0xc2, 0x8c, 0x17, 0x84, 0x8c, 0x96, + 0x55, 0x09, 0x85, 0x26, 0x01, 0x85, 0x26, 0x01, + 0x85, 0x26, 0x08, 0x86, 0x26, 0x00, 0x86, 0x26, + 0x00, 0xaa, 0x45, 0x80, 0x19, 0x88, 0x45, 0x80, + 0x2b, 0x83, 0x45, 0x81, 0x19, 0x03, 0xcf, 0x17, + 0xad, 0x55, 0x01, 0x89, 0x55, 0x05, 0xf0, 0x1b, + 0x43, 0x30, 0x0b, 0x96, 0x30, 0x03, 0xb0, 0x30, + 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x2f, 0x01, 0xe0, + 0x09, 0x2f, 0x25, 0x86, 0x45, 0x0b, 0x84, 0x05, + 0x04, 0x99, 0x34, 0x00, 0x84, 0x34, 0x00, 0x80, + 0x34, 0x00, 0x81, 0x34, 0x00, 0x81, 0x34, 0x00, + 0x89, 0x34, 0xe0, 0x11, 0x04, 0x10, 0xe1, 0x0a, + 0x04, 0x81, 0x19, 0x0f, 0xbf, 0x04, 0x01, 0xb5, + 0x04, 0x27, 0x8d, 0x04, 0x01, 0x8f, 0x37, 0x89, + 0x19, 0x05, 0x8d, 0x37, 0x81, 0x1d, 0xa2, 0x19, + 0x00, 0x92, 0x19, 0x00, 0x83, 0x19, 0x03, 0x84, + 0x04, 0x00, 0xe0, 0x26, 0x04, 0x01, 0x80, 0x19, + 0x00, 0x9f, 0x19, 0x99, 0x45, 0x85, 0x19, 0x99, + 0x45, 0x8a, 0x19, 0x89, 0x3d, 0x80, 0x19, 0xac, + 0x3d, 0x81, 0x19, 0x9e, 0x30, 0x02, 0x85, 0x30, + 0x01, 0x85, 0x30, 0x01, 0x85, 0x30, 0x01, 0x82, + 0x30, 0x02, 0x86, 0x19, 0x00, 0x86, 0x19, 0x09, + 0x84, 0x19, 0x01, 0x8b, 0x49, 0x00, 0x99, 0x49, + 0x00, 0x92, 0x49, 0x00, 0x81, 0x49, 0x00, 0x8e, + 0x49, 0x01, 0x8d, 0x49, 0x21, 0xe0, 0x1a, 0x49, + 0x04, 0x82, 0x19, 0x03, 0xac, 0x19, 0x02, 0x88, + 0x19, 0xce, 0x2b, 0x00, 0x8c, 0x19, 0x02, 0x80, + 0x2b, 0x2e, 0xac, 0x19, 0x80, 0x37, 0x60, 0x21, + 0x9c, 0x4b, 0x02, 0xb0, 0x13, 0x0e, 0x80, 0x37, + 0x9a, 0x19, 0x03, 0xa3, 0x69, 0x08, 0x82, 0x69, + 0x9a, 0x29, 0x04, 0xaa, 0x6b, 0x04, 0x9d, 0x96, + 0x00, 0x80, 0x96, 0xa3, 0x6c, 0x03, 0x8d, 0x6c, + 0x29, 0xcf, 0x1e, 0xaf, 0x7e, 0x9d, 0x72, 0x01, + 0x89, 0x72, 0x05, 0xa3, 0x71, 0x03, 0xa3, 0x71, + 0x03, 0xa7, 0x24, 0x07, 0xb3, 0x14, 0x0a, 0x80, + 0x14, 0x60, 0x2f, 0xe0, 0xd6, 0x48, 0x08, 0x95, + 0x48, 0x09, 0x87, 0x48, 0x60, 0x37, 0x85, 0x1c, + 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, 0x00, 0x81, + 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, 0x1c, 0x95, + 0x36, 0x00, 0x88, 0x36, 0x9f, 0x74, 0x9e, 0x5f, + 0x07, 0x88, 0x5f, 0x2f, 0x92, 0x33, 0x00, 0x81, + 0x33, 0x04, 0x84, 0x33, 0x9b, 0x77, 0x02, 0x80, + 0x77, 0x99, 0x4c, 0x04, 0x80, 0x4c, 0x3f, 0x9f, + 0x58, 0x97, 0x57, 0x03, 0x93, 0x57, 0x01, 0xad, + 0x57, 0x83, 0x3f, 0x00, 0x81, 0x3f, 0x04, 0x87, + 0x3f, 0x00, 0x82, 0x3f, 0x00, 0x9c, 0x3f, 0x01, + 0x82, 0x3f, 0x03, 0x89, 0x3f, 0x06, 0x88, 0x3f, + 0x06, 0x9f, 0x6e, 0x9f, 0x6a, 0x1f, 0xa6, 0x51, + 0x03, 0x8b, 0x51, 0x08, 0xb5, 0x06, 0x02, 0x86, + 0x06, 0x95, 0x39, 0x01, 0x87, 0x39, 0x92, 0x38, + 0x04, 0x87, 0x38, 0x91, 0x78, 0x06, 0x83, 0x78, + 0x0b, 0x86, 0x78, 0x4f, 0xc8, 0x6f, 0x36, 0xb2, + 0x68, 0x0c, 0xb2, 0x68, 0x06, 0x85, 0x68, 0xa7, + 0x31, 0x07, 0x89, 0x31, 0x60, 0xc5, 0x9e, 0x04, + 0x00, 0xa9, 0x9a, 0x00, 0x82, 0x9a, 0x01, 0x81, + 0x9a, 0x4d, 0xa7, 0x6d, 0x07, 0xa9, 0x82, 0x55, + 0x9b, 0x18, 0x13, 0x96, 0x25, 0x08, 0xcd, 0x0e, + 0x03, 0x9d, 0x0e, 0x0e, 0x80, 0x0e, 0xc1, 0x3b, + 0x0a, 0x80, 0x3b, 0x01, 0x98, 0x83, 0x06, 0x89, + 0x83, 0x05, 0xb4, 0x15, 0x00, 0x91, 0x15, 0x07, + 0xa6, 0x4e, 0x08, 0xdf, 0x7d, 0x00, 0x93, 0x81, + 0x0a, 0x91, 0x41, 0x00, 0xab, 0x41, 0x40, 0x86, + 0x5d, 0x00, 0x80, 0x5d, 0x00, 0x83, 0x5d, 0x00, + 0x8e, 0x5d, 0x00, 0x8a, 0x5d, 0x05, 0xba, 0x43, + 0x04, 0x89, 0x43, 0x05, 0x83, 0x2a, 0x00, 0x87, + 0x2a, 0x01, 0x81, 0x2a, 0x01, 0x95, 0x2a, 0x00, + 0x86, 0x2a, 0x00, 0x81, 0x2a, 0x00, 0x84, 0x2a, + 0x00, 0x80, 0x37, 0x88, 0x2a, 0x01, 0x81, 0x2a, + 0x01, 0x82, 0x2a, 0x01, 0x80, 0x2a, 0x05, 0x80, + 0x2a, 0x04, 0x86, 0x2a, 0x01, 0x86, 0x2a, 0x02, + 0x84, 0x2a, 0x60, 0x2a, 0xdb, 0x62, 0x00, 0x84, + 0x62, 0x1d, 0xc7, 0x95, 0x07, 0x89, 0x95, 0x60, + 0x45, 0xb5, 0x7f, 0x01, 0xa5, 0x7f, 0x21, 0xc4, + 0x5a, 0x0a, 0x89, 0x5a, 0x05, 0x8c, 0x5b, 0x12, + 0xb8, 0x8d, 0x06, 0x89, 0x8d, 0x35, 0x9a, 0x02, + 0x01, 0x8e, 0x02, 0x03, 0x8f, 0x02, 0x60, 0x5f, + 0xbb, 0x21, 0x60, 0x03, 0xd2, 0x99, 0x0b, 0x80, + 0x99, 0x86, 0x20, 0x01, 0x80, 0x20, 0x01, 0x87, + 0x20, 0x00, 0x81, 0x20, 0x00, 0x9d, 0x20, 0x00, + 0x81, 0x20, 0x01, 0x8b, 0x20, 0x08, 0x89, 0x20, + 0x45, 0x87, 0x60, 0x01, 0xad, 0x60, 0x01, 0x8a, + 0x60, 0x1a, 0xc7, 0x9c, 0x07, 0xd2, 0x84, 0x1c, + 0xb8, 0x75, 0x60, 0xa6, 0x88, 0x0c, 0x00, 0xac, + 0x0c, 0x00, 0x8d, 0x0c, 0x09, 0x9c, 0x0c, 0x02, + 0x9f, 0x52, 0x01, 0x95, 0x52, 0x00, 0x8d, 0x52, + 0x48, 0x86, 0x53, 0x00, 0x81, 0x53, 0x00, 0xab, + 0x53, 0x02, 0x80, 0x53, 0x00, 0x81, 0x53, 0x00, + 0x88, 0x53, 0x07, 0x89, 0x53, 0x05, 0x85, 0x2d, + 0x00, 0x81, 0x2d, 0x00, 0xa4, 0x2d, 0x00, 0x81, + 0x2d, 0x00, 0x85, 0x2d, 0x06, 0x89, 0x2d, 0x60, + 0xd5, 0x98, 0x4d, 0x60, 0x56, 0x80, 0x4a, 0x0e, + 0xb1, 0x8e, 0x0c, 0x80, 0x8e, 0xe3, 0x39, 0x1b, + 0x60, 0x05, 0xe0, 0x0e, 0x1b, 0x00, 0x84, 0x1b, + 0x0a, 0xe0, 0x63, 0x1b, 0x6a, 0x5b, 0xe3, 0xce, + 0x23, 0x00, 0x88, 0x23, 0x6f, 0x66, 0xe1, 0xe6, + 0x03, 0x70, 0x11, 0x58, 0xe1, 0xd8, 0x08, 0x06, + 0x9e, 0x5c, 0x00, 0x89, 0x5c, 0x03, 0x81, 0x5c, + 0x5f, 0x9d, 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, + 0x73, 0x09, 0x89, 0x73, 0x00, 0x86, 0x73, 0x00, + 0x94, 0x73, 0x04, 0x92, 0x73, 0x62, 0x4f, 0xda, + 0x54, 0x60, 0x04, 0xca, 0x59, 0x03, 0xb8, 0x59, + 0x06, 0x90, 0x59, 0x3f, 0x80, 0x8f, 0x80, 0x64, + 0x81, 0x19, 0x80, 0x42, 0x0a, 0x81, 0x2f, 0x0d, + 0xf0, 0x07, 0x97, 0x8f, 0x07, 0xe2, 0x9f, 0x8f, + 0xe1, 0x75, 0x42, 0x29, 0x88, 0x8f, 0x70, 0x12, + 0x96, 0x80, 0x3d, 0xe0, 0xbd, 0x35, 0x30, 0x82, + 0x35, 0x10, 0x83, 0x3d, 0x07, 0xe1, 0x2b, 0x64, + 0x68, 0xa3, 0xe0, 0x0a, 0x22, 0x04, 0x8c, 0x22, + 0x02, 0x88, 0x22, 0x06, 0x89, 0x22, 0x01, 0x83, + 0x22, 0x83, 0x19, 0x70, 0x02, 0xfb, 0xe0, 0x95, + 0x19, 0x09, 0xa6, 0x19, 0x01, 0xbd, 0x19, 0x82, + 0x37, 0x90, 0x19, 0x87, 0x37, 0x81, 0x19, 0x86, + 0x37, 0x9d, 0x19, 0x83, 0x37, 0xba, 0x19, 0x16, + 0xc5, 0x2b, 0x60, 0x39, 0x93, 0x19, 0x0b, 0xd6, + 0x19, 0x08, 0x98, 0x19, 0x60, 0x26, 0xd4, 0x19, + 0x00, 0xc6, 0x19, 0x00, 0x81, 0x19, 0x01, 0x80, + 0x19, 0x01, 0x81, 0x19, 0x01, 0x83, 0x19, 0x00, + 0x8b, 0x19, 0x00, 0x80, 0x19, 0x00, 0x86, 0x19, + 0x00, 0xc0, 0x19, 0x00, 0x83, 0x19, 0x01, 0x87, + 0x19, 0x00, 0x86, 0x19, 0x00, 0x9b, 0x19, 0x00, + 0x83, 0x19, 0x00, 0x84, 0x19, 0x00, 0x80, 0x19, + 0x02, 0x86, 0x19, 0x00, 0xe0, 0xf3, 0x19, 0x01, + 0xe0, 0xc3, 0x19, 0x01, 0xb1, 0x19, 0xe2, 0x2b, + 0x80, 0x0e, 0x84, 0x80, 0x00, 0x8e, 0x80, 0x64, + 0xef, 0x86, 0x28, 0x00, 0x90, 0x28, 0x01, 0x86, + 0x28, 0x00, 0x81, 0x28, 0x00, 0x84, 0x28, 0x60, + 0x74, 0xac, 0x65, 0x02, 0x8d, 0x65, 0x01, 0x89, + 0x65, 0x03, 0x81, 0x65, 0x61, 0x0f, 0xb9, 0x98, + 0x04, 0x80, 0x98, 0x64, 0x9f, 0xe0, 0x64, 0x56, + 0x01, 0x8f, 0x56, 0x28, 0xcb, 0x01, 0x03, 0x89, + 0x01, 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, + 0x4b, 0xbc, 0x19, 0x60, 0x61, 0x83, 0x04, 0x00, + 0x9a, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, + 0x01, 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, + 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, + 0x80, 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, + 0x04, 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, + 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, + 0x04, 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, + 0x83, 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, + 0x00, 0x89, 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, + 0x04, 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, + 0x81, 0x04, 0x60, 0xad, 0xab, 0x19, 0x03, 0xe0, + 0x03, 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, + 0x00, 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, + 0x4d, 0x19, 0x37, 0x99, 0x19, 0x80, 0x35, 0x81, + 0x19, 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, + 0x81, 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, + 0x77, 0x19, 0x07, 0x8c, 0x19, 0x02, 0x8c, 0x19, + 0x02, 0xe0, 0x13, 0x19, 0x0b, 0xd8, 0x19, 0x06, + 0x8b, 0x19, 0x13, 0x8b, 0x19, 0x03, 0xb7, 0x19, + 0x07, 0x89, 0x19, 0x05, 0xa7, 0x19, 0x07, 0x9d, + 0x19, 0x01, 0x81, 0x19, 0x4d, 0xe0, 0x18, 0x19, + 0x00, 0xd1, 0x19, 0x00, 0xe0, 0x26, 0x19, 0x0b, + 0x8d, 0x19, 0x01, 0x84, 0x19, 0x02, 0x82, 0x19, + 0x04, 0x86, 0x19, 0x08, 0x98, 0x19, 0x06, 0x86, + 0x19, 0x08, 0x82, 0x19, 0x0c, 0x86, 0x19, 0x28, + 0xe0, 0x32, 0x19, 0x00, 0xb6, 0x19, 0x24, 0x89, + 0x19, 0x63, 0xa5, 0xf0, 0x96, 0x7d, 0x2f, 0x21, + 0xef, 0xd4, 0x2f, 0x0a, 0xe0, 0x7d, 0x2f, 0x01, + 0xf0, 0x06, 0x21, 0x2f, 0x0d, 0xf0, 0x0c, 0xd0, + 0x2f, 0x6b, 0xbe, 0xe1, 0xbd, 0x2f, 0x65, 0x81, + 0xf0, 0x02, 0xea, 0x2f, 0x7a, 0xdc, 0x55, 0x80, + 0x19, 0x1d, 0xdf, 0x19, 0x60, 0x1f, 0xe0, 0x8f, + 0x37, }; -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_script_ext_table[799] = { + 0x82, 0xc1, 0x00, 0x00, 0x01, 0x2b, 0x01, 0x00, + 0x00, 0x01, 0x2b, 0x1c, 0x00, 0x0c, 0x01, 0x45, + 0x80, 0x92, 0x00, 0x00, 0x02, 0x1d, 0x6b, 0x00, + 0x02, 0x1d, 0x28, 0x01, 0x02, 0x1d, 0x45, 0x00, + 0x02, 0x1d, 0x28, 0x81, 0x03, 0x00, 0x00, 0x05, + 0x04, 0x31, 0x87, 0x91, 0x9a, 0x0d, 0x00, 0x00, + 0x05, 0x04, 0x31, 0x87, 0x91, 0x9a, 0x00, 0x03, + 0x04, 0x87, 0x91, 0x01, 0x00, 0x00, 0x05, 0x04, + 0x31, 0x87, 0x91, 0x9a, 0x1f, 0x00, 0x00, 0x08, + 0x01, 0x04, 0x50, 0x51, 0x78, 0x31, 0x82, 0x87, + 0x09, 0x00, 0x0a, 0x02, 0x04, 0x87, 0x09, 0x00, + 0x09, 0x03, 0x04, 0x91, 0x9a, 0x05, 0x00, 0x00, + 0x02, 0x04, 0x87, 0x62, 0x00, 0x00, 0x02, 0x04, + 0x31, 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x1f, + 0x2a, 0x2c, 0x2e, 0x3c, 0x45, 0x4f, 0x70, 0x7d, + 0x8e, 0x90, 0x95, 0x00, 0x0c, 0x0b, 0x1f, 0x2a, + 0x2c, 0x2e, 0x3c, 0x45, 0x4f, 0x70, 0x8e, 0x90, + 0x95, 0x10, 0x00, 0x00, 0x14, 0x0b, 0x1f, 0x21, + 0x2d, 0x53, 0x2a, 0x2c, 0x2e, 0x3c, 0x4e, 0x4f, + 0x60, 0x70, 0x43, 0x81, 0x86, 0x8d, 0x8e, 0x90, + 0x95, 0x00, 0x15, 0x0b, 0x1f, 0x21, 0x2d, 0x53, + 0x2a, 0x2c, 0x2e, 0x3c, 0x47, 0x4e, 0x4f, 0x60, + 0x70, 0x43, 0x81, 0x86, 0x8d, 0x8e, 0x90, 0x95, + 0x09, 0x04, 0x1f, 0x21, 0x3b, 0x4e, 0x75, 0x00, + 0x09, 0x03, 0x0b, 0x15, 0x86, 0x75, 0x00, 0x09, + 0x02, 0x2e, 0x5d, 0x75, 0x00, 0x09, 0x02, 0x2c, + 0x41, 0x80, 0x75, 0x00, 0x0d, 0x02, 0x2a, 0x8e, + 0x80, 0x71, 0x00, 0x09, 0x02, 0x3c, 0x60, 0x82, + 0xcf, 0x00, 0x09, 0x03, 0x15, 0x5e, 0x8a, 0x80, + 0x30, 0x00, 0x00, 0x02, 0x27, 0x45, 0x85, 0xb8, + 0x00, 0x01, 0x04, 0x11, 0x32, 0x89, 0x88, 0x80, + 0x4a, 0x00, 0x01, 0x02, 0x5b, 0x76, 0x00, 0x00, + 0x00, 0x02, 0x5b, 0x76, 0x84, 0x49, 0x00, 0x00, + 0x04, 0x0b, 0x1f, 0x2a, 0x3c, 0x00, 0x01, 0x1f, + 0x00, 0x04, 0x0b, 0x1f, 0x2a, 0x3c, 0x00, 0x02, + 0x1f, 0x2a, 0x00, 0x01, 0x1f, 0x01, 0x02, 0x0b, + 0x1f, 0x00, 0x02, 0x1f, 0x7d, 0x00, 0x02, 0x0b, + 0x1f, 0x00, 0x02, 0x1f, 0x7d, 0x00, 0x06, 0x1f, + 0x3c, 0x4f, 0x70, 0x8e, 0x90, 0x00, 0x01, 0x1f, + 0x01, 0x02, 0x1f, 0x7d, 0x01, 0x01, 0x1f, 0x00, + 0x02, 0x1f, 0x7d, 0x00, 0x02, 0x0b, 0x1f, 0x06, + 0x01, 0x1f, 0x00, 0x02, 0x1f, 0x60, 0x00, 0x02, + 0x0b, 0x1f, 0x01, 0x01, 0x1f, 0x00, 0x02, 0x0b, + 0x1f, 0x03, 0x01, 0x1f, 0x00, 0x08, 0x0b, 0x1f, + 0x2a, 0x3c, 0x60, 0x70, 0x90, 0x95, 0x00, 0x02, + 0x1f, 0x2a, 0x00, 0x03, 0x1f, 0x2a, 0x3c, 0x01, + 0x02, 0x0b, 0x1f, 0x00, 0x01, 0x0b, 0x01, 0x02, + 0x1f, 0x2a, 0x00, 0x01, 0x60, 0x80, 0x44, 0x00, + 0x01, 0x01, 0x2b, 0x35, 0x00, 0x00, 0x02, 0x1d, + 0x87, 0x81, 0xb5, 0x00, 0x00, 0x02, 0x45, 0x5b, + 0x80, 0x3f, 0x00, 0x00, 0x03, 0x1f, 0x2a, 0x45, + 0x8c, 0xd1, 0x00, 0x00, 0x02, 0x1d, 0x28, 0x81, + 0x3c, 0x00, 0x01, 0x06, 0x0d, 0x30, 0x2f, 0x35, + 0x3d, 0x9b, 0x00, 0x05, 0x0d, 0x30, 0x2f, 0x35, + 0x3d, 0x01, 0x00, 0x00, 0x01, 0x2f, 0x00, 0x00, + 0x09, 0x06, 0x0d, 0x30, 0x2f, 0x35, 0x3d, 0x9b, + 0x00, 0x00, 0x00, 0x05, 0x0d, 0x30, 0x2f, 0x35, + 0x3d, 0x07, 0x06, 0x0d, 0x30, 0x2f, 0x35, 0x3d, + 0x9b, 0x03, 0x05, 0x0d, 0x30, 0x2f, 0x35, 0x3d, + 0x09, 0x00, 0x03, 0x02, 0x0d, 0x2f, 0x01, 0x00, + 0x00, 0x05, 0x0d, 0x30, 0x2f, 0x35, 0x3d, 0x04, + 0x02, 0x35, 0x3d, 0x00, 0x00, 0x00, 0x05, 0x0d, + 0x30, 0x2f, 0x35, 0x3d, 0x03, 0x00, 0x01, 0x03, + 0x2f, 0x35, 0x3d, 0x01, 0x01, 0x2f, 0x58, 0x00, + 0x03, 0x02, 0x35, 0x3d, 0x02, 0x00, 0x00, 0x02, + 0x35, 0x3d, 0x59, 0x00, 0x00, 0x06, 0x0d, 0x30, + 0x2f, 0x35, 0x3d, 0x9b, 0x00, 0x02, 0x35, 0x3d, + 0x80, 0x12, 0x00, 0x0f, 0x01, 0x2f, 0x1f, 0x00, + 0x23, 0x01, 0x2f, 0x3b, 0x00, 0x27, 0x01, 0x2f, + 0x37, 0x00, 0x30, 0x01, 0x2f, 0x0e, 0x00, 0x0b, + 0x01, 0x2f, 0x32, 0x00, 0x00, 0x01, 0x2f, 0x57, + 0x00, 0x18, 0x01, 0x2f, 0x09, 0x00, 0x04, 0x01, + 0x2f, 0x5f, 0x00, 0x1e, 0x01, 0x2f, 0xc0, 0x31, + 0xef, 0x00, 0x00, 0x02, 0x1d, 0x28, 0x80, 0x0f, + 0x00, 0x07, 0x02, 0x2f, 0x45, 0x80, 0xa7, 0x00, + 0x02, 0x0e, 0x1f, 0x21, 0x2c, 0x2e, 0x41, 0x3c, + 0x3b, 0x4e, 0x4f, 0x5a, 0x60, 0x43, 0x8d, 0x95, + 0x02, 0x0d, 0x1f, 0x21, 0x2c, 0x2e, 0x41, 0x3c, + 0x3b, 0x4e, 0x5a, 0x60, 0x43, 0x8d, 0x95, 0x03, + 0x0b, 0x1f, 0x21, 0x2c, 0x2e, 0x41, 0x3b, 0x4e, + 0x5a, 0x43, 0x8d, 0x95, 0x80, 0x36, 0x00, 0x00, + 0x02, 0x0b, 0x1f, 0x00, 0x00, 0x00, 0x02, 0x1f, + 0x8e, 0x39, 0x00, 0x00, 0x03, 0x3e, 0x45, 0x5e, + 0x80, 0x1f, 0x00, 0x00, 0x02, 0x10, 0x3a, 0xc0, + 0x13, 0xa1, 0x00, 0x00, 0x02, 0x04, 0x91, 0x09, + 0x00, 0x00, 0x02, 0x04, 0x91, 0x46, 0x00, 0x01, + 0x05, 0x0d, 0x30, 0x2f, 0x35, 0x3d, 0x80, 0x99, + 0x00, 0x04, 0x06, 0x0d, 0x30, 0x2f, 0x35, 0x3d, + 0x9b, 0x09, 0x00, 0x00, 0x02, 0x35, 0x3d, 0x2c, + 0x00, 0x01, 0x02, 0x35, 0x3d, 0x80, 0xdf, 0x00, + 0x02, 0x02, 0x1c, 0x49, 0x03, 0x00, 0x2c, 0x03, + 0x1c, 0x48, 0x49, 0x02, 0x00, 0x08, 0x02, 0x1c, + 0x49, 0x81, 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, + 0x8f, 0x84, 0x00, 0x00, 0x02, 0x2a, 0x8e, 0x00, + 0x00, 0x00, 0x02, 0x2a, 0x8e, 0x36, 0x00, 0x01, + 0x02, 0x2a, 0x8e, 0x8c, 0x12, 0x00, 0x01, 0x02, + 0x2a, 0x8e, 0x00, 0x00, 0x00, 0x02, 0x2a, 0x8e, + 0xc0, 0x5c, 0x4b, 0x00, 0x03, 0x01, 0x22, 0x96, + 0x3b, 0x00, 0x11, 0x01, 0x2f, 0x9e, 0x5d, 0x00, + 0x01, 0x01, 0x2f, 0xce, 0xcd, 0x2d, 0x00, }; static const uint8_t unicode_prop_Hyphen_table[28] = { @@ -3501,7 +3544,7 @@ static const uint8_t unicode_prop_Other_Math_table[200] = { 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, }; -static const uint8_t unicode_prop_Other_Alphabetic_table[396] = { +static const uint8_t unicode_prop_Other_Alphabetic_table[411] = { 0x43, 0x44, 0x80, 0x42, 0x69, 0x8d, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, 0x06, 0x8f, 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, 0xa2, 0x80, @@ -3517,7 +3560,7 @@ static const uint8_t unicode_prop_Other_Alphabetic_table[396] = { 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, 0x9c, 0x82, 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, @@ -3525,33 +3568,35 @@ static const uint8_t unicode_prop_Other_Alphabetic_table[396] = { 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, + 0x84, 0xb8, 0x89, 0x81, 0x93, 0xc9, 0x81, 0xbe, + 0x84, 0xaf, 0x8e, 0xbb, 0x82, 0x9d, 0x88, 0x09, + 0xb8, 0x8a, 0xb1, 0x92, 0x41, 0xaf, 0x8d, 0x46, + 0xc0, 0xb3, 0x48, 0xf5, 0x9f, 0x60, 0x78, 0x73, + 0x87, 0xa1, 0x81, 0x41, 0x61, 0x07, 0x80, 0x96, + 0x84, 0xd7, 0x81, 0xb1, 0x8f, 0x00, 0xb8, 0x80, + 0xa5, 0x84, 0x9b, 0x8b, 0xac, 0x83, 0xaf, 0x8b, + 0xa4, 0x80, 0xc2, 0x8d, 0x8b, 0x07, 0x81, 0xac, + 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x80, 0xab, 0x24, + 0x80, 0x40, 0xec, 0x87, 0x60, 0x4f, 0x32, 0x80, + 0x48, 0x56, 0x84, 0x46, 0x85, 0x10, 0x0c, 0x83, + 0x43, 0x13, 0x83, 0x41, 0x82, 0x81, 0x41, 0x52, + 0x82, 0xb4, 0x8d, 0xbb, 0x80, 0xac, 0x88, 0xc6, + 0x82, 0xa3, 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, + 0x8c, 0x8d, 0x81, 0xdb, 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, 0x40, 0xf6, 0x28, 0x09, 0x0a, 0x00, + 0x80, 0x40, 0x8d, 0x31, 0x2b, 0x80, 0x9b, 0x89, + 0xa9, 0x20, 0x83, 0x91, 0x8a, 0xad, 0x8d, 0x41, + 0x96, 0x38, 0x86, 0xd2, 0x95, 0x80, 0x8d, 0xf9, + 0x2a, 0x00, 0x08, 0x10, 0x02, 0x80, 0xc1, 0x20, + 0x08, 0x83, 0x41, 0x5b, 0x83, 0x60, 0x50, 0x57, + 0x00, 0xb6, 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, + 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] = { @@ -3569,15 +3614,16 @@ static const uint8_t unicode_prop_Other_Uppercase_table[15] = { 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, }; -static const uint8_t unicode_prop_Other_Grapheme_Extend_table[62] = { +static const uint8_t unicode_prop_Other_Grapheme_Extend_table[65] = { 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe9, 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, 0x80, 0xf6, 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5, 0x80, 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81, 0x53, 0x9d, 0x80, 0x97, 0x80, 0x41, 0x57, 0x80, - 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x60, 0xbb, 0xb4, - 0x07, 0x84, 0x6c, 0x2e, 0xac, 0xdf, + 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x43, 0x7f, 0x80, + 0x60, 0xb8, 0x33, 0x07, 0x84, 0x6c, 0x2e, 0xac, + 0xdf, }; static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { @@ -3630,7 +3676,7 @@ static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = { 0x84, }; -static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[436] = { +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[441] = { 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, 0x10, 0x82, 0x9f, 0x80, 0xcf, 0x01, 0x80, 0x8b, 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80, @@ -3663,29 +3709,30 @@ static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[436] = { 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, + 0x86, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, 0x60, + 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9, 0xa5, + 0x86, 0x8b, 0x24, 0x00, 0x97, 0x04, 0x00, 0x01, + 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, 0x91, 0xbf, + 0x81, 0xb5, 0xa7, 0x8c, 0x82, 0x99, 0x95, 0x94, + 0x81, 0x8b, 0x80, 0x92, 0x03, 0x1a, 0x00, 0x80, + 0x40, 0x86, 0x08, 0x80, 0x9f, 0x99, 0x40, 0x83, + 0x15, 0x0d, 0x0d, 0x0a, 0x16, 0x06, 0x80, 0x88, + 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, 0x49, 0x9d, 0x89, 0x60, + 0xfc, 0x05, 0x42, 0x1d, 0x6b, 0x05, 0xe1, 0x4f, + 0xff, }; static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = { @@ -3697,14 +3744,14 @@ static const uint8_t unicode_prop_Bidi_Control_table[10] = { 0xb6, 0x83, }; -static const uint8_t unicode_prop_Dash_table[50] = { +static const uint8_t unicode_prop_Dash_table[53] = { 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, + 0xa8, 0x80, 0x4f, 0x9e, 0x80, }; static const uint8_t unicode_prop_Deprecated_table[23] = { @@ -3713,7 +3760,7 @@ static const uint8_t unicode_prop_Deprecated_table[23] = { 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, }; -static const uint8_t unicode_prop_Diacritic_table[350] = { +static const uint8_t unicode_prop_Diacritic_table[358] = { 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b, 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, @@ -3723,55 +3770,57 @@ static const uint8_t unicode_prop_Diacritic_table[350] = { 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, + 0x82, 0xbb, 0x80, 0x8f, 0x06, 0x80, 0xf6, 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, 0x88, 0x82, + 0xff, 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, 0x01, 0x0b, 0x80, 0x40, 0x9b, 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, 0xcf, + 0x81, 0x60, 0x61, 0x74, 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] = { +static const uint8_t unicode_prop_Extender_table[89] = { 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, + 0x80, 0x41, 0xb8, 0x80, 0x43, 0x59, 0x80, 0x42, + 0xef, 0x80, 0xfe, 0x80, 0x49, 0x42, 0x80, 0xb7, + 0x80, 0x42, 0x62, 0x80, 0x41, 0x8d, 0x80, 0xc3, + 0x80, 0x53, 0x88, 0x80, 0xaa, 0x84, 0xe6, 0x81, + 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x45, 0xf5, + 0x80, 0x43, 0xc1, 0x80, 0x95, 0x80, 0x40, 0x88, + 0x80, 0xeb, 0x80, 0x94, 0x81, 0x60, 0x54, 0x7a, + 0x80, 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] = { @@ -3787,15 +3836,16 @@ static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { 0x60, 0x2f, 0xf1, 0x81, }; -static const uint8_t unicode_prop_Ideographic_table[58] = { +static const uint8_t unicode_prop_Ideographic_table[66] = { 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, + 0x43, 0xc4, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xfc, + 0x60, 0x59, 0x02, 0x41, 0x6d, 0x81, 0xe9, 0x60, + 0x75, 0x09, 0x80, 0x9a, 0x57, 0xf7, 0x87, 0x44, + 0xd5, 0xa9, 0x88, 0x60, 0x24, 0x66, 0x41, 0x8b, + 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdd, 0xa1, 0x50, + 0x34, 0x8a, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, + 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1, + 0x53, 0x4a, }; static const uint8_t unicode_prop_Join_Control_table[4] = { @@ -3851,7 +3901,7 @@ static const uint8_t unicode_prop_Regional_Indicator_table[4] = { 0x61, 0xf1, 0xe5, 0x99, }; -static const uint8_t unicode_prop_Sentence_Terminal_table[184] = { +static const uint8_t unicode_prop_Sentence_Terminal_table[188] = { 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, 0x80, 0x40, 0x93, 0x81, 0x40, 0xb3, 0x80, 0xaa, 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81, @@ -3871,10 +3921,11 @@ static const uint8_t unicode_prop_Sentence_Terminal_table[184] = { 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, + 0x42, 0x04, 0x00, 0x80, 0x40, 0xfa, 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] = { @@ -3889,7 +3940,7 @@ static const uint8_t unicode_prop_Soft_Dotted_table[71] = { 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, }; -static const uint8_t unicode_prop_Terminal_Punctuation_table[237] = { +static const uint8_t unicode_prop_Terminal_Punctuation_table[241] = { 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8, 0x80, 0xc7, 0x80, 0x8d, 0x01, 0x81, 0x40, 0xb3, @@ -3913,21 +3964,23 @@ static const uint8_t unicode_prop_Terminal_Punctuation_table[237] = { 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, + 0x80, 0x41, 0xa0, 0x82, 0x8b, 0x81, 0x41, 0x65, + 0x1a, 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x42, + 0x04, 0x00, 0x80, 0x40, 0xfa, 0x81, 0xd6, 0x0b, + 0x81, 0x41, 0x9d, 0x82, 0xac, 0x80, 0x42, 0x84, + 0x81, 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, +static const uint8_t unicode_prop_Unified_Ideograph_table[42] = { + 0x60, 0x33, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x51, + 0xfc, 0x60, 0x5a, 0x10, 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, + 0xa6, 0xdd, 0xa1, 0x50, 0x34, 0x8a, 0x40, 0xdd, + 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x54, 0x1e, + 0x53, 0x4a, }; static const uint8_t unicode_prop_Variation_Selector_table[12] = { @@ -3966,7 +4019,7 @@ static const uint8_t unicode_prop_Bidi_Mirrored_table[171] = { 0x80, 0xb8, 0x80, }; -static const uint8_t unicode_prop_Emoji_table[236] = { +static const uint8_t unicode_prop_Emoji_table[238] = { 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, @@ -3976,27 +4029,27 @@ static const uint8_t unicode_prop_Emoji_table[236] = { 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, + 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, + 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03, + 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, + 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, + 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88, + 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, + 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, + 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, + 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, + 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, + 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88, + 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, + 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, + 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, + 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, + 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, + 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 0x3d, + 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2, 0x8b, + 0x41, 0x1f, 0xae, 0x80, 0x89, 0x80, 0xb1, 0x80, + 0xd1, 0x80, 0xb2, 0xef, 0x22, 0x14, 0x86, 0x88, + 0x98, 0x36, 0x88, 0x82, 0x8c, 0x86, }; static const uint8_t unicode_prop_Emoji_Component_table[28] = { @@ -4010,18 +4063,19 @@ 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] = { +static const uint8_t unicode_prop_Emoji_Modifier_Base_table[66] = { 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, + 0x42, 0x3e, 0x01, 0x07, 0x3d, 0x80, 0x88, 0x89, + 0x0a, 0xb7, 0x80, 0xbc, 0x08, 0x08, 0x80, 0x90, + 0x10, 0x8c, }; -static const uint8_t unicode_prop_Emoji_Presentation_table[143] = { +static const uint8_t unicode_prop_Emoji_Presentation_table[144] = { 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b, 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, @@ -4036,13 +4090,13 @@ static const uint8_t unicode_prop_Emoji_Presentation_table[143] = { 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, + 0xc5, 0x28, 0x12, 0x0a, 0x92, 0x0e, 0x88, 0x40, + 0xe2, 0x8b, 0x41, 0x1f, 0xae, 0x80, 0x89, 0x80, + 0xb1, 0x80, 0xd1, 0x80, 0xb2, 0xef, 0x22, 0x14, + 0x86, 0x88, 0x98, 0x36, 0x88, 0x82, 0x8c, 0x86, }; -static const uint8_t unicode_prop_Extended_Pictographic_table[152] = { +static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, @@ -4061,7 +4115,8 @@ static const uint8_t unicode_prop_Extended_Pictographic_table[152] = { 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, + 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, + 0x40, 0xff, 0x43, 0xfd, }; static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { @@ -4175,11 +4230,11 @@ static const char unicode_prop_name_table[] = "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" + "Emoji_Component,EComp" "\0" + "Emoji_Modifier,EMod" "\0" + "Emoji_Modifier_Base,EBase" "\0" + "Emoji_Presentation,EPres" "\0" + "Extended_Pictographic,ExtPict" "\0" "Default_Ignorable_Code_Point,DI" "\0" "ID_Start,IDS" "\0" "Case_Ignorable,CI" "\0" diff --git a/libunicode.c b/libunicode.c index 96ff002..63c12a0 100644 --- a/libunicode.c +++ b/libunicode.c @@ -527,7 +527,13 @@ static int unicode_decomp_entry(uint32_t *res, uint32_t c, } else { d = unicode_decomp_data + unicode_decomp_table2[idx]; switch(type) { - case DECOMP_TYPE_L1 ... DECOMP_TYPE_L7: + case DECOMP_TYPE_L1: + case DECOMP_TYPE_L2: + case DECOMP_TYPE_L3: + case DECOMP_TYPE_L4: + case DECOMP_TYPE_L5: + case DECOMP_TYPE_L6: + case DECOMP_TYPE_L7: l = type - DECOMP_TYPE_L1 + 1; d += (c - code) * l * 2; for(i = 0; i < l; i++) { @@ -535,7 +541,8 @@ static int unicode_decomp_entry(uint32_t *res, uint32_t c, return 0; } return l; - case DECOMP_TYPE_LL1 ... DECOMP_TYPE_LL2: + case DECOMP_TYPE_LL1: + case DECOMP_TYPE_LL2: { uint32_t k, p; l = type - DECOMP_TYPE_LL1 + 1; @@ -551,7 +558,11 @@ static int unicode_decomp_entry(uint32_t *res, uint32_t c, } } return l; - case DECOMP_TYPE_S1 ... DECOMP_TYPE_S5: + case DECOMP_TYPE_S1: + case DECOMP_TYPE_S2: + case DECOMP_TYPE_S3: + case DECOMP_TYPE_S4: + case DECOMP_TYPE_S5: l = type - DECOMP_TYPE_S1 + 1; d += (c - code) * l; for(i = 0; i < l; i++) { @@ -582,7 +593,14 @@ static int unicode_decomp_entry(uint32_t *res, uint32_t c, case DECOMP_TYPE_B18: l = 18; goto decomp_type_b; - case DECOMP_TYPE_B1 ... DECOMP_TYPE_B8: + case DECOMP_TYPE_B1: + case DECOMP_TYPE_B2: + case DECOMP_TYPE_B3: + case DECOMP_TYPE_B4: + case DECOMP_TYPE_B5: + case DECOMP_TYPE_B6: + case DECOMP_TYPE_B7: + case DECOMP_TYPE_B8: l = type - DECOMP_TYPE_B1 + 1; decomp_type_b: { diff --git a/qjs.c b/qjs.c index fbff078..4dd11f8 100644 --- a/qjs.c +++ b/qjs.c @@ -1,8 +1,8 @@ /* * QuickJS stand alone interpreter * - * Copyright (c) 2017-2018 Fabrice Bellard - * Copyright (c) 2017-2018 Charlie Gordon + * 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 @@ -46,6 +46,7 @@ extern const uint32_t qjsc_repl_size; #ifdef CONFIG_BIGNUM extern const uint8_t qjsc_qjscalc[]; extern const uint32_t qjsc_qjscalc_size; +static int bignum_ext; #endif static int eval_buf(JSContext *ctx, const void *buf, int buf_len, @@ -101,6 +102,27 @@ static int eval_file(JSContext *ctx, const char *filename, int module) return ret; } +/* also used to initialize the worker context */ +static JSContext *JS_NewCustomContext(JSRuntime *rt) +{ + JSContext *ctx; + ctx = JS_NewContext(rt); + if (!ctx) + return NULL; +#ifdef CONFIG_BIGNUM + if (bignum_ext) { + JS_AddIntrinsicBigFloat(ctx); + JS_AddIntrinsicBigDecimal(ctx); + JS_AddIntrinsicOperators(ctx); + JS_EnableBignumExt(ctx, TRUE); + } +#endif + /* system modules */ + js_init_module_std(ctx, "std"); + js_init_module_os(ctx, "os"); + return ctx; +} + #if defined(__APPLE__) #define MALLOC_OVERHEAD 0 #else @@ -250,27 +272,28 @@ static const JSMallocFunctions trace_mf = { #endif }; -#ifdef CONFIG_BIGNUM -#define PROG_NAME "qjsbn" -#else #define PROG_NAME "qjs" -#endif void help(void) { printf("QuickJS version " CONFIG_VERSION "\n" - "usage: " PROG_NAME " [options] [file]\n" + "usage: " PROG_NAME " [options] [file [args]]\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" + "-I --include file include an additional file\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" + " --stack-size n limit the stack size to 'n' bytes\n" + " --unhandled-rejection dump unhandled promise rejections\n" "-q --quit just instantiate the interpreter and quit\n"); exit(1); } @@ -288,10 +311,15 @@ int main(int argc, char **argv) int empty_run = 0; int module = -1; int load_std = 0; + int dump_unhandled_promise_rejection = 0; + size_t memory_limit = 0; + char *include_list[32]; + int i, include_count = 0; #ifdef CONFIG_BIGNUM int load_jscalc; #endif - + size_t stack_size = 0; + #ifdef CONFIG_BIGNUM /* load jscalc runtime if invoked as 'qjscalc' */ { @@ -341,6 +369,18 @@ int main(int argc, char **argv) fprintf(stderr, "qjs: missing expression for -e\n"); exit(2); } + if (opt == 'I' || !strcmp(longopt, "include")) { + if (optind >= argc) { + fprintf(stderr, "expecting filename"); + exit(1); + } + if (include_count >= countof(include_list)) { + fprintf(stderr, "too many included files"); + exit(1); + } + include_list[include_count++] = argv[optind++]; + continue; + } if (opt == 'i' || !strcmp(longopt, "interactive")) { interactive++; continue; @@ -365,7 +405,15 @@ int main(int argc, char **argv) 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; @@ -375,6 +423,22 @@ int main(int argc, char **argv) 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 (!strcmp(longopt, "stack-size")) { + if (optind >= argc) { + fprintf(stderr, "expecting stack size"); + exit(1); + } + stack_size = (size_t)strtod(argv[optind++], NULL); + continue; + } if (opt) { fprintf(stderr, "qjs: unknown option '-%c'\n", opt); } else { @@ -384,6 +448,9 @@ int main(int argc, char **argv) } } + if (load_jscalc) + bignum_ext = 1; + if (trace_memory) { js_trace_malloc_init(&trace_data); rt = JS_NewRuntime2(&trace_mf, &trace_data); @@ -394,7 +461,13 @@ int main(int argc, char **argv) fprintf(stderr, "qjs: cannot allocate JS runtime\n"); exit(2); } - ctx = JS_NewContext(rt); + if (memory_limit != 0) + JS_SetMemoryLimit(rt, memory_limit); + if (stack_size != 0) + JS_SetMaxStackSize(rt, stack_size); + js_std_set_worker_new_context_func(JS_NewCustomContext); + js_std_init_handlers(rt); + ctx = JS_NewCustomContext(rt); if (!ctx) { fprintf(stderr, "qjs: cannot allocate JS context\n"); exit(2); @@ -402,7 +475,12 @@ int main(int argc, char **argv) /* 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) { @@ -411,10 +489,6 @@ int main(int argc, char **argv) #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" @@ -424,6 +498,11 @@ int main(int argc, char **argv) eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); } + for(i = 0; i < include_count; i++) { + if (eval_file(ctx, include_list[i], module)) + goto fail; + } + if (expr) { if (eval_buf(ctx, expr, strlen(expr), "", 0)) goto fail; diff --git a/qjsc.c b/qjsc.c index b0f20f7..f5bda57 100644 --- a/qjsc.c +++ b/qjsc.c @@ -1,7 +1,7 @@ /* * QuickJS command line compiler * - * Copyright (c) 2018-2019 Fabrice Bellard + * 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 @@ -76,6 +76,9 @@ static const FeatureEntry feature_list[] = { { "promise", "Promise" }, #define FE_MODULE_LOADER 9 { "module-loader", NULL }, +#ifdef CONFIG_BIGNUM + { "bigint", "BigInt" }, +#endif }; void namelist_add(namelist_t *lp, const char *name, const char *short_name, @@ -323,6 +326,8 @@ static const char main_c_template1[] = " JSRuntime *rt;\n" " JSContext *ctx;\n" " rt = JS_NewRuntime();\n" + " js_std_set_worker_new_context_func(JS_NewCustomContext);\n" + " js_std_init_handlers(rt);\n" ; static const char main_c_template2[] = @@ -332,11 +337,7 @@ static const char main_c_template2[] = " return 0;\n" "}\n"; -#ifdef CONFIG_BIGNUM -#define PROG_NAME "qjscbn" -#else #define PROG_NAME "qjsc" -#endif void help(void) { @@ -349,14 +350,17 @@ void help(void) "-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" + "-D module_name compile a dynamically loaded module or worker\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" - ); + "-S n set the maximum stack size to 'n' bytes (default=%d)\n", + JS_DEFAULT_STACK_SIZE); #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) @@ -420,11 +424,7 @@ static int output_executable(const char *out_filename, const char *cfilename, } lto_suffix = ""; -#ifdef CONFIG_BIGNUM - bn_suffix = ".bn"; -#else bn_suffix = ""; -#endif arg = argv; *arg++ = CONFIG_CC; @@ -451,6 +451,7 @@ static int output_executable(const char *out_filename, const char *cfilename, *arg++ = libjsname; *arg++ = "-lm"; *arg++ = "-ldl"; + *arg++ = "-lpthread"; *arg = NULL; if (verbose) { @@ -491,6 +492,11 @@ int main(int argc, char **argv) BOOL use_lto; int module; OutputTypeEnum output_type; + size_t stack_size; +#ifdef CONFIG_BIGNUM + BOOL bignum_ext = FALSE; +#endif + namelist_t dynamic_module_list; out_filename = NULL; output_type = OUTPUT_EXECUTABLE; @@ -500,13 +506,15 @@ int main(int argc, char **argv) byte_swap = FALSE; verbose = 0; use_lto = FALSE; - + stack_size = 0; + memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); + /* 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:"); + c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:D:"); if (c == -1) break; switch(c) { @@ -540,7 +548,13 @@ int main(int argc, char **argv) } if (i == countof(feature_list)) goto bad_feature; - } else { + } else +#ifdef CONFIG_BIGNUM + if (!strcmp(optarg, "bignum")) { + bignum_ext = TRUE; + } else +#endif + { bad_feature: fprintf(stderr, "unsupported feature: %s\n", optarg); exit(1); @@ -566,6 +580,9 @@ int main(int argc, char **argv) namelist_add(&cmodule_list, path, cname, 0); } break; + case 'D': + namelist_add(&dynamic_module_list, optarg, NULL, 0); + break; case 'x': byte_swap = TRUE; break; @@ -575,6 +592,9 @@ int main(int argc, char **argv) case 'p': c_ident_prefix = optarg; break; + case 'S': + stack_size = (size_t)strtod(optarg, NULL); + break; default: break; } @@ -610,9 +630,15 @@ int main(int argc, char **argv) outfile = fo; rt = JS_NewRuntime(); - ctx = JS_NewContextRaw(rt); - JS_AddIntrinsicEval(ctx); - JS_AddIntrinsicRegExpCompiler(ctx); + ctx = JS_NewContext(rt); +#ifdef CONFIG_BIGNUM + if (bignum_ext) { + JS_AddIntrinsicBigFloat(ctx); + JS_AddIntrinsicBigDecimal(ctx); + JS_AddIntrinsicOperators(ctx); + JS_EnableBignumExt(ctx, TRUE); + } +#endif /* loader for ES6 modules */ JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); @@ -637,17 +663,22 @@ int main(int argc, char **argv) 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"); + for(i = 0; i < dynamic_module_list.count; i++) { + if (!jsc_module_loader(ctx, dynamic_module_list.array[i].name, NULL)) { + fprintf(stderr, "Could not load dynamic module '%s'\n", + dynamic_module_list.array[i].name); + exit(1); } - + } + + if (output_type != OUTPUT_C) { + fprintf(fo, + "static JSContext *JS_NewCustomContext(JSRuntime *rt)\n" + "{\n" + " JSContext *ctx = JS_NewContextRaw(rt);\n" + " if (!ctx)\n" + " return 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)) && @@ -656,9 +687,17 @@ int main(int argc, char **argv) feature_list[i].init_name); } } - - fprintf(fo, " js_std_add_helpers(ctx, argc, argv);\n"); - +#ifdef CONFIG_BIGNUM + if (bignum_ext) { + fprintf(fo, + " JS_AddIntrinsicBigFloat(ctx);\n" + " JS_AddIntrinsicBigDecimal(ctx);\n" + " JS_AddIntrinsicOperators(ctx);\n" + " JS_EnableBignumExt(ctx, 1);\n"); + } +#endif + /* add the precompiled modules (XXX: could modify the module + loader instead) */ for(i = 0; i < init_module_list.count; i++) { namelist_entry_t *e = &init_module_list.array[i]; /* initialize the static C modules */ @@ -670,12 +709,39 @@ int main(int argc, char **argv) " }\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]; + if (e->flags) { + fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 1);\n", + e->name, e->name); + } + } + fprintf(fo, + " return ctx;\n" + "}\n\n"); + + fputs(main_c_template1, fo); + + if (stack_size != 0) { + fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n", + (unsigned int)stack_size); + } + + /* add the module loader if necessary */ + if (feature_bitmap & (1 << FE_MODULE_LOADER)) { + fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n"); + } + + fprintf(fo, + " ctx = JS_NewCustomContext(rt);\n" + " js_std_add_helpers(ctx, argc, argv);\n"); 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"); + if (!e->flags) { + fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n", + e->name, e->name); + } } fputs(main_c_template2, fo); } diff --git a/qjscalc.js b/qjscalc.js index 87382ec..b7c5001 100644 --- a/qjscalc.js +++ b/qjscalc.js @@ -1,7 +1,7 @@ /* * QuickJS Javascript Calculator * - * Copyright (c) 2017-2018 Fabrice Bellard + * 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 @@ -27,20 +27,8 @@ 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.Float = global.BigFloat; global.algebraicMode = true; /* add non enumerable properties */ @@ -63,6 +51,42 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio Object.defineProperty(obj, prop, desc); } } + + /* same as proto[Symbol.operatorSet] = Operators.create(..op_list) + but allow shortcuts: left: [], right: [] or both + */ + function operators_set(proto, ...op_list) + { + var new_op_list, i, a, j, b, k, obj, tab; + var fields = [ "left", "right" ]; + new_op_list = []; + for(i = 0; i < op_list.length; i++) { + a = op_list[i]; + if (a.left || a.right) { + tab = [ a.left, a.right ]; + delete a.left; + delete a.right; + for(k = 0; k < 2; k++) { + obj = tab[k]; + if (obj) { + if (!Array.isArray(obj)) { + obj = [ obj ]; + } + for(j = 0; j < obj.length; j++) { + b = {}; + Object.assign(b, a); + b[fields[k]] = obj[j]; + new_op_list.push(b); + } + } + } + } else { + new_op_list.push(a); + } + } + proto[Symbol.operatorSet] = + Operators.create.call(null, ...new_op_list); + } /* Integer */ @@ -104,11 +128,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio var d, r, s, i, j, a; d = n - 1; s = 0; - while (d & 1) { + while ((d & 1) == 0) { d >>= 1; s++; } - t = Math.min(t, small_primes.length); + if (small_primes.length < t) + t = small_primes.length; loop: for(j = 0; j < t; j++) { a = small_primes[j]; r = Integer.pmod(a, d, n); @@ -140,25 +165,31 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return fact_rec(a, i) * fact_rec(i + 1, b); } } - + + /* math mode specific quirk to overload the integer division and power */ + Operators.updateBigIntOperators( + { + "/"(a, b) { + if (algebraicMode) { + return Fraction.toFraction(a, b); + } else { + return Float(a) / Float(b); + } + }, + "**"(a, b) { + if (algebraicMode) { + return generic_pow(a, b); + } else { + return Float(a) ** Float(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); - } + /* integers are represented either as bigint or as number */ + return typeof a === "bigint" || + (typeof a === "number" && Number.isSafeInteger(a)); }, gcd(a, b) { var r; @@ -311,7 +342,10 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return this * this; }, abs() { - return Math.abs(this); + var v = this; + if (v < 0) + v = -v; + return v; }, conj() { return this; @@ -373,8 +407,119 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return obj; } + function fraction_add(a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den + a.den * b.num, a.den * b.den); + } + function fraction_sub(a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den - a.den * b.num, a.den * b.den); + } + function fraction_mul(a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.num, a.den * b.den); + } + function fraction_div(a, b) { + a = Fraction(a); + b = Fraction(b); + return Fraction.toFraction(a.num * b.den, a.den * b.num); + } + function fraction_mod(a, b) { + var a1 = Fraction(a); + var b1 = Fraction(b); + return a - Integer.ediv(a1.num * b1.den, a1.den * b1.num) * b; + } + function fraction_eq(a, b) { + a = Fraction(a); + b = Fraction(b); + /* we assume the fractions are normalized */ + return (a.num == b.num && a.den == b.den); + } + function fraction_lt(a, b) { + a = Fraction(a); + b = Fraction(b); + return (a.num * b.den < b.num * a.den); + } + + /* operators are needed for fractions */ + function float_add(a, b) { + return Float(a) + Float(b); + } + function float_sub(a, b) { + return Float(a) - Float(b); + } + function float_mul(a, b) { + return Float(a) * Float(b); + } + function float_div(a, b) { + return Float(a) / Float(b); + } + function float_mod(a, b) { + return Float(a) % Float(b); + } + function float_pow(a, b) { + return Float(a) ** Float(b); + } + function float_eq(a, b) { + /* XXX: may be better to use infinite precision for the comparison */ + return Float(a) === Float(b); + } + function float_lt(a, b) { + a = Float(a); + b = Float(b); + /* XXX: may be better to use infinite precision for the comparison */ + if (Float.isNaN(a) || Float.isNaN(b)) + return undefined; + else + return a < b; + } + + operators_set(Fraction.prototype, + { + "+": fraction_add, + "-": fraction_sub, + "*": fraction_mul, + "/": fraction_div, + "%": fraction_mod, + "**": generic_pow, + "==": fraction_eq, + "<": fraction_lt, + "pos"(a) { + return a; + }, + "neg"(a) { + return Fraction(-a.num, a.den); + }, + }, + { + left: [Number, BigInt], + right: [Number, BigInt], + "+": fraction_add, + "-": fraction_sub, + "*": fraction_mul, + "/": fraction_div, + "%": fraction_mod, + "**": generic_pow, + "==": fraction_eq, + "<": fraction_lt, + }, + { + left: Float, + right: Float, + "+": float_add, + "-": float_sub, + "*": float_mul, + "/": float_div, + "%": float_mod, + "**": float_pow, + "==": float_eq, + "<": float_lt, + }); + add_props(Fraction, { - [Symbol.operatorOrder]: OT_FRACTION, /* (internal use) simplify 'a' to an integer when possible */ toFraction(a, b) { var r = Fraction(a, b); @@ -383,72 +528,16 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio 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") { + 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); }, @@ -460,7 +549,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, abs() { if (this.num < 0) - return this[Symbol.operatorNeg](); + return -this; else return this; }, @@ -483,29 +572,9 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio /* 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; + return 1 / this; }, norm2() { return this * this; @@ -536,8 +605,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio /* Float */ - global.Float = global.BigFloat; - var const_tab = []; /* we cache the constants for small precisions */ @@ -603,26 +670,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio 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; @@ -631,7 +678,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return this * this; }, abs() { - return Math.abs(this); + return Float.abs(this); }, conj() { return this; @@ -672,8 +719,61 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return obj; } + + function complex_add(a, b) { + a = Complex(a); + b = Complex(b); + return Complex.toComplex(a.re + b.re, a.im + b.im); + } + function complex_sub(a, b) { + a = Complex(a); + b = Complex(b); + return Complex.toComplex(a.re - b.re, a.im - b.im); + } + function complex_mul(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); + } + function complex_div(a, b) { + a = Complex(a); + b = Complex(b); + return a * b.inverse(); + } + function complex_eq(a, b) { + a = Complex(a); + b = Complex(b); + return a.re == b.re && a.im == b.im; + } + + operators_set(Complex.prototype, + { + "+": complex_add, + "-": complex_sub, + "*": complex_mul, + "/": complex_div, + "**": generic_pow, + "==": complex_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + return Complex(-a.re, -a.im); + } + }, + { + left: [Number, BigInt, Float, Fraction], + right: [Number, BigInt, Float, Fraction], + "+": complex_add, + "-": complex_sub, + "*": complex_mul, + "/": complex_div, + "**": generic_pow, + "==": complex_eq, + }); + add_props(Complex, { - [Symbol.operatorOrder]: OT_COMPLEX, /* simplify to real number when possible */ toComplex(re, im) { if (algebraicMode && im == 0) @@ -681,43 +781,9 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio 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); @@ -785,14 +851,75 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio obj.mod = m; return obj; }; + + function mod_add(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); + } + } + function mod_sub(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); + } + } + function mod_mul(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); + } + } + function mod_div(a, b) { + if (!(b instanceof Mod)) + b = Mod(b, a.mod); + return mod_mul(a, b.inverse()); + } + function mod_eq(a, b) { + return (a.mod == b.mod && a.res == b.res); + } + + operators_set(Mod.prototype, + { + "+": mod_add, + "-": mod_sub, + "*": mod_mul, + "/": mod_div, + "**": generic_pow, + "==": mod_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + return Mod(-a.res, a.mod); + } + }, + { + left: [Number, BigInt, Float, Fraction], + right: [Number, BigInt, Float, Fraction], + "+": mod_add, + "-": mod_sub, + "*": mod_mul, + "/": mod_div, + "**": generic_pow, + }); 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)) { @@ -806,57 +933,21 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); - 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 */ + function polynomial_is_scalar(a) + { + if (typeof a === "number" || + typeof a === "bigint" || + typeof a === "bigfloat") + return true; + if (a instanceof Fraction || + a instanceof Complex || + a instanceof Mod) + return true; + return false; + } + Polynomial = function Polynomial(a) { if (new.target) @@ -868,7 +959,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio a = [ 0 ]; Object.setPrototypeOf(a, Polynomial.prototype); return a.trim(); - } else if (a.constructor[Symbol.operatorOrder] <= OT_MOD) { + } else if (polynomial_is_scalar(a)) { a = [a]; Object.setPrototypeOf(a, Polynomial.prototype); return a; @@ -1004,18 +1095,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio 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; @@ -1097,74 +1176,106 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); + + function polynomial_add(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); + } + function polynomial_sub(a, b) { + return polynomial_add(a, -b); + } + function polynomial_mul(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); + } + function polynomial_div_scalar(a, b) { + return a * (1 / b); + } + function polynomial_div(a, b) + { + return RationalFunction(Polynomial(a), + Polynomial(b)); + } + function polynomial_mod(a, b) { + return Polynomial.divrem(a, b)[1]; + } + function polynomial_eq(a, b) { + var n, i; + n = a.length; + if (n != b.length) + return false; + for(i = 0; i < n; i++) { + if (a[i] != b[i]) + return false; + } + return true; + } + + operators_set(Polynomial.prototype, + { + "+": polynomial_add, + "-": polynomial_sub, + "*": polynomial_mul, + "/": polynomial_div, + "**": generic_pow, + "==": polynomial_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + var r, i, n, a; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = -a[i]; + return Polynomial(r); + }, + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod], + "+": polynomial_add, + "-": polynomial_sub, + "*": polynomial_mul, + "/": polynomial_div, + "**": generic_pow, /* XXX: only for integer */ + }, + { + right: [Number, BigInt, Float, Fraction, Complex, Mod], + "+": polynomial_add, + "-": polynomial_sub, + "*": polynomial_mul, + "/": polynomial_div_scalar, + "**": generic_pow, /* XXX: only for integer */ + }); + 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) @@ -1252,13 +1363,66 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio return obj; }; + function polymod_add(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); + } + } + function polymod_sub(a, b) { + return polymod_add(a, -b); + } + function polymod_mul(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); + } + } + function polymod_div(a, b) { + if (!(b instanceof PolyMod)) + b = PolyMod(b, a.mod); + return polymod_mul(a, b.inverse()); + } + function polymod_eq(a, b) { + return (a.mod == b.mod && a.res == b.res); + } + + operators_set(PolyMod.prototype, + { + "+": polymod_add, + "-": polymod_sub, + "*": polymod_mul, + "/": polymod_div, + "**": generic_pow, + "==": polymod_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + return PolyMod(-a.res, a.mod); + }, + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + "+": polymod_add, + "-": polymod_sub, + "*": polymod_mul, + "/": polymod_div, + "**": generic_pow, /* XXX: only for integer */ + }); + 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) { @@ -1272,55 +1436,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); - 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) @@ -1347,12 +1462,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio } add_props(RationalFunction.prototype, { - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - return RationalFunction(-this.num, this.den); - }, inverse() { return RationalFunction(this.den, this.num); }, @@ -1378,8 +1487,59 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); + function ratfunc_add(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den + a.den * b.num, a.den * b.den); + } + function ratfunc_sub(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den - a.den * b.num, a.den * b.den); + } + function ratfunc_mul(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.num, a.den * b.den); + } + function ratfunc_div(a, b) { + a = RationalFunction.toRationalFunction(a); + b = RationalFunction.toRationalFunction(b); + return RationalFunction(a.num * b.den, a.den * b.num); + } + function ratfunc_eq(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); + } + + operators_set(RationalFunction.prototype, + { + "+": ratfunc_add, + "-": ratfunc_sub, + "*": ratfunc_mul, + "/": ratfunc_div, + "**": generic_pow, + "==": ratfunc_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + return RationalFunction(-this.num, this.den); + }, + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + "+": ratfunc_add, + "-": ratfunc_sub, + "*": ratfunc_mul, + "/": ratfunc_div, + "**": generic_pow, /* should only be used with integers */ + }); + 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) */ @@ -1394,33 +1554,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio 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 */ @@ -1435,6 +1568,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio } return n; }; + + function series_is_scalar_or_polynomial(a) + { + return polynomial_is_scalar(a) || + (a instanceof Polynomial); + } /* n is the maximum number of terms if 'a' is not a serie */ Series = function Series(a, n) { @@ -1442,7 +1581,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio if (a instanceof Series) { return a; - } else if (a.constructor[Symbol.operatorOrder] <= OT_POLY) { + } else if (series_is_scalar_or_polynomial(a)) { if (n <= 0) { /* XXX: should still use the polynomial degree */ return Series.zero(0, 0); @@ -1462,19 +1601,132 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio } }; - 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]; + function series_add(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 (series_is_scalar_or_polynomial(v2)) { + 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(); + } + function series_sub(a, b) { + return series_add(a, -b); + } + function series_mul(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 obj; + } + return r.trim(); + } + function series_div(v1, v2) { + if (!(v2 instanceof Series)) + v2 = Series(v2, v1.length); + return series_mul(v1, v2.inverse()); + } + function series_pow(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); + } + } + function series_eq(a, b) { + var n, i; + 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; + } + + operators_set(Series.prototype, + { + "+": series_add, + "-": series_sub, + "*": series_mul, + "/": series_div, + "**": series_pow, + "==": series_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + var obj, n, i; + n = a.length; + obj = Series.zero(a.length, a.emin); + for(i = 0; i < n; i++) { + obj[i] = -a[i]; + } + return obj; + }, }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial], + "+": series_add, + "-": series_sub, + "*": series_mul, + "/": series_div, + "**": series_pow, + }); + + add_props(Series.prototype, { conj() { var obj, n, i; n = this.length; @@ -1574,7 +1826,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio j = emin + i; if (j == -1) { if (a[i] != 0) - throw RangError("cannot represent integ(1/X)"); + throw RangeError("cannot represent integ(1/X)"); } else { r[i] = a[i] / (j + 1); } @@ -1601,7 +1853,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio log() { var a = this, r; if (a.emin != 0) - throw Range("log argument must have a non zero constant term"); + throw RangeError("log argument must have a non zero constant term"); r = integ(deriv(a) / a); /* add the constant term */ r += global.log(a[0]); @@ -1610,7 +1862,6 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }); add_props(Series, { - [Symbol.operatorOrder]: OT_SERIES, /* new series of length n and first exponent emin */ zero(n, emin) { var r, i, obj; @@ -1624,108 +1875,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio 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) { + if (series_is_scalar_or_polynomial(a)) { a = Polynomial(a); n = a.deg(); if (n < 0) @@ -2061,130 +2216,138 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio }, }); - add_props(Array, { - [Symbol.operatorOrder]: OT_ARRAY, - [Symbol.operatorAdd](a, b) { - var r, i, n; - if (!Array.isArray(a) || !Array.isArray(b)) - throw TypeError("array expected"); - n = a.length; - if (n != b.length) - throw TypeError("incompatible array size"); - r = []; - for(i = 0; i < n; i++) - r[i] = a[i] + b[i]; - return r; - }, - [Symbol.operatorSub](a, b) { - var r, i, n; - n = a.length; - if (!Array.isArray(a) || !Array.isArray(b)) - throw TypeError("array expected"); - if (n != b.length) - throw TypeError("incompatible array size"); - r = []; - for(i = 0; i < n; i++) - r[i] = a[i] - b[i]; - return r; - }, - scalar_mul(a, b) { - var r, i, n; - n = a.length; - r = []; - for(i = 0; i < n; i++) - r[i] = a[i] * b; - return r; - }, - [Symbol.operatorMul](a, b) { - var h, w, l, i, j, k, r, rl, sum, a_mat, b_mat, a_is_array, b_is_array; - a_is_array = Array.isArray(a); - b_is_array = Array.isArray(b); - if (!a_is_array && !b_is_array) { - throw TypeError("array expected"); - } else if (!a_is_array && b_is_array) { - return Array.scalar_mul(b, a); - } else if (a_is_array && !b_is_array) { - return Array.scalar_mul(a, b); - } - h = a.length; - a_mat = Array.isArray(a[0]); - if (a_mat) { - l = a[0].length; - } else { - l = 1; - } - if (l != b.length) - throw RangeError("incompatible matrix size"); - b_mat = Array.isArray(b[0]); - if (b_mat) - w = b[0].length; - else - w = 1; - r = []; - if (a_mat && b_mat) { - for(i = 0; i < h; i++) { - rl = []; - for(j = 0; j < w; j++) { - sum = 0; - for(k = 0; k < l; k++) { - sum += a[i][k] * b[k][j]; - } - rl[j] = sum; - } - r[i] = rl; - } - } else if (a_mat && !b_mat) { - for(i = 0; i < h; i++) { + function array_add(a, b) { + var r, i, n; + n = a.length; + if (n != b.length) + throw TypeError("incompatible array size"); + r = []; + for(i = 0; i < n; i++) + r[i] = a[i] + b[i]; + return r; + } + function array_sub(a, b) { + var r, i, n; + n = a.length; + if (n != b.length) + throw TypeError("incompatible array size"); + r = []; + for(i = 0; i < n; i++) + r[i] = a[i] - b[i]; + return r; + } + function array_scalar_mul(a, b) { + var r, i, n; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = a[i] * b; + return r; + } + function array_mul(a, b) { + var h, w, l, i, j, k, r, rl, sum, a_mat, b_mat; + h = a.length; + a_mat = Array.isArray(a[0]); + if (a_mat) { + l = a[0].length; + } else { + l = 1; + } + if (l != b.length) + throw RangeError("incompatible matrix size"); + b_mat = Array.isArray(b[0]); + if (b_mat) + w = b[0].length; + else + w = 1; + r = []; + if (a_mat && b_mat) { + for(i = 0; i < h; i++) { + rl = []; + for(j = 0; j < w; j++) { sum = 0; for(k = 0; k < l; k++) { - sum += a[i][k] * b[k]; + sum += a[i][k] * b[k][j]; } - r[i] = sum; - } - } else if (!a_mat && b_mat) { - for(i = 0; i < h; i++) { - rl = []; - for(j = 0; j < w; j++) { - rl[j] = a[i] * b[0][j]; - } - r[i] = rl; - } - } else { - for(i = 0; i < h; i++) { - r[i] = a[i] * b[0]; + rl[j] = sum; } + r[i] = rl; } - return r; - }, - [Symbol.operatorDiv](a, b) { - return Array[Symbol.operatorMul](a, b.inverse()); - }, - [Symbol.operatorPow]: generic_pow, - [Symbol.operatorCmpEQ](a, b) { - var n, i; - n = a.length; - if (n != b.length) + } else if (a_mat && !b_mat) { + for(i = 0; i < h; i++) { + sum = 0; + for(k = 0; k < l; k++) { + sum += a[i][k] * b[k]; + } + r[i] = sum; + } + } else if (!a_mat && b_mat) { + for(i = 0; i < h; i++) { + rl = []; + for(j = 0; j < w; j++) { + rl[j] = a[i] * b[0][j]; + } + r[i] = rl; + } + } else { + for(i = 0; i < h; i++) { + r[i] = a[i] * b[0]; + } + } + return r; + } + function array_div(a, b) { + return array_mul(a, b.inverse()); + } + function array_scalar_div(a, b) { + return a * b.inverse(); + } + function array_eq(a, b) { + var n, i; + n = a.length; + if (n != b.length) + return false; + for(i = 0; i < n; i++) { + if (a[i] != b[i]) return false; - for(i = 0; i < n; i++) { - if (a[i] != b[i]) - return false; + } + return true; + } + + operators_set(Array.prototype, + { + "+": array_add, + "-": array_sub, + "*": array_mul, + "/": array_div, + "==": array_eq, + "pos"(a) { + return a; + }, + "neg"(a) { + var i, n, r; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = -a[i]; + return r; } - return true; }, - }); + { + right: [Number, BigInt, Float, Fraction, Complex, Mod, + Polynomial, PolyMod, RationalFunction, Series], + "*": array_scalar_mul, + "/": array_scalar_div, + "**": generic_pow, /* XXX: only for integer */ + }, + { + left: [Number, BigInt, Float, Fraction, Complex, Mod, + Polynomial, PolyMod, RationalFunction, Series], + "*"(a, b) { return array_scalar_mul(b, a); }, + "/"(a, b) { return array_scalar_div(b, a); }, + }); add_props(Array.prototype, { - [Symbol.operatorPlus]() { - return this; - }, - [Symbol.operatorNeg]() { - var i, n, r; - n = this.length; - r = []; - for(i = 0; i < n; i++) - r[i] = -this[i]; - return r; - }, conj() { var i, n, r; n = this.length; @@ -2426,6 +2589,53 @@ function fromdeg(a) return a * Float.PI / 180; } +function sinh(a) +{ + var e = Float.exp(Float(a)); + return (e - 1/e) * 0.5; +} + +function cosh(a) +{ + var e = Float.exp(Float(a)); + return (e + 1/e) * 0.5; +} + +function tanh(a) +{ + var e = Float.exp(Float(a) * 2); + return (e - 1) / (e + 1); +} + +function asinh(a) +{ + var x = Float(a); + return log(sqrt(x * x + 1) + x); +} + +function acosh(a) +{ + var x = Float(a); + return log(sqrt(x * x - 1) + x); +} + +function atanh(a) +{ + var x = Float(a); + return 0.5 * log((1 + x) / (1 - x)); +} + +function sigmoid(x) +{ + x = Float(x); + return 1 / (1 + exp(-x)); +} + +function lerp(a, b, t) +{ + return a + (b - a) * t; +} + var idn = Matrix.idn; var diag = Matrix.diag; var trans = Matrix.trans; diff --git a/quickjs-atom.h b/quickjs-atom.h index 233294c..4c22794 100644 --- a/quickjs-atom.h +++ b/quickjs-atom.h @@ -82,6 +82,7 @@ DEF(length, "length") DEF(fileName, "fileName") DEF(lineNumber, "lineNumber") DEF(message, "message") +DEF(errors, "errors") DEF(stack, "stack") DEF(name, "name") DEF(toString, "toString") @@ -112,6 +113,7 @@ DEF(caller, "caller") DEF(_eval_, "") DEF(_ret_, "") DEF(_var_, "") +DEF(_arg_var_, "") DEF(_with_, "") DEF(lastIndex, "lastIndex") DEF(target, "target") @@ -170,6 +172,10 @@ 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") @@ -214,6 +220,9 @@ DEF(DataView, "DataView") DEF(BigInt, "BigInt") DEF(BigFloat, "BigFloat") DEF(BigFloatEnv, "BigFloatEnv") +DEF(BigDecimal, "BigDecimal") +DEF(OperatorSet, "OperatorSet") +DEF(Operators, "Operators") #endif DEF(Map, "Map") DEF(Set, "Set") /* Map + 1 */ @@ -258,27 +267,7 @@ 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") +DEF(Symbol_operatorSet, "Symbol.operatorSet") #endif - + #endif /* DEF */ diff --git a/quickjs-libc.c b/quickjs-libc.c index a44e463..ceae2de 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1,8 +1,8 @@ /* * QuickJS C library * - * Copyright (c) 2017-2019 Fabrice Bellard - * Copyright (c) 2017-2019 Charlie Gordon + * 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 @@ -40,28 +40,38 @@ #if defined(_WIN32) #include #include +#include #else #include #include #include #include + #if defined(__APPLE__) typedef sig_t sighandler_t; +#if !defined(environ) +#include +#define environ (*_NSGetEnviron()) #endif +#endif /* __APPLE__ */ + +#endif + +#if !defined(_WIN32) +/* enable the os.Worker API. IT relies on POSIX threads */ +#define USE_WORKER +#endif + +#ifdef USE_WORKER +#include +#include #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 */ @@ -84,14 +94,54 @@ typedef struct { 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); +typedef struct { + struct list_head link; + uint8_t *data; + size_t data_len; + /* list of SharedArrayBuffers, necessary to free the message */ + uint8_t **sab_tab; + size_t sab_tab_len; +} JSWorkerMessage; + +typedef struct { + int ref_count; +#ifdef USE_WORKER + pthread_mutex_t mutex; +#endif + struct list_head msg_queue; /* list of JSWorkerMessage.link */ + int read_fd; + int write_fd; +} JSWorkerMessagePipe; + +typedef struct { + struct list_head link; + JSWorkerMessagePipe *recv_pipe; + JSValue on_message_func; +} JSWorkerMessageHandler; + +typedef struct JSThreadState { + struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ + struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ + struct list_head os_timers; /* list of JSOSTimer.link */ + struct list_head port_list; /* list of JSWorkerMessageHandler.link */ + int eval_script_recurse; /* only used in the main thread */ + /* not used in the main thread */ + JSWorkerMessagePipe *recv_pipe, *send_pipe; +} JSThreadState; + static uint64_t os_pending_signals; -static int eval_script_recurse; static int (*os_poll_func)(JSContext *ctx); +static void js_std_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt); +} + +static BOOL my_isdigit(int c) +{ + return (c >= '0' && c <= '9'); +} + static JSValue js_printf_internal(JSContext *ctx, int argc, JSValueConst *argv, FILE *fp) { @@ -103,14 +153,12 @@ static JSValue js_printf_internal(JSContext *ctx, const uint8_t *fmt, *fmt_end; const uint8_t *p; char *q; - int i, c, len; + int i, c, len, mod; 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; @@ -132,151 +180,162 @@ static JSValue js_printf_internal(JSContext *ctx, break; q = fmtbuf; *q++ = *fmt++; /* copy '%' */ - part = PART_FLAGS; - modsize = 0; - for (;;) { + + /* flags */ + for(;;) { + c = *fmt; + if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' || + c == '\'') { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = c; + fmt++; + } else { + break; + } + } + /* width */ + if (*fmt == '*') { + 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); + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + } + } + if (*fmt == '.') { 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; - + *q++ = *fmt++; + if (*fmt == '*') { 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; + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; } - /* 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; + /* we only support the "l" modifier for 64 bit numbers */ + mod = ' '; + if (*fmt == 'l') { + mod = *fmt++; + } + + /* type */ + c = *fmt++; + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = c; + *q = '\0'; + + switch (c) { + case 'c': + if (i >= argc) + goto missing; + if (JS_IsString(argv[i])) { string_arg = JS_ToCString(ctx, argv[i++]); if (!string_arg) goto fail; - dbuf_printf_fun(&dbuf, fmtbuf, string_arg); + int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); 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++])) + } else { + if (JS_ToInt32(ctx, &int32_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"); + } + /* 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 (JS_ToInt64Ext(ctx, &int64_arg, argv[i++])) goto fail; + if (mod == 'l') { + /* 64 bit number */ +#if defined(_WIN32) + if (q >= fmtbuf + sizeof(fmtbuf) - 3) + goto invalid; + q[2] = q[-1]; + q[-1] = 'I'; + q[0] = '6'; + q[1] = '4'; + q[3] = '\0'; + dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg); +#else + if (q >= fmtbuf + sizeof(fmtbuf) - 2) + goto invalid; + q[1] = q[-1]; + q[-1] = q[0] = 'l'; + q[2] = '\0'; + dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg); +#endif + } else { + dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg); } break; + + case 's': + if (i >= argc) + goto missing; + /* XXX: handle strings containing null characters */ + 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; } } JS_FreeCString(ctx, fmt_str); @@ -369,6 +428,27 @@ static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, return ret; } +/* load a file as a UTF-8 encoded string */ +static JSValue js_std_loadFile(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); + JS_FreeCString(ctx, filename); + if (!buf) + return JS_NULL; + ret = JS_NewStringLen(ctx, (char *)buf, buf_len); + js_free(ctx, buf); + return ret; +} + typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, const char *module_name); @@ -543,6 +623,97 @@ static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, return JS_NewString(ctx, str); } +#if defined(_WIN32) +static void setenv(const char *name, const char *value, int overwrite) +{ + char *str; + size_t name_len, value_len; + name_len = strlen(name); + value_len = strlen(value); + str = malloc(name_len + 1 + value_len + 1); + memcpy(str, name, name_len); + str[name_len] = '='; + memcpy(str + name_len + 1, value, value_len); + str[name_len + 1 + value_len] = '\0'; + _putenv(str); + free(str); +} + +static void unsetenv(const char *name) +{ + setenv(name, "", TRUE); +} +#endif /* _WIN32 */ + +static JSValue js_std_setenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name, *value; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + value = JS_ToCString(ctx, argv[1]); + if (!value) { + JS_FreeCString(ctx, name); + return JS_EXCEPTION; + } + setenv(name, value, TRUE); + JS_FreeCString(ctx, name); + JS_FreeCString(ctx, value); + return JS_UNDEFINED; +} + +static JSValue js_std_unsetenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + unsetenv(name); + JS_FreeCString(ctx, name); + return JS_UNDEFINED; +} + +/* return an object containing the list of the available environment + variables. */ +static JSValue js_std_getenviron(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char **envp; + const char *name, *p, *value; + JSValue obj; + uint32_t idx; + size_t name_len; + JSAtom atom; + int ret; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + envp = environ; + for(idx = 0; envp[idx] != NULL; idx++) { + name = envp[idx]; + p = strchr(name, '='); + name_len = p - name; + if (!p) + continue; + value = p + 1; + atom = JS_NewAtomLen(ctx, name, name_len); + if (atom == JS_ATOM_NULL) + goto fail; + ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value), + JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) + goto fail; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + static JSValue js_std_gc(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -555,22 +726,53 @@ 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) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); 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) { + if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { /* install the interrupt handler */ JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); } - ret = JS_Eval(ctx, str, len, "", JS_EVAL_TYPE_GLOBAL); + 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) { + if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { /* remove the interrupt handler */ JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL); os_pending_signals &= ~((uint64_t)1 << SIGINT); @@ -604,30 +806,14 @@ static void js_std_file_finalizer(JSRuntime *rt, JSValue val) } } -static JSValue js_new_std_error(JSContext *ctx, int err) +static ssize_t js_get_errno(ssize_t ret) { - 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; + if (ret == -1) + ret = -errno; + return ret; } -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, +static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int err; @@ -636,13 +822,19 @@ static JSValue js_std_error_strerror(JSContext *ctx, JSValueConst this_val, return JS_NewString(ctx, strerror(err)); } -static JSValue js_std_throw_errno(JSContext *ctx, int err) +static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue obj; - obj = js_new_std_error(ctx, err); - if (JS_IsException(obj)) - obj = JS_NULL; - return JS_Throw(ctx, obj); + const char *str; + size_t len; + + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + obj = JS_ParseJSON2(ctx, str, len, "", JS_PARSE_JSON_EXT); + JS_FreeCString(ctx, str); + return obj; } static JSValue js_new_std_file(JSContext *ctx, FILE *f, @@ -666,12 +858,20 @@ static JSValue js_new_std_file(JSContext *ctx, FILE *f, return obj; } +static void js_set_error_object(JSContext *ctx, JSValue obj, int err) +{ + if (!JS_IsUndefined(obj)) { + JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err)); + } +} + static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { const char *filename, *mode = NULL; FILE *f; - + int err; + filename = JS_ToCString(ctx, argv[0]); if (!filename) goto fail; @@ -679,15 +879,21 @@ static JSValue js_std_open(JSContext *ctx, JSValueConst this_val, if (!mode) goto fail; if (mode[strspn(mode, "rwa+b")] != '\0') { - js_std_throw_errno(ctx, EINVAL); + JS_ThrowTypeError(ctx, "invalid file mode"); goto fail; } f = fopen(filename, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); JS_FreeCString(ctx, filename); JS_FreeCString(ctx, mode); if (!f) - return js_std_throw_errno(ctx, errno); + return JS_NULL; return js_new_std_file(ctx, f, TRUE, FALSE); fail: JS_FreeCString(ctx, filename); @@ -700,7 +906,8 @@ static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, { const char *filename, *mode = NULL; FILE *f; - + int err; + filename = JS_ToCString(ctx, argv[0]); if (!filename) goto fail; @@ -708,15 +915,21 @@ static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, if (!mode) goto fail; if (mode[strspn(mode, "rw")] != '\0') { - js_std_throw_errno(ctx, EINVAL); + JS_ThrowTypeError(ctx, "invalid file mode"); goto fail; } f = popen(filename, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); JS_FreeCString(ctx, filename); JS_FreeCString(ctx, mode); if (!f) - return js_std_throw_errno(ctx, errno); + return JS_NULL; return js_new_std_file(ctx, f, TRUE, TRUE); fail: JS_FreeCString(ctx, filename); @@ -729,7 +942,7 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, { const char *mode; FILE *f; - int fd; + int fd, err; if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; @@ -737,14 +950,20 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, if (!mode) goto fail; if (mode[strspn(mode, "rwa+")] != '\0') { - js_std_throw_errno(ctx, EINVAL); + JS_ThrowTypeError(ctx, "invalid file mode"); goto fail; } f = fdopen(fd, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); JS_FreeCString(ctx, mode); if (!f) - return js_std_throw_errno(ctx, errno); + return JS_NULL; return js_new_std_file(ctx, f, TRUE, FALSE); fail: JS_FreeCString(ctx, mode); @@ -756,8 +975,10 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, { FILE *f; f = tmpfile(); + if (argc >= 1) + js_set_error_object(ctx, argv[0], f ? 0 : errno); if (!f) - return js_std_throw_errno(ctx, errno); + return JS_NULL; return js_new_std_file(ctx, f, TRUE, FALSE); } @@ -779,7 +1000,7 @@ static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj) if (!s) return NULL; if (!s->f) { - js_std_throw_errno(ctx, EBADF); + JS_ThrowTypeError(ctx, "invalid file handle"); return NULL; } return s->f; @@ -791,6 +1012,7 @@ static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, FILE *f; int i; const char *str; + size_t len; if (magic == 0) { f = stdout; @@ -801,10 +1023,10 @@ static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, } for(i = 0; i < argc; i++) { - str = JS_ToCString(ctx, argv[i]); + str = JS_ToCStringLen(ctx, &len, argv[i]); if (!str) return JS_EXCEPTION; - fputs(str, f); + fwrite(str, 1, len, f); JS_FreeCString(ctx, str); } return JS_UNDEFINED; @@ -814,17 +1036,17 @@ 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); + int err; if (!s) return JS_EXCEPTION; if (!s->f) - return js_std_throw_errno(ctx, EBADF); - /* XXX: could return exit code */ + return JS_ThrowTypeError(ctx, "invalid file handle"); if (s->is_popen) - pclose(s->f); + err = js_get_errno(pclose(s->f)); else - fclose(s->f); + err = js_get_errno(fclose(s->f)); s->f = NULL; - return JS_UNDEFINED; + return JS_NewInt32(ctx, err); } static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, @@ -847,7 +1069,7 @@ static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, } static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, int is_bigint) { FILE *f = js_std_file_get(ctx, this_val); int64_t pos; @@ -858,7 +1080,10 @@ static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, #else pos = ftell(f); #endif - return JS_NewInt64(ctx, pos); + if (is_bigint) + return JS_NewBigInt64(ctx, pos); + else + return JS_NewInt64(ctx, pos); } static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, @@ -869,7 +1094,7 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, int whence, ret; if (!f) return JS_EXCEPTION; - if (JS_ToInt64(ctx, &pos, argv[0])) + if (JS_ToInt64Ext(ctx, &pos, argv[0])) return JS_EXCEPTION; if (JS_ToInt32(ctx, &whence, argv[1])) return JS_EXCEPTION; @@ -879,8 +1104,8 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val, ret = fseek(f, pos, whence); #endif if (ret < 0) - return js_std_throw_errno(ctx, EBADF); - return JS_UNDEFINED; + ret = -errno; + return JS_NewInt32(ctx, ret); } static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, @@ -892,6 +1117,25 @@ static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, return JS_NewBool(ctx, feof(f)); } +static JSValue js_std_file_error(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, ferror(f)); +} + +static JSValue js_std_file_clearerr(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + clearerr(f); + return JS_UNDEFINED; +} + static JSValue js_std_file_fileno(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1067,21 +1311,6 @@ static int http_get_status(const char *buf) return atoi(p); } -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_std_urlGet(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1137,7 +1366,7 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, f = popen((char *)cmd_buf.buf, "r"); dbuf_free(&cmd_buf); if (!f) { - return js_std_throw_errno(ctx, errno); + return JS_ThrowTypeError(ctx, "could not start curl"); } js_std_dbuf_init(ctx, data_buf); @@ -1148,20 +1377,21 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, goto fail; /* get the HTTP status */ - if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) { + status = 0; goto bad_header; + } status = http_get_status(buf); if (!full_flag && !(status >= 200 && status <= 299)) { - js_std_throw_errno(ctx, ENOENT); - goto fail; + goto bad_header; } /* wait until there is an empty line */ for(;;) { if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { bad_header: - js_std_throw_errno(ctx, EINVAL); - goto fail; + response = JS_NULL; + goto done; } if (!strcmp(buf, "\r\n")) break; @@ -1177,11 +1407,6 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, 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) { @@ -1190,10 +1415,15 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, } 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; + done: + js_free(ctx, buf); + buf = NULL; + pclose(f); + f = NULL; + dbuf_free(data_buf); + data_buf = NULL; if (full_flag) { ret_obj = JS_NewObject(ctx); @@ -1202,13 +1432,15 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, 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); + if (!JS_IsNull(response)) { + 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; } @@ -1231,34 +1463,7 @@ static JSClassDef js_std_file_class = { .finalizer = js_std_file_finalizer, }; -static const JSCFunctionListEntry js_std_funcs[] = { -#ifdef ALLOW_UNSAFE_JS - 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, ... */ -#endif -}; - -static const JSCFunctionListEntry js_std_error_funcs[] = { -#ifdef ALLOW_UNSAFE_JS - JS_CFUNC_DEF("strerror", 1, js_std_error_strerror ), +static const JSCFunctionListEntry js_std_error_props[] = { /* various errno values */ #define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) DEF(EINVAL), @@ -1273,32 +1478,60 @@ static const JSCFunctionListEntry js_std_error_funcs[] = { DEF(EPIPE), DEF(EBADF), #undef DEF -#endif }; +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("setenv", 1, js_std_setenv ), + JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ), + JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), + JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), + JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), + JS_CFUNC_DEF("strerror", 1, js_std_strerror ), + JS_CFUNC_DEF("parseExtJSON", 1, js_std_parseExtJSON ), + + /* FILE I/O */ + JS_CFUNC_DEF("open", 2, js_std_open ), + JS_CFUNC_DEF("popen", 2, js_std_popen ), + 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 ), + JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE), +}; + static const JSCFunctionListEntry js_std_file_proto_funcs[] = { -#ifdef ALLOW_UNSAFE_JS 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_MAGIC_DEF("tell", 0, js_std_file_tell, 0 ), + JS_CFUNC_MAGIC_DEF("tello", 0, js_std_file_tell, 1 ), 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_DEF("error", 0, js_std_file_error ), + JS_CFUNC_DEF("clearerr", 0, js_std_file_clearerr ), 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, ... */ -#endif + /* setvbuf, ... */ }; static int js_std_init(JSContext *ctx, JSModuleDef *m) { - JSValue proto, obj; + JSValue proto; /* FILE class */ /* the class ID is created once */ @@ -1315,13 +1548,6 @@ static int js_std_init(JSContext *ctx, JSModuleDef *m) 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; } @@ -1335,20 +1561,12 @@ JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) 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) { @@ -1374,9 +1592,9 @@ static JSValue js_os_open(JSContext *ctx, JSValueConst this_val, if (!(flags & O_TEXT)) flags |= O_BINARY; #endif - ret = open(filename, flags, mode); + ret = js_get_errno(open(filename, flags, mode)); JS_FreeCString(ctx, filename); - return js_os_return(ctx, ret); + return JS_NewInt32(ctx, ret); } static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, @@ -1385,24 +1603,31 @@ static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, int fd, ret; if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; - ret = close(fd); - return js_os_return(ctx, ret); + ret = js_get_errno(close(fd)); + return JS_NewInt32(ctx, ret); } static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - int fd, whence, ret; - int64_t pos; + int fd, whence; + int64_t pos, ret; + BOOL is_bigint; if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; - if (JS_ToInt64(ctx, &pos, argv[1])) + is_bigint = JS_IsBigInt(ctx, argv[1]); + if (JS_ToInt64Ext(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); + if (ret == -1) + ret = -errno; + if (is_bigint) + return JS_NewBigInt64(ctx, ret); + else + return JS_NewInt64(ctx, ret); } static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, @@ -1426,10 +1651,10 @@ static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, if (pos + len > size) return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); if (magic) - ret = write(fd, buf + pos, len); + ret = js_get_errno(write(fd, buf + pos, len)); else - ret = read(fd, buf + pos, len); - return js_os_return(ctx, ret); + ret = js_get_errno(read(fd, buf + pos, len)); + return JS_NewInt64(ctx, ret); } static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, @@ -1547,9 +1772,9 @@ static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, filename = JS_ToCString(ctx, argv[0]); if (!filename) return JS_EXCEPTION; - ret = remove(filename); + ret = js_get_errno(remove(filename)); JS_FreeCString(ctx, filename); - return js_os_return(ctx, ret); + return JS_NewInt32(ctx, ret); } static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, @@ -1566,17 +1791,24 @@ static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, oldpath); return JS_EXCEPTION; } - ret = rename(oldpath, newpath); + ret = js_get_errno(rename(oldpath, newpath)); JS_FreeCString(ctx, oldpath); JS_FreeCString(ctx, newpath); - return js_os_return(ctx, ret); + return JS_NewInt32(ctx, ret); } -static JSOSRWHandler *find_rh(int fd) +static BOOL is_main_thread(JSRuntime *rt) +{ + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + return !ts->recv_pipe; +} + +static JSOSRWHandler *find_rh(JSThreadState *ts, int fd) { JSOSRWHandler *rh; struct list_head *el; - list_for_each(el, &os_rw_handlers) { + + list_for_each(el, &ts->os_rw_handlers) { rh = list_entry(el, JSOSRWHandler, link); if (rh->fd == fd) return rh; @@ -1597,6 +1829,8 @@ static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); JSOSRWHandler *rh; int fd; JSValueConst func; @@ -1605,7 +1839,7 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; func = argv[1]; if (JS_IsNull(func)) { - rh = find_rh(fd); + rh = find_rh(ts, fd); if (rh) { JS_FreeValue(ctx, rh->rw_func[magic]); rh->rw_func[magic] = JS_NULL; @@ -1618,7 +1852,7 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, } else { if (!JS_IsFunction(ctx, func)) return JS_ThrowTypeError(ctx, "not a function"); - rh = find_rh(fd); + rh = find_rh(ts, fd); if (!rh) { rh = js_mallocz(ctx, sizeof(*rh)); if (!rh) @@ -1626,7 +1860,7 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, rh->fd = fd; rh->rw_func[0] = JS_NULL; rh->rw_func[1] = JS_NULL; - list_add_tail(&rh->link, &os_rw_handlers); + list_add_tail(&rh->link, &ts->os_rw_handlers); } JS_FreeValue(ctx, rh->rw_func[magic]); rh->rw_func[magic] = JS_DupValue(ctx, func); @@ -1634,11 +1868,11 @@ static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, return JS_UNDEFINED; } -static JSOSSignalHandler *find_sh(int sig_num) +static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num) { JSOSSignalHandler *sh; struct list_head *el; - list_for_each(el, &os_signal_handlers) { + list_for_each(el, &ts->os_signal_handlers) { sh = list_entry(el, JSOSSignalHandler, link); if (sh->sig_num == sig_num) return sh; @@ -1665,10 +1899,15 @@ typedef void (*sighandler_t)(int sig_num); static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); JSOSSignalHandler *sh; uint32_t sig_num; JSValueConst func; sighandler_t handler; + + if (!is_main_thread(rt)) + return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread"); if (JS_ToUint32(ctx, &sig_num, argv[0])) return JS_EXCEPTION; @@ -1677,7 +1916,7 @@ static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, func = argv[1]; /* func = null: SIG_DFL, func = undefined, SIG_IGN */ if (JS_IsNull(func) || JS_IsUndefined(func)) { - sh = find_sh(sig_num); + sh = find_sh(ts, sig_num); if (sh) { free_sh(JS_GetRuntime(ctx), sh); } @@ -1689,13 +1928,13 @@ static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, } else { if (!JS_IsFunction(ctx, func)) return JS_ThrowTypeError(ctx, "not a function"); - sh = find_sh(sig_num); + sh = find_sh(ts, 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); + list_add_tail(&sh->link, &ts->os_signal_handlers); } JS_FreeValue(ctx, sh->func); sh->func = JS_DupValue(ctx, func); @@ -1759,6 +1998,8 @@ static void js_os_timer_mark(JSRuntime *rt, JSValueConst val, static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); int64_t delay; JSValueConst func; JSOSTimer *th; @@ -1780,7 +2021,7 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, th->has_object = TRUE; th->timeout = get_time_ms() + delay; th->func = JS_DupValue(ctx, func); - list_add_tail(&th->link, &os_timers); + list_add_tail(&th->link, &ts->os_timers); JS_SetOpaque(obj, th); return obj; } @@ -1818,6 +2059,8 @@ static void call_handler(JSContext *ctx, JSValueConst func) static int js_os_poll(JSContext *ctx) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); int min_delay, console_fd; int64_t cur_time, delay; JSOSRWHandler *rh; @@ -1825,14 +2068,14 @@ static int js_os_poll(JSContext *ctx) /* XXX: handle signals if useful */ - if (list_empty(&os_rw_handlers) && list_empty(&os_timers)) + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers)) return -1; /* no more events */ /* XXX: only timers and basic console input are supported */ - if (!list_empty(&os_timers)) { + if (!list_empty(&ts->os_timers)) { cur_time = get_time_ms(); min_delay = 10000; - list_for_each(el, &os_timers) { + list_for_each(el, &ts->os_timers) { JSOSTimer *th = list_entry(el, JSOSTimer, link); delay = th->timeout - cur_time; if (delay <= 0) { @@ -1840,9 +2083,9 @@ static int js_os_poll(JSContext *ctx) /* the timer expired */ func = th->func; th->func = JS_UNDEFINED; - unlink_timer(JS_GetRuntime(ctx), th); + unlink_timer(rt, th); if (!th->has_object) - free_timer(JS_GetRuntime(ctx), th); + free_timer(rt, th); call_handler(ctx, func); JS_FreeValue(ctx, func); return 0; @@ -1855,7 +2098,7 @@ static int js_os_poll(JSContext *ctx) } console_fd = -1; - list_for_each(el, &os_rw_handlers) { + list_for_each(el, &ts->os_rw_handlers) { rh = list_entry(el, JSOSRWHandler, link); if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { console_fd = rh->fd; @@ -1873,7 +2116,7 @@ static int js_os_poll(JSContext *ctx) handle = (HANDLE)_get_osfhandle(console_fd); ret = WaitForSingleObject(handle, ti); if (ret == WAIT_OBJECT_0) { - list_for_each(el, &os_rw_handlers) { + list_for_each(el, &ts->os_rw_handlers) { rh = list_entry(el, JSOSRWHandler, link); if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { call_handler(ctx, rh->rw_func[0]); @@ -1888,8 +2131,88 @@ static int js_os_poll(JSContext *ctx) return 0; } #else + +#ifdef USE_WORKER + +static void js_free_message(JSWorkerMessage *msg); + +/* return 1 if a message was handled, 0 if no message */ +static int handle_posted_message(JSRuntime *rt, JSContext *ctx, + JSWorkerMessageHandler *port) +{ + JSWorkerMessagePipe *ps = port->recv_pipe; + int ret; + struct list_head *el; + JSWorkerMessage *msg; + JSValue obj, data_obj, func, retval; + + pthread_mutex_lock(&ps->mutex); + if (!list_empty(&ps->msg_queue)) { + el = ps->msg_queue.next; + msg = list_entry(el, JSWorkerMessage, link); + + /* remove the message from the queue */ + list_del(&msg->link); + + if (list_empty(&ps->msg_queue)) { + uint8_t buf[16]; + int ret; + for(;;) { + ret = read(ps->read_fd, buf, sizeof(buf)); + if (ret >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + break; + } + } + + pthread_mutex_unlock(&ps->mutex); + + data_obj = JS_ReadObject(ctx, msg->data, msg->data_len, + JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); + + js_free_message(msg); + + if (JS_IsException(data_obj)) + goto fail; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, data_obj); + goto fail; + } + JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E); + + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func = JS_DupValue(ctx, port->on_message_func); + retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj); + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, func); + if (JS_IsException(retval)) { + fail: + js_std_dump_error(ctx); + } else { + JS_FreeValue(ctx, retval); + } + ret = 1; + } else { + pthread_mutex_unlock(&ps->mutex); + ret = 0; + } + return ret; +} +#else +static int handle_posted_message(JSRuntime *rt, JSContext *ctx, + JSWorkerMessageHandler *port) +{ + return 0; +} +#endif + static int js_os_poll(JSContext *ctx) { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); int ret, fd_max, min_delay; int64_t cur_time, delay; fd_set rfds, wfds; @@ -1897,11 +2220,13 @@ static int js_os_poll(JSContext *ctx) struct list_head *el; struct timeval tv, *tvp; - if (unlikely(os_pending_signals != 0)) { + /* only check signals in the main thread */ + if (!ts->recv_pipe && + unlikely(os_pending_signals != 0)) { JSOSSignalHandler *sh; uint64_t mask; - list_for_each(el, &os_signal_handlers) { + list_for_each(el, &ts->os_signal_handlers) { sh = list_entry(el, JSOSSignalHandler, link); mask = (uint64_t)1 << sh->sig_num; if (os_pending_signals & mask) { @@ -1911,14 +2236,15 @@ static int js_os_poll(JSContext *ctx) } } } - - if (list_empty(&os_rw_handlers) && list_empty(&os_timers)) + + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && + list_empty(&ts->port_list)) return -1; /* no more events */ - if (!list_empty(&os_timers)) { + if (!list_empty(&ts->os_timers)) { cur_time = get_time_ms(); min_delay = 10000; - list_for_each(el, &os_timers) { + list_for_each(el, &ts->os_timers) { JSOSTimer *th = list_entry(el, JSOSTimer, link); delay = th->timeout - cur_time; if (delay <= 0) { @@ -1926,9 +2252,9 @@ static int js_os_poll(JSContext *ctx) /* the timer expired */ func = th->func; th->func = JS_UNDEFINED; - unlink_timer(JS_GetRuntime(ctx), th); + unlink_timer(rt, th); if (!th->has_object) - free_timer(JS_GetRuntime(ctx), th); + free_timer(rt, th); call_handler(ctx, func); JS_FreeValue(ctx, func); return 0; @@ -1946,7 +2272,7 @@ static int js_os_poll(JSContext *ctx) FD_ZERO(&rfds); FD_ZERO(&wfds); fd_max = -1; - list_for_each(el, &os_rw_handlers) { + list_for_each(el, &ts->os_rw_handlers) { rh = list_entry(el, JSOSRWHandler, link); fd_max = max_int(fd_max, rh->fd); if (!JS_IsNull(rh->rw_func[0])) @@ -1954,25 +2280,46 @@ static int js_os_poll(JSContext *ctx) if (!JS_IsNull(rh->rw_func[1])) FD_SET(rh->fd, &wfds); } - + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + fd_max = max_int(fd_max, ps->read_fd); + FD_SET(ps->read_fd, &rfds); + } + } + ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp); if (ret > 0) { - list_for_each(el, &os_rw_handlers) { + list_for_each(el, &ts->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; + goto done; } - if (!JS_IsNull(rh->rw_func[1])) { - FD_SET(rh->fd, &wfds); + if (!JS_IsNull(rh->rw_func[1]) && + FD_ISSET(rh->fd, &wfds)) { call_handler(ctx, rh->rw_func[1]); /* must stop because the list may have been modified */ - break; + goto done; + } + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (FD_ISSET(ps->read_fd, &rfds)) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } } } } + done: return 0; } #endif /* !_WIN32 */ @@ -2010,35 +2357,25 @@ static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, if (!getcwd(buf, sizeof(buf))) { buf[0] = '\0'; - err = -errno; + 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) +static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - const char *path; - char buf[PATH_MAX], *res; + const char *target; int err; - path = JS_ToCString(ctx, argv[0]); - if (!path) + target = JS_ToCString(ctx, argv[0]); + if (!target) 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); + err = js_get_errno(chdir(target)); + JS_FreeCString(ctx, target); + return JS_NewInt32(ctx, err); } static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val, @@ -2056,15 +2393,66 @@ static JSValue js_os_mkdir(JSContext *ctx, JSValueConst this_val, path = JS_ToCString(ctx, argv[0]); if (!path) return JS_EXCEPTION; - ret = mkdir(path, mode); +#if defined(_WIN32) + (void)mode; + ret = js_get_errno(mkdir(path)); +#else + ret = js_get_errno(mkdir(path, mode)); +#endif JS_FreeCString(ctx, path); - return js_os_return(ctx, ret); + return JS_NewInt32(ctx, ret); } +/* 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); + if (!f) + err = errno; + else + err = 0; + JS_FreeCString(ctx, path); + if (!f) + 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); +} + +#if !defined(_WIN32) static int64_t timespec_to_ms(const struct timespec *tv) { return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000); } +#endif /* return [obj, errcode] */ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, @@ -2078,13 +2466,17 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, path = JS_ToCString(ctx, argv[0]); if (!path) return JS_EXCEPTION; +#if defined(_WIN32) + res = stat(path, &st); +#else if (is_lstat) res = lstat(path, &st); else res = stat(path, &st); +#endif JS_FreeCString(ctx, path); if (res < 0) { - err = -errno; + err = errno; obj = JS_NULL; } else { err = 0; @@ -2115,10 +2507,22 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, JS_DefinePropertyValueStr(ctx, obj, "size", JS_NewInt64(ctx, st.st_size), JS_PROP_C_W_E); +#if !defined(_WIN32) JS_DefinePropertyValueStr(ctx, obj, "blocks", JS_NewInt64(ctx, st.st_blocks), JS_PROP_C_W_E); -#if defined(__APPLE__) +#endif +#if defined(_WIN32) + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, (int64_t)st.st_atime * 1000), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000), + JS_PROP_C_W_E); +#elif defined(__APPLE__) JS_DefinePropertyValueStr(ctx, obj, "atime", JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)), JS_PROP_C_W_E); @@ -2143,6 +2547,71 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val, return make_obj_error(ctx, obj, err); } +#if !defined(_WIN32) +static void ms_to_timeval(struct timeval *tv, uint64_t v) +{ + tv->tv_sec = v / 1000; + tv->tv_usec = (v % 1000) * 1000; +} +#endif + +static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + int64_t atime, mtime; + int ret; + + if (JS_ToInt64(ctx, &atime, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt64(ctx, &mtime, argv[2])) + return JS_EXCEPTION; + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + { + struct _utimbuf times; + times.actime = atime / 1000; + times.modtime = mtime / 1000; + ret = js_get_errno(_utime(path, ×)); + } +#else + { + struct timeval times[2]; + ms_to_timeval(×[0], atime); + ms_to_timeval(×[1], mtime); + ret = js_get_errno(utimes(path, times)); + } +#endif + JS_FreeCString(ctx, path); + return JS_NewInt32(ctx, ret); +} + +#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_symlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -2157,10 +2626,10 @@ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, target); return JS_EXCEPTION; } - err = symlink(target, linkpath); + err = js_get_errno(symlink(target, linkpath)); JS_FreeCString(ctx, target); JS_FreeCString(ctx, linkpath); - return js_os_return(ctx, err); + return JS_NewInt32(ctx, err); } /* return [path, errorcode] */ @@ -2176,86 +2645,130 @@ static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, if (!path) return JS_EXCEPTION; res = readlink(path, buf, sizeof(buf) - 1); - JS_FreeCString(ctx, path); if (res < 0) { buf[0] = '\0'; - err = -errno; + err = errno; } else { buf[res] = '\0'; err = 0; } + JS_FreeCString(ctx, path); return make_string_error(ctx, buf, err); } -/* return [array, errorcode] */ -static JSValue js_os_readdir(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +static char **build_envp(JSContext *ctx, JSValueConst obj) { - const char *path; - DIR *f; - struct dirent *d; - JSValue obj; - int err; - uint32_t len; + uint32_t len, i; + JSPropertyEnum *tab; + char **envp, *pair; + const char *key, *str; + JSValue val; + size_t key_len, str_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; + if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) + return NULL; + envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1)); + if (!envp) + goto fail; + for(i = 0; i < len; i++) { + val = JS_GetProperty(ctx, obj, tab[i].atom); + if (JS_IsException(val)) + goto fail; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto fail; + key = JS_AtomToCString(ctx, tab[i].atom); + if (!key) { + JS_FreeCString(ctx, str); + goto fail; } - JS_DefinePropertyValueUint32(ctx, obj, len++, - JS_NewString(ctx, d->d_name), - JS_PROP_C_W_E); + key_len = strlen(key); + str_len = strlen(str); + pair = js_malloc(ctx, key_len + str_len + 2); + if (!pair) { + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + goto fail; + } + memcpy(pair, key, key_len); + pair[key_len] = '='; + memcpy(pair + key_len + 1, str, str_len); + pair[key_len + 1 + str_len] = '\0'; + envp[i] = pair; + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); } - closedir(f); done: - return make_obj_error(ctx, obj, err); + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + return envp; + fail: + if (envp) { + for(i = 0; i < len; i++) + js_free(ctx, envp[i]); + js_free(ctx, envp); + envp = NULL; + } + goto done; } -static void ms_to_timeval(struct timeval *tv, uint64_t v) +/* execvpe is not available on non GNU systems */ +static int my_execvpe(const char *filename, char **argv, char **envp) { - 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]; + char *path, *p, *p_next, *p1; + char buf[PATH_MAX]; + size_t filename_len, path_len; + BOOL eacces_error; - 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]); + filename_len = strlen(filename); + if (filename_len == 0) { + errno = ENOENT; + return -1; + } + if (strchr(filename, '/')) + return execve(filename, argv, envp); + + path = getenv("PATH"); 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); + path = (char *)"/bin:/usr/bin"; + eacces_error = FALSE; + p = path; + for(p = path; p != NULL; p = p_next) { + p1 = strchr(p, ':'); + if (!p1) { + p_next = NULL; + path_len = strlen(p); + } else { + p_next = p1 + 1; + path_len = p1 - p; + } + /* path too long */ + if ((path_len + 1 + filename_len + 1) > PATH_MAX) + continue; + memcpy(buf, p, path_len); + buf[path_len] = '/'; + memcpy(buf + path_len + 1, filename, filename_len); + buf[path_len + 1 + filename_len] = '\0'; + + execve(buf, argv, envp); + + switch(errno) { + case EACCES: + eacces_error = TRUE; + break; + case ENOENT: + case ENOTDIR: + break; + default: + return -1; + } + } + if (eacces_error) + errno = EACCES; + return -1; } /* exec(args[, options]) -> exitcode */ @@ -2265,11 +2778,13 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, JSValueConst options, args = argv[0]; JSValue val, ret_val; const char **exec_argv, *file = NULL, *str, *cwd = NULL; + char **envp = environ; 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]; + uint32_t uid = -1, gid = -1; val = JS_GetPropertyStr(ctx, args, "length"); if (JS_IsException(val)) @@ -2343,6 +2858,36 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, std_fds[i] = fd; } } + + val = JS_GetPropertyStr(ctx, options, "env"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + envp = build_envp(ctx, val); + JS_FreeValue(ctx, val); + if (!envp) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "uid"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + ret = JS_ToUint32(ctx, &uid, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "gid"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + ret = JS_ToUint32(ctx, &gid, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + } } pid = fork(); @@ -2368,12 +2913,21 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (chdir(cwd) < 0) _exit(127); } + if (uid != -1) { + if (setuid(uid) < 0) + _exit(127); + } + if (gid != -1) { + if (setgid(gid) < 0) + _exit(127); + } + if (!file) file = exec_argv[0]; if (use_path) - ret = execvp(file, (char **)exec_argv); + ret = my_execvpe(file, (char **)exec_argv, envp); else - ret = execv(file, (char **)exec_argv); + ret = execve(file, (char **)exec_argv, envp); _exit(127); } /* parent */ @@ -2400,6 +2954,15 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, for(i = 0; i < exec_argc; i++) JS_FreeCString(ctx, exec_argv[i]); js_free(ctx, exec_argv); + if (envp != environ) { + char **p; + p = envp; + while (*p != NULL) { + js_free(ctx, *p); + p++; + } + js_free(ctx, envp); + } return ret_val; exception: ret_val = JS_EXCEPTION; @@ -2464,8 +3027,8 @@ static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; if (JS_ToInt32(ctx, &sig, argv[1])) return JS_EXCEPTION; - ret = kill(pid, sig); - return js_os_return(ctx, ret); + ret = js_get_errno(kill(pid, sig)); + return JS_NewInt32(ctx, ret); } /* sleep(delay_ms) */ @@ -2480,8 +3043,8 @@ static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; ts.tv_sec = delay / 1000; ts.tv_nsec = (delay % 1000) * 1000000; - ret = nanosleep(&ts, NULL); - return js_os_return(ctx, ret); + ret = js_get_errno(nanosleep(&ts, NULL)); + return JS_NewInt32(ctx, ret); } /* dup(fd) */ @@ -2492,8 +3055,8 @@ static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; - ret = dup(fd); - return js_os_return(ctx, ret); + ret = js_get_errno(dup(fd)); + return JS_NewInt32(ctx, ret); } /* dup2(fd) */ @@ -2506,12 +3069,457 @@ static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; if (JS_ToInt32(ctx, &fd2, argv[1])) return JS_EXCEPTION; - ret = dup2(fd, fd2); - return js_os_return(ctx, ret); + ret = js_get_errno(dup2(fd, fd2)); + return JS_NewInt32(ctx, ret); } #endif /* !_WIN32 */ +#ifdef USE_WORKER + +/* Worker */ + +typedef struct { + JSWorkerMessagePipe *recv_pipe; + JSWorkerMessagePipe *send_pipe; + JSWorkerMessageHandler *msg_handler; +} JSWorkerData; + +typedef struct { + char *filename; /* module filename */ + char *basename; /* module base name */ + JSWorkerMessagePipe *recv_pipe, *send_pipe; +} WorkerFuncArgs; + +typedef struct { + int ref_count; + uint64_t buf[0]; +} JSSABHeader; + +static JSClassID js_worker_class_id; +static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); + +static int atomic_add_int(int *ptr, int v) +{ + return atomic_fetch_add((_Atomic(uint32_t) *)ptr, v) + v; +} + +/* shared array buffer allocator */ +static void *js_sab_alloc(void *opaque, size_t size) +{ + JSSABHeader *sab; + sab = malloc(sizeof(JSSABHeader) + size); + if (!sab) + return NULL; + sab->ref_count = 1; + return sab->buf; +} + +static void js_sab_free(void *opaque, void *ptr) +{ + JSSABHeader *sab; + int ref_count; + sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); + ref_count = atomic_add_int(&sab->ref_count, -1); + assert(ref_count >= 0); + if (ref_count == 0) { + free(sab); + } +} + +static void js_sab_dup(void *opaque, void *ptr) +{ + JSSABHeader *sab; + sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); + atomic_add_int(&sab->ref_count, 1); +} + +static JSWorkerMessagePipe *js_new_message_pipe(void) +{ + JSWorkerMessagePipe *ps; + int pipe_fds[2]; + + if (pipe(pipe_fds) < 0) + return NULL; + + ps = malloc(sizeof(*ps)); + if (!ps) { + close(pipe_fds[0]); + close(pipe_fds[1]); + return NULL; + } + ps->ref_count = 1; + init_list_head(&ps->msg_queue); + pthread_mutex_init(&ps->mutex, NULL); + ps->read_fd = pipe_fds[0]; + ps->write_fd = pipe_fds[1]; + return ps; +} + +static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) +{ + atomic_add_int(&ps->ref_count, 1); + return ps; +} + +static void js_free_message(JSWorkerMessage *msg) +{ + size_t i; + /* free the SAB */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_free(NULL, msg->sab_tab[i]); + } + free(msg->sab_tab); + free(msg->data); + free(msg); +} + +static void js_free_message_pipe(JSWorkerMessagePipe *ps) +{ + struct list_head *el, *el1; + JSWorkerMessage *msg; + int ref_count; + + if (!ps) + return; + + ref_count = atomic_add_int(&ps->ref_count, -1); + assert(ref_count >= 0); + if (ref_count == 0) { + list_for_each_safe(el, el1, &ps->msg_queue) { + msg = list_entry(el, JSWorkerMessage, link); + js_free_message(msg); + } + pthread_mutex_destroy(&ps->mutex); + close(ps->read_fd); + close(ps->write_fd); + free(ps); + } +} + +static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port) +{ + if (port) { + js_free_message_pipe(port->recv_pipe); + JS_FreeValueRT(rt, port->on_message_func); + list_del(&port->link); + js_free_rt(rt, port); + } +} + +static void js_worker_finalizer(JSRuntime *rt, JSValue val) +{ + JSWorkerData *worker = JS_GetOpaque(val, js_worker_class_id); + if (worker) { + js_free_message_pipe(worker->recv_pipe); + js_free_message_pipe(worker->send_pipe); + js_free_port(rt, worker->msg_handler); + js_free_rt(rt, worker); + } +} + +static JSClassDef js_worker_class = { + "Worker", + .finalizer = js_worker_finalizer, +}; + +static void *worker_func(void *opaque) +{ + WorkerFuncArgs *args = opaque; + JSRuntime *rt; + JSThreadState *ts; + JSContext *ctx; + + rt = JS_NewRuntime(); + if (rt == NULL) { + fprintf(stderr, "JS_NewRuntime failure"); + exit(1); + } + js_std_init_handlers(rt); + + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + + /* set the pipe to communicate with the parent */ + ts = JS_GetRuntimeOpaque(rt); + ts->recv_pipe = args->recv_pipe; + ts->send_pipe = args->send_pipe; + + /* function pointer to avoid linking the whole JS_NewContext() if + not needed */ + ctx = js_worker_new_context_func(rt); + if (ctx == NULL) { + fprintf(stderr, "JS_NewContext failure"); + } + + JS_SetCanBlock(rt, TRUE); + + js_std_add_helpers(ctx, -1, NULL); + + if (!JS_RunModule(ctx, args->basename, args->filename)) + js_std_dump_error(ctx); + free(args->filename); + free(args->basename); + free(args); + + js_std_loop(ctx); + + JS_FreeContext(ctx); + js_std_free_handlers(rt); + JS_FreeRuntime(rt); + return NULL; +} + +static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target, + JSWorkerMessagePipe *recv_pipe, + JSWorkerMessagePipe *send_pipe) +{ + JSValue obj = JS_UNDEFINED, proto; + JSWorkerData *s; + + /* create the object */ + if (JS_IsUndefined(new_target)) { + proto = JS_GetClassProto(ctx, js_worker_class_id); + } else { + proto = JS_GetPropertyStr(ctx, new_target, "prototype"); + if (JS_IsException(proto)) + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, proto, js_worker_class_id); + JS_FreeValue(ctx, proto); + if (JS_IsException(obj)) + goto fail; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + s->recv_pipe = js_dup_message_pipe(recv_pipe); + s->send_pipe = js_dup_message_pipe(send_pipe); + + JS_SetOpaque(obj, s); + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + WorkerFuncArgs *args = NULL; + pthread_t tid; + pthread_attr_t attr; + JSValue obj = JS_UNDEFINED; + int ret; + const char *filename = NULL, *basename; + JSAtom basename_atom; + + /* XXX: in order to avoid problems with resource liberation, we + don't support creating workers inside workers */ + if (!is_main_thread(rt)) + return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker"); + + /* base name, assuming the calling function is a normal JS + function */ + basename_atom = JS_GetScriptOrModuleName(ctx, 1); + if (basename_atom == JS_ATOM_NULL) { + return JS_ThrowTypeError(ctx, "could not determine calling script or module name"); + } + basename = JS_AtomToCString(ctx, basename_atom); + JS_FreeAtom(ctx, basename_atom); + if (!basename) + goto fail; + + /* module name */ + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + + args = malloc(sizeof(*args)); + if (!args) + goto oom_fail; + memset(args, 0, sizeof(*args)); + args->filename = strdup(filename); + args->basename = strdup(basename); + + /* ports */ + args->recv_pipe = js_new_message_pipe(); + if (!args->recv_pipe) + goto oom_fail; + args->send_pipe = js_new_message_pipe(); + if (!args->send_pipe) + goto oom_fail; + + obj = js_worker_ctor_internal(ctx, new_target, + args->send_pipe, args->recv_pipe); + if (JS_IsException(obj)) + goto fail; + + pthread_attr_init(&attr); + /* no join at the end */ + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ret = pthread_create(&tid, &attr, worker_func, args); + pthread_attr_destroy(&attr); + if (ret != 0) { + JS_ThrowTypeError(ctx, "could not create worker"); + goto fail; + } + JS_FreeCString(ctx, basename); + JS_FreeCString(ctx, filename); + return obj; + oom_fail: + JS_ThrowOutOfMemory(ctx); + fail: + JS_FreeCString(ctx, basename); + JS_FreeCString(ctx, filename); + if (args) { + free(args->filename); + free(args->basename); + js_free_message_pipe(args->recv_pipe); + js_free_message_pipe(args->send_pipe); + free(args); + } + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); + JSWorkerMessagePipe *ps; + size_t data_len, sab_tab_len, i; + uint8_t *data; + JSWorkerMessage *msg; + uint8_t **sab_tab; + + if (!worker) + return JS_EXCEPTION; + + data = JS_WriteObject2(ctx, &data_len, argv[0], + JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE, + &sab_tab, &sab_tab_len); + if (!data) + return JS_EXCEPTION; + + msg = malloc(sizeof(*msg)); + if (!msg) + goto fail; + msg->data = NULL; + msg->sab_tab = NULL; + + /* must reallocate because the allocator may be different */ + msg->data = malloc(data_len); + if (!msg->data) + goto fail; + memcpy(msg->data, data, data_len); + msg->data_len = data_len; + + msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len); + if (!msg->sab_tab) + goto fail; + memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len); + msg->sab_tab_len = sab_tab_len; + + js_free(ctx, data); + js_free(ctx, sab_tab); + + /* increment the SAB reference counts */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_dup(NULL, msg->sab_tab[i]); + } + + ps = worker->send_pipe; + pthread_mutex_lock(&ps->mutex); + /* indicate that data is present */ + if (list_empty(&ps->msg_queue)) { + uint8_t ch = '\0'; + int ret; + for(;;) { + ret = write(ps->write_fd, &ch, 1); + if (ret == 1) + break; + if (ret < 0 && (errno != EAGAIN || errno != EINTR)) + break; + } + } + list_add_tail(&msg->link, &ps->msg_queue); + pthread_mutex_unlock(&ps->mutex); + return JS_UNDEFINED; + fail: + if (msg) { + free(msg->data); + free(msg->sab_tab); + free(msg); + } + js_free(ctx, data); + js_free(ctx, sab_tab); + return JS_EXCEPTION; + +} + +static JSValue js_worker_set_onmessage(JSContext *ctx, JSValueConst this_val, + JSValueConst func) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); + JSWorkerMessageHandler *port; + + if (!worker) + return JS_EXCEPTION; + + port = worker->msg_handler; + if (JS_IsNull(func)) { + if (port) { + js_free_port(rt, port); + worker->msg_handler = NULL; + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (!port) { + port = js_mallocz(ctx, sizeof(*port)); + if (!port) + return JS_EXCEPTION; + port->recv_pipe = js_dup_message_pipe(worker->recv_pipe); + port->on_message_func = JS_NULL; + list_add_tail(&port->link, &ts->port_list); + worker->msg_handler = port; + } + JS_FreeValue(ctx, port->on_message_func); + port->on_message_func = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSValue js_worker_get_onmessage(JSContext *ctx, JSValueConst this_val) +{ + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); + JSWorkerMessageHandler *port; + if (!worker) + return JS_EXCEPTION; + port = worker->msg_handler; + if (port) { + return JS_DupValue(ctx, port->on_message_func); + } else { + return JS_NULL; + } +} + +static const JSCFunctionListEntry js_worker_proto_funcs[] = { + JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ), + JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ), +}; + +#endif /* USE_WORKER */ + +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)) +{ +#ifdef USE_WORKER + js_worker_new_context_func = func; +#endif +} + #if defined(_WIN32) #define OS_PLATFORM "win32" #elif defined(__APPLE__) @@ -2525,7 +3533,6 @@ static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, #define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) static const JSCFunctionListEntry js_os_funcs[] = { -#ifdef ALLOW_UNSAFE_JS JS_CFUNC_DEF("open", 2, js_os_open ), OS_FLAG(O_RDONLY), OS_FLAG(O_WRONLY), @@ -2573,11 +3580,9 @@ static const JSCFunctionListEntry js_os_funcs[] = { 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("chdir", 0, js_os_chdir ), 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 ), + JS_CFUNC_DEF("readdir", 1, js_os_readdir ), /* st_mode constants */ OS_FLAG(S_IFMT), OS_FLAG(S_IFIFO), @@ -2585,14 +3590,19 @@ static const JSCFunctionListEntry js_os_funcs[] = { OS_FLAG(S_IFDIR), OS_FLAG(S_IFBLK), OS_FLAG(S_IFREG), +#if !defined(_WIN32) OS_FLAG(S_IFSOCK), OS_FLAG(S_IFLNK), OS_FLAG(S_ISGID), OS_FLAG(S_ISUID), +#endif + JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), + JS_CFUNC_DEF("utimes", 3, js_os_utimes ), +#if !defined(_WIN32) + JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), + JS_CFUNC_DEF("realpath", 1, js_os_realpath ), 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), @@ -2602,7 +3612,6 @@ static const JSCFunctionListEntry js_os_funcs[] = { JS_CFUNC_DEF("dup", 1, js_os_dup ), JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), #endif -#endif }; static int js_os_init(JSContext *ctx, JSModuleDef *m) @@ -2612,7 +3621,35 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m) /* OSTimer class */ JS_NewClassID(&js_os_timer_class_id); JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class); - + +#ifdef USE_WORKER + { + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSValue proto, obj; + /* Worker class */ + JS_NewClassID(&js_worker_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_worker_class_id, &js_worker_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs)); + + obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1, + JS_CFUNC_constructor, 0); + JS_SetConstructor(ctx, obj, proto); + + JS_SetClassProto(ctx, js_worker_class_id, proto); + + /* set 'Worker.parent' if necessary */ + if (ts->recv_pipe && ts->send_pipe) { + JS_DefinePropertyValueStr(ctx, obj, "parent", + js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe), + JS_PROP_C_W_E); + } + + JS_SetModuleExport(ctx, m, "Worker", obj); + } +#endif /* USE_WORKER */ + return JS_SetModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); } @@ -2624,6 +3661,9 @@ JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) if (!m) return NULL; JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); +#ifdef USE_WORKER + JS_AddModuleExport(ctx, m, "Worker"); +#endif return m; } @@ -2634,14 +3674,15 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val, { int i; const char *str; + size_t len; for(i = 0; i < argc; i++) { if (i != 0) putchar(' '); - str = JS_ToCString(ctx, argv[i]); + str = JS_ToCStringLen(ctx, &len, argv[i]); if (!str) return JS_EXCEPTION; - fputs(str, stdout); + fwrite(str, 1, len, stdout); JS_FreeCString(ctx, str); } putchar('\n'); @@ -2650,7 +3691,7 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val, void js_std_add_helpers(JSContext *ctx, int argc, char **argv) { - JSValue global_obj, console, args; + JSValue global_obj, args; int i; /* XXX: should these global definitions be enumerable? */ @@ -2664,71 +3705,132 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv) */ /* 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])); + if (argc >= 0) { + 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, "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_init_handlers(JSRuntime *rt) +{ + JSThreadState *ts; + + ts = malloc(sizeof(*ts)); + if (!ts) { + fprintf(stderr, "Could not allocate memory for the worker"); + exit(1); + } + memset(ts, 0, sizeof(*ts)); + init_list_head(&ts->os_rw_handlers); + init_list_head(&ts->os_signal_handlers); + init_list_head(&ts->os_timers); + init_list_head(&ts->port_list); + + JS_SetRuntimeOpaque(rt, ts); + +#ifdef USE_WORKER + /* set the SharedArrayBuffer memory handlers */ + { + JSSharedArrayBufferFunctions sf; + memset(&sf, 0, sizeof(sf)); + sf.sab_alloc = js_sab_alloc; + sf.sab_free = js_sab_free; + sf.sab_dup = js_sab_dup; + JS_SetSharedArrayBufferFunctions(rt, &sf); + } +#endif } void js_std_free_handlers(JSRuntime *rt) { + JSThreadState *ts = JS_GetRuntimeOpaque(rt); struct list_head *el, *el1; - list_for_each_safe(el, el1, &os_rw_handlers) { + list_for_each_safe(el, el1, &ts->os_rw_handlers) { JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); free_rw_handler(rt, rh); } - list_for_each_safe(el, el1, &os_signal_handlers) { + list_for_each_safe(el, el1, &ts->os_signal_handlers) { JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); free_sh(rt, sh); } - list_for_each_safe(el, el1, &os_timers) { + list_for_each_safe(el, el1, &ts->os_timers) { JSOSTimer *th = list_entry(el, JSOSTimer, link); unlink_timer(rt, th); if (!th->has_object) free_timer(rt, th); } + +#ifdef USE_WORKER + /* XXX: free port_list ? */ + js_free_message_pipe(ts->recv_pipe); + js_free_message_pipe(ts->send_pipe); +#endif + + free(ts); + JS_SetRuntimeOpaque(rt, NULL); /* fail safe */ +} + +static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val) +{ + const char *str; + + str = JS_ToCString(ctx, val); + if (str) { + fprintf(f, "%s\n", str); + JS_FreeCString(ctx, str); + } else { + fprintf(f, "[exception]\n"); + } +} + +static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val) +{ + JSValue val; + BOOL is_error; + + is_error = JS_IsError(ctx, exception_val); + js_dump_obj(ctx, stderr, exception_val); + if (is_error) { + val = JS_GetPropertyStr(ctx, exception_val, "stack"); + if (!JS_IsUndefined(val)) { + js_dump_obj(ctx, stderr, val); + } + JS_FreeValue(ctx, val); + } } void js_std_dump_error(JSContext *ctx) { - JSValue exception_val, val; - const char *stack; - BOOL is_error; + JSValue exception_val; exception_val = JS_GetException(ctx); - is_error = JS_IsError(ctx, exception_val); - if (!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); - } + js_std_dump_error1(ctx, exception_val); 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) { + fprintf(stderr, "Possibly unhandled promise rejection: "); + js_std_dump_error1(ctx, reason); + } +} + /* main loop which calls the user JS callbacks */ void js_std_loop(JSContext *ctx) { diff --git a/quickjs-libc.h b/quickjs-libc.h index ac7947e..fbbe5b0 100644 --- a/quickjs-libc.h +++ b/quickjs-libc.h @@ -29,10 +29,15 @@ #include "quickjs.h" +#ifdef __cplusplus +extern "C" { +#endif + 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_init_handlers(JSRuntime *rt); void js_std_free_handlers(JSRuntime *rt); void js_std_dump_error(JSContext *ctx); uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); @@ -42,5 +47,13 @@ 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); +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif #endif /* QUICKJS_LIBC_H */ diff --git a/quickjs-opcode.h b/quickjs-opcode.h index 910bb75..c731a14 100644 --- a/quickjs-opcode.h +++ b/quickjs-opcode.h @@ -114,12 +114,11 @@ 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( throw_error, 6, 0, 0, atom_u8) DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a bytecode string */ -DEF( get_super_ctor, 1, 1, 1, none) DEF( get_super, 1, 1, 1, none) DEF( import, 1, 1, 1, none) /* dynamic module import */ @@ -206,16 +205,15 @@ 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_check_object, 1, 1, 1, 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( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) DEF( initial_yield, 1, 0, 0, none) DEF( yield, 1, 1, 2, none) -DEF( yield_star, 1, 2, 2, none) +DEF( yield_star, 1, 1, 2, none) DEF(async_yield_star, 1, 1, 2, none) DEF( await, 1, 1, 1, none) @@ -257,20 +255,16 @@ 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 */ @@ -362,7 +356,8 @@ 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) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) #endif #undef DEF diff --git a/quickjs.c b/quickjs.c index 1ef4865..98cfb89 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1,8 +1,8 @@ /* * QuickJS Javascript Engine * - * Copyright (c) 2017-2019 Fabrice Bellard - * Copyright (c) 2017-2019 Charlie Gordon + * 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 @@ -41,10 +41,10 @@ #include "cutils.h" #include "list.h" #include "quickjs.h" +#include "libregexp.h" #ifdef ENABLE_JS_DEBUG #include "debugger.h" #endif -#include "libregexp.h" #ifdef CONFIG_BIGNUM #include "libbf.h" #endif @@ -74,6 +74,12 @@ #define CONFIG_ATOMICS #endif +#if !defined(EMSCRIPTEN) +/* enable stack limitation */ +#define CONFIG_STACK_CHECK +#endif + + /* dump object free */ //#define DUMP_FREE //#define DUMP_CLOSURE @@ -150,6 +156,8 @@ enum { 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 */ + JS_CLASS_OPERATOR_SET, /* u.operator_set */ #endif JS_CLASS_MAP, /* u.map_state */ JS_CLASS_SET, /* u.map_state */ @@ -175,6 +183,10 @@ enum { JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ }; +// define the deviation of line number +#define DEBUG_MODE_LINE_DEVIATION 4 +#define RELEASE_MODE_LINE_DEVIATION 13 + /* 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]; @@ -188,12 +200,13 @@ typedef enum JSErrorEnum { JS_TYPE_ERROR, JS_URI_ERROR, JS_INTERNAL_ERROR, - + JS_AGGREGATE_ERROR, + JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ } JSErrorEnum; #define JS_MAX_LOCAL_VARS 65536 -#define JS_STACK_SIZE_MAX 65536 +#define JS_STACK_SIZE_MAX 65534 #define JS_STRING_LEN_MAX ((1 << 30) - 1) #define __exception __attribute__((warn_unused_result)) @@ -202,6 +215,34 @@ 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; @@ -219,28 +260,43 @@ struct JSRuntime { JSClass *class_array; struct list_head context_list; /* list of JSContext.link */ - /* list of allocated objects (used by the garbage collector) */ - struct list_head obj_list; /* list of JSObject.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 - struct list_head tmp_obj_list; /* used during gc */ - struct list_head free_obj_list; /* used during gc */ - struct list_head *el_next; /* used during gc */ + /* stack limitation */ + 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; + + struct JSStackFrame *current_stack_frame; JSInterruptHandler *interrupt_handler; void *interrupt_opaque; + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; + void *host_promise_rejection_tracker_opaque; + struct list_head job_list; /* list of JSJobEntry.link */ JSModuleNormalizeFunc *module_normalize_func; JSModuleLoaderFunc *module_loader_func; void *module_loader_opaque; - BOOL in_gc_sweep : 8; BOOL can_block : 8; /* TRUE if Atomics.wait can block */ - + /* used to allocate, free and clone SharedArrayBuffers */ + JSSharedArrayBufferFunctions sab_funcs; + /* Shape hash table */ int shape_hash_bits; int shape_hash_size; @@ -248,7 +304,12 @@ struct JSRuntime { JSShape **shape_hash; #ifdef CONFIG_BIGNUM bf_context_t bf_ctx; + JSNumericOperations bigint_ops; + JSNumericOperations bigfloat_ops; + JSNumericOperations bigdecimal_ops; + uint32_t operator_count; #endif + void *user_opaque; }; struct JSClass { @@ -263,8 +324,7 @@ struct JSClass { #define JS_MODE_STRICT (1 << 0) #define JS_MODE_STRIP (1 << 1) -#define JS_MODE_BIGINT (1 << 2) -#define JS_MODE_MATH (1 << 3) +#define JS_MODE_MATH (1 << 2) typedef struct JSStackFrame { struct JSStackFrame *prev_frame; /* NULL if first stack frame */ @@ -275,23 +335,50 @@ typedef struct JSStackFrame { const uint8_t *cur_pc; /* only used in bytecode functions : PC of the instruction after the call */ int arg_count; - int js_mode; /* for C functions: 0 */ + int js_mode; /* 0 or JS_MODE_MATH for C functions */ /* only used in generators. Current stack pointer value. NULL if the function is running. */ JSValue *cur_sp; } JSStackFrame; -typedef struct JSGCHeader { - uint8_t mark; -} JSGCHeader; +typedef enum { + JS_GC_OBJ_TYPE_JS_OBJECT, + JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, + JS_GC_OBJ_TYPE_SHAPE, + JS_GC_OBJ_TYPE_VAR_REF, + JS_GC_OBJ_TYPE_ASYNC_FUNCTION, + JS_GC_OBJ_TYPE_JS_CONTEXT, +} JSGCObjectTypeEnum; + +/* header for GC objects. GC objects are C data structures with a + reference count that can reference other GC objects. JS Objects are + a particular type of GC object. */ +struct JSGCObjectHeader { + int ref_count; /* must come first, 32-bit */ + JSGCObjectTypeEnum gc_obj_type : 4; + uint8_t mark : 4; /* used by the GC */ + uint8_t dummy1; /* not used by the GC */ + uint16_t dummy2; /* not used by the GC */ + struct list_head link; +}; typedef struct JSVarRef { - JSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after JSRefCountHeader, 8-bit */ - uint8_t is_arg : 1; - int var_idx; /* index of the corresponding function variable on - the stack */ - struct list_head link; /* prev = NULL if no longer on the stack */ + 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 */ @@ -310,37 +397,41 @@ 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 +typedef enum { + JS_AUTOINIT_ID_PROTOTYPE, + JS_AUTOINIT_ID_MODULE_NS, + JS_AUTOINIT_ID_PROP, +} JSAutoInitIDEnum; + /* must be large enough to have a negligible runtime cost and small enough to call the interrupt callback often. */ #define JS_INTERRUPT_COUNTER_INIT 10000 struct JSContext { + JSGCObjectHeader header; /* must come first */ JSRuntime *rt; struct list_head link; - const uint8_t *stack_top; - size_t stack_size; /* in bytes */ - JSValue current_exception; - /* true if a backtrace needs to be added to the current exception - (the backtrace generation cannot be done immediately in a bytecode - function) */ - BOOL exception_needs_backtrace : 8; - /* 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; #ifdef ENABLE_JS_DEBUG DebuggerInfo debugger_info; #endif + JSValue *class_proto; JSValue function_proto; JSValue function_ctor; + JSValue array_ctor; JSValue regexp_ctor; JSValue promise_ctor; JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; @@ -357,6 +448,8 @@ struct JSContext { #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 : 8; /* enable math mode */ + BOOL allow_operator_overloading : 8; #endif /* when the counter reaches zero, JSRutime.interrupt_handler is called */ int interrupt_counter; @@ -424,14 +517,17 @@ typedef struct JSClosureVar { 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 */ + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* 8 bits available */ uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the parent function. otherwise: index to a closure variable of the parent function */ JSAtom var_name; } JSClosureVar; +#define ARG_SCOPE_INDEX 1 +#define ARG_SCOPE_END (-2) + typedef struct JSVarScope { int parent; /* index into fd->scopes of the enclosing scope */ int first; /* index into fd->vars of the last variable in this scope */ @@ -444,6 +540,7 @@ typedef enum { JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator function declaration */ JS_VAR_CATCH, + JS_VAR_FUNCTION_NAME, /* function expression name */ JS_VAR_PRIVATE_FIELD, JS_VAR_PRIVATE_METHOD, JS_VAR_PRIVATE_GETTER, @@ -451,12 +548,21 @@ typedef enum { JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ } JSVarKindEnum; +/* XXX: could use a different structure in bytecode functions to save + memory */ typedef struct JSVarDef { JSAtom var_name; - 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 */ + /* index into fd->scopes of this variable lexical scope */ + int scope_level; + /* during compilation: + - if scope_level = 0: scope in which the variable is defined + - if scope_level != 0: index into fd->vars of the next + variable in the same or enclosing lexical scope + in a bytecode function: + index into fd->vars of the next + variable in the same or enclosing lexical scope + */ + int scope_next; uint8_t is_const : 1; uint8_t is_lexical : 1; uint8_t is_captured : 1; @@ -466,7 +572,9 @@ typedef struct JSVarDef { 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 */ + int func_pool_idx : 24; /* only used during compilation : index in + the constant pool for hoisted function + definition */ } JSVarDef; /* for the encoding of the pc2line table */ @@ -483,8 +591,7 @@ typedef enum JSFunctionKindEnum { } JSFunctionKindEnum; typedef struct JSFunctionBytecode { - JSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after header, 8-bit */ + 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; @@ -497,6 +604,7 @@ typedef struct JSFunctionBytecode { uint8_t super_allowed : 1; uint8_t arguments_allowed : 1; uint8_t has_debug : 1; + uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ uint8_t read_only_bytecode : 1; /* XXX: 4 bits available */ uint8_t *byte_code_buf; /* (self pointer) */ @@ -508,6 +616,7 @@ typedef struct JSFunctionBytecode { uint16_t var_count; uint16_t defined_arg_count; /* for length function property */ uint16_t stack_size; /* maximum stack size */ + JSContext *realm; /* function realm */ JSValue *cpool; /* constant pool (self pointer) */ int cpool_count; int closure_var_count; @@ -553,7 +662,6 @@ typedef struct JSRegExp { typedef struct JSProxyData { JSValue target; JSValue handler; - JSValue proto; uint8_t is_func; uint8_t is_revoked; } JSProxyData; @@ -586,13 +694,59 @@ typedef struct JSAsyncFunctionState { /* XXX: could use an object instead to avoid the JS_TAG_ASYNC_FUNCTION tag for the GC */ typedef struct JSAsyncFunctionData { - JSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after JSRefCountHeader, 8-bit */ + JSGCObjectHeader header; /* must come first */ JSValue resolving_funcs[2]; BOOL is_active; /* true if the async function state is valid */ JSAsyncFunctionState func_state; } JSAsyncFunctionData; +typedef enum { + /* binary operators */ + JS_OVOP_ADD, + JS_OVOP_SUB, + JS_OVOP_MUL, + JS_OVOP_DIV, + JS_OVOP_MOD, + JS_OVOP_POW, + JS_OVOP_OR, + JS_OVOP_AND, + JS_OVOP_XOR, + JS_OVOP_SHL, + JS_OVOP_SAR, + JS_OVOP_SHR, + JS_OVOP_EQ, + JS_OVOP_LESS, + + JS_OVOP_BINARY_COUNT, + /* unary operators */ + JS_OVOP_POS = JS_OVOP_BINARY_COUNT, + JS_OVOP_NEG, + JS_OVOP_INC, + JS_OVOP_DEC, + JS_OVOP_NOT, + + JS_OVOP_COUNT, +} JSOverloadableOperatorEnum; + +typedef struct { + uint32_t operator_index; + JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ +} JSBinaryOperatorDefEntry; + +typedef struct { + int count; + JSBinaryOperatorDefEntry *tab; +} JSBinaryOperatorDef; + +typedef struct { + uint32_t operator_counter; + BOOL is_primitive; /* OperatorSet for a primitive type */ + /* NULL if no operator is defined */ + JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ + JSBinaryOperatorDef left; + JSBinaryOperatorDef right; +} JSOperatorSetData; + typedef struct JSReqModuleEntry { JSAtom module_name; JSModuleDef *module; /* used using resolution */ @@ -652,6 +806,7 @@ struct JSModuleDef { JSValue func_obj; /* only used for JS modules */ JSModuleInitFunc *init_func; /* only used for C modules */ BOOL resolved : 8; + BOOL func_created : 8; BOOL instantiated : 8; BOOL evaluated : 8; BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ @@ -679,8 +834,10 @@ typedef struct JSProperty { } getset; JSVarRef *var_ref; /* JS_PROP_VARREF */ struct { /* JS_PROP_AUTOINIT */ - int (*init_func)(JSContext *ctx, JSObject *obj, - JSAtom prop, void *opaque); + /* in order to use only 2 pointers, we compress the realm + and the init function pointer */ + uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) + in the 2 low bits */ void *opaque; } init; } u; @@ -697,10 +854,9 @@ typedef struct JSShapeProperty { } JSShapeProperty; struct JSShape { - uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1 - before the start of the structure. */ - JSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after JSRefCountHeader, 8-bit */ + /* hash table of size hash_mask + 1 before the start of the + structure (see prop_hash_end()). */ + JSGCObjectHeader header; /* true if the shape is inserted in the shape hash table. If not, JSShape.hash is not valid */ uint8_t is_hashed; @@ -711,25 +867,31 @@ struct JSShape { uint32_t hash; /* current hash value */ uint32_t prop_hash_mask; int prop_size; /* allocated properties */ - int prop_count; + int prop_count; /* include deleted properties */ + int deleted_prop_count; JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ JSObject *proto; JSShapeProperty prop[0]; /* prop_size elements */ }; + struct JSObject { - JSRefCountHeader header; /* must come first, 32-bit */ - JSGCHeader gc_header; /* must come after JSRefCountHeader, 8-bit */ - 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: 8/8 */ - struct list_head link; /* object list */ + union { + JSGCObjectHeader header; + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ + uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ + uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ + uint16_t class_id; /* see JS_CLASS_x */ + }; + }; /* byte offsets: 16/24 */ JSShape *shape; /* prototype and property names + flag */ JSProperty *prop; /* array of properties */ @@ -745,6 +907,7 @@ struct JSObject { struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ #ifdef CONFIG_BIGNUM struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ + struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ #endif struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ @@ -763,7 +926,8 @@ struct JSObject { JSVarRef **var_refs; JSObject *home_object; /* for 'super' access */ } func; - struct { /* JS_CLASS_C_FUNCTION: 8/12 bytes */ + struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ + JSContext *realm; JSCFunctionType c_function; uint8_t length; uint8_t cproto; @@ -797,7 +961,7 @@ struct JSObject { /* byte sizes: 40/48/72 */ }; enum { - JS_ATOM_NULL, + __JS_ATOM_NULL = JS_ATOM_NULL, #define DEF(name, str) JS_ATOM_ ## name, #include "quickjs-atom.h" #undef DEF @@ -820,7 +984,7 @@ typedef enum OPCodeFormat { #undef FMT } OPCodeFormat; -typedef enum OPCodeEnum { +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) @@ -840,13 +1004,19 @@ typedef enum OPCodeEnum { #undef DEF #undef FMT OP_TEMP_END, -} OPCodeEnum; +}; 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); @@ -859,7 +1029,7 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o 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); + JSValue val, BOOL is_array_ctor); static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, JSValueConst val, int flags, int scope_idx); JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); @@ -868,6 +1038,7 @@ 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); @@ -883,6 +1054,9 @@ static void js_array_mark(JSRuntime *rt, JSValueConst val, static void js_object_data_finalizer(JSRuntime *rt, JSValue val); static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); +static void js_c_function_finalizer(JSRuntime *rt, JSValue val); +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); @@ -921,10 +1095,11 @@ static void js_promise_mark(JSRuntime *rt, JSValueConst val, static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -static JSValue js_promise_resolve_function_call(JSContext *ctx, - JSValueConst func_obj, - JSValueConst this_val, - int argc, JSValueConst *argv); +#ifdef CONFIG_BIGNUM +static void js_operator_set_finalizer(JSRuntime *rt, JSValue val); +static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +#endif 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); @@ -955,22 +1130,38 @@ 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_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint); -static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a); +static JSValue JS_NewBigFloat(JSContext *ctx); +static inline bf_t *JS_GetBigFloat(JSValueConst val) +{ + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_NewBigDecimal(JSContext *ctx); +static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) +{ + JSBigDecimal *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_NewBigInt(JSContext *ctx); +static inline bf_t *JS_GetBigInt(JSValueConst val) +{ + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, + BOOL convert_to_safe_integer); +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); -static bf_t *JS_ToBigFloat(JSContext *ctx, BOOL *pis_float, bf_t *buf, - JSValueConst 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 JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv); -static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValueConst *argv); -static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, JSValueConst proto_val, BOOL throw_flag); static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); @@ -982,6 +1173,17 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, int flags); static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); static void reset_weak_ref(JSRuntime *rt, JSObject *p); +static JSValue js_array_buffer_constructor3(JSContext *ctx, + JSValueConst new_target, + uint64_t len, JSClassID class_id, + uint8_t *buf, + JSFreeArrayBufferDataFunc *free_func, + void *opaque, BOOL alloc_flag); +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int classid); static 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); @@ -989,7 +1191,8 @@ 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 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); @@ -997,6 +1200,8 @@ static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx); static void js_free_module_def(JSContext *ctx, JSModuleDef *m); +static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, + JS_MarkFunc *mark_func); static JSValue js_import_meta(JSContext *ctx); static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); @@ -1030,8 +1235,6 @@ static int js_shape_prepare_update(JSContext *ctx, JSObject *p, 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); @@ -1044,8 +1247,18 @@ 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 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 JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque); +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque); +void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag); static const JSClassExoticMethods js_arguments_exotic_methods; static const JSClassExoticMethods js_string_exotic_methods; @@ -1053,18 +1266,6 @@ 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 - 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; @@ -1207,6 +1408,33 @@ char *js_strdup(JSContext *ctx, const char *str) return js_strndup(ctx, str, strlen(str)); } +static no_inline int js_realloc_array(JSContext *ctx, void **parray, + int elem_size, int *psize, int req_size) +{ + int new_size; + size_t slack; + void *new_array; + /* XXX: potential arithmetic overflow */ + new_size = max_int(req_size, *psize * 3 / 2); + new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); + if (!new_array) + return -1; + new_size += slack / elem_size; + *psize = new_size; + *parray = new_array; + return 0; +} + +/* resize the array and update its size if req_size > *psize */ +static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, + int *psize, int req_size) +{ + if (unlikely(req_size > *psize)) + return js_realloc_array(ctx, parray, elem_size, psize, req_size); + else + return 0; +} + static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) { dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); @@ -1225,7 +1453,7 @@ typedef struct 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_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 */ @@ -1234,7 +1462,7 @@ static JSClassShortDef const js_std_class_def[] = { { 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_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ @@ -1261,6 +1489,8 @@ static JSClassShortDef const js_std_class_def[] = { { 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 */ + { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */ #endif { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ @@ -1270,7 +1500,7 @@ static JSClassShortDef const js_std_class_def[] = { { 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_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ }; @@ -1291,6 +1521,90 @@ static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, 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 */ + +#if !defined(CONFIG_STACK_CHECK) +/* no stack limitation */ +static inline uint8_t *js_get_stack_pointer(void) +{ + return NULL; +} + +static inline BOOL js_check_stack_overflow(JSRuntime *rt, 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(JSRuntime *rt, size_t alloca_size) +{ + size_t size; + size = rt->stack_top - js_get_stack_pointer(); + return unlikely((size + alloca_size) > rt->stack_size); +} +#endif + JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) { JSRuntime *rt; @@ -1314,10 +1628,16 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) #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->obj_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 @@ -1334,16 +1654,33 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; + rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; + rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; if (init_shape_hash(rt)) goto fail; + + rt->stack_top = js_get_stack_pointer(); + rt->stack_size = JS_DEFAULT_STACK_SIZE; + rt->current_exception = JS_NULL; + return rt; fail: JS_FreeRuntime(rt); return NULL; } +void *JS_GetRuntimeOpaque(JSRuntime *rt) +{ + return rt->user_opaque; +} + +void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) +{ + rt->user_opaque = opaque; +} + /* default memory allocation functions with memory limitation */ static inline size_t js_def_malloc_usable_size(void *ptr) { @@ -1466,6 +1803,12 @@ void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) rt->can_block = can_block; } +void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, + const JSSharedArrayBufferFunctions *sf) +{ + rt->sab_funcs = *sf; +} + /* return 0 if OK, < 0 if exception */ int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv) @@ -1523,6 +1866,20 @@ int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) return ret; } +JSValue JS_GetCurrentException(JSRuntime *rt) +{ + return rt->current_exception; +} + +uint8_t *JS_GetStringFromObject(JSValue obj) +{ + if (!JS_IsString(obj)) { + return NULL; + } + JSString *str = str = JS_VALUE_GET_STRING(obj); + return str->u.str8; +} + static inline uint32_t atom_get_free(const JSAtomStruct *p) { return (uintptr_t)p >> 1; @@ -1594,10 +1951,7 @@ 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); - } + JS_FreeValueRT(rt, rt->current_exception); list_for_each_safe(el, el1, &rt->job_list) { JSJobEntry *e = list_entry(el, JSJobEntry, link); @@ -1613,34 +1967,34 @@ void JS_FreeRuntime(JSRuntime *rt) /* leaking objects */ { BOOL header_done; - JSObject *p; + JSGCObjectHeader *p; int count; /* remove the internal refcounts to display only the object referenced externally */ - list_for_each(el, &rt->obj_list) { - p = list_entry(el, JSObject, link); - p->gc_header.mark = 0; + 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->obj_list) { - p = list_entry(el, JSObject, link); - if (p->header.ref_count != 0) { + 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_DumpObject(rt, p); + JS_DumpGCObject(rt, p); } } count = 0; - list_for_each(el, &rt->obj_list) { - p = list_entry(el, JSObject, link); - if (p->header.ref_count == 0) { + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count == 0) { count++; } } @@ -1648,7 +2002,7 @@ void JS_FreeRuntime(JSRuntime *rt) printf("Secondary object leaks: %d\n", count); } #endif - assert(list_empty(&rt->obj_list)); + assert(list_empty(&rt->gc_obj_list)); /* free the classes */ for(i = 0; i < rt->class_count; i++) { @@ -1780,32 +2134,6 @@ void JS_FreeRuntime(JSRuntime *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; @@ -1814,6 +2142,9 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) ctx = js_mallocz_rt(rt, sizeof(JSContext)); if (!ctx) return NULL; + ctx->header.ref_count = 1; + add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); + ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * rt->class_count); if (!ctx->class_proto) { @@ -1822,16 +2153,14 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) } 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 = 53; - ctx->fp_env.flags = bf_set_exp_bits(11) | BF_RNDN | BF_FLAG_SUBNORMAL; + ctx->fp_env.prec = 113; + ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL; #endif for(i = 0; i < rt->class_count; i++) ctx->class_proto[i] = JS_NULL; + ctx->array_ctor = JS_NULL; ctx->regexp_ctor = JS_NULL; ctx->promise_ctor = JS_NULL; init_list_head(&ctx->loaded_modules); @@ -1858,7 +2187,9 @@ JSContext *JS_NewContext(JSRuntime *rt) JS_AddIntrinsicMapSet(ctx); JS_AddIntrinsicTypedArrays(ctx); JS_AddIntrinsicPromise(ctx); - +#ifdef CONFIG_BIGNUM + JS_AddIntrinsicBigInt(ctx); +#endif return ctx; } @@ -1916,11 +2247,60 @@ static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) } } +JSContext *JS_DupContext(JSContext *ctx) +{ + ctx->header.ref_count++; + return ctx; +} + +/* used by the GC */ +static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, + JS_MarkFunc *mark_func) +{ + int i; + struct list_head *el; + + /* modules are not seen by the GC, so we directly mark the objects + referenced by each module */ + list_for_each(el, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + js_mark_module_def(rt, m, mark_func); + } + + JS_MarkValue(rt, ctx->global_obj, mark_func); + JS_MarkValue(rt, ctx->global_var_obj, mark_func); + + JS_MarkValue(rt, ctx->throw_type_error, mark_func); + JS_MarkValue(rt, ctx->eval_obj, mark_func); + + JS_MarkValue(rt, ctx->array_proto_values, mark_func); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); + } + for(i = 0; i < rt->class_count; i++) { + JS_MarkValue(rt, ctx->class_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->iterator_proto, mark_func); + JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); + JS_MarkValue(rt, ctx->promise_ctor, mark_func); + JS_MarkValue(rt, ctx->array_ctor, mark_func); + JS_MarkValue(rt, ctx->regexp_ctor, mark_func); + JS_MarkValue(rt, ctx->function_ctor, mark_func); + JS_MarkValue(rt, ctx->function_proto, mark_func); + + if (ctx->array_shape) + mark_func(rt, &ctx->array_shape->header); +} + void JS_FreeContext(JSContext *ctx) { JSRuntime *rt = ctx->rt; int i; + if (--ctx->header.ref_count > 0) + return; + assert(ctx->header.ref_count == 0); + #ifdef DUMP_ATOMS JS_DumpAtoms(ctx->rt); #endif @@ -1930,12 +2310,12 @@ void JS_FreeContext(JSContext *ctx) #ifdef DUMP_OBJECTS { struct list_head *el; - JSObject *p; + JSGCObjectHeader *p; printf("JSObjects: {\n"); JS_DumpObjectHeader(ctx->rt); - list_for_each(el, &rt->obj_list) { - p = list_entry(el, JSObject, link); - JS_DumpObject(rt, p); + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + JS_DumpGCObject(rt, p); } printf("}\n"); } @@ -1950,8 +2330,6 @@ void JS_FreeContext(JSContext *ctx) 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); @@ -1969,6 +2347,7 @@ void JS_FreeContext(JSContext *ctx) JS_FreeValue(ctx, ctx->iterator_proto); JS_FreeValue(ctx, ctx->async_iterator_proto); JS_FreeValue(ctx, ctx->promise_ctor); + JS_FreeValue(ctx, ctx->array_ctor); JS_FreeValue(ctx, ctx->regexp_ctor); JS_FreeValue(ctx, ctx->function_ctor); JS_FreeValue(ctx, ctx->function_proto); @@ -1976,6 +2355,7 @@ void JS_FreeContext(JSContext *ctx) js_free_shape_null(ctx->rt, ctx->array_shape); list_del(&ctx->link); + remove_gc_object(&ctx->header); js_free_rt(ctx->rt, ctx); } @@ -1986,57 +2366,23 @@ JSRuntime *JS_GetRuntime(JSContext *ctx) void JS_SetMaxStackSize(JSContext *ctx, size_t stack_size) { - ctx->stack_size = stack_size; + ctx->rt->stack_size = stack_size; } static inline BOOL is_strict_mode(JSContext *ctx) { - JSStackFrame *sf = ctx->current_stack_frame; + JSStackFrame *sf = ctx->rt->current_stack_frame; return (sf && (sf->js_mode & JS_MODE_STRICT)); } #ifdef CONFIG_BIGNUM -static inline BOOL is_bignum_mode(JSContext *ctx) +static inline BOOL is_math_mode(JSContext *ctx) { - JSStackFrame *sf = ctx->current_stack_frame; - return (sf && (sf->js_mode & JS_MODE_BIGINT)); + JSStackFrame *sf = ctx->rt->current_stack_frame; + return (sf && (sf->js_mode & JS_MODE_MATH)); } #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_bignum_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) @@ -3894,9 +4240,14 @@ static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); } +static inline uint32_t *prop_hash_end(JSShape *sh) +{ + return (uint32_t *)sh; +} + static inline void *get_alloc_from_shape(JSShape *sh) { - return sh->prop_hash_end - ((intptr_t)sh->prop_hash_mask + 1); + return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); } static inline JSShapeProperty *get_shape_prop(JSShape *sh) @@ -4003,16 +4354,17 @@ static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, return NULL; sh = get_shape_from_alloc(sh_alloc, hash_size); sh->header.ref_count = 1; - sh->gc_header.mark = 0; + 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]) * + memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * hash_size); sh->prop_hash_mask = hash_size - 1; - sh->prop_count = 0; sh->prop_size = prop_size; - + sh->prop_count = 0; + sh->deleted_prop_count = 0; + /* insert in the hash table */ sh->hash = shape_initial_hash(proto); sh->is_hashed = TRUE; @@ -4046,7 +4398,7 @@ static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) memcpy(sh_alloc, sh_alloc1, size); sh = get_shape_from_alloc(sh_alloc, hash_size); sh->header.ref_count = 1; - sh->gc_header.mark = 0; + 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)); @@ -4063,24 +4415,30 @@ static JSShape *js_dup_shape(JSShape *sh) return sh; } -static void js_free_shape(JSRuntime *rt, JSShape *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)) { - 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++; - } - js_free_rt(rt, get_alloc_from_shape(sh)); + js_free_shape0(rt, sh); } } @@ -4122,34 +4480,109 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, 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); + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { if (pr->atom != JS_ATOM_NULL) { h = ((uintptr_t)pr->atom & new_hash_mask); - pr->hash_next = sh->prop_hash_end[-h - 1]; - sh->prop_hash_end[-h - 1] = i + 1; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = i + 1; } } js_free(ctx, get_alloc_from_shape(old_sh)); } else { /* only resize the properties */ + list_del(&sh->header.link); sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), get_shape_size(new_hash_size, new_size)); - if (unlikely(!sh_alloc)) + 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; } +/* remove the deleted properties. */ +static int compact_properties(JSContext *ctx, JSObject *p) +{ + JSShape *sh, *old_sh; + void *sh_alloc; + intptr_t h; + uint32_t new_hash_size, i, j, new_hash_mask, new_size; + JSShapeProperty *old_pr, *pr; + JSProperty *prop, *new_prop; + + sh = p->shape; + assert(!sh->is_hashed); + + new_size = max_int(JS_PROP_INITIAL_SIZE, + sh->prop_count - sh->deleted_prop_count); + assert(new_size <= sh->prop_size); + + new_hash_size = sh->prop_hash_mask + 1; + while ((new_hash_size / 2) >= new_size) + new_hash_size = new_hash_size / 2; + new_hash_mask = new_hash_size - 1; + + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + memcpy(sh, old_sh, sizeof(JSShape)); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + + j = 0; + old_pr = old_sh->prop; + pr = sh->prop; + prop = p->prop; + for(i = 0; i < sh->prop_count; i++) { + if (old_pr->atom != JS_ATOM_NULL) { + pr->atom = old_pr->atom; + pr->flags = old_pr->flags; + h = ((uintptr_t)old_pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = j + 1; + prop[j] = prop[i]; + j++; + pr++; + } + old_pr++; + } + assert(j == (sh->prop_count - sh->deleted_prop_count)); + sh->prop_hash_mask = new_hash_mask; + sh->prop_size = new_size; + sh->deleted_prop_count = 0; + sh->prop_count = j; + + p->shape = sh; + js_free(ctx, get_alloc_from_shape(old_sh)); + + /* reduce the size of the object properties */ + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (new_prop) + p->prop = new_prop; + return 0; +} + static int add_shape_property(JSContext *ctx, JSShape **psh, JSObject *p, JSAtom atom, int prop_flags) { @@ -4189,8 +4622,8 @@ static int add_shape_property(JSContext *ctx, JSShape **psh, /* 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; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = sh->prop_count; return 0; } @@ -4268,7 +4701,8 @@ static __maybe_unused void JS_DumpShapes(JSRuntime *rt) 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++) { @@ -4278,10 +4712,13 @@ static __maybe_unused void JS_DumpShapes(JSRuntime *rt) } } /* dump non-hashed shapes */ - list_for_each(el, &rt->obj_list) { - p = list_entry(el, JSObject, link); - if (!p->shape->is_hashed) { - JS_DumpShape(rt, -1, p->shape); + 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"); @@ -4295,8 +4732,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p = js_malloc(ctx, sizeof(JSObject)); if (unlikely(!p)) goto fail; - p->header.ref_count = 1; - p->gc_header.mark = 0; p->class_id = class_id; p->extensible = TRUE; p->free_mark = 0; @@ -4304,8 +4739,8 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->fast_array = 0; p->is_constructor = 0; p->is_uncatchable_error = 0; - p->is_class = 0; p->tmp_mark = 0; + p->is_HTMLDDA = 0; p->first_weak_ref = NULL; p->u.opaque = NULL; p->shape = sh; @@ -4344,7 +4779,19 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->prop[0].u.value = JS_UNDEFINED; break; case JS_CLASS_ARGUMENTS: - case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: +#endif + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: p->is_exotic = 1; p->fast_array = 1; p->u.array.u.ptr = NULL; @@ -4362,6 +4809,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas #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; @@ -4376,7 +4824,8 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas } break; } - list_add_tail(&p->link, &ctx->rt->obj_list); + 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); } @@ -4423,6 +4872,7 @@ static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) #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); } @@ -4446,6 +4896,7 @@ static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) #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; @@ -4584,6 +5035,7 @@ static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, if (JS_IsException(func_obj)) return func_obj; p = JS_VALUE_GET_OBJ(func_obj); + p->u.cfunc.realm = JS_DupContext(ctx); p->u.cfunc.c_function.generic = func; p->u.cfunc.length = length; p->u.cfunc.cproto = cproto; @@ -4645,7 +5097,7 @@ static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, int flags) { JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); JSValueConst *arg_buf; @@ -4694,6 +5146,27 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, return func_obj; } +static JSContext *js_autoinit_get_realm(JSProperty *pr) +{ + return (JSContext *)(pr->u.init.realm_and_id & ~3); +} + +static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) +{ + return pr->u.init.realm_and_id & 3; +} + +static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) +{ + JS_FreeContext(js_autoinit_get_realm(pr)); +} + +static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, + JS_MarkFunc *mark_func) +{ + mark_func(rt, &js_autoinit_get_realm(pr)->header); +} + static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) { if (unlikely(prop_flags & JS_PROP_TMASK)) { @@ -4705,7 +5178,7 @@ static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) } 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 */ + js_autoinit_free(rt, pr); } } else { JS_FreeValueRT(rt, pr->u.value); @@ -4720,7 +5193,7 @@ static force_inline JSShapeProperty *find_own_property1(JSObject *p, intptr_t h; sh = p->shape; h = (uintptr_t)atom & sh->prop_hash_mask; - h = sh->prop_hash_end[-h - 1]; + h = prop_hash_end(sh)[-h - 1]; prop = get_shape_prop(sh); while (h) { pr = &prop[h - 1]; @@ -4741,7 +5214,7 @@ static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, intptr_t h; sh = p->shape; h = (uintptr_t)atom & sh->prop_hash_mask; - h = sh->prop_hash_end[-h - 1]; + h = prop_hash_end(sh)[-h - 1]; prop = get_shape_prop(sh); while (h) { pr = &prop[h - 1]; @@ -4766,10 +5239,11 @@ 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->link.prev != NULL) { - list_del(&var_ref->link); /* still on the stack */ - } else { + 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); } @@ -4812,6 +5286,23 @@ static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkValue(rt, p->u.object_data, mark_func); } +static void js_c_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + JS_FreeContext(p->u.cfunc.realm); +} + +static void js_c_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + mark_func(rt, &p->u.cfunc.realm->header); +} + static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) { JSObject *p1, *p = JS_VALUE_GET_OBJ(val); @@ -4851,9 +5342,8 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, if (var_refs) { for(i = 0; i < b->closure_var_count; i++) { JSVarRef *var_ref = var_refs[i]; - if (var_ref) { - JS_MarkValue(rt, JS_MKPTR(JS_TAG_VAR_REF, var_ref), - mark_func); + if (var_ref && var_ref->is_detached) { + mark_func(rt, &var_ref->header); } } } @@ -4906,13 +5396,15 @@ static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkValue(rt, it->obj, mark_func); } -static void free_object_struct(JSRuntime *rt, JSObject *p) +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); @@ -4921,6 +5413,8 @@ static void free_object_struct(JSRuntime *rt, JSObject *p) 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 */ @@ -4940,33 +5434,44 @@ static void free_object_struct(JSRuntime *rt, JSObject *p) p->u.opaque = NULL; p->u.func.var_refs = NULL; p->u.func.home_object = NULL; -} -static void free_object2(JSRuntime *rt, JSObject *p) -{ - if (p->free_mark) { - /* already freed (happen when freeing cycles) */ - return; - } - /* indicate that the object is being freed */ - p->free_mark = 1; - - free_object_struct(rt, p); - - /* remove from the global object list */ - list_del(&p->link); - if (rt->in_gc_sweep) { - list_add_tail(&p->link, &rt->free_obj_list); + 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_object(JSRuntime *rt, JSObject *p) +static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) { - assert(p->header.ref_count == 0); - if (!rt->in_gc_sweep) - free_object2(rt, p); + 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. */ @@ -5001,14 +5506,21 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) } break; case JS_TAG_OBJECT: - free_object(rt, JS_VALUE_GET_OBJ(v)); - break; case JS_TAG_FUNCTION_BYTECODE: - free_function_bytecode(rt, JS_VALUE_GET_PTR(v)); + { + JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); + if (p->mark) { + return; + } + 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_SHAPE: - case JS_TAG_ASYNC_FUNCTION: - case JS_TAG_VAR_REF: case JS_TAG_MODULE: abort(); /* never freed here */ break; @@ -5021,6 +5533,13 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) 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: { @@ -5041,38 +5560,45 @@ void __JS_FreeValue(JSContext *ctx, JSValue v) /* garbage collection */ -static BOOL has_children(JSValueConst val) +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type) { - switch(JS_VALUE_GET_TAG(val)) { - case JS_TAG_VAR_REF: - case JS_TAG_OBJECT: - case JS_TAG_FUNCTION_BYTECODE: - case JS_TAG_ASYNC_FUNCTION: - case JS_TAG_SHAPE: - return TRUE; - default: - return FALSE; - } + 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) && has_children(val)) { - mark_func(rt, val); + 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, JSValueConst val, JS_MarkFunc *mark_func) +static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, + JS_MarkFunc *mark_func) { - switch(JS_VALUE_GET_TAG(val)) { - case JS_TAG_OBJECT: + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: { - JSObject *p = JS_VALUE_GET_OBJ(val); + JSObject *p = (JSObject *)gp; JSShapeProperty *prs; JSShape *sh; int i; sh = p->shape; - mark_func(rt, JS_MKPTR(JS_TAG_SHAPE, sh)); + mark_func(rt, &sh->header); /* mark all the fields */ prs = get_shape_prop(sh); for(i = 0; i < sh->prop_count; i++) { @@ -5081,13 +5607,17 @@ static void mark_children(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_fun if (prs->flags & JS_PROP_TMASK) { if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { if (pr->u.getset.getter) - mark_func(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + mark_func(rt, &pr->u.getset.getter->header); if (pr->u.getset.setter) - mark_func(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + mark_func(rt, &pr->u.getset.setter->header); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - JS_MarkValue(rt, JS_MKPTR(JS_TAG_VAR_REF, pr->u.var_ref), mark_func); + 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 */ + js_autoinit_mark(rt, pr, mark_func); } } else { JS_MarkValue(rt, pr->u.value, mark_func); @@ -5104,267 +5634,170 @@ static void mark_children(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_fun } } break; - case JS_TAG_FUNCTION_BYTECODE: + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: /* the template objects can be part of a cycle */ { - JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); + JSFunctionBytecode *b = (JSFunctionBytecode *)gp; int i; for(i = 0; i < b->cpool_count; i++) { JS_MarkValue(rt, b->cpool[i], mark_func); } + if (b->realm) + mark_func(rt, &b->realm->header); } break; - case JS_TAG_VAR_REF: + case JS_GC_OBJ_TYPE_VAR_REF: { - JSVarRef *var_ref = JS_VALUE_GET_PTR(val); - /* the refcount of stack values is not incremented, hence - the test */ - if (var_ref->link.prev == NULL) { - JS_MarkValue(rt, *var_ref->pvalue, mark_func); - } + 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_TAG_ASYNC_FUNCTION: + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: { - JSAsyncFunctionData *s = JS_VALUE_GET_PTR(val); + 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_TAG_SHAPE: + case JS_GC_OBJ_TYPE_SHAPE: { - JSShape *sh = JS_VALUE_GET_PTR(val); + JSShape *sh = (JSShape *)gp; if (sh->proto != NULL) { - mark_func(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + mark_func(rt, &sh->proto->header); } } break; - default: - /* no children */ + case JS_GC_OBJ_TYPE_JS_CONTEXT: + { + JSContext *ctx = (JSContext *)gp; + JS_MarkContext(rt, ctx, mark_func); + } break; + default: + abort(); } } -#if 0 -/* not useful until realms are supported */ -static void mark_context(JSRuntime *rt, JSContext *ctx) +static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) { - 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 - -/* 1 = possible member of a cycle - 2 = member of a cycle -*/ - -static void gc_decref_mark(JSRuntime *rt, JSValueConst obj); - -//#define DUMP_GC_DECREF - -#ifdef DUMP_GC_DECREF -static int decref_indent; -#endif - -static void gc_decref_child(JSRuntime *rt, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - -#ifdef DUMP_LEAKS -#ifdef DUMP_GC_DECREF - { - int i; - for(i = 0; i < decref_indent; i++) - printf(" "); - if (JS_IsObject(obj)) { - JS_DumpObject(rt, p); - } else { - JS_DumpValueShort(rt, obj); printf("\n"); - } - } -#endif - if (p->header.ref_count <= 0) { - printf("%p: invalid refcount (%d)\n", p, p->header.ref_count); - if (JS_IsObject(obj)) { - JS_DumpObject(rt, p); - } else { - JS_DumpValueShort(rt, obj); - } - } -#endif - assert(p->header.ref_count > 0); - p->header.ref_count--; - gc_decref_mark(rt, obj); -} - -static void gc_decref_mark(JSRuntime *rt, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->gc_header.mark == 0) { - p->gc_header.mark = 1; -#ifdef DUMP_GC_DECREF - decref_indent++; -#endif - mark_children(rt, obj, gc_decref_child); -#ifdef DUMP_GC_DECREF - decref_indent--; -#endif + 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; - JSObject *p; + struct list_head *el, *el1; + JSGCObjectHeader *p; + + init_list_head(&rt->tmp_obj_list); - list_for_each(el, &rt->obj_list) { - p = list_entry(el, JSObject, link); - gc_decref_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p)); - } -} - -static void gc_scan_incref(JSRuntime *rt, JSValueConst obj); - -static void gc_scan_incref_child(JSRuntime *rt, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - p->header.ref_count++; - if (p->gc_header.mark != 0) { - gc_scan_incref(rt, obj); - } -} - -static void gc_scan_incref(JSRuntime *rt, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - p->gc_header.mark = 0; /* not freed */ - if (JS_IsObject(obj)) { - /* remove the tmp_obj_list and update the next object to - explore */ - if (rt->el_next == &p->link) - rt->el_next = rt->el_next->next; - /* add to rt->obj_list */ - list_del(&p->link); - list_add_tail(&p->link, &rt->obj_list); - } - mark_children(rt, obj, gc_scan_incref_child); -} - -static void gc_scan_obj(JSRuntime *rt, JSValueConst obj) -{ - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->gc_header.mark == 1) { - if (p->header.ref_count > 0) { - gc_scan_incref(rt, obj); - } else { - p->gc_header.mark = 2; - mark_children(rt, obj, gc_scan_obj); + /* 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_obj2(JSRuntime *rt, JSValueConst obj); - -static void gc_scan_incref_child2(JSRuntime *rt, JSValueConst obj) +static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - p->header.ref_count++; - gc_scan_obj2(rt, obj); + 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_obj2(JSRuntime *rt, JSValueConst obj) +static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->gc_header.mark == 2) { - p->gc_header.mark = 3; - mark_children(rt, obj, gc_scan_incref_child2); - } + p->ref_count++; } static void gc_scan(JSRuntime *rt) { struct list_head *el; - JSObject *p; + JSGCObjectHeader *p; - /* move obj_list to tmp_obj_list */ - list_add(&rt->tmp_obj_list, &rt->obj_list); - list_del(&rt->obj_list); - init_list_head(&rt->obj_list); - - /* keep the objects with a refcount > 0 and their children. After - this pass, obj_list contains the objects to be deleted. Their - mark is 2. */ - for(el = rt->tmp_obj_list.next; el != &rt->tmp_obj_list; - el = rt->el_next) { - rt->el_next = el->next; /* may be modified by gc_scan_obj() */ - p = list_entry(el, JSObject, link); - gc_scan_obj(rt, JS_MKPTR(JS_TAG_OBJECT, 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. After this - pass, their mark is 3 */ + + /* restore the refcount of the objects to be deleted. */ list_for_each(el, &rt->tmp_obj_list) { - p = list_entry(el, JSObject, link); - gc_scan_obj2(rt, JS_MKPTR(JS_TAG_OBJECT, p)); + 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; - JSObject *p; + JSGCObjectHeader *p; #ifdef DUMP_GC_FREE BOOL header_done = FALSE; #endif - init_list_head(&rt->free_obj_list); - rt->in_gc_sweep = TRUE; - list_for_each_safe(el, el1, &rt->tmp_obj_list) { - p = list_entry(el, JSObject, link); - assert(p->gc_header.mark == 3); -#ifdef DUMP_GC_FREE - if (!header_done) { - printf("Freeing cycles:\n"); - JS_DumpObjectHeader(rt); - header_done = TRUE; - } - JS_DumpObject(rt, p); -#endif - free_object2(rt, p); - } - rt->in_gc_sweep = FALSE; - /* free all the object structures */ - list_for_each_safe(el, el1, &rt->free_obj_list) { - p = list_entry(el, JSObject, link); + 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) @@ -5373,10 +5806,10 @@ void JS_RunGC(JSRuntime *rt) 1 after this pass. */ gc_decref(rt); - /* keep objects with a non zero refcount and their childs */ + /* keep the GC objects with a non zero refcount and their childs */ gc_scan(rt); - /* free the objects with a zero refcount */ + /* free the GC objects in a cycle */ gc_free_cycles(rt); } @@ -5392,23 +5825,17 @@ BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj) return !p->free_mark; } -/* Return true during the GC sweep phase (can be useful inside in finalizer) */ -BOOL JS_IsInGCSweep(JSRuntime *rt) -{ - return rt->in_gc_sweep; -} - /* 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; - double js_func_count; + int64_t js_func_count; double js_func_size; - double js_func_code_size; - double js_func_pc2line_count; - double js_func_pc2line_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); @@ -5426,7 +5853,6 @@ static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) { int memory_used_count, js_func_size, i; - double ref_count = b->header.ref_count; memory_used_count = 0; js_func_size = offsetof(JSFunctionBytecode, debug); @@ -5444,7 +5870,7 @@ static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *h 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 / ref_count; + hp->js_func_code_size += b->byte_code_len; } if (b->has_debug) { js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug); @@ -5454,13 +5880,13 @@ static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *h } if (b->debug.pc2line_len) { memory_used_count++; - hp->js_func_pc2line_count += 1 / ref_count; - hp->js_func_pc2line_size += b->debug.pc2line_len / ref_count; + hp->js_func_pc2line_count += 1; + hp->js_func_pc2line_size += b->debug.pc2line_len; } } - hp->js_func_size += js_func_size / ref_count; - hp->js_func_count += 1 / ref_count; - hp->memory_used_count += memory_used_count / ref_count; + 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) @@ -5469,12 +5895,10 @@ static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) case JS_TAG_STRING: compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); break; - case JS_TAG_FUNCTION_BYTECODE: - compute_bytecode_size(JS_VALUE_GET_PTR(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 @@ -5543,10 +5967,21 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) } } - list_for_each(el, &rt->obj_list) { - JSObject *p = list_entry(el, JSObject, link); - JSShape *sh = p->shape; + 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++; @@ -5593,6 +6028,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) #ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT: /* u.object_data */ case JS_CLASS_BIG_FLOAT: /* u.object_data */ + case JS_CLASS_BIG_DECIMAL: /* u.object_data */ #endif compute_value_size(p->u.object_data, hp); break; @@ -5620,7 +6056,6 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) } } } - compute_bytecode_size(b, hp); } break; case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ @@ -5746,11 +6181,11 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) } s->str_count = round(mem.str_count); s->str_size = round(mem.str_size); - s->js_func_count = round(mem.js_func_count); + s->js_func_count = mem.js_func_count; s->js_func_size = round(mem.js_func_size); - s->js_func_code_size = round(mem.js_func_code_size); - s->js_func_pc2line_count = round(mem.js_func_pc2line_count); - s->js_func_pc2line_size = round(mem.js_func_pc2line_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 + @@ -5766,7 +6201,7 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) #ifdef CONFIG_BIGNUM "BigNum " #endif - "2019-09-18 version, %d-bit, malloc limit: %"PRId64"\n\n", + "2020-11-08 version, %d-bit, malloc limit: %"PRId64"\n\n", (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit); #if 1 if (rt) { @@ -5801,9 +6236,13 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; int class_id; struct list_head *el; - list_for_each(el, &rt->obj_list) { - JSObject *p = list_entry(el, JSObject, link); - obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; + 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]) @@ -5893,9 +6332,9 @@ JSValue JS_GetGlobalObject(JSContext *ctx) /* WARNING: obj is freed */ JSValue JS_Throw(JSContext *ctx, JSValue obj) { - JS_FreeValue(ctx, ctx->current_exception); - ctx->current_exception = obj; - ctx->exception_needs_backtrace = JS_IsError(ctx, obj); + JSRuntime *rt = ctx->rt; + JS_FreeValue(ctx, rt->current_exception); + rt->current_exception = obj; return JS_EXCEPTION; } @@ -5903,9 +6342,9 @@ JSValue JS_Throw(JSContext *ctx, JSValue obj) JSValue JS_GetException(JSContext *ctx) { JSValue val; - val = ctx->current_exception; - ctx->current_exception = JS_NULL; - ctx->exception_needs_backtrace = FALSE; + JSRuntime *rt = ctx->rt; + val = rt->current_exception; + rt->current_exception = JS_NULL; return val; } @@ -6031,12 +6470,15 @@ static const char *get_func_name(JSContext *ctx, JSValueConst func) 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). If 'pc' != - NULL, it is used as the cur_pc value for the current stack frame */ + and line number information (used for parse error). */ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, const char *filename, int line_num, - const uint8_t *cur_pc) + int backtrace_flags) { JSStackFrame *sf; JSValue str; @@ -6044,21 +6486,32 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, const char *func_name_str; const char *str1; JSObject *p; - - ctx->exception_needs_backtrace = FALSE; + 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); + if (line_num != -1) { +#ifdef ENABLE_JS_DEBUG + dbuf_printf(&dbuf, ":%d", (line_num - DEBUG_MODE_LINE_DEVIATION)); +#else + dbuf_printf(&dbuf, ":%d", (line_num - RELEASE_MODE_LINE_DEVIATION)); +#endif + } 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) { + for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) { + if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { + backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; + continue; + } func_name_str = get_func_name(ctx, sf->cur_func); if (!func_name_str || func_name_str[0] == '\0') str1 = ""; @@ -6068,50 +6521,70 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, JS_FreeCString(ctx, func_name_str); p = JS_VALUE_GET_OBJ(sf->cur_func); + backtrace_barrier = FALSE; if (js_class_has_bytecode(p->class_id)) { JSFunctionBytecode *b; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; + const char *atom_str; int line_num1; b = p->u.func.function_bytecode; + backtrace_barrier = b->backtrace_barrier; if (b->has_debug) { - if (sf == ctx->current_stack_frame) { - if (!cur_pc) { - line_num1 = b->debug.line_num; - } else { - line_num1 = find_line_num(ctx, b, - cur_pc - b->byte_code_buf - 1); - } - } else { - line_num1 = find_line_num(ctx, b, - sf->cur_pc - b->byte_code_buf - 1); - } + line_num1 = find_line_num(ctx, b, + sf->cur_pc - b->byte_code_buf - 1); + atom_str = JS_AtomToCString(ctx, b->debug.filename); dbuf_printf(&dbuf, " (%s", - JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), - b->debug.filename)); - if (line_num1 != -1) - dbuf_printf(&dbuf, ":%d", line_num1); + atom_str ? atom_str : ""); + JS_FreeCString(ctx, atom_str); + if (line_num1 != -1) { +#ifdef ENABLE_JS_DEBUG + dbuf_printf(&dbuf, ":%d", line_num1 - DEBUG_MODE_LINE_DEVIATION); +#else + dbuf_printf(&dbuf, ":%d", line_num1 - RELEASE_MODE_LINE_DEVIATION); +#endif + } 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); + if (dbuf_error(&dbuf)) + str = JS_NULL; + else + str = JS_NewString(ctx, (char *)dbuf.buf); dbuf_free(&dbuf); JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } +/* Note: it is important that no exception is returned by this function */ +static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ERROR) + return FALSE; + if (find_own_property1(p, JS_ATOM_stack)) + return FALSE; + return TRUE; +} + JSValue JS_NewError(JSContext *ctx) { return JS_NewObjectClass(ctx, JS_CLASS_ERROR); } -static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, - const char *fmt, va_list ap) +static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, + const char *fmt, va_list ap, BOOL add_backtrace) { char buf[256]; JSValue obj, ret; @@ -6127,10 +6600,27 @@ static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, JS_NewString(ctx, buf), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } + if (add_backtrace) { + build_backtrace(ctx, obj, NULL, 0, 0); + } ret = JS_Throw(ctx, obj); return ret; } +static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, + const char *fmt, va_list ap) +{ + JSRuntime *rt = ctx->rt; + JSStackFrame *sf; + BOOL add_backtrace; + + /* the backtrace is added later if called from a bytecode function */ + sf = rt->current_stack_frame; + add_backtrace = !rt->in_out_of_memory && + (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); + return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); +} + JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) { JSValue val; @@ -6168,13 +6658,32 @@ static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSCont } } +/* never use it directly */ +static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowTypeError(ctx, fmt, + JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); +} + +/* never use it directly */ +static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowSyntaxError(ctx, fmt, + JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); +} + +/* %s is replaced by 'atom'. The macro is used so that gcc can check + the format string. */ +#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") +#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") + static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) { if ((flags & JS_PROP_THROW) || ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - char buf[ATOM_GET_STR_BUF_SIZE]; - JS_ThrowTypeError(ctx, "%s is read-only", - JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); + JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); return -1; } else { return FALSE; @@ -6216,10 +6725,11 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *c JSValue JS_ThrowOutOfMemory(JSContext *ctx) { - if (!ctx->in_out_of_memory) { - ctx->in_out_of_memory = TRUE; + JSRuntime *rt = ctx->rt; + if (!rt->in_out_of_memory) { + rt->in_out_of_memory = TRUE; JS_ThrowInternalError(ctx, "out of memory"); - ctx->in_out_of_memory = FALSE; + rt->in_out_of_memory = FALSE; } return JS_EXCEPTION; } @@ -6242,7 +6752,7 @@ static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) { char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowReferenceError(ctx, "%s is not defined", + return JS_ThrowReferenceError(ctx, "'%s' is not defined", JS_AtomGetStr(ctx, buf, sizeof(buf), name)); } @@ -6254,14 +6764,51 @@ static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) JS_AtomGetStr(ctx, buf, sizeof(buf), name)); } +static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, + JSFunctionBytecode *b, + int idx, BOOL is_ref) +{ + JSAtom atom = JS_ATOM_NULL; + if (is_ref) { + atom = b->closure_var[idx].var_name; + } else { + /* not present if the function is stripped and contains no eval() */ + if (b->vardefs) + atom = b->vardefs[b->arg_count + idx].var_name; + } + return JS_ThrowReferenceErrorUninitialized(ctx, atom); +} + static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) { JSRuntime *rt = ctx->rt; - 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 JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); +} + +static no_inline __exception int __js_poll_interrupts(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; + if (rt->interrupt_handler) { + if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { + /* XXX: should set a specific flag to avoid catching */ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); + return -1; + } + } + return 0; +} + +static inline __exception int js_poll_interrupts(JSContext *ctx) +{ + if (unlikely(--ctx->interrupt_counter <= 0)) { + return __js_poll_interrupts(ctx); + } else { + return 0; + } } /* return -1 (exception) or TRUE/FALSE */ @@ -6341,35 +6888,25 @@ 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) +/* Only works for primitive types, otherwise return JS_NULL. */ +static JSValueConst JS_GetPrototypePrimitive(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_bignum_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; -#else + case JS_TAG_BIG_DECIMAL: + val = ctx->class_proto[JS_CLASS_BIG_DECIMAL]; + break; +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: val = ctx->class_proto[JS_CLASS_NUMBER]; break; -#endif case JS_TAG_BOOL: val = ctx->class_proto[JS_CLASS_BOOLEAN]; break; @@ -6380,17 +6917,6 @@ JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val) 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: @@ -6400,6 +6926,36 @@ JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val) return val; } +/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ +JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) +{ + JSValue val; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + val = js_proxy_getPrototypeOf(ctx, obj); + } else { + p = p->shape->proto; + if (!p) + val = JS_NULL; + else + val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj)); + } + return val; +} + +static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) +{ + JSValue obj1; + obj1 = JS_GetPrototype(ctx, obj); + JS_FreeValue(ctx, obj); + return obj1; +} + /* return TRUE, FALSE or (-1) in case of exception */ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) @@ -6420,7 +6976,6 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, /* 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)) @@ -6433,19 +6988,36 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst 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; + /* slow case if proxy in the prototype chain */ + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + JSValue obj1; + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsException(obj1)) { + ret = -1; + break; + } + if (JS_IsNull(obj1)) { + ret = FALSE; + break; + } + if (proto == JS_VALUE_GET_OBJ(obj1)) { + JS_FreeValue(ctx, obj1); + ret = TRUE; + break; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + ret = -1; + break; + } } - proto1 = JS_VALUE_GET_OBJ(proto_val); - if (!proto1) - break; } else { - break; + ret = FALSE; } + break; } p = proto1; if (proto == p) { @@ -6483,9 +7055,37 @@ int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) return JS_OrdinaryIsInstanceOf(ctx, val, obj); } -static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSProperty *pr) +/* return the value associated to the autoinit property or an exception */ +typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); + +static JSAutoInitFunc *js_autoinit_func_table[] = { + js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ + js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ + JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ +}; + +/* warning: 'prs' is reallocated after it */ +static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + JSProperty *pr, JSShapeProperty *prs) { - return (*pr->u.init.init_func)(ctx, p, prop, pr->u.init.opaque); + JSValue val; + JSContext *realm; + JSAutoInitFunc *func; + + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + + realm = js_autoinit_get_realm(pr); + func = js_autoinit_func_table[js_autoinit_get_id(pr)]; + /* 'func' shall not modify the object properties 'pr' */ + val = func(realm, p, prop, pr->u.init.opaque); + js_autoinit_free(ctx->rt, pr); + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + if (JS_IsException(val)) + return -1; + pr->u.value = val; + return 0; } JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, @@ -6501,8 +7101,9 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, if (unlikely(tag != JS_TAG_OBJECT)) { switch(tag) { case JS_TAG_NULL: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); case JS_TAG_UNDEFINED: - return JS_ThrowTypeError(ctx, "value has no property"); + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); case JS_TAG_EXCEPTION: return JS_EXCEPTION; case JS_TAG_STRING: @@ -6527,7 +7128,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, break; } /* cannot raise an exception */ - p = JS_VALUE_GET_OBJ(JS_GetPrototype(ctx, obj)); + p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); if (!p) return JS_UNDEFINED; } else { @@ -6555,7 +7156,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 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)) + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) return JS_EXCEPTION; continue; } @@ -6573,7 +7174,7 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 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; + return JS_UNDEFINED; } } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { @@ -6582,9 +7183,6 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 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; } } @@ -6592,15 +7190,25 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; if (em) { if (em->get_property) { + JSValue obj1, retval; /* XXX: should pass throw_ref_error */ - return em->get_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), - prop, this_obj); + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + retval = em->get_property(ctx, obj1, prop, this_obj); + JS_FreeValue(ctx, obj1); + return retval; } if (em->get_own_property) { JSPropertyDescriptor desc; int ret; + JSValue obj1; - ret = em->get_own_property(ctx, &desc, JS_MKPTR(JS_TAG_OBJECT, p), prop); + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->get_own_property(ctx, &desc, obj1, prop); + JS_FreeValue(ctx, obj1); if (ret < 0) return JS_EXCEPTION; if (ret) { @@ -6628,9 +7236,8 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 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)); + return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", + atom); } /* Private fields can be added even on non extensible objects or @@ -6656,9 +7263,8 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, 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)); + JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", + prop); goto fail; } pr = add_property(ctx, p, prop, JS_PROP_C_W_E); @@ -6809,6 +7415,22 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) return 0; } +static uint32_t js_string_obj_get_length(JSContext *ctx, + JSValueConst obj) +{ + JSObject *p; + JSString *p1; + uint32_t len = 0; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + len = p1->len; + } + return len; +} + static int num_keys_cmp(const void *p1, const void *p2, void *opaque) { JSContext *ctx = opaque; @@ -6851,7 +7473,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 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; + uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; BOOL is_enumerable, num_sorted; uint32_t num_key; JSAtomKindEnum kind; @@ -6864,6 +7486,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, num_keys_count = 0; str_keys_count = 0; sym_keys_count = 0; + exotic_keys_count = 0; exotic_count = 0; tab_exotic = NULL; sh = p->shape; @@ -6897,17 +7520,13 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 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; + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += p->u.array.count; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); } - 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) { @@ -6936,13 +7555,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 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++; - } + exotic_keys_count++; } } } @@ -6952,7 +7565,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, /* fill them */ - atom_count = num_keys_count + str_keys_count + sym_keys_count; + atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; /* avoid allocating 0 bytes */ tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); if (!tab_atom) { @@ -6988,41 +7601,43 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, } if (p->is_exotic) { + int len; 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 (flags & JS_GPN_STRING_MASK) { + len = p->u.array.count; + goto add_array_keys; } - } - if (exotic_count > 0) { + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + add_array_keys: + for(i = 0; i < len; i++) { + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); + if (tab_atom[num_index].atom == JS_ATOM_NULL) { + js_free_prop_enum(ctx, tab_atom, num_index); + return -1; + } + tab_atom[num_index].is_enumerable = TRUE; + num_index++; + } + } + } else { + /* Note: exotic keys are not reordered and comes after the object own properties. */ for(i = 0; i < exotic_count; i++) { atom = tab_exotic[i].atom; is_enumerable = tab_exotic[i].is_enumerable; kind = JS_AtomGetKind(ctx, atom); if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && ((flags >> kind) & 1) != 0) { - 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; + tab_atom[sym_index].atom = atom; + tab_atom[sym_index].is_enumerable = is_enumerable; + sym_index++; } else { JS_FreeAtom(ctx, atom); } } + js_free(ctx, tab_exotic); } - js_free(ctx, tab_exotic); } assert(num_index == num_keys_count); @@ -7082,7 +7697,7 @@ retry: 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)) + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) return -1; goto retry; } @@ -7110,10 +7725,8 @@ retry: 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->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | + JS_PROP_CONFIGURABLE; desc->getter = JS_UNDEFINED; desc->setter = JS_UNDEFINED; desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); @@ -7121,19 +7734,6 @@ retry: 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) { @@ -7188,6 +7788,7 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) { JSObject *p; int ret; + JSValue obj1; if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) return FALSE; @@ -7195,10 +7796,18 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) 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); + if (em && em->has_property) { + /* has_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->has_property(ctx, obj1, prop); + JS_FreeValue(ctx, obj1); + return ret; + } } + /* JS_GetOwnPropertyInternal can free the prototype */ + JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); if (ret != 0) return ret; if (p->class_id >= JS_CLASS_UINT8C_ARRAY && @@ -7207,8 +7816,6 @@ int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) if (ret != 0) { if (ret < 0) return -1; - /* the detached array test was done in - JS_GetOwnPropertyInternal() */ return FALSE; } } @@ -7353,7 +7960,7 @@ static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, return present; } -static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) +JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) { JSAtom prop; JSValue val; @@ -7472,7 +8079,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) redo: sh = p->shape; h1 = atom & sh->prop_hash_mask; - h = sh->prop_hash_end[-h1 - 1]; + h = prop_hash_end(sh)[-h1 - 1]; prop = get_shape_prop(sh); lpr = NULL; lpr_idx = 0; /* prevent warning */ @@ -7493,8 +8100,9 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) lpr = get_shape_prop(sh) + lpr_idx; lpr->hash_next = pr->hash_next; } else { - sh->prop_hash_end[-h1 - 1] = pr->hash_next; + prop_hash_end(sh)[-h1 - 1] = pr->hash_next; } + sh->deleted_prop_count++; /* free the entry */ pr1 = &p->prop[h - 1]; free_property(ctx->rt, pr1, pr->flags); @@ -7503,6 +8111,12 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) pr->flags = 0; pr->atom = JS_ATOM_NULL; pr1->u.value = JS_UNDEFINED; + + /* compact the properties if too many deleted properties */ + if (sh->deleted_prop_count >= 8 && + sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { + compact_properties(ctx, p); + } return TRUE; } lpr = pr; @@ -7526,7 +8140,7 @@ static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) return -1; goto redo; } else { - return FALSE; /* not configurable */ + return FALSE; } } } else { @@ -7553,6 +8167,7 @@ static int call_setter(JSContext *ctx, JSObject *setter, if (JS_IsException(ret)) return -1; JS_FreeValue(ctx, ret); + return TRUE; } else { JS_FreeValue(ctx, val); if ((flags & JS_PROP_THROW) || @@ -7560,21 +8175,25 @@ static int call_setter(JSContext *ctx, JSObject *setter, JS_ThrowTypeError(ctx, "no setter for property"); return -1; } - /* XXX: should return FALSE? */ + 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) +static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, + int flags) { uint32_t len, idx, cur_len; int i, ret; - ret = JS_ToArrayLengthFree(ctx, &len, val); + /* Note: this call can reallocate the properties of 'p' */ + ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); if (ret) return -1; + /* JS_ToArrayLengthFree() must be done before the read-only test */ + if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + if (likely(p->fast_array)) { uint32_t old_len = p->u.array.count; if (len < old_len) { @@ -7583,15 +8202,11 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSProperty *prop, } 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 + p->prop[0].u.value = JS_NewUint32(ctx, len); } else { /* Note: length is always a uint32 because the object is an array */ - JS_ToUint32(ctx, &cur_len, prop->u.value); + JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); if (len < cur_len) { uint32_t d; JSShape *sh; @@ -7716,8 +8331,12 @@ static int JS_SetPropertyGeneric(JSContext *ctx, 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, + JSValue obj1; + /* set_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->set_property(ctx, obj1, prop, val, this_obj, flags); + JS_FreeValue(ctx, obj1); JS_FreeValue(ctx, val); return ret; } @@ -7810,14 +8429,17 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, if (unlikely(tag != JS_TAG_OBJECT)) { switch(tag) { case JS_TAG_NULL: + JS_FreeValue(ctx, val); + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); + return -1; case JS_TAG_UNDEFINED: JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "value has no property"); + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); return -1; default: /* even on a primitive type we can have setters on the prototype */ p = NULL; - p1 = JS_VALUE_GET_OBJ(JS_GetPrototype(ctx, this_obj)); + p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj)); goto prototype_lookup; } } @@ -7830,11 +8452,10 @@ retry: /* 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)) { + } else if (prs->flags & JS_PROP_LENGTH) { assert(p->class_id == JS_CLASS_ARRAY); assert(prop == JS_ATOM_length); - return set_array_length(ctx, p, pr, val, flags); + return set_array_length(ctx, p, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { @@ -7847,7 +8468,7 @@ retry: 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)) { + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) { JS_FreeValue(ctx, val); return -1; } @@ -7885,25 +8506,28 @@ retry: 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) { + JSValue obj1; if (em->set_property) { - ret = em->set_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p1), prop, + /* set_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->set_property(ctx, obj1, prop, val, this_obj, flags); + JS_FreeValue(ctx, obj1); JS_FreeValue(ctx, val); return ret; } if (em->get_own_property) { + /* get_own_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); ret = em->get_own_property(ctx, &desc, - JS_MKPTR(JS_TAG_OBJECT, p1), prop); + obj1, prop); + JS_FreeValue(ctx, obj1); if (ret < 0) { JS_FreeValue(ctx, val); return ret; @@ -7950,7 +8574,7 @@ retry: 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)) + if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) return -1; goto retry2; } else if (!(prs->flags & JS_PROP_WRITABLE)) { @@ -7978,7 +8602,8 @@ retry: } if (p->is_exotic) { - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + 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 */ @@ -8117,12 +8742,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, 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"); - } + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); } p->u.array.u.double_ptr[idx] = d; break; @@ -8407,13 +9027,32 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, redo_prop_update: prs = find_own_property(&pr, p, prop); if (prs) { + /* the range of the Array length property is always tested before */ + if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { + uint32_t array_length; + if (JS_ToArrayLengthFree(ctx, &array_length, + JS_DupValue(ctx, val), FALSE)) { + return -1; + } + /* this code relies on the fact that Uint32 are never allocated */ + val = (JSValueConst)JS_NewUint32(ctx, array_length); + /* prs may have been modified */ + prs = find_own_property(&pr, p, prop); + assert(prs != NULL); + } /* property already exists */ if (!check_define_prop_flags(prs->flags, flags)) { not_configurable: return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); } - retry: + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto redo_prop_update; + } + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { @@ -8436,13 +9075,6 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, /* 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); } @@ -8490,38 +9122,17 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 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; + (flags & JS_PROP_HAS_VALUE)) { + if (!js_same_value(ctx, val, pr->u.value)) { + goto not_configurable; + } else { + return TRUE; + } } } - if (prs->flags & JS_PROP_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 ((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 @@ -8545,9 +9156,27 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 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 (prs->flags & JS_PROP_LENGTH) { + if (flags & JS_PROP_HAS_VALUE) { + /* Note: no JS code is executable because + 'val' is guaranted to be a Uint32 */ + res = set_array_length(ctx, p, JS_DupValue(ctx, val), + flags); + } else { + res = TRUE; + } + /* still need to reset the writable flag if + needed. The JS_PROP_LENGTH is kept because the + Uint32 test is still done if the length + property is read-only. */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + JS_PROP_HAS_WRITABLE) { + prs = get_shape_prop(p->shape); + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~JS_PROP_WRITABLE)) + return -1; + } + return res; } else { if (flags & JS_PROP_HAS_VALUE) { JS_FreeValue(ctx, pr->u.value); @@ -8632,9 +9261,9 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 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); + prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || - prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE)) { + prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); } if (flags & JS_PROP_HAS_VALUE) { @@ -8649,8 +9278,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, } static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, int (*init_func)(JSContext *ctx, JSObject *obj, - JSAtom prop, void *opaque), + JSAtom prop, JSAutoInitIDEnum id, void *opaque, int flags) { JSObject *p; @@ -8671,8 +9299,10 @@ static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, 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.realm_and_id = (uintptr_t)JS_DupContext(ctx); + assert((pr->u.init.realm_and_id & 3) == 0); + assert(id <= 3); + pr->u.init.realm_and_id |= id; pr->u.init.opaque = opaque; return TRUE; } @@ -8809,9 +9439,7 @@ static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj, 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)); + return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); } /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ @@ -8820,7 +9448,6 @@ 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); @@ -8838,8 +9465,8 @@ static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) ((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)); + JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", + prop); return -1; } } @@ -9021,9 +9648,8 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, set_value(ctx, &pr->u.value, val); return 0; } - flags = JS_PROP_THROW_STRICT; - if (flag != 2 && is_strict_mode(ctx)) + if (is_strict_mode(ctx)) flags |= JS_PROP_NO_ADD; return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); } @@ -9078,8 +9704,6 @@ BOOL JS_IsFunction(JSContext *ctx, JSValueConst val) p = JS_VALUE_GET_OBJ(val); switch(p->class_id) { case JS_CLASS_BYTECODE_FUNCTION: - case JS_CLASS_C_FUNCTION: - case JS_CLASS_BOUND_FUNCTION: return TRUE; case JS_CLASS_PROXY: return p->u.proxy_data->is_func; @@ -9109,8 +9733,7 @@ BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val) return p->is_constructor; } -static BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, - BOOL val) +BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val) { JSObject *p; if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) @@ -9126,22 +9749,7 @@ BOOL JS_IsError(JSContext *ctx, JSValueConst val) if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) return FALSE; p = JS_VALUE_GET_OBJ(val); - if (p->class_id == JS_CLASS_ERROR) - return TRUE; - if (ctx->is_error_property_enabled) { - /* check for a special property for test262 test suites */ - JSValue isError; - isError = JS_GetPropertyStr(ctx, val, "isError"); - return JS_ToBoolFree(ctx, isError); - } else { - return FALSE; - } -} - -/* only used for test262 test suites */ -void JS_EnableIsErrorProperty(JSContext *ctx, BOOL enable) -{ - ctx->is_error_property_enabled = enable; + return (p->class_id == JS_CLASS_ERROR); } /* used to avoid catching interrupt exceptions */ @@ -9166,7 +9774,7 @@ void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag) void JS_ResetUncatchableError(JSContext *ctx) { - JS_SetUncatchableError(ctx, ctx->current_exception, FALSE); + JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE); } void JS_SetOpaque(JSValue obj, void *opaque) @@ -9202,9 +9810,6 @@ void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) #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) @@ -9239,11 +9844,6 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) 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); @@ -9292,6 +9892,24 @@ static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); } +void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(obj); + p->is_HTMLDDA = TRUE; +} + +static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + return p->is_HTMLDDA; +} + static int JS_ToBoolFree(JSContext *ctx, JSValue val) { uint32_t tag = JS_VALUE_GET_TAG(val); @@ -9320,7 +9938,24 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) 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 + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + BOOL ret; + ret = !p->is_HTMLDDA; + JS_FreeValue(ctx, val); + return ret; + } + break; default: if (JS_TAG_IS_FLOAT64(tag)) { double d = JS_VALUE_GET_FLOAT64(val); @@ -9359,55 +9994,6 @@ static int skip_spaces(const char *pc) return p - p_start; } -#ifdef CONFIG_BIGNUM - -/* force big int type if integer result */ -#define BF_ATOF_BIG_INT (1 << 30) -/* return JS_EXCEPTION if invalid syntax. Otherwise return NaN */ -#define BF_ATOF_THROW (1 << 29) -#define BF_ATOF_FLOAT64 (1 << 28) - -static JSValue js_atof(JSContext *ctx, const char *p, const char **pp, - int radix, int flags) -{ - bf_t a_s, *a = &a_s; - int res; - slimb_t prec; - JSValue val; - - bf_init(ctx->bf_ctx, a); - if (flags & BF_ATOF_FLOAT64) { - prec = 53; - flags |= bf_set_exp_bits(11) | BF_RNDN | BF_FLAG_SUBNORMAL; - } else { - /* use the current precision */ - prec = ctx->fp_env.prec; - flags |= ctx->fp_env.flags; - } - p += skip_spaces(p); - res = bf_atof(a, p, &p, radix, prec, flags); - if ((flags & BF_ATOF_THROW) && bf_is_nan(a)) { - bf_delete(a); - return JS_EXCEPTION; - } - if (res & BF_ATOF_ST_INTEGER) { - val = JS_NewBigInt2(ctx, a, (flags & BF_ATOF_BIG_INT) != 0); - } else if (flags & BF_ATOF_FLOAT64) { - double d; - bf_get_float64(a, &d, BF_RNDN); - bf_delete(a); - /* return int or float64 */ - val = JS_NewFloat64(ctx, d); - } else { - val = JS_NewBigFloat(ctx, a); - } - if (pp) - *pp = p; - return val; -} - -#else - static inline int to_digit(int c) { if (c >= '0' && c <= '9') @@ -9420,44 +10006,180 @@ static inline int to_digit(int c) 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) -/* return JS_EXCEPTION if invalid syntax. Otherwise return NaN */ -#define ATOD_THROW (1 << 1) /* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ #define ATOD_ACCEPT_BIN_OCT (1 << 2) -/* if set return NaN if empty number string */ -#define ATOD_NAN_IF_EMPTY (1 << 3) /* 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) -/* radix = 0 accepts prefixes. radix = 16 also - accepts 0x prefix. radix must be 0 or between 2 and 36 */ -static JSValue js_atod(JSContext *ctx, const char *str, const char **pp, - int radix, int flags) +#ifdef CONFIG_BIGNUM +static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent) { - const char *p; - const char *p_start; - int is_neg, c, sep; - double d; + bf_t a_s, *a = &a_s; + int ret; + JSValue val; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); + return val; +} +static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent) +{ + bf_t *a; + int ret; + JSValue val; + + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigFloat(val); + if (flags & ATOD_ACCEPT_SUFFIX) { + /* return the exponent to get infinite precision */ + ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF, + BF_RNDZ | BF_ATOF_EXPONENT); + } else { + ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec, + ctx->fp_env.flags); + } + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return val; +} + +static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, + int radix, int flags, slimb_t *pexponent) +{ + bfdec_t *a; + int ret; + JSValue val; + + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigDecimal(val); + ret = bfdec_atof(a, buf, NULL, BF_PREC_INF, + BF_RNDZ | BF_ATOF_NO_NAN_INF); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return val; +} + +#endif + +/* return an exception in case of memory error. Return JS_NAN if + invalid syntax */ +#ifdef CONFIG_BIGNUM +static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp, + int radix, int flags, slimb_t *pexponent) +#else +static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, + int radix, int flags) +#endif +{ + const char *p, *p_start; + int sep, is_neg; + BOOL is_float, has_legacy_octal; + int atod_type = flags & ATOD_TYPE_MASK; + char buf1[64], *buf; + int i, j, len; + BOOL buf_allocated = FALSE; + JSValue val; + /* optional separator between digits */ sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; - - p = str + skip_spaces(str); + has_legacy_octal = FALSE; + + p = str; p_start = p; is_neg = 0; if (p[0] == '+') { p++; p_start++; - if (!(flags & ATOD_INT_ONLY)) + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) goto no_radix_prefix; } else if (p[0] == '-') { - is_neg = 1; p++; p_start++; - if (!(flags & ATOD_INT_ONLY)) + is_neg = 1; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) goto no_radix_prefix; } if (p[0] == '0') { @@ -9476,6 +10198,7 @@ static JSValue js_atod(JSContext *ctx, const char *str, const char **pp, } 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; @@ -9492,58 +10215,54 @@ static JSValue js_atod(JSContext *ctx, const char *str, const char **pp, no_prefix: ; } else { no_radix_prefix: - if (!(flags & ATOD_INT_ONLY) && strstart(p, "Infinity", &p)) { - d = 1.0 / 0.0; + 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; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + goto done; + a = JS_GetBigFloat(val); + bf_set_inf(a, is_neg); + } else +#endif + { + double d = 1.0 / 0.0; + if (is_neg) + d = -d; + val = JS_NewFloat64(ctx, d); + } goto done; } } if (radix == 0) radix = 10; - if ((flags & ATOD_INT_ONLY) || radix != 10) { - uint64_t n_max, n; - int int_exp; - - /* skip leading zeros */ - while (*p == '0') + 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++; - 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') { - if (*p == sep && to_digit(p[1]) < radix) - p++; - 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); - } - } else { - p_start = p; - while (is_digit((uint8_t)*p) - || (*p == sep && (p != p_start + 1 || p[-1] != '0') && - is_digit((uint8_t)p[1]))) { - p++; - } - if (*p == '.' && (p > p_start || is_digit((uint8_t)p[1]))) { - p++; - while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) + if (*p == sep) + goto fail; + while (to_digit((uint8_t)*p) < radix || + (*p == sep && to_digit((uint8_t)p[1]) < radix)) p++; } - if (p > p_start && (*p == 'e' || *p == 'E')) { + 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 == '-') { @@ -9555,54 +10274,128 @@ static JSValue js_atod(JSContext *ctx, const char *str, const char **pp, p++; } } - if (*p == '\0' && sep != '_') { - d = strtod(p_start, NULL); - } else { - char buf1[64], *buf; - int i, j, len; - BOOL buf_allocated; + } + if (p == p_start) + goto fail; - buf = buf1; - buf_allocated = FALSE; - len = p - p_start; - if (len >= sizeof(buf1)) { - buf = js_malloc(ctx, len + 1); - if (!buf) - return JS_EXCEPTION; - buf_allocated = TRUE; + 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 == 'm') { + p++; + atod_type = ATOD_TYPE_BIG_DECIMAL; + } else { + if (flags & ATOD_MODE_BIGINT) { + if (!is_float) + atod_type = ATOD_TYPE_BIG_INT; + if (has_legacy_octal) + goto fail; + } else { + if (is_float && radix != 10) + goto fail; } - for (i = j = 0; i < len; i++) { - if (p_start[i] != '_') - buf[j++] = p_start[i]; + } + } 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; } - buf[j] = '\0'; - d = strtod(buf, NULL); - if (buf_allocated) - js_free(ctx, buf); } } -done: - if (is_neg) - d = -d; - if (p == p_start && (flags & ATOD_NAN_IF_EMPTY)) { - d = JS_FLOAT64_NAN; + + 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 JS_NewFloat64(ctx, d); + return val; fail: - if (pp) - *pp = p; - if (flags & ATOD_THROW) - return JS_EXCEPTION; - else - return JS_NAN; + 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; @@ -9611,19 +10404,32 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, { 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_bignum_mode(ctx)) { + if (flag != TON_FLAG_NUMERIC) { JS_FreeValue(ctx, val); return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); } - /* fall thru */ + ret = val; + break; case JS_TAG_BIG_FLOAT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number"); + } + ret = val; + break; #endif case JS_TAG_FLOAT64: case JS_TAG_INT: @@ -9638,12 +10444,7 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, 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); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) return JS_EXCEPTION; goto redo; @@ -9651,29 +10452,26 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, { const char *str; const char *p; - - str = JS_ToCString(ctx, val); + size_t len; + + str = JS_ToCStringLen(ctx, &len, val); JS_FreeValue(ctx, val); if (!str) return JS_EXCEPTION; -#ifdef CONFIG_BIGNUM - { - int flags; - flags = BF_ATOF_BIN_OCT | BF_ATOF_NO_PREFIX_AFTER_SIGN | - BF_ATOF_JS_QUIRKS | BF_ATOF_FLOAT64; - if (is_bignum_mode(ctx)) - flags |= BF_ATOF_INT_PREC_INF; - else - flags |= BF_ATOF_ONLY_DEC_FLOAT; - ret = js_atof(ctx, str, &p, 0, flags); - } -#else - ret = js_atod(ctx, str, &p, 0, ATOD_ACCEPT_BIN_OCT); -#endif + p = str; p += skip_spaces(p); - if (*p != '\0') { - JS_FreeValue(ctx, ret); - ret = JS_NAN; + if ((p - str) == len) { + ret = JS_NewInt32(ctx, 0); + } else { + int flags = ATOD_ACCEPT_BIN_OCT; + ret = js_atof(ctx, p, &p, 0, flags); + if (!JS_IsException(ret)) { + p += skip_spaces(p); + if ((p - str) != len) { + JS_FreeValue(ctx, ret); + ret = JS_NAN; + } + } } JS_FreeCString(ctx, str); } @@ -9694,7 +10492,6 @@ 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); @@ -9704,7 +10501,6 @@ 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) @@ -9771,6 +10567,7 @@ static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); } +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) { uint32_t tag; @@ -9792,24 +10589,18 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) 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_bignum_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_float, is_nan; + BOOL is_nan; - a = JS_ToBigFloat(ctx, &is_float, &a_s, val); + a = JS_ToBigFloat(ctx, &a_s, val); if (!bf_is_finite(a)) { is_nan = bf_is_nan(a); if (is_nan) @@ -9817,10 +10608,13 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) else ret = JS_DupValue(ctx, val); } else { - bf_init(ctx->bf_ctx, r); - bf_set(r, a); - bf_rint(r, BF_PREC_INF, BF_RNDZ); - ret = JS_NewBigInt(ctx, r); + ret = JS_NewBigInt(ctx); + if (!JS_IsException(ret)) { + r = JS_GetBigInt(ret); + bf_set(r, a); + bf_rint(r, BF_RNDZ); + ret = JS_CompactBigInt(ctx, ret); + } } if (a == &a_s) bf_delete(a); @@ -9829,9 +10623,6 @@ static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) break; #endif default: -#ifdef CONFIG_BIGNUM - to_number: -#endif val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) return val; @@ -9875,17 +10666,12 @@ static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) 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_bignum_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -9954,17 +10740,12 @@ static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) 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_bignum_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10039,17 +10820,12 @@ static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) 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_bignum_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10068,6 +10844,14 @@ int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val)); } +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + if (JS_IsBigInt(ctx, val)) + return JS_ToBigInt64(ctx, pres, val); + else + return JS_ToInt64(ctx, pres, val); +} + /* return (<0, 0) in case of exception */ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) { @@ -10111,17 +10895,12 @@ static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) 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_bignum_mode(ctx)) - goto to_bf; - /* fall thru */ #endif default: val = JS_ToNumberFree(ctx, val); @@ -10179,22 +10958,13 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) } break; #ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - if (!is_bignum_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_PREC_INF, BF_RNDN); + bf_rint(r, BF_RNDN); bf_get_int32(&res, r, 0); bf_delete(r); JS_FreeValue(ctx, val); @@ -10202,9 +10972,6 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) goto int_clamp; #endif default: -#ifdef CONFIG_BIGNUM - to_number: -#endif val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) { *pres = 0; @@ -10217,11 +10984,10 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) } static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val) + JSValue val, BOOL is_array_ctor) { uint32_t tag, len; - redo: tag = JS_VALUE_GET_TAG(val); switch(tag) { case JS_TAG_INT: @@ -10258,16 +11024,36 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, double d; d = JS_VALUE_GET_FLOAT64(val); len = (uint32_t)d; - if (len != d) { - fail: - JS_ThrowRangeError(ctx, "invalid array length"); - return -1; - } + if (len != d) + goto fail; } else { - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - goto redo; + uint32_t len1; + + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } } break; } @@ -10277,6 +11063,12 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) +static BOOL is_safe_integer(double d) +{ + return isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER; +} + int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) { int64_t v; @@ -10302,7 +11094,6 @@ static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, } /* Note: can return an exception */ -/* XXX: bignum case */ static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) { double d; @@ -10344,6 +11135,12 @@ static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst 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; @@ -10352,19 +11149,47 @@ static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) #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; - BOOL is_float; char *str; int saved_sign; val = JS_ToNumeric(ctx, val1); if (JS_IsException(val)) return val; - a = JS_ToBigFloat(ctx, &is_float, &a_s, val); + a = JS_ToBigFloat(ctx, &a_s, val); saved_sign = a->sign; if (a->expn == BF_EXP_ZERO) a->sign = 0; @@ -10372,45 +11197,74 @@ static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, 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. For integers, the full precision is - always returned. */ - if (is_float || (flags & BF_FTOA_FORCE_EXP)) { - 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); - bf_ftoa(&str, r, radix, prec, flags1 | flags); - bf_delete(r); + 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 { - bf_ftoa(&str, a, radix, BF_PREC_INF, flags); + 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 { - bf_ftoa(&str, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | BF_FTOA_JS_QUIRKS); + str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags); } } else { - bf_ftoa(&str, a, radix, prec, flags); + 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_realloc(ctx->bf_ctx, str, 0); + bf_free(ctx->bf_ctx, str); return ret; } -#else /* !CONFIG_BIGNUM */ +static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val) +{ + return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); +} + +static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, + limb_t prec, int flags) +{ + JSValue ret; + bfdec_t *a; + char *str; + int saved_sign; + + a = JS_ToBigDecimal(ctx, val); + saved_sign = a->sign; + if (a->expn == BF_EXP_ZERO) + a->sign = 0; + str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS); + a->sign = saved_sign; + if (!str) + return JS_ThrowOutOfMemory(ctx); + ret = JS_NewString(ctx, str); + bf_free(ctx->bf_ctx, str); + return ret; +} + +static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) +{ + return js_bigdecimal_to_string1(ctx, val, 0, + BF_RNDZ | BF_FTOA_FORMAT_FREE); +} + +#endif /* CONFIG_BIGNUM */ /* 2 <= base <= 36 */ static char *i64toa(char *buf_end, int64_t n, unsigned int base) @@ -10578,7 +11432,7 @@ static void js_fcvt(char *buf, int buf_size, double d, int n_digits) /* force exponential notation either in fixed or variable format */ #define JS_DTOA_FORCE_EXP (1 << 2) -/* XXX: slow and maybe not fully correct +/* 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) @@ -10678,8 +11532,6 @@ static JSValue js_dtoa(JSContext *ctx, return JS_NewString(ctx, buf); } -#endif /* !CONFIG_BIGNUM */ - JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) { uint32_t tag; @@ -10728,15 +11580,16 @@ JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToProperty return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); #endif } -#ifdef CONFIG_BIGNUM - case JS_TAG_FLOAT64: - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_INT: - return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); -#else 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]"; @@ -10886,7 +11739,19 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) case JS_CLASS_ARGUMENTS: JS_DumpValueShort(rt, p->u.array.u.values[i]); break; - case JS_CLASS_UINT8C_ARRAY ... JS_CLASS_FLOAT64_ARRAY: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: +#endif + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: { int size = 1 << typed_array_size_log2(p->class_id); const uint8_t *b = p->u.array.u.uint8_ptr + i * size; @@ -10914,7 +11779,9 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) } 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, + printf("[autoinit %p %d %p]", + (void *)js_autoinit_get_realm(pr), + js_autoinit_get_id(pr), (void *)pr->u.init.opaque); } else { JS_DumpValueShort(rt, pr->u.value); @@ -10944,6 +11811,38 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) printf("\n"); } +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) +{ + if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + JS_DumpObject(rt, (JSObject *)p); + } else { + printf("%14p %4d ", + (void *)p, + p->ref_count); + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + printf("[function bytecode]"); + break; + case JS_GC_OBJ_TYPE_SHAPE: + printf("[shape]"); + break; + case JS_GC_OBJ_TYPE_VAR_REF: + printf("[var_ref]"); + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + printf("[async_function]"); + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + printf("[js_context]"); + break; + default: + printf("[unknown %d]", p->gc_obj_type); + break; + } + printf("\n"); + } +} + static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, JSValueConst val) { @@ -10982,8 +11881,8 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, { JSBigFloat *p = JS_VALUE_GET_PTR(val); char *str; - bf_ftoa(&str, &p->num, 10, 0, - BF_RNDZ | BF_FTOA_FORMAT_FRAC); + str = bf_ftoa(NULL, &p->num, 10, 0, + BF_RNDZ | BF_FTOA_FORMAT_FRAC); printf("%sn", str); bf_realloc(&rt->bf_ctx, str, 0); } @@ -10992,10 +11891,20 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, { JSBigFloat *p = JS_VALUE_GET_PTR(val); char *str; - bf_ftoa(&str, &p->num, 16, BF_PREC_INF, - BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX); + str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF, + BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX); printf("%sl", str); - bf_realloc(&rt->bf_ctx, str, 0); + 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 @@ -11033,9 +11942,6 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, case JS_TAG_MODULE: printf("[module]"); break; - case JS_TAG_VAR_REF: - printf("[var_ref]"); - break; default: printf("[unknown tag %d]", tag); break; @@ -11089,40 +11995,57 @@ static double js_pow(double a, double b) #ifdef CONFIG_BIGNUM +JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) +{ + JSValue val; + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_si(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return val; +} + JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) { - BOOL is_bignum = is_bignum_mode(ctx); - if (is_bignum && v == (int32_t)v) { - return JS_NewInt32(ctx, v); + if (is_math_mode(ctx) && + v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { + return JS_NewInt64(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); + return JS_NewBigInt64_1(ctx, v); } } JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) { - BOOL is_bignum = is_bignum_mode(ctx); - if (is_bignum && v == (int32_t)v) { - return JS_NewInt32(ctx, v); + JSValue val; + if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { + val = JS_NewInt64(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); + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_ui(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } } + return val; } /* 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_ToBigFloat(JSContext *ctx, BOOL *pis_float, bf_t *buf, - JSValueConst val) + '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; - BOOL is_float; JSBigFloat *p; tag = JS_VALUE_GET_NORM_TAG(val); @@ -11132,21 +12055,20 @@ static bf_t *JS_ToBigFloat(JSContext *ctx, BOOL *pis_float, bf_t *buf, case JS_TAG_NULL: r = buf; bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - is_float = FALSE; + 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); - bf_set_float64(r, JS_VALUE_GET_FLOAT64(val)); - is_float = TRUE; + if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { + fail: + bf_delete(r); + return NULL; + } break; case JS_TAG_BIG_INT: - is_float = FALSE; - goto get_ptr; case JS_TAG_BIG_FLOAT: - is_float = TRUE; - get_ptr: p = JS_VALUE_GET_PTR(val); r = &p->num; break; @@ -11155,46 +12077,69 @@ static bf_t *JS_ToBigFloat(JSContext *ctx, BOOL *pis_float, bf_t *buf, r = buf; bf_init(ctx->bf_ctx, r); bf_set_nan(r); - is_float = TRUE; break; } - *pis_float = is_float; + 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; - const char *p; - int flags, err; - - str = JS_ToCString(ctx, 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; - flags = BF_ATOF_BIN_OCT | BF_ATOF_NO_PREFIX_AFTER_SIGN | - BF_ATOF_JS_QUIRKS | BF_ATOF_INT_ONLY | BF_ATOF_INT_PREC_INF; - if (!is_bignum_mode(ctx)) - flags |= BF_ATOF_BIG_INT; - val = js_atof(ctx, str, &p, 0, flags); + p = str; p += skip_spaces(p); - err = (*p != '\0'); - JS_FreeCString(ctx, str); - if (err) { - JS_FreeValue(ctx, val); - val = JS_NAN; + if ((p - str) == len) { + val = JS_NewBigInt64(ctx, 0); + } else { + flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; + if (is_math_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_GET_TAG(val) != JS_TAG_BIG_INT) { - JS_FreeValue(ctx, val); + if (JS_VALUE_IS_NAN(val)) return JS_ThrowSyntaxError(ctx, "invalid bigint literal"); - } return val; } @@ -11212,7 +12157,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) case JS_TAG_INT: case JS_TAG_NULL: case JS_TAG_UNDEFINED: - if (!is_bignum_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; /* fall tru */ case JS_TAG_BOOL: @@ -11223,7 +12168,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) case JS_TAG_FLOAT64: { double d = JS_VALUE_GET_FLOAT64(val); - if (!is_bignum_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; if (!isfinite(d)) goto fail; @@ -11238,7 +12183,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) r = &p->num; break; case JS_TAG_BIG_FLOAT: - if (!is_bignum_mode(ctx)) + if (!is_math_mode(ctx)) goto fail; p = JS_VALUE_GET_PTR(val); if (!bf_is_finite(&p->num)) @@ -11246,7 +12191,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) r = buf; bf_init(ctx->bf_ctx, r); bf_set(r, &p->num); - bf_rint(r, BF_PREC_INF, BF_RNDZ); + bf_rint(r, BF_RNDZ); JS_FreeValue(ctx, val); break; case JS_TAG_STRING: @@ -11255,8 +12200,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) return NULL; goto redo; case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, - is_bignum_mode(ctx) ? HINT_INTEGER : HINT_NUMBER); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) return NULL; goto redo; @@ -11279,11 +12223,26 @@ 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; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) + bf_t a_s, *a, *r; + int ret; + JSValue res; + + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) return JS_EXCEPTION; - return JS_NewBigInt2(ctx, a, TRUE); + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) { + JS_FreeValue(ctx, res); + return JS_EXCEPTION; + } + r = JS_GetBigInt(res); + ret = bf_set(r, a); + JS_FreeBigInt(ctx, a, &a_s); + if (ret) { + JS_FreeValue(ctx, res); + return JS_ThrowOutOfMemory(ctx); + } + return JS_CompactBigInt(ctx, res); } } @@ -11322,7 +12281,7 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) static JSBigFloat *js_new_bf(JSContext *ctx) { JSBigFloat *p; - p = js_mallocz(ctx, sizeof(*p)); + p = js_malloc(ctx, sizeof(*p)); if (!p) return NULL; p->header.ref_count = 1; @@ -11330,208 +12289,388 @@ static JSBigFloat *js_new_bf(JSContext *ctx) return p; } -/* WARNING: 'a' is freed */ -static JSValue JS_NewBigFloat(JSContext *ctx, bf_t *a) +static JSValue JS_NewBigFloat(JSContext *ctx) { - JSValue ret; JSBigFloat *p; - - p = js_new_bf(ctx); - p->num = *a; - ret = JS_MKPTR(JS_TAG_BIG_FLOAT, p); - return ret; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_FLOAT, p); } -/* WARNING: 'a' is freed */ -static JSValue JS_NewBigInt2(JSContext *ctx, bf_t *a, BOOL force_bigint) +static JSValue JS_NewBigDecimal(JSContext *ctx) { - JSValue ret; - JSBigFloat *p; - int32_t v; + JSBigDecimal *p; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bfdec_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_DECIMAL, p); +} - 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); - 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); +static JSValue JS_NewBigInt(JSContext *ctx) +{ + JSBigFloat *p; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_INT, p); +} + +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, + BOOL convert_to_safe_integer) +{ + int64_t v; + bf_t *a; + + if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) + return val; /* fail safe */ + a = JS_GetBigInt(val); + if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 && + v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { + JS_FreeValue(ctx, val); + return JS_NewInt64(ctx, v); + } else if (a->expn == BF_EXP_ZERO && a->sign) { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + assert(p->header.ref_count == 1); + a->sign = 0; } - return ret; + return val; } -static JSValue JS_NewBigInt(JSContext *ctx, bf_t *a) +/* Convert the big int to a safe integer if in math mode. normalize + the zero representation. Could also be used to convert the bigint + to a short bigint value. The reference count of the value must be + 1. Cannot fail */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) { - return JS_NewBigInt2(ctx, a, FALSE); + return JS_CompactBigInt1(ctx, val, is_math_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]; +/* must be kept in sync with JSOverloadableOperatorEnum */ +/* XXX: use atoms ? */ +static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = { + "+", + "-", + "*", + "/", + "%", + "**", + "|", + "&", + "^", + "<<", + ">>", + ">>>", + "==", + "<", + "pos", + "neg", + "++", + "--", + "~", +}; - bool_result = FALSE; - swap_op = FALSE; - c1 = JS_UNDEFINED; - c2 = JS_UNDEFINED; +static int get_ovop_from_opcode(OPCodeEnum op) +{ switch(op) { case OP_add: - op_name = JS_ATOM_Symbol_operatorAdd; - break; + return JS_OVOP_ADD; case OP_sub: - op_name = JS_ATOM_Symbol_operatorSub; - break; + return JS_OVOP_SUB; case OP_mul: - op_name = JS_ATOM_Symbol_operatorMul; - break; + return JS_OVOP_MUL; case OP_div: - case OP_math_div: - op_name = JS_ATOM_Symbol_operatorDiv; - break; + return JS_OVOP_DIV; 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; + return JS_OVOP_MOD; + case OP_pow: + return JS_OVOP_POW; case OP_or: - op_name = JS_ATOM_Symbol_operatorOr; - break; + return JS_OVOP_OR; + case OP_and: + return JS_OVOP_AND; 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; + return JS_OVOP_XOR; + case OP_shl: + return JS_OVOP_SHL; + case OP_sar: + return JS_OVOP_SAR; + case OP_shr: + return JS_OVOP_SHR; case OP_eq: case OP_neq: - op_name = JS_ATOM_Symbol_operatorCmpEQ; - bool_result = TRUE; - break; + return JS_OVOP_EQ; + case OP_lt: + case OP_lte: + case OP_gt: + case OP_gte: + return JS_OVOP_LESS; + case OP_plus: + return JS_OVOP_POS; + case OP_neg: + return JS_OVOP_NEG; + case OP_inc: + return JS_OVOP_INC; + case OP_dec: + return JS_OVOP_DEC; default: - goto fail; + abort(); } - 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); +/* return NULL if not present */ +static JSObject *find_binary_op(JSBinaryOperatorDef *def, + uint32_t operator_index, + JSOverloadableOperatorEnum op) +{ + JSBinaryOperatorDefEntry *ent; + int i; + for(i = 0; i < def->count; i++) { + ent = &def->tab[i]; + if (ent->operator_index == operator_index) + return ent->ops[op]; } - JS_FreeValue(ctx, c1); - JS_FreeValue(ctx, c2); - c1 = JS_UNDEFINED; - c2 = JS_UNDEFINED; - if (JS_IsException(method)) + return NULL; +} + +/* return -1 if exception, 0 if no operator overloading, 1 if + overloaded operator called */ +static __exception int js_call_binary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op, + BOOL is_numeric, + int hint) +{ + JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2; + JSOperatorSetData *opset1, *opset2; + JSOverloadableOperatorEnum ovop; + JSObject *p; + JSValueConst args[2]; + + if (!ctx->allow_operator_overloading) + return 0; + + opset2_obj = JS_UNDEFINED; + opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) goto exception; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - *pret = JS_UNDEFINED; + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) + goto exception; + + opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset2_obj)) + goto exception; + if (JS_IsUndefined(opset2_obj)) { + JS_FreeValue(ctx, opset1_obj); return 0; } - if (swap_op) { - args[0] = op2; - args[1] = op1; + opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET); + if (!opset2) + goto exception; + + if (opset1->is_primitive && opset2->is_primitive) { + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + return 0; + } + + ovop = get_ovop_from_opcode(op); + + if (opset1->operator_counter == opset2->operator_counter) { + p = opset1->self_ops[ovop]; + } else if (opset1->operator_counter > opset2->operator_counter) { + p = find_binary_op(&opset1->left, opset2->operator_counter, ovop); } else { - args[0] = op1; - args[1] = op2; + p = find_binary_op(&opset2->right, opset1->operator_counter, ovop); + } + if (!p) { + JS_ThrowTypeError(ctx, "operator %s: no function defined", + js_overloadable_operator_names[ovop]); + goto exception; + } + + if (opset1->is_primitive) { + if (is_numeric) { + new_op1 = JS_ToNumeric(ctx, op1); + } else { + new_op1 = JS_ToPrimitive(ctx, op1, hint); + } + if (JS_IsException(new_op1)) + goto exception; + } else { + new_op1 = JS_DupValue(ctx, op1); + } + + if (opset2->is_primitive) { + if (is_numeric) { + new_op2 = JS_ToNumeric(ctx, op2); + } else { + new_op2 = JS_ToPrimitive(ctx, op2, hint); + } + if (JS_IsException(new_op2)) { + JS_FreeValue(ctx, new_op1); + goto exception; + } + } else { + new_op2 = JS_DupValue(ctx, op2); + } + + /* XXX: could apply JS_ToPrimitive() if primitive type so that the + operator function does not get a value object */ + + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) { + args[0] = new_op2; + args[1] = new_op1; + } else { + args[0] = new_op1; + args[1] = new_op2; } ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); + JS_FreeValue(ctx, new_op1); + JS_FreeValue(ctx, new_op2); if (JS_IsException(ret)) goto exception; - if (bool_result) { + if (ovop == JS_OVOP_EQ) { BOOL res = JS_ToBoolFree(ctx, ret); if (op == OP_neq) res ^= 1; ret = JS_NewBool(ctx, res); + } else if (ovop == JS_OVOP_LESS) { + if (JS_IsUndefined(ret)) { + ret = JS_FALSE; + } else { + BOOL res = JS_ToBoolFree(ctx, ret); + if (op == OP_lte || op == OP_gte) + res ^= 1; + ret = JS_NewBool(ctx, res); + } } + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); *pret = ret; return 1; - fail: - JS_ThrowTypeError(ctx, "invalid types for binary operator"); exception: - JS_FreeValue(ctx, c1); - JS_FreeValue(ctx, c2); + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + *pret = JS_UNDEFINED; + return -1; +} + +/* try to call the operation on the operatorSet field of 'obj'. Only + used for "/" and "**" on the BigInt prototype in math mode */ +static __exception int js_call_binary_op_simple(JSContext *ctx, + JSValue *pret, + JSValueConst obj, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op) +{ + JSValue opset1_obj, method, ret, new_op1, new_op2; + JSOperatorSetData *opset1; + JSOverloadableOperatorEnum ovop; + JSObject *p; + JSValueConst args[2]; + + opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) + goto exception; + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) + goto exception; + ovop = get_ovop_from_opcode(op); + + p = opset1->self_ops[ovop]; + if (!p) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + + new_op1 = JS_ToNumeric(ctx, op1); + if (JS_IsException(new_op1)) + goto exception; + new_op2 = JS_ToNumeric(ctx, op2); + if (JS_IsException(new_op2)) { + JS_FreeValue(ctx, new_op1); + goto exception; + } + + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + args[0] = new_op1; + args[1] = new_op2; + ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); + JS_FreeValue(ctx, new_op1); + JS_FreeValue(ctx, new_op2); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, opset1_obj); + *pret = ret; + return 1; + exception: + JS_FreeValue(ctx, opset1_obj); + *pret = JS_UNDEFINED; + return -1; +} + +/* return -1 if exception, 0 if no operator overloading, 1 if + overloaded operator called */ +static __exception int js_call_unary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + OPCodeEnum op) +{ + JSValue opset1_obj, method, ret; + JSOperatorSetData *opset1; + JSOverloadableOperatorEnum ovop; + JSObject *p; + + if (!ctx->allow_operator_overloading) + return 0; + + opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) + goto exception; + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) + goto exception; + if (opset1->is_primitive) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + + ovop = get_ovop_from_opcode(op); + + p = opset1->self_ops[ovop]; + if (!p) { + JS_ThrowTypeError(ctx, "no overloaded operator %s", + js_overloadable_operator_names[ovop]); + goto exception; + } + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, opset1_obj); + *pret = ret; + return 1; + exception: + JS_FreeValue(ctx, opset1_obj); *pret = JS_UNDEFINED; return -1; } @@ -11539,6 +12678,8 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx, static JSValue throw_bf_exception(JSContext *ctx, int status) { const char *str; + if (status & BF_ST_MEM_ERROR) + return JS_ThrowOutOfMemory(ctx); if (status & BF_ST_DIVIDE_ZERO) { str = "division by zero"; } else if (status & BF_ST_INVALID_OP) { @@ -11549,15 +12690,160 @@ static JSValue throw_bf_exception(JSContext *ctx, int status) 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, *a; + int ret, v; + JSValue res; + + if (op == OP_plus && !is_math_mode(ctx)) { + JS_ThrowTypeError(ctx, "bigint argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigInt(res); + a = JS_ToBigInt(ctx, &a_s, op1); + ret = 0; + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); + break; + case OP_plus: + ret = bf_set(r, a); + break; + case OP_neg: + ret = bf_set(r, a); + bf_neg(r); + break; + case OP_not: + ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ); + bf_neg(r); + break; + default: + abort(); + } + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeValue(ctx, op1); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + res = JS_CompactBigInt(ctx, res); + *pres = res; + return 0; +} + +static int js_unary_arith_bigfloat(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) +{ + bf_t a_s, *r, *a; + int ret, v; + JSValue res; + + if (op == OP_plus && !is_math_mode(ctx)) { + JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigFloat(res); + a = JS_ToBigFloat(ctx, &a_s, op1); + ret = 0; + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_plus: + ret = bf_set(r, a); + break; + case OP_neg: + ret = bf_set(r, a); + bf_neg(r); + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + if (unlikely(ret & BF_ST_MEM_ERROR)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = res; + return 0; +} + +static int js_unary_arith_bigdecimal(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) +{ + bfdec_t *r, *a; + int ret, v; + JSValue res; + + if (op == OP_plus && !is_math_mode(ctx)) { + JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigDecimal(res); + a = JS_ToBigDecimal(ctx, op1); + ret = 0; + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); + break; + case OP_plus: + ret = bfdec_set(r, a); + break; + case OP_neg: + ret = bfdec_set(r, a); + bfdec_neg(r); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = res; + return 0; +} + static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { - JSValue op1, val, method; - bf_t a_s, r_s, *r = &r_s, *a; - BOOL is_float, is_legacy; - JSAtom op_name; - int ret, v; + JSValue op1, val; + int v, ret; uint32_t tag; op1 = sp[-1]; @@ -11565,154 +12851,84 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) goto handle_float64; if (JS_IsObject(op1)) { - 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(); + ret = js_call_unary_op_fallback(ctx, &val, op1, op); + if (ret < 0) + return -1; + if (ret) { + JS_FreeValue(ctx, op1); + sp[-1] = val; + return 0; } - 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)) + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); + switch(tag) { + case JS_TAG_INT: + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = __JS_NewFloat64(ctx, -0.0); + return 0; + } else { + v64 = -v64; + } + break; + default: + abort(); + } + sp[-1] = JS_NewInt64(ctx, v64); + } + break; + case JS_TAG_BIG_INT: + handle_bigint: + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) goto exception; - is_legacy = is_bignum_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 (is_legacy && op == OP_plus) { - JS_ThrowTypeError(ctx, "bigint argument with unary +"); - JS_FreeValue(ctx, op1); - goto exception; - } - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); - bf_init(ctx->bf_ctx, r); - ret = 0; + 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; + if (is_math_mode(ctx)) + goto handle_bigint; + d = JS_VALUE_GET_FLOAT64(op1); switch(op) { case OP_inc: case OP_dec: v = 2 * (op - OP_dec) - 1; - ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ) & BF_ST_OVERFLOW; + d += v; break; case OP_plus: - bf_set(r, a); break; case OP_neg: - bf_set(r, a); - bf_neg(r); + d = -d; break; default: abort(); } - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - if (unlikely(ret)) { - bf_delete(r); - throw_bf_exception(ctx, ret); - goto exception; - } - sp[-1] = JS_NewBigInt2(ctx, r, is_legacy); - break; - case JS_TAG_BIG_FLOAT: - a = JS_ToBigFloat(ctx, &is_float, &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; - bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_plus: - bf_set(r, a); - break; - case OP_neg: - 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_delete(r); - throw_bf_exception(ctx, ret); - goto exception; - } - sp[-1] = JS_NewBigFloat(ctx, r); - 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; + sp[-1] = __JS_NewFloat64(ctx, d); } + break; } return 0; exception: @@ -11739,49 +12955,32 @@ static __exception int js_post_inc_slow(JSContext *ctx, static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) { - JSValue op1, method, val; - bf_t a_s, r_s, *r = &r_s, *a; + JSValue op1, val; int ret; - BOOL is_legacy; - + op1 = sp[-1]; if (JS_IsObject(op1)) { - method = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorNot); - if (JS_IsException(method)) + ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); + if (ret < 0) 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; + if (ret) { + JS_FreeValue(ctx, op1); + sp[-1] = val; + return 0; + } + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) + goto exception; } else { - if (JS_IsString(op1)) { - to_number: - op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER); - if (JS_IsException(op1)) - goto exception; - } - is_legacy = is_bignum_mode(ctx) ^ 1; - if (!is_legacy || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { - a = JS_ToBigIntFree(ctx, &a_s, op1); - bf_init(ctx->bf_ctx, r); - ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ) & BF_ST_OVERFLOW; - bf_neg(r); - JS_FreeBigInt(ctx, a, &a_s); - if (unlikely(ret)) { - bf_delete(r); - throw_bf_exception(ctx, ret); - goto exception; - } - sp[-1] = JS_NewBigInt2(ctx, r, is_legacy); - } else { - int32_t v1; - if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) - goto exception; - sp[-1] = JS_NewInt32(ctx, ~v1); - } + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = JS_NewInt32(ctx, ~v1); } return 0; exception: @@ -11789,13 +12988,315 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) 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, *a, *b; + int ret; + JSValue res; + + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return -1; + } + r = JS_GetBigFloat(res); + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + bf_init(ctx->bf_ctx, r); + switch(op) { + case OP_add: + ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_sub: + ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_mul: + ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_div: + ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case OP_math_mod: + /* Euclidian remainder */ + ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + BF_DIVREM_EUCLIDIAN); + break; + case OP_mod: + ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, + BF_RNDZ); + break; + case OP_pow: + ret = bf_pow(r, a, b, ctx->fp_env.prec, + ctx->fp_env.flags | BF_POW_JS_QUIRKS); + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (unlikely(ret & BF_ST_MEM_ERROR)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = res; + return 0; +} + +static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, *r, *a, *b; + int ret; + JSValue res; + + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + goto fail; + a = JS_ToBigInt(ctx, &a_s, op1); + if (!a) + goto fail; + b = JS_ToBigInt(ctx, &b_s, op2); + if (!b) { + JS_FreeBigInt(ctx, a, &a_s); + goto fail; + } + r = JS_GetBigInt(res); + ret = 0; + switch(op) { + case OP_add: + ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_sub: + ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_mul: + ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_div: + if (!is_math_mode(ctx)) { + bf_t rem_s, *rem = &rem_s; + bf_init(ctx->bf_ctx, rem); + ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ, + BF_RNDZ); + bf_delete(rem); + } else { + goto math_mode_div_pow; + } + break; + case OP_math_mod: + /* Euclidian remainder */ + ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, + BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP; + break; + case OP_mod: + ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, + BF_RNDZ) & BF_ST_INVALID_OP; + break; + case OP_pow: + if (b->sign) { + if (!is_math_mode(ctx)) { + ret = BF_ST_INVALID_OP; + } else { + math_mode_div_pow: + JS_FreeValue(ctx, res); + ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op); + if (ret != 0) { + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + return -1; + } else { + *pres = res; + return 0; + } + } + /* if no BigInt power operator defined, return a + bigfloat */ + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + goto fail; + } + r = JS_GetBigFloat(res); + if (op == OP_div) { + ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR; + } else { + ret = bf_pow(r, a, b, ctx->fp_env.prec, + ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR; + } + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = res; + return 0; + } + } else { + ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); + } + break; + + /* logical operations */ + case OP_shl: + case OP_sar: + { + slimb_t v2; +#if LIMB_BITS == 32 + bf_get_int32(&v2, b, 0); + if (v2 == INT32_MIN) + v2 = INT32_MIN + 1; +#else + bf_get_int64(&v2, b, 0); + if (v2 == INT64_MIN) + v2 = INT64_MIN + 1; +#endif + if (op == OP_sar) + v2 = -v2; + ret = bf_set(r, a); + ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ); + if (v2 < 0) { + ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR); + } + } + break; + case OP_and: + ret = bf_logic_and(r, a, b); + break; + case OP_or: + ret = bf_logic_or(r, a, b); + break; + case OP_xor: + ret = bf_logic_xor(r, a, b); + break; + default: + abort(); + } + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_CompactBigInt(ctx, res); + return 0; + fail: + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return -1; +} + +/* b must be a positive integer */ +static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b) +{ + bfdec_t b1; + int32_t b2; + int ret; + + bfdec_init(b->ctx, &b1); + ret = bfdec_set(&b1, b); + if (ret) { + bfdec_delete(&b1); + return ret; + } + ret = bfdec_rint(&b1, BF_RNDZ); + if (ret) { + bfdec_delete(&b1); + return BF_ST_INVALID_OP; /* must be an integer */ + } + ret = bfdec_get_int32(&b2, &b1); + bfdec_delete(&b1); + if (ret) + return ret; /* overflow */ + if (b2 < 0) + return BF_ST_INVALID_OP; /* must be positive */ + return bfdec_pow_ui(r, a, b2); +} + +static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2) +{ + bfdec_t *r, *a, *b; + int ret; + JSValue res; + + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) + goto fail; + r = JS_GetBigDecimal(res); + + a = JS_ToBigDecimal(ctx, op1); + if (!a) + goto fail; + b = JS_ToBigDecimal(ctx, op2); + if (!b) + goto fail; + switch(op) { + case OP_add: + ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_sub: + ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_mul: + ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_div: + ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_math_mod: + /* Euclidian remainder */ + ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN); + break; + case OP_mod: + ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); + break; + case OP_pow: + ret = js_bfdec_pow(r, a, b); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = res; + return 0; + fail: + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return -1; +} + static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1, op2, res; - BOOL is_float, is_legacy; uint32_t tag1, tag2; - int ret, rnd_mode; + int ret; double d1, d2; op1 = sp[-2]; @@ -11814,7 +13315,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || (tag2 == JS_TAG_OBJECT && (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -11840,15 +13341,6 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - is_legacy = is_bignum_mode(ctx) ^ 1; - if (is_legacy && (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && - tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be bigint"); - goto exception; - } - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { int32_t v1, v2; int64_t v; @@ -11860,14 +13352,17 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s break; case OP_mul: v = (int64_t)v1 * (int64_t)v2; - if (is_legacy && v == 0 && (v1 | v2) < 0) { + if (is_math_mode(ctx) && + (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) + goto handle_bigint; + if (v == 0 && (v1 | v2) < 0) { sp[-2] = __JS_NewFloat64(ctx, -0.0); return 0; } break; - case OP_math_div: - goto op_fallback2; case OP_div: + if (is_math_mode(ctx)) + goto handle_bigint; sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); return 0; case OP_math_mod: @@ -11884,20 +13379,15 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s } break; case OP_mod: - if (is_legacy && (v1 < 0 || v2 <= 0)) { + if (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) { + if (!is_math_mode(ctx)) { sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); return 0; } else { @@ -11908,124 +13398,28 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s abort(); } sp[-2] = JS_NewInt64(ctx, v); - } else if ((tag1 == JS_TAG_BIG_INT && (tag2 == JS_TAG_INT || - tag2 == JS_TAG_BIG_INT)) || - (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_INT)) { - /* big int result */ - bf_t a_s, b_s, r_s, *r, *a, *b; - handle_bigint: - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); - b = JS_ToBigFloat(ctx, &is_float, &b_s, op2); - r = &r_s; - bf_init(ctx->bf_ctx, r); - ret = 0; - switch(op) { - case OP_sub: - ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ) & BF_ST_OVERFLOW; - break; - case OP_mul: - ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ) & BF_ST_OVERFLOW; - break; - case OP_math_div: - goto op_fallback; - case OP_div: - if (is_legacy) { - 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_ST_INVALID_OP; - 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_legacy) { - ret = BF_ST_INVALID_OP; - } else if (op == OP_math_pow) { - op_fallback: - bf_delete(r); - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - op_fallback2: - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (ret < 0) { - goto exception; - } else if (ret == 0) { - JS_ThrowTypeError(ctx, "operator must be defined for exact division or power"); - goto exception; - } - sp[-2] = 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); - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = __JS_NewFloat64(ctx, dr); - return 0; - } - } else { - ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUICKS) & - BF_ST_OVERFLOW; - } - 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_delete(r); - throw_bf_exception(ctx, ret); + } 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; - } - sp[-2] = JS_NewBigInt2(ctx, r, is_legacy); - } else if ((tag1 == JS_TAG_FLOAT64 && (tag2 == JS_TAG_FLOAT64 || - tag2 == JS_TAG_INT || - tag2 == JS_TAG_BIG_INT)) || - (tag2 == JS_TAG_FLOAT64 && (tag1 == JS_TAG_INT || - tag1 == JS_TAG_BIG_INT))) { + } 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_BIG_INT || tag2 == JS_TAG_BIG_INT) { + handle_bigint: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } else { double dr; /* float64 result */ - JS_ToFloat64Free(ctx, &d1, op1); - JS_ToFloat64Free(ctx, &d2, op2); + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; handle_float64: + if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) + goto handle_bigint; switch(op) { case OP_sub: dr = d1 - d2; @@ -12034,95 +13428,25 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s 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: - if (d1 >= 0 && d2 >= 0) { - dr = fmod(d1, d2); - } else { - /* XXX: slow */ - bf_t a, b, r, q; - bf_init(ctx->bf_ctx, &a); - bf_init(ctx->bf_ctx, &b); - bf_init(ctx->bf_ctx, &r); - bf_set_float64(&a, d1); - bf_set_float64(&b, d2); - bf_divrem(&q, &r, &a, &b, - 53, bf_set_exp_bits(11) | BF_RNDN | BF_FLAG_SUBNORMAL, - BF_DIVREM_EUCLIDIAN); - bf_get_float64(&q, &dr, BF_RNDN); - bf_delete(&a); - bf_delete(&b); - bf_delete(&q); - bf_delete(&r); - } + 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 { - bf_t a_s, b_s, r_s, *r, *a, *b; - /* big float result */ - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); - b = JS_ToBigFloat(ctx, &is_float, &b_s, op2); - r = &r_s; - bf_init(ctx->bf_ctx, r); - ret = 0; - switch(op) { - case OP_sub: - bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_mul: - bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case OP_math_div: - case OP_div: - 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); - 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: - 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_delete(r); - throw_bf_exception(ctx, ret); - goto exception; - } - sp[-2] = JS_NewBigFloat(ctx, r); } return 0; exception: @@ -12135,7 +13459,6 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) { JSValue op1, op2, res; uint32_t tag1, tag2; - BOOL is_float, is_legacy; int ret; op1 = sp[-2]; @@ -12155,10 +13478,13 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) 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_NULL && tag2 != JS_TAG_UNDEFINED && + tag2 != JS_TAG_STRING)) || (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add); + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED && + tag1 != JS_TAG_STRING))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add, + FALSE, HINT_NONE); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12206,15 +13532,6 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); - is_legacy = is_bignum_mode(ctx) ^ 1; - if (is_legacy && (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && - tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be bigint"); - goto exception; - } - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { int32_t v1, v2; int64_t v; @@ -12222,54 +13539,28 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) v2 = JS_VALUE_GET_INT(op2); v = (int64_t)v1 + (int64_t)v2; sp[-2] = JS_NewInt64(ctx, v); - } else if ((tag1 == JS_TAG_BIG_INT && (tag2 == JS_TAG_INT || - tag2 == JS_TAG_BIG_INT)) || - (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_INT)) { - bf_t a_s, b_s, r_s, *r, *a, *b; - /* big int result */ - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); - b = JS_ToBigFloat(ctx, &is_float, &b_s, op2); - r = &r_s; - bf_init(ctx->bf_ctx, r); - ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ) & BF_ST_OVERFLOW; - 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_delete(r); - throw_bf_exception(ctx, ret); + } 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; - } - sp[-2] = JS_NewBigInt2(ctx, r, is_legacy); - } else if ((tag1 == JS_TAG_FLOAT64 && (tag2 == JS_TAG_FLOAT64 || - tag2 == JS_TAG_INT || - tag2 == JS_TAG_BIG_INT)) || - (tag2 == JS_TAG_FLOAT64 && (tag1 == JS_TAG_INT || - tag1 == JS_TAG_BIG_INT))) { + } 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_BIG_INT || tag2 == JS_TAG_BIG_INT) { + handle_bigint: + if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } else { double d1, d2; /* float64 result */ - JS_ToFloat64Free(ctx, &d1, op1); - JS_ToFloat64Free(ctx, &d2, op2); + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) + goto handle_bigint; sp[-2] = __JS_NewFloat64(ctx, d1 + d2); - } else { - bf_t a_s, b_s, r_s, *r, *a, *b; - /* big float result */ - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); - b = JS_ToBigFloat(ctx, &is_float, &b_s, op2); - - r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBigFloat(ctx, r); } return 0; exception: @@ -12283,10 +13574,9 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, OPCodeEnum op) { JSValue op1, op2, res; - bf_t a_s, b_s, r_s, *r, *a, *b; int ret; uint32_t tag1, tag2; - BOOL is_legacy; + uint32_t v1, v2, r; op1 = sp[-2]; op2 = sp[-1]; @@ -12298,7 +13588,7 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || (tag2 == JS_TAG_OBJECT && (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op); + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12311,111 +13601,61 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, } } - op1 = JS_ToNumberHintFree(ctx, op1, TON_FLAG_INTEGER); + op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); goto exception; } - op2 = JS_ToNumberHintFree(ctx, op2, TON_FLAG_INTEGER); + op2 = JS_ToNumericFree(ctx, op2); if (JS_IsException(op2)) { JS_FreeValue(ctx, op1); goto exception; } - is_legacy = is_bignum_mode(ctx) ^ 1; - if (is_legacy) { - uint32_t v1, v2, r; + if (is_math_mode(ctx)) + goto bigint_op; - tag1 = JS_VALUE_GET_TAG(op1); - tag2 = JS_VALUE_GET_TAG(op2); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be bigint"); - goto exception; - } + 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 { - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { - JS_FreeValue(ctx, op2); + bigint_op: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, 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; } - } - - a = JS_ToBigIntFree(ctx, &a_s, op1); - b = JS_ToBigIntFree(ctx, &b_s, op2); - - r = &r_s; - bf_init(ctx->bf_ctx, r); - ret = 0; - switch(op) { - 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; - bf_set(r, a); - ret = bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ); - if (v2 < 0) { - ret |= bf_rint(r, BF_PREC_INF, BF_RNDD); - } - ret &= BF_ST_OVERFLOW; + } else { + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; } - break; - case OP_and: - bf_logic_and(r, a, b); - break; - case OP_or: - bf_logic_or(r, a, b); - break; - case OP_xor: - bf_logic_xor(r, a, b); - break; - default: - abort(); + 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); } - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - if (unlikely(ret)) { - bf_delete(r); - throw_bf_exception(ctx, ret); - goto exception; - } - sp[-2] = JS_NewBigInt2(ctx, r, is_legacy); return 0; exception: sp[-2] = JS_UNDEFINED; @@ -12423,6 +13663,99 @@ static no_inline __exception int js_binary_logic_slow(JSContext *ctx, return -1; } +/* Note: also used for bigint */ +static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2) +{ + 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) { @@ -12439,7 +13772,8 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || (tag2 == JS_TAG_OBJECT && (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op); + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op, + FALSE, HINT_NUMBER); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12488,40 +13822,12 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, 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; - } + /* fast path for float64/int */ + goto float64_compare; } else { - bf_t a_s, b_s, *a, *b; - BOOL is_float; - if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && - !is_bignum_mode(ctx)) { + !is_math_mode(ctx)) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) @@ -12550,30 +13856,52 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, } } - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); - b = JS_ToBigFloat(ctx, &is_float, &b_s, op2); + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); - 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; - default: - case OP_gte: - res = bf_cmp_le(b, a); /* if NaN return false */ - break; + if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2); + if (res < 0) + goto exception; + } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { + res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2); + if (res < 0) + goto exception; + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2); + if (res < 0) + goto exception; + } else { + double d1, d2; + + float64_compare: + /* can use floating point comparison */ + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } } - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); } done: sp[-2] = JS_NewBool(ctx, res); @@ -12587,7 +13915,8 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, static BOOL tag_is_number(uint32_t tag) { return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || - tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT); + 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, @@ -12621,24 +13950,25 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, 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 { - bf_t a_s, b_s, *a, *b; - BOOL is_float; - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); - b = JS_ToBigFloat(ctx, &is_float, &b_s, op2); - res = bf_cmp_eq(a, b); - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); + 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) { /* try the fallback operator */ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq); + is_neq ? OP_neq : OP_eq, + FALSE, HINT_NONE); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12658,7 +13988,7 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && - !is_bignum_mode(ctx)) { + !is_math_mode(ctx)) { if (tag1 == JS_TAG_STRING) { op1 = JS_StringToBigInt(ctx, op1); if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) @@ -12700,7 +14030,8 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, /* try the fallback operator */ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq); + is_neq ? OP_neq : OP_eq, + FALSE, HINT_NONE); if (res != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -12724,9 +14055,17 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, } goto redo; } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = TRUE; + } else { + res = FALSE; + } JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - res = FALSE; } done: sp[-2] = JS_NewBool(ctx, res ^ is_neq); @@ -12755,7 +14094,7 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) goto exception; } /* XXX: could forbid >>> in bignum mode */ - if (!is_bignum_mode(ctx) && + if (!is_math_mode(ctx) && (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); @@ -12775,56 +14114,87 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) return -1; } -static double bf_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, - int64_t exponent) +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); - bf_mul_pow_radix(r, a, 10, exponent, - 53, bf_set_exp_bits(11) | BF_RNDN | BF_FLAG_SUBNORMAL); + 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); - return d; + 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; - BOOL is_float; - JSValue op1, op2; - slimb_t e; + bf_t a_s, *a, *r; + JSValue op1, op2, res; + int64_t e; int ret; + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) + return -1; + r = JS_GetBigFloat(res); op1 = sp[-2]; op2 = sp[-1]; - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); + 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 (JS_IsBigInt(ctx, op2)) { + ret = JS_ToBigInt64(ctx, &e, op2); + } else { + ret = JS_ToInt64(ctx, &e, op2); + } if (ret) { if (a == &a_s) bf_delete(a); + JS_FreeValue(ctx, res); return -1; } - bf_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); + sp[-2] = res; return 0; } #else /* !CONFIG_BIGNUM */ +static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "bigint is not supported"); +} + +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) +{ + return JS_ThrowUnsupportedBigint(ctx); +} + +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) +{ + return JS_ThrowUnsupportedBigint(ctx); +} + +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + JS_ThrowUnsupportedBigint(ctx); + *pres = 0; + return -1; +} + static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) @@ -13146,9 +14516,17 @@ static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, } goto redo; } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = TRUE; + } else { + res = FALSE; + } JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); - res = FALSE; } sp[-2] = JS_NewBool(ctx, res ^ is_neq); return 0; @@ -13241,22 +14619,9 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, d2 = JS_VALUE_GET_INT(op2); goto number_test; } else if (tag2 == JS_TAG_FLOAT64) { -#ifdef CONFIG_BIGNUM - if (is_bignum_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_bignum_mode(ctx)) { - goto bigint_test; - } else -#endif - { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } else { res = FALSE; } break; @@ -13264,11 +14629,7 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, 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_bignum_mode(ctx) -#endif - ) { + } else if (tag2 == JS_TAG_INT) { d2 = JS_VALUE_GET_INT(op2); } else { res = FALSE; @@ -13295,23 +14656,17 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, case JS_TAG_BIG_INT: { bf_t a_s, *a, b_s, *b; - BOOL is_float; - if (tag1 == tag2) { - /* OK */ - } else if (tag2 == JS_TAG_INT && is_bignum_mode(ctx)) { - /* OK */ - } else { + if (tag1 != tag2) { res = FALSE; break; } - bigint_test: - a = JS_ToBigFloat(ctx, &is_float, &a_s, op1); - b = JS_ToBigFloat(ctx, &is_float, &b_s, op2); + 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(a); + bf_delete(b); } break; case JS_TAG_BIG_FLOAT: @@ -13338,6 +14693,21 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, } } 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; @@ -13437,7 +14807,7 @@ static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) return 0; } -static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) +static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) { JSAtom atom; uint32_t tag; @@ -13445,27 +14815,20 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) tag = JS_VALUE_GET_NORM_TAG(op1); switch(tag) { #ifdef CONFIG_BIGNUM - case JS_TAG_INT: - if (is_bignum_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; -#else + case JS_TAG_BIG_DECIMAL: + atom = JS_ATOM_bigdecimal; + break; +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: atom = JS_ATOM_number; break; -#endif case JS_TAG_UNDEFINED: atom = JS_ATOM_undefined; break; @@ -13476,10 +14839,16 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) atom = JS_ATOM_string; break; case JS_TAG_OBJECT: - if (JS_IsFunction(ctx, op1)) - atom = JS_ATOM_function; - else - goto obj_type; + { + JSObject *p; + p = JS_VALUE_GET_OBJ(op1); + if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined; + else if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + } break; case JS_TAG_NULL: obj_type: @@ -13672,7 +15041,7 @@ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, 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_DupValue(ctx, ctx->rt->current_stack_frame->cur_func), JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); return val; fail: @@ -13757,15 +15126,6 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) 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; @@ -13994,8 +15354,8 @@ static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, int res; if (is_exception_pending) { - ex_obj = ctx->current_exception; - ctx->current_exception = JS_NULL; + ex_obj = ctx->rt->current_exception; + ctx->rt->current_exception = JS_NULL; res = -1; } else { ex_obj = JS_UNDEFINED; @@ -14044,7 +15404,10 @@ static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, return 0; } -/* enum_rec -> enum_rec value done */ +/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' + objs. If 'done' is true or in case of exception, 'enum_rec' is set + to undefined. If 'done' is true, 'value' is always set to + undefined. */ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) { JSValue value = JS_UNDEFINED; @@ -14059,8 +15422,12 @@ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) /* replace the iteration object with undefined */ JS_FreeValue(ctx, sp[offset]); sp[offset] = JS_UNDEFINED; - if (done < 0) + if (done < 0) { return -1; + } else { + JS_FreeValue(ctx, value); + value = JS_UNDEFINED; + } } } sp[0] = value; @@ -14068,16 +15435,6 @@ static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) return 0; } -static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp) -{ - JSValue 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) { @@ -14176,10 +15533,10 @@ static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) { JSValue iterator, enumobj, method, value; - int pos, is_array_iterator; + int is_array_iterator; JSValue *arrp; - uint32_t i, count32; - + uint32_t i, count32, pos; + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { JS_ThrowInternalError(ctx, "invalid index for append"); return -1; @@ -14212,24 +15569,21 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) 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])) + uint32_t len; + if (js_get_length32(ctx, &len, sp[-1])) goto exception; + /* if len > count32, the elements >= count32 might be read in + the prototypes and might have side effects */ + if (len != count32) + goto general_case; + /* Handle fast arrays explicitly */ for (i = 0; i < count32; i++) { if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) goto exception; } - 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 { + general_case: for (;;) { BOOL done; value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); @@ -14243,6 +15597,7 @@ static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) goto exception; } } + /* Note: could raise an error if too many elements */ sp[-2] = JS_NewInt32(ctx, pos); JS_FreeValue(ctx, enumobj); JS_FreeValue(ctx, method); @@ -14311,7 +15666,7 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, /* only valid inside C functions */ static JSValueConst JS_GetActiveFunction(JSContext *ctx) { - return ctx->current_stack_frame->cur_func; + return ctx->rt->current_stack_frame->cur_func; } static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, @@ -14321,7 +15676,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, struct list_head *el; list_for_each(el, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, link); + 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; @@ -14332,10 +15687,10 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, if (!var_ref) return NULL; var_ref->header.ref_count = 1; - var_ref->gc_header.mark = 0; + var_ref->is_detached = FALSE; var_ref->is_arg = is_arg; var_ref->var_idx = var_idx; - list_add_tail(&var_ref->link, &sf->var_ref_list); + list_add_tail(&var_ref->header.link, &sf->var_ref_list); if (is_arg) var_ref->pvalue = &sf->arg_buf[var_idx]; else @@ -14384,7 +15739,7 @@ static JSValue js_closure2(JSContext *ctx, JSValue func_obj, return JS_EXCEPTION; } -static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) { JSValue obj, this_val; int ret; @@ -14392,17 +15747,26 @@ static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, vo this_val = JS_MKPTR(JS_TAG_OBJECT, p); obj = JS_NewObject(ctx); if (JS_IsException(obj)) - return -1; + return JS_EXCEPTION; set_cycle_flag(ctx, obj); set_cycle_flag(ctx, this_val); ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, JS_DupValue(ctx, this_val), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (JS_DefinePropertyValue(ctx, this_val, atom, obj, JS_PROP_WRITABLE) < 0 || ret < 0) - return -1; - return 0; + if (ret < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; } +static const uint16_t func_kind_to_class_id[] = { + [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, + [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, + [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, + [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, +}; + static JSValue js_closure(JSContext *ctx, JSValue bfunc, JSVarRef **cur_var_refs, JSStackFrame *sf) @@ -14410,12 +15774,6 @@ static JSValue js_closure(JSContext *ctx, JSValue bfunc, 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]); @@ -14454,7 +15812,7 @@ static JSValue js_closure(JSContext *ctx, JSValue bfunc, 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_AUTOINIT_ID_PROTOTYPE, NULL, JS_PROP_WRITABLE); } return func_obj; @@ -14568,15 +15926,16 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) int var_idx; list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, link); + 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; - var_ref->link.prev = NULL; /* the reference is no longer to a - local variable */ + /* 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); } } @@ -14587,25 +15946,26 @@ static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_ int var_idx = idx; list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, link); + 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->link); - var_ref->link.prev = NULL; /* the reference is no longer to a - local variable */ + 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 CALL_FLAG_CONSTRUCTOR (1 << 0) -#define CALL_FLAG_COPY_ARGV (1 << 1) -#define CALL_FLAG_GENERATOR (1 << 2) +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst *argv, int flags) { + JSRuntime *rt = ctx->rt; JSCFunctionType func; JSObject *p; JSStackFrame sf_s, *sf = &sf_s, *prev_sf; @@ -14619,17 +15979,19 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, arg_count = p->u.cfunc.length; /* better to always check stack overflow */ - if (js_check_stack_overflow(ctx, sizeof(arg_buf[0]) * arg_count)) + if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) return JS_ThrowStackOverflow(ctx); - prev_sf = ctx->current_stack_frame; + prev_sf = rt->current_stack_frame; sf->prev_frame = prev_sf; - ctx->current_stack_frame = sf; + rt->current_stack_frame = sf; + ctx = p->u.cfunc.realm; /* change the current realm */ + #ifdef CONFIG_BIGNUM /* we only propagate the bignum mode as some runtime functions test it */ if (prev_sf) - sf->js_mode = prev_sf->js_mode & JS_MODE_BIGINT; + sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; else sf->js_mode = 0; #else @@ -14654,7 +16016,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, switch(cproto) { case JS_CFUNC_constructor: case JS_CFUNC_constructor_or_func: - if (!(flags & CALL_FLAG_CONSTRUCTOR)) { + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { if (cproto == JS_CFUNC_constructor) { not_a_constructor: ret_val = JS_ThrowTypeError(ctx, "must be called with new"); @@ -14670,7 +16032,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, break; case JS_CFUNC_constructor_magic: case JS_CFUNC_constructor_or_func_magic: - if (!(flags & CALL_FLAG_CONSTRUCTOR)) { + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { if (cproto == JS_CFUNC_constructor_magic) { goto not_a_constructor; } else { @@ -14734,7 +16096,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, abort(); } - ctx->current_stack_frame = sf->prev_frame; + rt->current_stack_frame = sf->prev_frame; return ret_val; } @@ -14750,7 +16112,7 @@ static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, 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)) + if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) return JS_ThrowStackOverflow(ctx); arg_buf = alloca(sizeof(JSValue) * arg_count); for(i = 0; i < bf->argc; i++) { @@ -14759,7 +16121,7 @@ static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, for(i = 0; i < argc; i++) { arg_buf[bf->argc + i] = argv[i]; } - if (flags & CALL_FLAG_CONSTRUCTOR) { + if (flags & JS_CALL_FLAG_CONSTRUCTOR) { new_target = this_obj; if (js_same_value(ctx, func_obj, new_target)) new_target = bf->func_obj; @@ -14771,30 +16133,6 @@ static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, } } -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, @@ -14810,11 +16148,13 @@ typedef enum { #define FUNC_RET_YIELD 1 #define FUNC_RET_YIELD_STAR 2 -/* argv[] is modified if (flags & CALL_FLAG_COPY_ARGV) = 0. */ -static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JSValueConst this_obj, JSValueConst new_target, int argc, JSValue *argv, int flags) { + JSRuntime *rt = caller_ctx->rt; + JSContext *ctx; JSObject *p; JSFunctionBytecode *b; JSStackFrame sf_s, *sf = &sf_s; @@ -14853,8 +16193,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, [ OP_COUNT ... 255 ] = &&case_default }; #define SWITCH(pc) goto *active_dispatch_table[opcode = *pc++]; -#define CASE(op) case_debugger_ ## op: DBG_CallDebugger(ctx, pc); case_ ## op - const void * const * active_dispatch_table = ctx->debugger_info.isConnected +#define CASE(op) case_debugger_ ## op: DBG_CallDebugger(caller_ctx, pc); case_ ## op + const void * const * active_dispatch_table = caller_ctx->debugger_info.isConnected ? debugger_dispatch_table : dispatch_table; #else #define SWITCH(pc) goto *dispatch_table[opcode = *pc++]; @@ -14865,16 +16205,17 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, #define BREAK SWITCH(pc) #endif - if (js_poll_interrupts(ctx)) + if (js_poll_interrupts(caller_ctx)) return JS_EXCEPTION; if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { - if (flags & CALL_FLAG_GENERATOR) { + if (flags & JS_CALL_FLAG_GENERATOR) { JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); /* func_obj get contains a pointer to JSFuncAsyncState */ /* the stack frame is already allocated */ sf = &s->frame; p = JS_VALUE_GET_OBJ(sf->cur_func); b = p->u.func.function_bytecode; + ctx = b->realm; var_refs = p->u.func.var_refs; local_buf = arg_buf = sf->arg_buf; var_buf = sf->var_buf; @@ -14882,8 +16223,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; if (s->throw_flag) goto exception; else @@ -14894,29 +16235,18 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } p = JS_VALUE_GET_OBJ(func_obj); if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { - switch(p->class_id) { - case JS_CLASS_C_FUNCTION: - return js_call_c_function(ctx, func_obj, this_obj, argc, - (JSValueConst *)argv, flags); - case JS_CLASS_BOUND_FUNCTION: - return js_call_bound_function(ctx, func_obj, this_obj, argc, - (JSValueConst *)argv, flags); - default: - { - 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); - } + JSClassCall *call_func; + call_func = rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(caller_ctx, "not a function"); } + return call_func(caller_ctx, func_obj, this_obj, argc, + (JSValueConst *)argv, flags); } b = p->u.func.function_bytecode; - if (unlikely(argc < b->arg_count || (flags & CALL_FLAG_COPY_ARGV))) { + if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { arg_allocated_size = b->arg_count; } else { arg_allocated_size = 0; @@ -14924,8 +16254,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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); + if (js_check_stack_overflow(rt, alloca_size)) + return JS_ThrowStackOverflow(caller_ctx); sf->js_mode = b->js_mode; arg_buf = argv; @@ -14939,7 +16269,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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]); + arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); for(; i < b->arg_count; i++) arg_buf[i] = JS_UNDEFINED; sf->arg_count = b->arg_count; @@ -14954,8 +16284,10 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ctx = b->realm; /* set the current realm */ + restart: for(;;) { int call_argc; @@ -15381,7 +16713,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, /* 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"); + JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); goto exception; } sp[0] = JS_TRUE; @@ -15392,7 +16724,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, BREAK; CASE(OP_check_ctor): if (JS_IsUndefined(new_target)) { - JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); + JS_ThrowTypeError(caller_ctx, "class constructors must be invoked with 'new'"); goto exception; } BREAK; @@ -15412,11 +16744,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, JS_Throw(ctx, *--sp); goto exception; - CASE(OP_throw_var): + CASE(OP_throw_error): #define JS_THROW_VAR_RO 0 #define JS_THROW_VAR_REDECL 1 #define JS_THROW_VAR_UNINITIALIZED 2 -#define JS_THROW_VAR_DELETE_SUPER 3 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 { JSAtom atom; int type; @@ -15432,8 +16765,11 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, if (type == JS_THROW_VAR_UNINITIALIZED) JS_ThrowReferenceErrorUninitialized(ctx, atom); else - if (type == JS_THROW_VAR_DELETE_SUPER) + if (type == JS_THROW_ERROR_DELETE_SUPER) JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); + else + if (type == JS_THROW_ERROR_ITERATOR_THROW) + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); else JS_ThrowInternalError(ctx, "invalid throw var type %d", type); } @@ -15509,26 +16845,10 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } 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])); + proto = JS_GetPrototype(ctx, sp[-1]); if (JS_IsException(proto)) goto exception; JS_FreeValue(ctx, sp[-1]); @@ -15778,7 +17098,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, pc += 2; val = *var_refs[idx]->pvalue; if (unlikely(JS_IsUninitialized(val))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } sp[0] = JS_DupValue(ctx, val); @@ -15791,7 +17111,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } set_value(ctx, var_refs[idx]->pvalue, sp[-1]); @@ -15804,7 +17124,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); goto exception; } set_value(ctx, var_refs[idx]->pvalue, sp[-1]); @@ -15825,7 +17145,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); goto exception; } sp[0] = JS_DupValue(ctx, var_buf[idx]); @@ -15838,7 +17158,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, idx = get_u16(pc); pc += 2; if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized(ctx, JS_ATOM_NULL); + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); goto exception; } set_value(ctx, &var_buf[idx], sp[-1]); @@ -15892,7 +17212,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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); + free_var_ref(rt, var_ref); goto exception; } pr->u.var_ref = var_ref; @@ -16077,18 +17397,20 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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_check_object): + if (unlikely(!JS_IsObject(sp[-1]))) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto exception; + } + BREAK; CASE(OP_iterator_close): + /* iter_obj next catch_offset -> */ sp--; /* drop the catch offset to avoid getting caught by exception */ JS_FreeValue(ctx, sp[-1]); /* drop the next method */ sp--; @@ -16122,33 +17444,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } 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): + CASE(OP_iterator_next): /* stack: iter_obj next catch_offset val */ { JSValue ret; @@ -16161,25 +17457,28 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } BREAK; - CASE(OP_async_iterator_get): + CASE(OP_iterator_call): /* 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); + method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? + JS_ATOM_throw : JS_ATOM_return); if (JS_IsException(method)) goto exception; if (JS_IsUndefined(method) || JS_IsNull(method)) { ret_flag = TRUE; } else { - ret = JS_CallFree(ctx, method, sp[-4], - 1, (JSValueConst *)(sp - 1)); + if (flags & 2) { + /* no argument */ + ret = JS_CallFree(ctx, method, sp[-4], + 0, NULL); + } else { + ret = JS_CallFree(ctx, method, sp[-4], + 1, (JSValueConst *)(sp - 1)); + } if (JS_IsException(ret)) goto exception; JS_FreeValue(ctx, sp[-1]); @@ -16505,7 +17804,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, CASE(OP_put_ref_value): { - int ret; + int ret, flags; + flags = JS_PROP_THROW_STRICT; if (unlikely(JS_IsUndefined(sp[-3]))) { if (is_strict_mode(ctx)) { JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); @@ -16517,8 +17817,11 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } else { sp[-3] = JS_DupValue(ctx, ctx->global_obj); } + } else { + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; } - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); JS_FreeValue(ctx, sp[-3]); sp -= 3; if (unlikely(ret < 0)) @@ -16611,47 +17914,42 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, BREAK; CASE(OP_add_loc): { - JSValue ops[2]; + JSValue *pv; 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]))) { + pv = &var_buf[idx]; + if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { int64_t r; - r = (int64_t)JS_VALUE_GET_INT(ops[0]) + JS_VALUE_GET_INT(ops[1]); + r = (int64_t)JS_VALUE_GET_INT(*pv) + + JS_VALUE_GET_INT(sp[-1]); if (unlikely((int)r != r)) goto add_loc_slow; - var_buf[idx] = JS_NewInt32(ctx, r); + *pv = JS_NewInt32(ctx, r); sp--; - } else if (JS_VALUE_GET_TAG(ops[0]) == JS_TAG_STRING) { + } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { + JSValue op1; + op1 = sp[-1]; sp--; - ops[1] = JS_ToPrimitiveFree(ctx, ops[1], HINT_NONE); - if (JS_IsException(ops[1])) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) 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; + op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1); + if (JS_IsException(op1)) goto exception; - } - var_buf[idx] = ops[0]; + set_value(ctx, pv, op1); } else { + JSValue ops[2]; 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; + and ops[1], so we must duplicate *pv */ + ops[0] = JS_DupValue(ctx, *pv); + ops[1] = sp[-1]; + sp--; + if (js_add_slow(ctx, ops + 2)) goto exception; - } - var_buf[idx] = ops[0]; + set_value(ctx, pv, ops[0]); } } BREAK; @@ -16688,11 +17986,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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_bignum_mode(ctx))) - goto binary_arith_slow; -#else if (unlikely((int)r != r)) { +#ifdef CONFIG_BIGNUM + if (unlikely(sf->js_mode & JS_MODE_MATH) && + (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) + goto binary_arith_slow; +#endif d = (double)r; goto mul_fp_res; } @@ -16701,14 +18000,15 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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: +#ifdef CONFIG_BIGNUM + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; #endif + d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); + mul_fp_res: sp[-2] = __JS_NewFloat64(ctx, d); sp--; } else { @@ -16716,11 +18016,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } } BREAK; -#ifdef CONFIG_BIGNUM - CASE(OP_math_div): - CASE(OP_div): - goto binary_arith_slow; -#else CASE(OP_div): { JSValue op1, op2; @@ -16728,6 +18023,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, op2 = sp[-1]; if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { int v1, v2; + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; v1 = JS_VALUE_GET_INT(op1); v2 = JS_VALUE_GET_INT(op2); sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); @@ -16737,7 +18034,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } } BREAK; -#endif CASE(OP_mod): #ifdef CONFIG_BIGNUM CASE(OP_math_mod): @@ -16763,9 +18059,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } 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; @@ -16795,11 +18088,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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_bignum_mode(ctx))) - goto slow_neg; -#else /* Note: -0 cannot be expressed as integer */ if (unlikely(val == 0)) { d = -0.0; @@ -16809,18 +18097,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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; } @@ -16882,8 +18164,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, var_buf[idx] = JS_NewInt32(ctx, val + 1); } else { inc_loc_slow: - if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_inc)) + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = JS_DupValue(ctx, op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) goto exception; + set_value(ctx, &var_buf[idx], op1); } } BREAK; @@ -16903,8 +18189,12 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, var_buf[idx] = JS_NewInt32(ctx, val - 1); } else { dec_loc_slow: - if (js_unary_arith_slow(ctx, var_buf + idx + 1, OP_dec)) + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = JS_DupValue(ctx, op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) goto exception; + set_value(ctx, &var_buf[idx], op1); } } BREAK; @@ -16933,7 +18223,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, #ifdef CONFIG_BIGNUM { int64_t r; - if (is_bignum_mode(ctx)) { + if (unlikely(sf->js_mode & JS_MODE_MATH)) { if (v2 > 0x1f) goto shl_slow; r = (int64_t)v1 << v2; @@ -16989,7 +18279,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, v2 = JS_VALUE_GET_INT(op2); #ifdef CONFIG_BIGNUM if (unlikely(v2 > 0x1f)) { - if (is_bignum_mode(ctx)) + if (unlikely(sf->js_mode & JS_MODE_MATH)) goto sar_slow; else v2 &= 0x1f; @@ -17091,7 +18381,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, #ifdef CONFIG_BIGNUM CASE(OP_mul_pow10): - if (js_mul_pow10(ctx, sp)) + if (rt->bigfloat_ops.mul_pow10(ctx, sp)) goto exception; sp--; BREAK; @@ -17230,6 +18520,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, set_value(ctx, &sp[-1], val); break; case OP_with_put_var: + /* XXX: check if strict mode */ ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], JS_PROP_THROW_STRICT); JS_FreeValue(ctx, sp[-1]); @@ -17292,6 +18583,13 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 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) { @@ -17305,7 +18603,15 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } else { goto free_and_set_false; } - CASE(OP_is_function): + /* XXX: could merge to a single opcode */ + CASE(OP_typeof_is_undefined): + /* different from OP_is_undefined because of isHTMLDDA */ + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + CASE(OP_typeof_is_function): if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { goto free_and_set_true; } else { @@ -17313,6 +18619,7 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } free_and_set_true: JS_FreeValue(ctx, sp[-1]); +#endif set_true: sp[-1] = JS_TRUE; BREAK; @@ -17320,7 +18627,6 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, JS_FreeValue(ctx, sp[-1]); sp[-1] = JS_FALSE; BREAK; -#endif CASE(OP_invalid): DEFAULT: JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", @@ -17329,13 +18635,14 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, } } exception: - if (ctx->exception_needs_backtrace) { + if (is_backtrace_needed(ctx, rt->current_exception)) { /* add the backtrace information now (it is not done before if the exception happens in a bytecode operation */ - build_backtrace(ctx, ctx->current_exception, NULL, 0, pc); + sf->cur_pc = pc; + build_backtrace(ctx, rt->current_exception, NULL, 0, 0); } - if (!JS_IsUncatchableError(ctx, ctx->current_exception)) { + if (!JS_IsUncatchableError(ctx, rt->current_exception)) { while (sp > stack_buf) { JSValue val = *--sp; JS_FreeValue(ctx, val); @@ -17347,8 +18654,8 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, sp--; JS_IteratorClose(ctx, sp[-1], TRUE); } else { - *sp++ = ctx->current_exception; - ctx->current_exception = JS_NULL; + *sp++ = rt->current_exception; + rt->current_exception = JS_NULL; pc = b->byte_code_buf + pos; goto restart; } @@ -17367,14 +18674,14 @@ static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, done: if (unlikely(!list_empty(&sf->var_ref_list))) { /* variable references reference the stack: must close them */ - close_var_refs(ctx->rt, sf); + close_var_refs(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; + rt->current_stack_frame = sf->prev_frame; return ret_val; } @@ -17382,36 +18689,74 @@ 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, CALL_FLAG_COPY_ARGV); + 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, CALL_FLAG_COPY_ARGV); + 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) +/* warning: the refcount of the context is not incremented. Return + NULL in case of exception (case of revoked proxy only) */ +static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) { - 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); + JSObject *p; + JSContext *realm; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return ctx; + p = JS_VALUE_GET_OBJ(func_obj); + switch(p->class_id) { + case JS_CLASS_C_FUNCTION: + realm = p->u.cfunc.realm; + break; + case JS_CLASS_BYTECODE_FUNCTION: + case JS_CLASS_GENERATOR_FUNCTION: + case JS_CLASS_ASYNC_FUNCTION: + case JS_CLASS_ASYNC_GENERATOR_FUNCTION: + { + JSFunctionBytecode *b; + b = p->u.func.function_bytecode; + realm = b->realm; + } + break; + case JS_CLASS_PROXY: + { + JSProxyData *s = p->u.opaque; + if (!s) + return ctx; + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return NULL; + } else { + realm = JS_GetFunctionRealm(ctx, s->target); + } + } + break; + case JS_CLASS_BOUND_FUNCTION: + { + JSBoundFunction *bf = p->u.bound_function; + realm = JS_GetFunctionRealm(ctx, bf->func_obj); + } + break; + default: + realm = ctx; + break; } - return proto; + return realm; } static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, int class_id) { JSValue proto, obj; + JSContext *realm; + if (JS_IsUndefined(ctor)) { proto = JS_DupValue(ctx, ctx->class_proto[class_id]); } else { @@ -17420,14 +18765,10 @@ static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, 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]); + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) + return JS_EXCEPTION; + proto = JS_DupValue(ctx, realm->class_proto[class_id]); } } obj = JS_NewObjectProtoClass(ctx, proto, class_id); @@ -17435,7 +18776,7 @@ static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, return obj; } -/* argv[] is modified if (flags & CALL_FLAG_COPY_ARGV) = 0. */ +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ static JSValue JS_CallConstructorInternal(JSContext *ctx, JSValueConst func_obj, JSValueConst new_target, @@ -17446,27 +18787,21 @@ static JSValue JS_CallConstructorInternal(JSContext *ctx, if (js_poll_interrupts(ctx)) return JS_EXCEPTION; - flags |= CALL_FLAG_CONSTRUCTOR; + 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)) { - switch(p->class_id) { - case JS_CLASS_C_FUNCTION: - return js_call_c_function(ctx, func_obj, new_target, - argc, (JSValueConst *)argv, flags); - case JS_CLASS_BOUND_FUNCTION: - return js_call_bound_function(ctx, func_obj, new_target, argc, - (JSValueConst *)argv, flags); - case JS_CLASS_PROXY: - return js_proxy_call_constructor(ctx, func_obj, new_target, argc, - (JSValueConst *)argv); - default: + 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; @@ -17496,7 +18831,7 @@ JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, { return JS_CallConstructorInternal(ctx, func_obj, new_target, argc, (JSValue *)argv, - CALL_FLAG_COPY_ARGV); + JS_CALL_FLAG_COPY_ARGV); } JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, @@ -17504,7 +18839,7 @@ JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, { return JS_CallConstructorInternal(ctx, func_obj, func_obj, argc, (JSValue *)argv, - CALL_FLAG_COPY_ARGV); + JS_CALL_FLAG_COPY_ARGV); } JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, @@ -17605,10 +18940,13 @@ static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) { JSValue func_obj; + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + /* 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, CALL_FLAG_GENERATOR); + s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR); } @@ -17673,13 +19011,11 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, 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: @@ -17691,85 +19027,16 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, } 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) { + if (magic == GEN_MAGIC_THROW && + s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { 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[0] = JS_NewInt32(ctx, magic); sf->cur_sp++; exec_no_arg: s->func_state.throw_flag = FALSE; @@ -17783,17 +19050,14 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, return func_ret; } if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + /* get the returned yield value at the top of the stack */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { - /* 'yield *' */ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; - iter_args[0] = JS_UNDEFINED; - argc = 1; - argv = iter_args; - goto redo; + /* return (value, done) object */ + *pdone = 2; } 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 { @@ -17829,7 +19093,8 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, + int flags) { JSValue obj, func_ret; JSGeneratorData *s; @@ -17862,8 +19127,7 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, /* AsyncFunction */ -static void js_async_function_terminate(JSRuntime *rt, JSContext *ctx, - JSAsyncFunctionData *s) +static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) { if (s->is_active) { async_func_free(rt, &s->func_state); @@ -17871,15 +19135,19 @@ static void js_async_function_terminate(JSRuntime *rt, JSContext *ctx, } } -/* ctx can be NULL */ -static void js_async_function_free(JSRuntime *rt, JSContext *ctx, - JSAsyncFunctionData *s) +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_terminate(rt, ctx, s); - JS_FreeValueRT(rt, s->resolving_funcs[0]); - JS_FreeValueRT(rt, s->resolving_funcs[1]); - js_free_rt(rt, s); + js_async_function_free0(rt, s); } } @@ -17888,7 +19156,7 @@ 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, NULL, s); + js_async_function_free(rt, s); } } @@ -17898,7 +19166,7 @@ static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, JSObject *p = JS_VALUE_GET_OBJ(val); JSAsyncFunctionData *s = p->u.async_function_data; if (s) { - JS_MarkValue(rt, JS_MKPTR(JS_TAG_ASYNC_FUNCTION, s), mark_func); + mark_func(rt, &s->header); } } @@ -17937,7 +19205,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, 1, (JSValueConst *)&error); JS_FreeValue(ctx, error); - js_async_function_terminate(ctx->rt, ctx, s); + js_async_function_terminate(ctx->rt, s); JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ } else { JSValue value; @@ -17949,7 +19217,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) 1, (JSValueConst *)&value); JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, value); - js_async_function_terminate(ctx->rt, ctx, s); + js_async_function_terminate(ctx->rt, s); } else { JSValue promise, resolving_funcs[2], resolving_funcs1[2]; int i, res; @@ -17985,7 +19253,8 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) static JSValue js_async_function_resolve_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, + int flags) { JSObject *p = JS_VALUE_GET_OBJ(func_obj); JSAsyncFunctionData *s = p->u.async_function_data; @@ -18009,7 +19278,7 @@ static JSValue js_async_function_resolve_call(JSContext *ctx, static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, int flags) { JSValue promise; JSAsyncFunctionData *s; @@ -18018,7 +19287,7 @@ static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, if (!s) return JS_EXCEPTION; s->header.ref_count = 1; - s->gc_header.mark = 0; + 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; @@ -18030,14 +19299,14 @@ static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, 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, ctx, s); + 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, ctx, s); + js_async_function_free(ctx->rt, s); return promise; } @@ -18439,7 +19708,8 @@ static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, + int flags) { JSValue obj, func_ret; JSAsyncGeneratorData *s; @@ -18496,6 +19766,9 @@ enum { TOK_MATH_POW_ASSIGN, #endif TOK_POW_ASSIGN, + TOK_LAND_ASSIGN, + TOK_LOR_ASSIGN, + TOK_DOUBLE_QUESTION_MARK_ASSIGN, TOK_DEC, TOK_INC, TOK_SHL, @@ -18517,6 +19790,8 @@ enum { TOK_POW, TOK_ARROW, TOK_ELLIPSIS, + TOK_DOUBLE_QUESTION_MARK, + TOK_QUESTION_MARK_DOT, TOK_ERROR, TOK_PRIVATE_NAME, TOK_EOF, @@ -18593,15 +19868,15 @@ typedef struct BlockEnv { int has_iterator; } BlockEnv; -typedef struct JSHoistedDef { - int cpool_idx; /* -1 means variable global definition */ - uint8_t force_init : 1; /* initialize to undefined */ +typedef struct JSGlobalVar { + int cpool_idx; /* if >= 0, index in the constant pool for hoisted + function defintion*/ + uint8_t force_init : 1; /* force initialization to undefined */ uint8_t is_lexical : 1; /* global let/const definition */ uint8_t is_const : 1; /* const definition */ - int 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; + JSAtom var_name; /* variable name */ +} JSGlobalVar; typedef struct RelocEntry { struct RelocEntry *next; @@ -18664,6 +19939,7 @@ typedef struct JSFunctionDef { BOOL has_home_object; /* TRUE if the home object is available */ BOOL has_prototype; /* true if a prototype field is necessary */ BOOL has_simple_parameter_list; + BOOL has_parameter_expressions; /* if true, an argument scope is created */ BOOL has_use_strict; /* to reject directive in special cases */ BOOL has_eval_call; /* true if the function contains a call to eval() */ BOOL has_arguments_binding; /* true if the 'arguments' binding is @@ -18677,6 +19953,7 @@ typedef struct JSFunctionDef { BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ BOOL is_derived_class_constructor; BOOL in_function_body; + BOOL backtrace_barrier; JSFunctionKindEnum func_kind : 8; JSParseFunctionEnum func_type : 8; uint8_t js_mode; /* bitmap of JS_MODE_x */ @@ -18690,7 +19967,10 @@ typedef struct JSFunctionDef { int arg_count; /* number of arguments */ int defined_arg_count; int var_object_idx; /* -1 if none */ + int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ int arguments_var_idx; /* -1 if none */ + int arguments_arg_idx; /* argument variable definition in argument scope, + -1 if none */ int func_var_idx; /* variable containing the current function (-1 if none, only used if is_func_expr is true) */ int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ @@ -18706,10 +19986,11 @@ typedef struct JSFunctionDef { int scope_count; /* number of entries used in the fd->scopes array */ JSVarScope *scopes; JSVarScope def_scope_array[4]; + int body_scope; /* scope of the body of the function or eval */ - int hoisted_def_count; - int hoisted_def_size; - JSHoistedDef *hoisted_def; + int global_var_count; + int global_var_size; + JSGlobalVar *global_vars; DynBuf byte_code; int last_opcode_pos; /* -1 if no last opcode */ @@ -18723,8 +20004,8 @@ typedef struct JSFunctionDef { /* constant pool (strings, functions, numbers) */ JSValue *cpool; - uint32_t cpool_count; - uint32_t cpool_size; + int cpool_count; + int cpool_size; /* list of variables in the closure */ int closure_var_count; @@ -18794,6 +20075,7 @@ typedef struct JSParseState { JSFunctionDef *cur_func; BOOL is_module; /* parsing a module */ BOOL allow_html_comments; + BOOL ext_json; /* true if accepting JSON superset */ } JSParseState; typedef struct JSOpCode { @@ -18851,11 +20133,14 @@ static void free_token(JSParseState *s, JSToken *token) 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: + if (token->val >= TOK_FIRST_KEYWORD && + token->val <= TOK_LAST_KEYWORD) { + JS_FreeAtom(s->ctx, token->u.ident.atom); + } break; } } @@ -18925,12 +20210,16 @@ int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const { JSContext *ctx = s->ctx; va_list ap; - + int backtrace_flags; + va_start(ap, fmt); - JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); + JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); va_end(ap); - - build_backtrace(ctx, ctx->current_exception, s->filename, s->line_num, NULL); + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num, + backtrace_flags); return -1; } @@ -19065,6 +20354,8 @@ static __exception int js_parse_string(JSParseState *s, int sep, } if (c == '\\') { c = *p; + /* XXX: need a specific JSON case to avoid + accepting invalid escapes */ switch(c) { case '\0': if (p >= s->buf_end) @@ -19088,18 +20379,23 @@ static __exception int js_parse_string(JSParseState *s, int sep, s->line_num++; continue; default: - if (c >= '0' && c <= '7') { + if (c >= '0' && c <= '9') { if (!s->cur_func) - goto invalid_octal; /* JSON case */ + goto invalid_escape; /* JSON case */ if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') goto parse_escape; if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { p++; c = '\0'; } else { - invalid_octal: - if (do_throw) - js_parse_error(s, "invalid octal syntax in strict mode"); + if (c >= '8' || sep == '`') { + /* Note: according to ES2021, \8 and \9 are not + accepted in strict mode or in templates. */ + goto invalid_escape; + } else { + if (do_throw) + js_parse_error(s, "octal escape sequences are not allowed in strict mode"); + } goto fail; } } else if (c >= 0x80) { @@ -19116,6 +20412,7 @@ static __exception int js_parse_string(JSParseState *s, int sep, parse_escape: ret = lre_parse_escape(&p, TRUE); if (ret == -1) { + invalid_escape: if (do_throw) js_parse_error(s, "malformed escape sequence in string literal"); goto fail; @@ -19259,13 +20556,93 @@ static __exception int js_parse_regexp(JSParseState *s) return -1; } +static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, + char *static_buf) +{ + char *buf, *new_buf; + size_t size, new_size; + + buf = *pbuf; + size = *psize; + if (size >= (SIZE_MAX / 3) * 2) + new_size = SIZE_MAX; + else + new_size = size + (size >> 1); + if (buf == static_buf) { + new_buf = js_malloc(ctx, new_size); + if (!new_buf) + return -1; + memcpy(new_buf, buf, size); + } else { + new_buf = js_realloc(ctx, buf, new_size); + if (!new_buf) + return -1; + } + *pbuf = new_buf; + *psize = new_size; + return 0; +} + +/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, + BOOL *pident_has_escape, int c, BOOL is_private) +{ + const uint8_t *p, *p1; + char ident_buf[128], *buf; + size_t ident_size, ident_pos; + JSAtom atom; + + p = *pp; + buf = ident_buf; + ident_size = sizeof(ident_buf); + ident_pos = 0; + if (is_private) + buf[ident_pos++] = '#'; + for(;;) { + p1 = p; + + if (c < 128) { + buf[ident_pos++] = c; + } else { + ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c); + } + c = *p1++; + if (c == '\\' && *p1 == 'u') { + c = lre_parse_escape(&p1, TRUE); + *pident_has_escape = TRUE; + } else if (c >= 128) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); + } + if (!lre_js_is_ident_next(c)) + break; + p = p1; + if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { + if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { + atom = JS_ATOM_NULL; + goto done; + } + } + } + atom = JS_NewAtomLen(s->ctx, buf, ident_pos); + done: + if (unlikely(buf != ident_buf)) + js_free(s->ctx, buf); + *pp = p; + return atom; +} + + static __exception int next_token(JSParseState *s) { const uint8_t *p; int c; - char buf[4096], *q; BOOL ident_has_escape; - + JSAtom atom; + + if (js_check_stack_overflow(s->ctx->rt, 0)) { + return js_parse_error(s, "stack overflow"); + } + free_token(s, &s->token); p = s->last_ptr = s->buf_ptr; @@ -19277,23 +20654,18 @@ static __exception int next_token(JSParseState *s) c = *p; switch(c) { case 0: - s->token.val = TOK_EOF; - break; - case '`': - if (!s->cur_func) { - /* JSON does not accept templates */ + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { goto def_token; } + break; + case '`': if (js_parse_template_part(s, p + 1)) goto fail; p = s->buf_ptr; break; case '\'': - 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; @@ -19311,11 +20683,6 @@ static __exception int next_token(JSParseState *s) goto redo; case '\f': case '\v': - if (!s->cur_func) { - /* JSONWhitespace does not match , nor */ - goto def_token; - } - /* fall through */ case ' ': case '\t': p++; @@ -19344,6 +20711,8 @@ static __exception int next_token(JSParseState *s) c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); if (c == CP_LS || c == CP_PS) { s->got_lf = TRUE; /* considered as LF for ASI */ + } else if (c == -1) { + p++; /* skip invalid UTF-8 */ } } else { p++; @@ -19362,8 +20731,11 @@ static __exception int next_token(JSParseState *s) 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) + if (c == CP_LS || c == CP_PS) { break; + } else if (c == -1) { + p++; /* skip invalid UTF-8 */ + } } else { p++; } @@ -19391,55 +20763,43 @@ static __exception int next_token(JSParseState *s) } } goto def_token; - case 'a' ... 'z': - case 'A' ... 'Z': + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': case '_': case '$': /* identifier */ p++; ident_has_escape = FALSE; has_ident: - 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); + atom = parse_ident(s, &p, &ident_has_escape, c, FALSE); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; s->token.u.ident.has_escape = ident_has_escape; s->token.u.ident.is_reserved = FALSE; 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->js_mode & JS_MODE_STRICT)) || + (s->token.u.ident.atom == JS_ATOM_yield && ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && !s->cur_func->in_function_body && s->cur_func->parent && (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || (s->token.u.ident.atom == JS_ATOM_await && (s->is_module || - (s->cur_func && - ((s->cur_func->func_kind & JS_FUNC_ASYNC) || + (((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))))))) { @@ -19459,8 +20819,6 @@ static __exception int next_token(JSParseState *s) { const uint8_t *p1; p++; - q = buf; - *q++ = '#'; p1 = p; c = *p1++; if (c == '\\' && *p1 == 'u') { @@ -19473,31 +20831,11 @@ static __exception int next_token(JSParseState *s) 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); + ident_has_escape = FALSE; /* not used */ + atom = parse_ident(s, &p, &ident_has_escape, c, TRUE); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; s->token.val = TOK_PRIVATE_NAME; } break; @@ -19514,122 +20852,52 @@ static __exception int next_token(JSParseState *s) } 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))) { + /* in strict mode, octal literals are not accepted */ + if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) { js_parse_error(s, "octal literals are deprecated in strict mode"); goto fail; } goto parse_number; - case '1' ... '9': + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': /* number */ parse_number: -#ifdef CONFIG_BIGNUM - { - const uint8_t *p1; - int flags; - slimb_t exponent; - JSValue val; - - s->token.val = TOK_NUMBER; - s->token.u.num.val = JS_UNDEFINED; - s->token.u.num.exponent = 0; - if (!s->cur_func) { - /* JSON: use current float precision */ - flags = BF_ATOF_THROW; - if (is_bignum_mode(s->ctx)) - flags |= BF_ATOF_INT_PREC_INF; - else - flags |= BF_ATOF_FLOAT64; - val = js_atof(s->ctx, (const char *)p, (const char **)&p, - 10, flags); - if (JS_IsException(val)) - goto bad_number; - } else { - bf_t r_s, *r = &r_s; - BOOL bigint_mode, is_bigint, is_bigfloat; - int res; - - bigint_mode = (s->cur_func->js_mode & JS_MODE_BIGINT) != 0; - flags = BF_ATOF_BIN_OCT | BF_ATOF_LEGACY_OCTAL | - BF_ATOF_UNDERSCORE_SEP | BF_ATOF_INT_PREC_INF | BF_RNDZ; - if (!bigint_mode) - flags |= BF_ATOF_ONLY_DEC_FLOAT; - p += skip_spaces((char *)p); - bf_init(s->ctx->bf_ctx, r); - res = bf_atof2(r, &exponent, (const char *)p, - (const char **)&p, 0, BF_PREC_INF, flags); - if (bf_is_nan(r)) { - bf_delete(r); - goto bad_number; - } - is_bigint = FALSE; - is_bigfloat = FALSE; - if (*p == 'n') { - if (!(res & BF_ATOF_ST_INTEGER) || - (res & BF_ATOF_ST_LEGACY_OCTAL)) { - bf_delete(r); - goto bad_number; - } - p++; - is_bigint = TRUE; - } else if (*p == 'l') { - p++; - is_bigfloat = TRUE; - } - if ((res & BF_ATOF_ST_INTEGER) && !is_bigfloat && - (bigint_mode || is_bigint)) { - /* in bigint mode: small integer or bigint - in legacy mode: always a bigint */ - val = JS_NewBigInt2(s->ctx, r, bigint_mode ^ 1); - } else { - /* `n` suffix is implied in math mode */ - if (s->cur_func->js_mode & JS_MODE_MATH) - is_bigfloat = TRUE; - if (is_bigfloat) { - /* infinite precision */ - val = JS_NewBigFloat(s->ctx, r); - s->token.u.num.exponent = exponent; - } else { - double d; - d = bf_mul_pow10_to_float64(s->ctx, r, exponent); - bf_delete(r); - val = JS_NewFloat64(s->ctx, d); - } - } - } - s->token.u.num.val = val; - if (lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) { - bad_number: - js_parse_error(s, "invalid number literal"); - goto fail; - } - } -#else { JSValue ret; const uint8_t *p1; int flags, radix; - if (!s->cur_func) { - flags = ATOD_THROW; - radix = 10; - } else { - flags = ATOD_ACCEPT_BIN_OCT | ATOD_THROW | ATOD_ACCEPT_LEGACY_OCTAL | - ATOD_ACCEPT_UNDERSCORES; - radix = 0; + 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_MATH) { + flags |= ATOD_MODE_BIGINT; + if (s->cur_func->js_mode & JS_MODE_MATH) + flags |= ATOD_TYPE_BIG_FLOAT; } - ret = js_atod(s->ctx, (const char *)p, (const char **)&p, radix, +#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_IsException(ret) || + 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; } -#endif break; case '*': if (p[1] == '=') { @@ -19762,8 +21030,13 @@ static __exception int next_token(JSParseState *s) p += 2; s->token.val = TOK_AND_ASSIGN; } else if (p[1] == '&') { - p += 2; - s->token.val = TOK_LAND; + if (p[2] == '=') { + p += 3; + s->token.val = TOK_LAND_ASSIGN; + } else { + p += 2; + s->token.val = TOK_LAND; + } } else { goto def_token; } @@ -19774,7 +21047,7 @@ static __exception int next_token(JSParseState *s) case '^': if (p[1] == '=') { p += 2; - if (s->cur_func && (s->cur_func->js_mode & JS_MODE_MATH)) + if (s->cur_func->js_mode & JS_MODE_MATH) s->token.val = TOK_MATH_POW_ASSIGN; else s->token.val = TOK_XOR_ASSIGN; @@ -19788,7 +21061,7 @@ static __exception int next_token(JSParseState *s) } } else { p++; - if (s->cur_func && (s->cur_func->js_mode & JS_MODE_MATH)) + if (s->cur_func->js_mode & JS_MODE_MATH) s->token.val = TOK_MATH_POW; else s->token.val = '^'; @@ -19809,8 +21082,29 @@ static __exception int next_token(JSParseState *s) p += 2; s->token.val = TOK_OR_ASSIGN; } else if (p[1] == '|') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_LOR_ASSIGN; + } else { + p += 2; + s->token.val = TOK_LOR; + } + } else { + goto def_token; + } + break; + case '?': + if (p[1] == '?') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN; + } else { + p += 2; + s->token.val = TOK_DOUBLE_QUESTION_MARK; + } + } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) { p += 2; - s->token.val = TOK_LOR; + s->token.val = TOK_QUESTION_MARK_DOT; } else { goto def_token; } @@ -19822,22 +21116,12 @@ static __exception int next_token(JSParseState *s) switch(c) { case CP_PS: case CP_LS: - if (!s->cur_func) { - /* and are not JSONWhitespace */ - goto def_token; - } else { - /* XXX: should avoid incrementing line_number, but - needed to handle HTML comments */ - goto line_terminator; - } + /* XXX: should avoid incrementing line_number, but + needed to handle HTML comments */ + goto line_terminator; default: if (lre_is_space(c)) { - if (!s->cur_func) { - /* category z spaces are not JSONWhitespace */ - goto def_token; - } else { - goto redo; - } + goto redo; } else if (lre_js_is_ident_first(c)) { ident_has_escape = FALSE; goto has_ident; @@ -19862,6 +21146,234 @@ static __exception int next_token(JSParseState *s) return -1; } +/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) +{ + const uint8_t *p; + char ident_buf[128], *buf; + size_t ident_size, ident_pos; + JSAtom atom; + + p = *pp; + buf = ident_buf; + ident_size = sizeof(ident_buf); + ident_pos = 0; + for(;;) { + buf[ident_pos++] = c; + c = *p; + if (c >= 128 || + !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1)) + break; + p++; + if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { + if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { + atom = JS_ATOM_NULL; + goto done; + } + } + } + atom = JS_NewAtomLen(s->ctx, buf, ident_pos); + done: + if (unlikely(buf != ident_buf)) + js_free(s->ctx, buf); + *pp = p; + return atom; +} + +static __exception int json_next_token(JSParseState *s) +{ + const uint8_t *p; + int c; + JSAtom atom; + + if (js_check_stack_overflow(s->ctx->rt, 0)) { + return js_parse_error(s, "stack overflow"); + } + + free_token(s, &s->token); + + p = s->last_ptr = s->buf_ptr; + s->last_line_num = s->token.line_num; + redo: + s->token.line_num = s->line_num; + s->token.ptr = p; + c = *p; + switch(c) { + case 0: + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { + goto def_token; + } + break; + case '\'': + if (!s->ext_json) { + /* JSON does not accept single quoted strings */ + goto def_token; + } + /* fall through */ + case '\"': + if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) + goto fail; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + p++; + s->line_num++; + goto redo; + case '\f': + case '\v': + if (!s->ext_json) { + /* JSONWhitespace does not match , nor */ + goto def_token; + } + /* fall through */ + case ' ': + case '\t': + p++; + goto redo; + case '/': + if (!s->ext_json) { + /* JSON does not accept comments */ + goto def_token; + } + if (p[1] == '*') { + /* comment */ + p += 2; + for(;;) { + if (*p == '\0' && p >= s->buf_end) { + js_parse_error(s, "unexpected end of comment"); + goto fail; + } + if (p[0] == '*' && p[1] == '/') { + p += 2; + break; + } + if (*p == '\n') { + s->line_num++; + p++; + } else if (*p == '\r') { + p++; + } else if (*p >= 0x80) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + if (c == -1) { + p++; /* skip invalid UTF-8 */ + } + } else { + p++; + } + } + goto redo; + } else if (p[1] == '/') { + /* line comment */ + p += 2; + for(;;) { + if (*p == '\0' && p >= s->buf_end) + break; + if (*p == '\r' || *p == '\n') + break; + if (*p >= 0x80) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + break; + } else if (c == -1) { + p++; /* skip invalid UTF-8 */ + } + } else { + p++; + } + } + goto redo; + } else { + goto def_token; + } + break; + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': + case '$': + /* identifier : only pure ascii characters are accepted */ + p++; + atom = json_parse_ident(s, &p, c); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.u.ident.has_escape = FALSE; + s->token.u.ident.is_reserved = FALSE; + s->token.val = TOK_IDENT; + break; + case '+': + if (!s->ext_json || !is_digit(p[1])) + goto def_token; + goto parse_number; + case '0': + if (is_digit(p[1])) + goto def_token; + goto parse_number; + case '-': + if (!is_digit(p[1])) + goto def_token; + goto parse_number; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + /* number */ + parse_number: + { + JSValue ret; + int flags, radix; + if (!s->ext_json) { + flags = 0; + radix = 10; + } else { + flags = ATOD_ACCEPT_BIN_OCT; + radix = 0; + } + ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix, + flags); + if (JS_IsException(ret)) + goto fail; + s->token.val = TOK_NUMBER; + s->token.u.num.val = ret; + } + break; + default: + if (c >= 128) { + js_parse_error(s, "unexpected character"); + goto fail; + } + def_token: + s->token.val = c; + p++; + break; + } + s->buf_ptr = p; + + // dump_token(s, &s->token); + return 0; + + fail: + s->token.val = TOK_ERROR; + return -1; +} + /* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is only set if TOK_IMPORT is returned */ /* XXX: handle all unicode cases */ @@ -19922,6 +21434,12 @@ static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) } } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) { return TOK_OF; + } else if (c == 'e' && + p[0] == 'x' && p[1] == 'p' && p[2] == 'o' && + p[3] == 'r' && p[4] == 't' && + !lre_js_is_ident_next(p[5])) { + *pp = p + 5; + return TOK_EXPORT; } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' && p[2] == 'c' && p[3] == 't' && p[4] == 'i' && p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) { @@ -19945,16 +21463,21 @@ static int peek_token(JSParseState *s, BOOL no_line_terminator) (heuristic). 'input' must be a zero terminated. Heuristic: skip comments and expect 'import' keyword not followed - by '(' or '.' + by '(' or '.' or export keyword. */ BOOL JS_DetectModule(const char *input, size_t input_len) { const uint8_t *p = (const uint8_t *)input; int tok; - if (simple_next_token(&p, FALSE) != TOK_IMPORT) + switch(simple_next_token(&p, FALSE)) { + case TOK_IMPORT: + tok = simple_next_token(&p, FALSE); + return (tok != '.' && tok != '('); + case TOK_EXPORT: + return TRUE; + default: return FALSE; - tok = simple_next_token(&p, FALSE); - return (tok != '.' && tok != '('); + } } static inline int get_prev_opcode(JSFunctionDef *fd) { @@ -19972,7 +21495,7 @@ static BOOL js_is_live_code(JSParseState *s) { case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_goto: #if SHORT_OPCODES case OP_goto8: @@ -20038,20 +21561,10 @@ static int new_label_fd(JSFunctionDef *fd, int label) LabelSlot *ls; if (label < 0) { - if (fd->label_count >= fd->label_size) { - int new_size; - size_t slack; - LabelSlot *new_tab; - - /* XXX: potential arithmetic overflow */ - new_size = fd->label_size * 3 / 2 + 4; - new_tab = js_realloc2(fd->ctx, fd->label_slots, new_size * sizeof(*new_tab), &slack); - if (!new_tab) - return -1; - new_size += slack / sizeof(*new_tab); - fd->label_slots = new_tab; - fd->label_size = new_size; - } + if (js_resize_array(fd->ctx, (void *)&fd->label_slots, + sizeof(fd->label_slots[0]), + &fd->label_size, fd->label_count + 1)) + return -1; label = fd->label_count++; ls = &fd->label_slots[label]; ls->ref_count = 0; @@ -20099,19 +21612,10 @@ static int emit_goto(JSParseState *s, int opcode, int label) static int cpool_add(JSParseState *s, JSValue val) { JSFunctionDef *fd = s->cur_func; - if (fd->cpool_count >= fd->cpool_size) { - int new_size; - size_t slack; - JSValue *new_tab; - /* XXX: potential arithmetic overflow */ - new_size = max_int(fd->cpool_count + 1, fd->cpool_size * 3 / 2); - new_tab = js_realloc2(s->ctx, fd->cpool, new_size * sizeof(JSValue), &slack); - if (!new_tab) - return -1; - new_size += slack / sizeof(*new_tab); - fd->cpool = new_tab; - fd->cpool_size = new_size; - } + + if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]), + &fd->cpool_size, fd->cpool_count + 1)) + return -1; fd->cpool[fd->cpool_count++] = val; return fd->cpool_count - 1; } @@ -20163,6 +21667,21 @@ static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) return find_arg(ctx, fd, name); } +/* find a variable declaration in a given scope */ +static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd, + JSAtom name, int scope_level) +{ + int scope_idx; + for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0; + scope_idx = fd->vars[scope_idx].scope_next) { + if (fd->vars[scope_idx].scope_level != scope_level) + break; + if (fd->vars[scope_idx].var_name == name) + return scope_idx; + } + return -1; +} + /* return true if scope == parent_scope or if scope is a child of parent_scope */ static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd, @@ -20184,7 +21703,7 @@ static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, for(i = 0; i < fd->var_count; i++) { JSVarDef *vd = &fd->vars[i]; if (vd->var_name == name && vd->scope_level == 0) { - if (is_child_scope(ctx, fd, vd->func_pool_or_scope_idx, + if (is_child_scope(ctx, fd, vd->scope_next, scope_level)) return i; } @@ -20193,11 +21712,11 @@ static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, } -static JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name) +static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name) { int i; - for(i = 0; i < fd->hoisted_def_count; i++) { - JSHoistedDef *hf = &fd->hoisted_def[i]; + for(i = 0; i < fd->global_var_count; i++) { + JSGlobalVar *hf = &fd->global_vars[i]; if (hf->var_name == name) return hf; } @@ -20205,9 +21724,9 @@ static JSHoistedDef *find_hoisted_def(JSFunctionDef *fd, JSAtom name) } -static JSHoistedDef *find_lexical_hoisted_def(JSFunctionDef *fd, JSAtom name) +static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name) { - JSHoistedDef *hf = find_hoisted_def(fd, name); + JSGlobalVar *hf = find_global_var(fd, name); if (hf && hf->is_lexical) return hf; else @@ -20227,7 +21746,7 @@ static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name, } if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) { - if (find_lexical_hoisted_def(fd, name)) + if (find_lexical_global_var(fd, name)) return GLOBAL_VAR_OFFSET; } return -1; @@ -20310,21 +21829,13 @@ static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) JS_ThrowInternalError(ctx, "too many local variables"); return -1; } - if ((fd->var_count + 1) > fd->var_size) { - int new_size; - size_t slack; - JSVarDef *new_buf; - new_size = max_int(fd->var_count + 1, fd->var_size * 3 / 2); - new_buf = js_realloc2(ctx, fd->vars, new_size * sizeof(*fd->vars), &slack); - if (!new_buf) - return -1; - new_size += slack / sizeof(*new_buf); - fd->vars = new_buf; - fd->var_size = new_size; - } + if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]), + &fd->var_size, fd->var_count + 1)) + return -1; vd = &fd->vars[fd->var_count++]; memset(vd, 0, sizeof(*vd)); vd->var_name = JS_DupAtom(ctx, name); + vd->func_pool_idx = -1; return fd->var_count - 1; } @@ -20348,22 +21859,47 @@ static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) int idx = fd->func_var_idx; if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { fd->func_var_idx = idx; - fd->vars[idx].is_func_var = TRUE; + fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME; if (fd->js_mode & JS_MODE_STRICT) fd->vars[idx].is_const = TRUE; } return idx; } -static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) +static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd) { int idx = fd->arguments_var_idx; - if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { + if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) { fd->arguments_var_idx = idx; } return idx; } +/* add an argument definition in the argument scope. Only needed when + "eval()" may be called in the argument scope. Return 0 if OK. */ +static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd) +{ + int idx; + if (fd->arguments_arg_idx < 0) { + idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX); + if (idx < 0) { + /* XXX: the scope links are not fully updated. May be an + issue if there are child scopes of the argument + scope */ + idx = add_var(ctx, fd, JS_ATOM_arguments); + if (idx < 0) + return -1; + fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first; + fd->scopes[ARG_SCOPE_INDEX].first = idx; + fd->vars[idx].scope_level = ARG_SCOPE_INDEX; + fd->vars[idx].is_lexical = TRUE; + + fd->arguments_arg_idx = idx; + } + } + return 0; +} + static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) { JSVarDef *vd; @@ -20373,56 +21909,33 @@ static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) JS_ThrowInternalError(ctx, "too many arguments"); return -1; } - if ((fd->arg_count + 1) > fd->arg_size) { - int new_size; - size_t slack; - JSVarDef *new_buf; - new_size = max_int(fd->arg_count + 1, fd->arg_size * 3 / 2); - new_buf = js_realloc2(ctx, fd->args, new_size * sizeof(*fd->args), &slack); - if (!new_buf) - return -1; - new_size += slack / sizeof(*new_buf); - fd->args = new_buf; - fd->arg_size = new_size; - } + if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]), + &fd->arg_size, fd->arg_count + 1)) + return -1; vd = &fd->args[fd->arg_count++]; memset(vd, 0, sizeof(*vd)); vd->var_name = JS_DupAtom(ctx, name); + vd->func_pool_idx = -1; return fd->arg_count - 1; } -/* add a Hoisted definition for a function (cpool_idx >= 0) or a - global variable (cpool_idx = -1) */ -static JSHoistedDef *add_hoisted_def(JSContext *ctx, - JSFunctionDef *s, int cpool_idx, - JSAtom name, int var_idx, BOOL is_lexical) +/* add a global variable definition */ +static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s, + JSAtom name) { - JSHoistedDef *hf; + JSGlobalVar *hf; - if (s->hoisted_def_count >= s->hoisted_def_size) { - int new_size; - size_t slack; - JSHoistedDef *new_tab; - new_size = max_int(s->hoisted_def_count + 1, - s->hoisted_def_size * 3 / 2); - new_tab = js_realloc2(ctx, s->hoisted_def, new_size * sizeof(s->hoisted_def[0]), &slack); - if (!new_tab) - return NULL; - new_size += slack / sizeof(*new_tab); - s->hoisted_def = new_tab; - s->hoisted_def_size = new_size; - } - hf = &s->hoisted_def[s->hoisted_def_count++]; - hf->cpool_idx = cpool_idx; - hf->force_init = 0; - hf->is_lexical = is_lexical; + if (js_resize_array(ctx, (void **)&s->global_vars, + sizeof(s->global_vars[0]), + &s->global_var_size, s->global_var_count + 1)) + return NULL; + hf = &s->global_vars[s->global_var_count++]; + hf->cpool_idx = -1; + hf->force_init = FALSE; + hf->is_lexical = FALSE; hf->is_const = FALSE; - hf->var_idx = var_idx; hf->scope_level = s->scope_level; - hf->var_name = JS_ATOM_NULL; - if (name != JS_ATOM_NULL) { - hf->var_name = JS_DupAtom(ctx, name); - } + hf->var_name = JS_DupAtom(ctx, name); return hf; } @@ -20467,7 +21980,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, goto redef_lex_error; } } else { - if (fd->scope_level == 1) { + if (fd->scope_level == fd->body_scope) { redef_lex_error: /* redefining a scoped var in the same scope: error */ return js_parse_error(s, "invalid redefinition of lexical identifier"); @@ -20476,11 +21989,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, } if (var_def_type != JS_VAR_DEF_FUNCTION_DECL && var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL && - (fd->func_kind == JS_FUNC_ASYNC || - fd->func_kind == JS_FUNC_GENERATOR || - fd->func_kind == JS_FUNC_ASYNC_GENERATOR || - fd->func_type == JS_PARSE_FUNC_METHOD || - fd->scope_level == 1) && + fd->scope_level == fd->body_scope && find_arg(ctx, fd, name) >= 0) { /* lexical variable redefines a parameter name */ return js_parse_error(s, "invalid redefinition of parameter name"); @@ -20491,8 +22000,8 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, } if (fd->is_global_var) { - JSHoistedDef *hf; - hf = find_hoisted_def(fd, name); + JSGlobalVar *hf; + hf = find_global_var(fd, name); if (hf && is_child_scope(ctx, fd, hf->scope_level, fd->scope_level)) { return js_parse_error(s, "invalid redefinition of global identifier"); @@ -20502,11 +22011,12 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, if (fd->is_eval && (fd->eval_type == JS_EVAL_TYPE_GLOBAL || fd->eval_type == JS_EVAL_TYPE_MODULE) && - fd->scope_level == 1) { - JSHoistedDef *hf; - hf = add_hoisted_def(s->ctx, fd, -1, name, -1, TRUE); + fd->scope_level == fd->body_scope) { + JSGlobalVar *hf; + hf = add_global_var(s->ctx, fd, name); if (!hf) return -1; + hf->is_lexical = TRUE; hf->is_const = (var_def_type == JS_VAR_DEF_CONST); idx = GLOBAL_VAR_OFFSET; } else { @@ -20538,13 +22048,13 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, return js_parse_error(s, "invalid redefinition of lexical identifier"); } if (fd->is_global_var) { - JSHoistedDef *hf; - hf = find_hoisted_def(fd, name); + JSGlobalVar *hf; + hf = find_global_var(fd, name); if (hf && hf->is_lexical && hf->scope_level == fd->scope_level && fd->eval_type == JS_EVAL_TYPE_MODULE) { goto invalid_lexical_redefinition; } - hf = add_hoisted_def(s->ctx, fd, -1, name, -1, FALSE); + hf = add_global_var(s->ctx, fd, name); if (!hf) return -1; idx = GLOBAL_VAR_OFFSET; @@ -20557,7 +22067,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, if (idx >= 0) { if (name == JS_ATOM_arguments && fd->has_arguments_binding) fd->arguments_var_idx = idx; - fd->vars[idx].func_pool_or_scope_idx = fd->scope_level; + fd->vars[idx].scope_next = fd->scope_level; } } break; @@ -20599,8 +22109,9 @@ static __exception int js_parse_function_decl2(JSParseState *s, int function_line_num, JSParseExportEnum export_flag, JSFunctionDef **pfd); -static __exception int js_parse_assign_expr(JSParseState *s, int in_accepted); -static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag); +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); +static __exception int js_parse_assign_expr(JSParseState *s); +static __exception int js_parse_unary(JSParseState *s, int parse_flags); static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, JSAtom label_name, int label_break, int label_cont, @@ -20707,7 +22218,7 @@ static __exception int js_parse_template(JSParseState *s, int call, int *argc) goto done; if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_expr(s)) return -1; depth++; if (s->token.val != '}') { @@ -20715,7 +22226,8 @@ static __exception int js_parse_template(JSParseState *s, int call, int *argc) } /* XXX: should convert to string at this stage? */ free_token(s, &s->token); - /* Resume TOK_TEMPLATE parsing (s->token.line_num and s->token.ptr are OK) */ + /* Resume TOK_TEMPLATE parsing (s->token.line_num and + * s->token.ptr are OK) */ s->got_lf = FALSE; s->last_line_num = s->token.line_num; if (js_parse_template_part(s, s->buf_ptr)) @@ -20835,10 +22347,11 @@ static int __exception js_parse_property_name(JSParseState *s, #ifdef CONFIG_BIGNUM if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { JSBigFloat *p = JS_VALUE_GET_PTR(val); - double d; - d = bf_mul_pow10_to_float64(s->ctx, &p->num, - s->token.u.num.exponent); - val = __JS_NewFloat64(s->ctx, d); + val = s->ctx->rt->bigfloat_ops. + mul_pow10_to_float64(s->ctx, &p->num, + s->token.u.num.exponent); + if (JS_IsException(val)) + goto fail; name = JS_ValueToAtom(s->ctx, val); JS_FreeValue(s->ctx, val); } else @@ -20930,8 +22443,9 @@ static BOOL is_regexp_allowed(int tok) } } -#define SKIP_HAS_SEMI (1 << 0) -#define SKIP_HAS_ELLIPSIS (1 << 1) +#define SKIP_HAS_SEMI (1 << 0) +#define SKIP_HAS_ELLIPSIS (1 << 1) +#define SKIP_HAS_ASSIGNMENT (1 << 2) /* XXX: improve speed with early bailout */ /* XXX: no longer works if regexps are present. Could use previous @@ -20942,7 +22456,7 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ size_t level = 0; JSParsePos pos; int last_tok, tok = TOK_EOF; - int tok_len, bits = 0; + int c, tok_len, bits = 0; /* protect from underflow */ state[level++] = 0; @@ -20967,8 +22481,30 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ goto done; break; case '}': - if (state[--level] != '{') + c = state[--level]; + if (c == '`') { + /* continue the parsing of the template */ + free_token(s, &s->token); + /* Resume TOK_TEMPLATE parsing (s->token.line_num and + * s->token.ptr are OK) */ + s->got_lf = FALSE; + s->last_line_num = s->token.line_num; + if (js_parse_template_part(s, s->buf_ptr)) + goto done; + goto handle_template; + } else if (c != '{') { goto done; + } + break; + case TOK_TEMPLATE: + handle_template: + if (s->token.u.str.sep != '`') { + /* '${' inside the template : closing '}' and continue + parsing the template */ + if (level >= sizeof(state)) + goto done; + state[level++] = '`'; + } break; case TOK_EOF: goto done; @@ -20982,7 +22518,10 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ bits |= SKIP_HAS_ELLIPSIS; } break; - + case '=': + bits |= SKIP_HAS_ASSIGNMENT; + break; + case TOK_DIV_ASSIGN: tok_len = 2; goto parse_regexp; @@ -21097,7 +22636,7 @@ static __exception int js_parse_object_literal(JSParseState *s) if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; emit_op(s, OP_null); /* dummy excludeList */ emit_op(s, OP_copy_data_properties); @@ -21156,7 +22695,7 @@ static __exception int js_parse_object_literal(JSParseState *s) } else { if (js_parse_expect(s, ':')) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; if (name == JS_ATOM_NULL) { set_object_name_computed(s); @@ -21191,8 +22730,23 @@ static __exception int js_parse_object_literal(JSParseState *s) return -1; } -static __exception int js_parse_postfix_expr(JSParseState *s, - BOOL accept_lparen); +/* allow the 'in' binary operator */ +#define PF_IN_ACCEPTED (1 << 0) +/* allow function calls parsing in js_parse_postfix_expr() */ +#define PF_POSTFIX_CALL (1 << 1) +/* allow arrow functions parsing in js_parse_postfix_expr() */ +#define PF_ARROW_FUNC (1 << 2) +/* allow the exponentiation operator in js_parse_unary() */ +#define PF_POW_ALLOWED (1 << 3) +/* forbid the exponentiation operator in js_parse_unary() */ +#define PF_POW_FORBIDDEN (1 << 4) + +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); + +static __exception int js_parse_left_hand_side_expr(JSParseState *s) +{ + return js_parse_postfix_expr(s, PF_POSTFIX_CALL); +} /* XXX: is there is nicer solution ? */ static __exception int js_parse_class_default_ctor(JSParseState *s, @@ -21396,8 +22950,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, class_flags = JS_DEFINE_CLASS_HAS_HERITAGE; if (next_token(s)) goto fail; - /* XXX: the grammar only allows LeftHandSideExpression */ - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) goto fail; } else { emit_op(s, OP_undefined); @@ -21449,16 +23002,25 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, continue; } is_static = (s->token.val == TOK_STATIC); + prop_type = -1; if (is_static) { if (next_token(s)) goto fail; + /* allow "static" field name */ + if (s->token.val == ';' || s->token.val == '=') { + is_static = FALSE; + name = JS_DupAtom(ctx, JS_ATOM_static); + prop_type = PROP_TYPE_IDENT; + } } if (is_static) emit_op(s, OP_swap); start_ptr = s->token.ptr; - prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE); - if (prop_type < 0) - goto fail; + if (prop_type < 0) { + prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE); + if (prop_type < 0) + goto fail; + } is_private = prop_type & PROP_TYPE_PRIVATE; prop_type &= ~PROP_TYPE_PRIVATE; @@ -21595,7 +23157,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, if (s->token.val == '=') { if (next_token(s)) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; } else { emit_op(s, OP_undefined); @@ -21799,7 +23361,7 @@ static __exception int js_parse_array_literal(JSParseState *s) while (s->token.val != ']' && idx < 32) { if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS) break; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; idx++; /* accept trailing comma */ @@ -21820,7 +23382,7 @@ static __exception int js_parse_array_literal(JSParseState *s) break; need_length = TRUE; if (s->token.val != ',') { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; emit_op(s, OP_define_field); emit_u32(s, __JS_AtomFromUInt32(idx)); @@ -21855,7 +23417,7 @@ static __exception int js_parse_array_literal(JSParseState *s) if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; #if 1 emit_op(s, OP_append); @@ -21887,7 +23449,7 @@ static __exception int js_parse_array_literal(JSParseState *s) } else { need_length = TRUE; if (s->token.val != ',') { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; /* a idx val */ emit_op(s, OP_define_array_el); @@ -21933,15 +23495,6 @@ static BOOL has_with_scope(JSFunctionDef *s, int scope_level) return FALSE; } -typedef struct JSLValue { - int opcode; - int scope; - int label; - int depth; - int tok; - JSAtom name; -} JSLValue; - static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, JSAtom *pname, int *plabel, int *pdepth, BOOL keep, int tok) @@ -22066,84 +23619,112 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, return 0; } -/* if special = TRUE: specific post inc/dec case */ -/* name has a live reference */ +typedef enum { + PUT_LVALUE_NOKEEP, /* [depth] v -> */ + PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently + just disable optimizations) */ + PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ + PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ + PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ +} PutLValueEnum; + +/* name has a live reference. 'is_let' is only used with opcode = + OP_scope_get_var which is never generated by get_lvalue(). */ static void put_lvalue(JSParseState *s, int opcode, int scope, - JSAtom name, int label, BOOL special) + JSAtom name, int label, PutLValueEnum special, + BOOL is_let) { switch(opcode) { case OP_get_field: - if (!special) + case OP_scope_get_private_field: + /* depth = 1 */ + switch(special) { + case PUT_LVALUE_NOKEEP: + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: emit_op(s, OP_insert2); /* obj v -> v obj v */ - else + break; + case PUT_LVALUE_KEEP_SECOND: emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_swap); + break; + default: + abort(); + } + break; + case OP_get_array_el: + case OP_get_ref_value: + /* depth = 2 */ + if (opcode == OP_get_ref_value) { + JS_FreeAtom(s->ctx, name); + emit_label(s, label); + } + switch(special) { + case PUT_LVALUE_NOKEEP: + emit_op(s, OP_nop); /* will trigger optimization */ + break; + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ + break; + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_rot3l); + break; + default: + abort(); + } + break; + case OP_get_super_value: + /* depth = 3 */ + switch(special) { + case PUT_LVALUE_NOKEEP: + case PUT_LVALUE_NOKEEP_DEPTH: + break; + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */ + break; + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */ + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_rot4l); + break; + default: + abort(); + } + break; + default: + break; + } + + switch(opcode) { + case OP_scope_get_var: /* val -- */ + assert(special == PUT_LVALUE_NOKEEP || + special == PUT_LVALUE_NOKEEP_DEPTH); + emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); + emit_u32(s, name); /* has refcount */ + emit_u16(s, scope); + break; + case OP_get_field: emit_op(s, OP_put_field); emit_u32(s, name); /* name has refcount */ break; case OP_scope_get_private_field: - if (!special) - emit_op(s, OP_insert2); /* obj v -> v obj v */ - else - emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ emit_op(s, OP_scope_put_private_field); emit_u32(s, name); /* name has refcount */ emit_u16(s, scope); break; case OP_get_array_el: - if (!special) - emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ - else - emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ emit_op(s, OP_put_array_el); break; case OP_get_ref_value: - JS_FreeAtom(s->ctx, name); - emit_label(s, label); - if (!special) - emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ - else - emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ - emit_op(s, OP_put_ref_value); - break; - case OP_get_super_value: - if (!special) - emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */ - else - emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */ - emit_op(s, OP_put_super_value); - break; - default: - abort(); - } -} - -static void put_lvalue_nokeep(JSParseState *s, int opcode, int scope, - JSAtom name, int label, int var_tok) -{ - switch(opcode) { - case OP_scope_get_var: /* val -- */ - emit_op(s, (var_tok == TOK_CONST || var_tok == TOK_LET) ? - OP_scope_put_var_init : OP_scope_put_var); - emit_u32(s, name); /* has refcount */ - emit_u16(s, scope); - break; - case OP_get_field: /* obj val -- */ - emit_op(s, OP_put_field); - emit_u32(s, name); /* has refcount */ - break; - case OP_scope_get_private_field: - emit_op(s, OP_scope_put_private_field); - emit_u32(s, name); /* has refcount */ - emit_u16(s, scope); - break; - case OP_get_array_el: /* obj prop val -- */ - emit_op(s, OP_put_array_el); - break; - case OP_get_ref_value: /* obj prop val -- */ - /* XXX: currently this reference is never optimized */ - JS_FreeAtom(s->ctx, name); - emit_label(s, label); - //emit_op(s, OP_nop); /* emit 2 bytes for optimizer */ emit_op(s, OP_put_ref_value); break; case OP_get_super_value: @@ -22254,7 +23835,7 @@ duplicate: return js_parse_error(s, "duplicate parameter names not allowed in this context"); } -static JSAtom js_parse_destructing_var(JSParseState *s, int tok, int is_arg) +static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) { JSAtom name; @@ -22276,7 +23857,9 @@ fail: return JS_ATOM_NULL; } -static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, +/* Return -1 if error, 0 if no initializer, 1 if an initializer is + present at the top level. */ +static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, int hasval, int has_ellipsis, BOOL allow_initializer) { @@ -22284,7 +23867,8 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, int start_addr, assign_addr; JSAtom prop_name, var_name; int opcode, scope, tok1, skip_bits; - + BOOL has_initializer; + if (has_ellipsis < 0) { /* pre-parse destructuration target for spread detection */ js_parse_skip_parens_token(s, &skip_bits, FALSE); @@ -22329,7 +23913,7 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, if (next_token(s)) return -1; if (tok) { - var_name = js_parse_destructing_var(s, tok, is_arg); + var_name = js_parse_destructuring_var(s, tok, is_arg); if (var_name == JS_ATOM_NULL) return -1; opcode = OP_scope_get_var; @@ -22337,7 +23921,7 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, label_lvalue = -1; depth_lvalue = 0; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, @@ -22393,7 +23977,7 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, emit_op(s, OP_get_field2); emit_u32(s, prop_name); } - if (js_parse_destructing_element(s, tok, is_arg, TRUE, -1, TRUE)) + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0) return -1; if (s->token.val == '}') break; @@ -22426,11 +24010,11 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, emit_op(s, OP_dup); } if (tok) { - var_name = js_parse_destructing_var(s, tok, is_arg); + var_name = js_parse_destructuring_var(s, tok, is_arg); if (var_name == JS_ATOM_NULL) goto prop_error; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) goto prop_error; lvalue: if (get_lvalue(s, &opcode, &scope, &var_name, @@ -22528,14 +24112,16 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, if (next_token(s)) goto var_error; emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto var_error; if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) set_object_name(s, var_name); emit_label(s, label_hasval); } /* store value into lvalue object */ - put_lvalue_nokeep(s, opcode, scope, var_name, label_lvalue, tok); + put_lvalue(s, opcode, scope, var_name, label_lvalue, + PUT_LVALUE_NOKEEP_DEPTH, + (tok == TOK_CONST || tok == TOK_LET)); if (s->token.val == '}') break; /* accept a trailing comma before the '}' */ @@ -22590,13 +24176,13 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, emit_u8(s, 0); emit_op(s, OP_drop); } - if (js_parse_destructing_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) + if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { var_name = JS_ATOM_NULL; enum_depth = 0; if (tok) { - var_name = js_parse_destructing_var(s, tok, is_arg); + var_name = js_parse_destructuring_var(s, tok, is_arg); if (var_name == JS_ATOM_NULL) goto var_error; if (js_define_var(s, var_name, tok)) @@ -22604,7 +24190,7 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, opcode = OP_scope_get_var; scope = s->cur_func->scope_level; } else { - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, &label_lvalue, &enum_depth, FALSE, '[')) { @@ -22628,15 +24214,16 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, if (next_token(s)) goto var_error; emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto var_error; if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) set_object_name(s, var_name); emit_label(s, label_hasval); } /* store value into lvalue object */ - put_lvalue_nokeep(s, opcode, scope, var_name, - label_lvalue, tok); + put_lvalue(s, opcode, scope, var_name, + label_lvalue, PUT_LVALUE_NOKEEP_DEPTH, + (tok == TOK_CONST || tok == TOK_LET)); } if (s->token.val == ']') break; @@ -22662,18 +24249,26 @@ static int js_parse_destructing_element(JSParseState *s, int tok, int is_arg, emit_label(s, label_parse); if (hasval) emit_op(s, OP_drop); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; emit_goto(s, OP_goto, label_assign); emit_label(s, label_done); + has_initializer = TRUE; } else { - assert(hasval); + /* normally hasval is true except if + js_parse_skip_parens_token() was wrong in the parsing */ + // assert(hasval); + if (!hasval) { + js_parse_error(s, "too complicated destructuring expression"); + return -1; + } /* remove test and decrement label ref count */ memset(s->cur_func->byte_code.buf + start_addr, OP_nop, assign_addr - start_addr); s->cur_func->label_slots[label_parse].ref_count--; + has_initializer = FALSE; } - return 0; + return has_initializer; prop_error: JS_FreeAtom(s->ctx, prop_name); @@ -22689,10 +24284,30 @@ typedef enum FuncCallType { FUNC_CALL_TEMPLATE, } FuncCallType; -static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen) +static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, + int drop_count) +{ + int label_next, i; + if (*poptional_chaining_label < 0) + *poptional_chaining_label = new_label(s); + /* XXX: could be more efficient with a specific opcode */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + label_next = emit_goto(s, OP_if_false, -1); + for(i = 0; i < drop_count; i++) + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + emit_goto(s, OP_goto, *poptional_chaining_label); + emit_label(s, label_next); +} + +/* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */ +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) { FuncCallType call_type; - + int optional_chaining_label; + BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0; + call_type = FUNC_CALL_NORMAL; switch(s->token.val) { case TOK_NUMBER: @@ -22706,7 +24321,6 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } else #ifdef CONFIG_BIGNUM if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - bf_t r_s, *r = &r_s; slimb_t e; int ret; @@ -22720,9 +24334,9 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_op(s, OP_push_i32); emit_u32(s, e); } else { - bf_init(s->ctx->bf_ctx, r); - bf_set_si(r, e); - val = JS_NewBigInt(s->ctx, r); + val = JS_NewBigInt64_1(s->ctx, e); + if (JS_IsException(val)) + return -1; ret = emit_push_const(s, val, 0); JS_FreeValue(s->ctx, val); if (ret < 0) @@ -22758,7 +24372,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen parse_regexp: { JSValue str; - int ret; + int ret, backtrace_flags; if (!s->ctx->compile_regexp) return js_parse_error(s, "RegExp are not supported"); /* the previous token is '/' or '/=', so no need to free */ @@ -22769,8 +24383,12 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen s->token.u.regexp.flags); if (JS_IsException(str)) { /* add the line number info */ - build_backtrace(s->ctx, s->ctx->current_exception, - s->filename, s->token.line_num, NULL); + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(s->ctx, s->ctx->rt->current_exception, + s->filename, s->token.line_num, + backtrace_flags); return -1; } ret = emit_push_const(s, str, 0); @@ -22786,7 +24404,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } break; case '(': - if (js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { + if ((parse_flags & PF_ARROW_FUNC) && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, JS_FUNC_NORMAL, JS_ATOM_NULL, s->token.ptr, s->token.line_num)) @@ -22834,7 +24453,8 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen if (s->token.u.ident.is_reserved) { return js_parse_error_reserved_identifier(s); } - if (peek_token(s, TRUE) == TOK_ARROW) { + if ((parse_flags & PF_ARROW_FUNC) && + peek_token(s, TRUE) == TOK_ARROW) { if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, JS_FUNC_NORMAL, JS_ATOM_NULL, s->token.ptr, s->token.line_num)) @@ -22853,10 +24473,11 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen JS_FUNC_ASYNC, JS_ATOM_NULL, source_ptr, source_line_num)) return -1; - } else if ((s->token.val == '(' && - js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || - (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && - peek_token(s, TRUE) == TOK_ARROW)) { + } else if ((parse_flags & PF_ARROW_FUNC) && + ((s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && + peek_token(s, TRUE) == TOK_ARROW))) { if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, JS_FUNC_ASYNC, JS_ATOM_NULL, source_ptr, source_line_num)) @@ -22886,7 +24507,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen { int skip_bits; if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { - if (js_parse_destructing_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) + if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { if (s->token.val == '{') { @@ -22915,7 +24536,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_atom(s, JS_ATOM_new_target); emit_u16(s, 0); } else { - if (js_parse_postfix_expr(s, FALSE)) + if (js_parse_postfix_expr(s, 0)) return -1; accept_lparen = TRUE; if (s->token.val != '(') { @@ -22968,7 +24589,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen return -1; if (!accept_lparen) return js_parse_error(s, "invalid use of 'import()'"); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; if (js_parse_expect(s, ')')) return -1; @@ -22980,35 +24601,55 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen (int)(s->buf_ptr - s->token.ptr), s->token.ptr); } + optional_chaining_label = -1; for(;;) { JSFunctionDef *fd = s->cur_func; - - if (s->token.val == TOK_TEMPLATE && call_type == FUNC_CALL_NORMAL) { + BOOL has_optional_chain = FALSE; + + if (s->token.val == TOK_QUESTION_MARK_DOT) { + /* optional chaining */ + if (next_token(s)) + return -1; + has_optional_chain = TRUE; + if (s->token.val == '(' && accept_lparen) { + goto parse_func_call; + } else if (s->token.val == '[') { + goto parse_array_access; + } else { + goto parse_property; + } + } else if (s->token.val == TOK_TEMPLATE && + call_type == FUNC_CALL_NORMAL) { + if (optional_chaining_label >= 0) { + return js_parse_error(s, "template literal cannot appear in an optional chain"); + } call_type = FUNC_CALL_TEMPLATE; - goto parse_func_call; - } - if (s->token.val == '(' && accept_lparen) { - int opcode; - int arg_count; + goto parse_func_call2; + } else if (s->token.val == '(' && accept_lparen) { + int opcode, arg_count, drop_count; /* function call */ + parse_func_call: if (next_token(s)) return -1; if (call_type == FUNC_CALL_NORMAL) { - parse_func_call: + parse_func_call2: switch(opcode = get_prev_opcode(fd)) { case OP_get_field: /* keep the object on the stack */ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; + drop_count = 2; break; case OP_scope_get_private_field: /* keep the object on the stack */ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2; + drop_count = 2; break; case OP_get_array_el: /* keep the object on the stack */ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; + drop_count = 2; break; case OP_scope_get_var: { @@ -23016,7 +24657,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen int scope; name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); - if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL) { + if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) { /* direct 'eval' */ opcode = OP_eval; } else { @@ -23033,17 +24674,24 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen fd->byte_code.buf[fd->last_opcode_pos] = opcode; } } + drop_count = 1; } break; case OP_get_super_value: fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el; /* on stack: this func_obj */ opcode = OP_get_array_el; + drop_count = 2; break; default: opcode = OP_invalid; + drop_count = 1; break; } + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, + drop_count); + } } else { opcode = OP_invalid; } @@ -23057,7 +24705,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_atom(s, JS_ATOM_this_active_func); emit_u16(s, 0); - emit_op(s, OP_get_super_ctor); + emit_op(s, OP_get_super); emit_op(s, OP_scope_get_var); emit_atom(s, JS_ATOM_new_target); @@ -23074,7 +24722,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } if (s->token.val == TOK_ELLIPSIS) break; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; arg_count++; if (s->token.val == ')') @@ -23094,7 +24742,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen if (s->token.val == TOK_ELLIPSIS) { if (next_token(s)) return -1; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; #if 1 /* XXX: could pass is_last indicator? */ @@ -23125,7 +24773,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen emit_op(s, OP_nip1); #endif } else { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; /* array idx val */ emit_op(s, OP_define_array_el); @@ -23227,11 +24875,15 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } else if (s->token.val == '.') { if (next_token(s)) return -1; + parse_property: if (s->token.val == TOK_PRIVATE_NAME) { /* private class field */ if (get_prev_opcode(fd) == OP_get_super) { return js_parse_error(s, "private class field forbidden after super"); } + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } emit_op(s, OP_scope_get_private_field); emit_atom(s, s->token.u.ident.atom); emit_u16(s, s->cur_func->scope_level); @@ -23249,6 +24901,9 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen return -1; emit_op(s, OP_get_super_value); } else { + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } emit_op(s, OP_get_field); emit_atom(s, s->token.u.ident.atom); } @@ -23257,9 +24912,14 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen return -1; } else if (s->token.val == '[') { int prev_op; + + parse_array_access: + prev_op = get_prev_opcode(fd); + if (has_optional_chain) { + optional_chain_test(s, &optional_chaining_label, 1); + } if (next_token(s)) return -1; - prev_op = get_prev_opcode(fd); if (js_parse_expr(s)) return -1; if (js_parse_expect(s, ']')) @@ -23269,20 +24929,12 @@ static __exception int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen } else { emit_op(s, OP_get_array_el); } - } else if (!s->got_lf && (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { - int opcode, op, scope, label; - JSAtom name; - op = s->token.val; - if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) - return -1; - emit_op(s, OP_post_dec + op - TOK_DEC); - put_lvalue(s, opcode, scope, name, label, TRUE); - if (next_token(s)) - return -1; } else { break; } } + if (optional_chaining_label >= 0) + emit_label(s, optional_chaining_label); return 0; } @@ -23294,7 +24946,7 @@ static __exception int js_parse_delete(JSParseState *s) if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; switch(opcode = get_prev_opcode(fd)) { case OP_get_field: @@ -23333,9 +24985,9 @@ static __exception int js_parse_delete(JSParseState *s) case OP_scope_get_private_field: return js_parse_error(s, "cannot delete a private class field"); case OP_get_super_value: - emit_op(s, OP_throw_var); + emit_op(s, OP_throw_error); emit_atom(s, JS_ATOM_NULL); - emit_u8(s, JS_THROW_VAR_DELETE_SUPER); + emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); break; default: ret_true: @@ -23346,7 +24998,8 @@ static __exception int js_parse_delete(JSParseState *s) return 0; } -static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) +/* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */ +static __exception int js_parse_unary(JSParseState *s, int parse_flags) { int op; @@ -23359,7 +25012,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) op = s->token.val; if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; switch(op) { case '-': @@ -23381,7 +25034,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) default: abort(); } - exponentiation_flag = 0; + parse_flags = 0; break; case TOK_DEC: case TOK_INC: @@ -23391,13 +25044,13 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) op = s->token.val; if (next_token(s)) return -1; - /* XXX: should parse LeftHandSideExpression */ if (js_parse_unary(s, 0)) return -1; if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) return -1; emit_op(s, OP_dec + op - TOK_DEC); - put_lvalue(s, opcode, scope, name, label, FALSE); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, + FALSE); } break; case TOK_TYPEOF: @@ -23405,7 +25058,7 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) JSFunctionDef *fd; if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; /* reference access should not return an exception, so we patch the get_var */ @@ -23414,13 +25067,13 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef; } emit_op(s, OP_typeof); - exponentiation_flag = 0; + parse_flags = 0; } break; case TOK_DELETE: if (js_parse_delete(s)) return -1; - exponentiation_flag = 0; + parse_flags = 0; break; case TOK_AWAIT: if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) @@ -23429,36 +25082,47 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) return js_parse_error(s, "await in default expression"); if (next_token(s)) return -1; - if (js_parse_unary(s, -1)) + if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; emit_op(s, OP_await); - exponentiation_flag = 0; + parse_flags = 0; break; default: - if (js_parse_postfix_expr(s, TRUE)) + if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) | + PF_POSTFIX_CALL)) return -1; + if (!s->got_lf && + (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { + int opcode, op, scope, label; + JSAtom name; + op = s->token.val; + if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) + return -1; + emit_op(s, OP_post_dec + op - TOK_DEC); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND, + FALSE); + if (next_token(s)) + return -1; + } break; } - if (exponentiation_flag) { + if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) { #ifdef CONFIG_BIGNUM if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) { /* Extended exponentiation syntax rules: we extend the ES7 grammar in order to have more intuitive semantics: -2**2 evaluates to -4. */ if (!(s->cur_func->js_mode & JS_MODE_MATH)) { - if (exponentiation_flag < 0) { + if (parse_flags & PF_POW_FORBIDDEN) { JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); return -1; } } if (next_token(s)) return -1; - if (js_parse_unary(s, 1)) + if (js_parse_unary(s, PF_POW_ALLOWED)) return -1; - if (s->cur_func->js_mode & JS_MODE_MATH) - emit_op(s, OP_math_pow); - else - emit_op(s, OP_pow); + emit_op(s, OP_pow); } #else if (s->token.val == TOK_POW) { @@ -23467,13 +25131,13 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) regarding the precedence of prefix operators and the postifx exponential, ES7 specifies that -2**2 is a syntax error. */ - if (exponentiation_flag < 0) { + if (parse_flags & PF_POW_FORBIDDEN) { JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); return -1; } if (next_token(s)) return -1; - if (js_parse_unary(s, 1)) + if (js_parse_unary(s, PF_POW_ALLOWED)) return -1; emit_op(s, OP_pow); } @@ -23482,15 +25146,17 @@ static __exception int js_parse_unary(JSParseState *s, int exponentiation_flag) return 0; } +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ static __exception int js_parse_expr_binary(JSParseState *s, int level, - BOOL in_accepted) + int parse_flags) { int op, opcode; if (level == 0) { - return js_parse_unary(s, 1); + return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) | + PF_POW_ALLOWED); } - if (js_parse_expr_binary(s, level - 1, in_accepted)) + if (js_parse_expr_binary(s, level - 1, parse_flags)) return -1; for(;;) { op = s->token.val; @@ -23501,12 +25167,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, opcode = OP_mul; break; case '/': -#ifdef CONFIG_BIGNUM - if (s->cur_func->js_mode & JS_MODE_MATH) - opcode = OP_math_div; - else -#endif - opcode = OP_div; + opcode = OP_div; break; case '%': #ifdef CONFIG_BIGNUM @@ -23565,7 +25226,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, opcode = OP_instanceof; break; case TOK_IN: - if (in_accepted) { + if (parse_flags & PF_IN_ACCEPTED) { opcode = OP_in; } else { return 0; @@ -23625,23 +25286,24 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, } if (next_token(s)) return -1; - if (js_parse_expr_binary(s, level - 1, in_accepted)) + if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC)) return -1; emit_op(s, opcode); } return 0; } +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ static __exception int js_parse_logical_and_or(JSParseState *s, int op, - BOOL in_accepted) + int parse_flags) { int label1; if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, in_accepted)) + if (js_parse_expr_binary(s, 8, parse_flags)) return -1; } else { - if (js_parse_logical_and_or(s, TOK_LAND, in_accepted)) + if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) return -1; } if (s->token.val == op) { @@ -23655,14 +25317,18 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, emit_op(s, OP_drop); if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, in_accepted)) + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) return -1; } else { - if (js_parse_logical_and_or(s, TOK_LAND, in_accepted)) + if (js_parse_logical_and_or(s, TOK_LAND, + parse_flags & ~PF_ARROW_FUNC)) return -1; } - if (s->token.val != op) + if (s->token.val != op) { + if (s->token.val == TOK_DOUBLE_QUESTION_MARK) + return js_parse_error(s, "cannot mix ?? with && or ||"); break; + } } emit_label(s, label1); @@ -23670,18 +25336,46 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, return 0; } -static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) +static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) +{ + int label1; + + if (js_parse_logical_and_or(s, TOK_LOR, parse_flags)) + return -1; + if (s->token.val == TOK_DOUBLE_QUESTION_MARK) { + label1 = new_label(s); + for(;;) { + if (next_token(s)) + return -1; + + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + emit_goto(s, OP_if_false, label1); + emit_op(s, OP_drop); + + if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) + return -1; + if (s->token.val != TOK_DOUBLE_QUESTION_MARK) + break; + } + emit_label(s, label1); + } + return 0; +} + +/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ +static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) { int label1, label2; - if (js_parse_logical_and_or(s, TOK_LOR, in_accepted)) + if (js_parse_coalesce_expr(s, parse_flags)) return -1; if (s->token.val == '?') { if (next_token(s)) return -1; label1 = emit_goto(s, OP_if_false, -1); - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; if (js_parse_expect(s, ':')) return -1; @@ -23690,7 +25384,7 @@ static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) emit_label(s, label1); - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED)) return -1; emit_label(s, label2); @@ -23700,14 +25394,16 @@ static __exception int js_parse_cond_expr(JSParseState *s, BOOL in_accepted) static void emit_return(JSParseState *s, BOOL hasval); -static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) { int opcode, op, scope; JSAtom name0 = JS_ATOM_NULL; JSAtom name; if (s->token.val == TOK_YIELD) { - BOOL is_star = FALSE; + BOOL is_star = FALSE, is_async; + if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR)) return js_parse_error(s, "unexpected 'yield' keyword"); if (!s->cur_func->in_function_body) @@ -23724,109 +25420,120 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) if (next_token(s)) return -1; } - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags)) return -1; } else { emit_op(s, OP_undefined); } - if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR); + + if (is_star) { int label_loop, label_return, label_next; int label_return1, label_yield, label_throw, label_throw1; int label_throw2; - if (is_star) { - label_loop = new_label(s); - label_yield = new_label(s); + label_loop = new_label(s); + label_yield = new_label(s); - emit_op(s, OP_for_await_of_start); + emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start); - /* remove the catch offset (XXX: could avoid pushing back - undefined) */ - emit_op(s, OP_drop); - emit_op(s, OP_undefined); - - emit_op(s, OP_undefined); /* initial value */ - - emit_label(s, label_loop); - emit_op(s, OP_async_iterator_next); + /* remove the catch offset (XXX: could avoid pushing back + undefined) */ + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + + emit_op(s, OP_undefined); /* initial value */ + + emit_label(s, label_loop); + emit_op(s, OP_iterator_next); + if (is_async) emit_op(s, OP_await); - emit_op(s, OP_iterator_get_value_done); - label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ + emit_label(s, label_yield); + if (is_async) { + /* OP_async_yield_star takes the value as parameter */ + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); emit_op(s, OP_await); - emit_label(s, label_yield); emit_op(s, OP_async_yield_star); - emit_op(s, OP_dup); - label_return = emit_goto(s, OP_if_true, -1); - emit_op(s, OP_drop); - emit_goto(s, OP_goto, label_loop); - - emit_label(s, label_return); - emit_op(s, OP_push_i32); - emit_u32(s, 2); - emit_op(s, OP_strict_eq); - label_throw = emit_goto(s, OP_if_true, -1); - - /* return handling */ - emit_op(s, OP_await); - emit_op(s, OP_async_iterator_get); - emit_u8(s, 0); - label_return1 = emit_goto(s, OP_if_true, -1); - emit_op(s, OP_await); - emit_op(s, OP_iterator_get_value_done); - /* XXX: the spec does not indicate that an await should be - performed in case done = true, but the tests assume it */ - emit_goto(s, OP_if_false, label_yield); - - emit_label(s, label_return1); - emit_op(s, OP_nip); - emit_op(s, OP_nip); - emit_op(s, OP_nip); - emit_return(s, TRUE); - - /* throw handling */ - emit_label(s, label_throw); - emit_op(s, OP_async_iterator_get); - emit_u8(s, 1); - label_throw1 = emit_goto(s, OP_if_true, -1); - emit_op(s, OP_await); - emit_op(s, OP_iterator_get_value_done); - emit_goto(s, OP_if_false, label_yield); - /* XXX: the spec does not indicate that an await should be - performed in case done = true, but the tests assume it */ - emit_op(s, OP_await); - emit_goto(s, OP_goto, label_next); - /* close the iterator and throw a type error exception */ - emit_label(s, label_throw1); - emit_op(s, OP_async_iterator_get); - emit_u8(s, 0); - label_throw2 = emit_goto(s, OP_if_true, -1); - emit_op(s, OP_await); - emit_label(s, label_throw2); - emit_op(s, OP_async_iterator_get); - emit_u8(s, 2); /* throw the type error exception */ - emit_op(s, OP_drop); /* never reached */ - - emit_label(s, label_next); - emit_op(s, OP_nip); /* keep the value associated with - done = true */ - emit_op(s, OP_nip); - emit_op(s, OP_nip); } else { - emit_op(s, OP_await); - emit_op(s, OP_yield); - label_next = emit_goto(s, OP_if_false, -1); - emit_return(s, TRUE); - emit_label(s, label_next); + /* OP_yield_star takes (value, done) as parameter */ + emit_op(s, OP_yield_star); } + emit_op(s, OP_dup); + label_return = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_drop); + emit_goto(s, OP_goto, label_loop); + + emit_label(s, label_return); + emit_op(s, OP_push_i32); + emit_u32(s, 2); + emit_op(s, OP_strict_eq); + label_throw = emit_goto(s, OP_if_true, -1); + + /* return handling */ + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_call); + emit_u8(s, 0); + label_return1 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_goto(s, OP_if_false, label_yield); + + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + + emit_label(s, label_return1); + emit_op(s, OP_nip); + emit_op(s, OP_nip); + emit_op(s, OP_nip); + emit_return(s, TRUE); + + /* throw handling */ + emit_label(s, label_throw); + emit_op(s, OP_iterator_call); + emit_u8(s, 1); + label_throw1 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_done); + emit_goto(s, OP_if_false, label_yield); + emit_goto(s, OP_goto, label_next); + /* close the iterator and throw a type error exception */ + emit_label(s, label_throw1); + emit_op(s, OP_iterator_call); + emit_u8(s, 2); + label_throw2 = emit_goto(s, OP_if_true, -1); + if (is_async) + emit_op(s, OP_await); + emit_label(s, label_throw2); + + emit_op(s, OP_throw_error); + emit_atom(s, JS_ATOM_NULL); + emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW); + + emit_label(s, label_next); + emit_op(s, OP_get_field); + emit_atom(s, JS_ATOM_value); + emit_op(s, OP_nip); /* keep the value associated with + done = true */ + emit_op(s, OP_nip); + emit_op(s, OP_nip); } else { int label_next; - if (is_star) { - emit_op(s, OP_for_of_start); - emit_op(s, OP_drop); /* drop the catch offset */ - emit_op(s, OP_yield_star); - } else { - emit_op(s, OP_yield); - } + + if (is_async) + emit_op(s, OP_await); + emit_op(s, OP_yield); label_next = emit_goto(s, OP_if_false, -1); emit_return(s, TRUE); emit_label(s, label_next); @@ -23837,7 +25544,7 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) /* name0 is used to check for OP_set_name pattern, not duplicated */ name0 = s->token.u.ident.atom; } - if (js_parse_cond_expr(s, in_accepted)) + if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC)) return -1; op = s->token.val; @@ -23848,7 +25555,7 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) return -1; - if (js_parse_assign_expr(s, in_accepted)) { + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(s->ctx, name); return -1; } @@ -23869,26 +25576,82 @@ static __exception int js_parse_assign_expr(JSParseState *s, BOOL in_accepted) op = assign_opcodes[op - TOK_MUL_ASSIGN]; #ifdef CONFIG_BIGNUM if (s->cur_func->js_mode & JS_MODE_MATH) { - if (op == OP_div) - op = OP_math_div; - else if (op == OP_mod) + if (op == OP_mod) op = OP_math_mod; - else if (op == OP_pow) - op = OP_math_pow; } #endif emit_op(s, op); } - put_lvalue(s, opcode, scope, name, label, FALSE); + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE); + } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) { + int label, label1, depth_lvalue, label2; + + if (next_token(s)) + return -1; + if (get_lvalue(s, &opcode, &scope, &name, &label, + &depth_lvalue, TRUE, op) < 0) + return -1; + + emit_op(s, OP_dup); + if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN) + emit_op(s, OP_is_undefined_or_null); + label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false, + -1); + emit_op(s, OP_drop); + + if (js_parse_assign_expr2(s, parse_flags)) { + JS_FreeAtom(s->ctx, name); + return -1; + } + + if (opcode == OP_get_ref_value && name == name0) { + set_object_name(s, name); + } + + switch(depth_lvalue) { + case 1: + emit_op(s, OP_insert2); + break; + case 2: + emit_op(s, OP_insert3); + break; + case 3: + emit_op(s, OP_insert4); + break; + default: + abort(); + } + + /* XXX: we disable the OP_put_ref_value optimization by not + using put_lvalue() otherwise depth_lvalue is not correct */ + put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH, + FALSE); + label2 = emit_goto(s, OP_goto, -1); + + emit_label(s, label1); + + /* remove the lvalue stack entries */ + while (depth_lvalue != 0) { + emit_op(s, OP_nip); + depth_lvalue--; + } + + emit_label(s, label2); } return 0; } -static __exception int js_parse_expr2(JSParseState *s, BOOL in_accepted) +static __exception int js_parse_assign_expr(JSParseState *s) +{ + return js_parse_assign_expr2(s, PF_IN_ACCEPTED); +} + +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_expr2(JSParseState *s, int parse_flags) { BOOL comma = FALSE; for(;;) { - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags)) return -1; if (comma) { /* prevent get_lvalue from using the last expression @@ -23910,7 +25673,7 @@ static __exception int js_parse_expr2(JSParseState *s, BOOL in_accepted) static __exception int js_parse_expr(JSParseState *s) { - return js_parse_expr2(s, TRUE); + return js_parse_expr2(s, PF_IN_ACCEPTED); } static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, @@ -24006,12 +25769,25 @@ static void emit_return(JSParseState *s, BOOL hasval) } emit_op(s, OP_iterator_close_return); if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { - int label_next; - emit_op(s, OP_async_iterator_close); + int label_next, label_next2; + + emit_op(s, OP_drop); /* catch offset */ + emit_op(s, OP_drop); /* next */ + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_return); + /* stack: iter_obj return_func */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); label_next = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_iterator_check_object); emit_op(s, OP_await); + label_next2 = emit_goto(s, OP_goto, -1); emit_label(s, label_next); emit_op(s, OP_drop); + emit_label(s, label_next2); + emit_op(s, OP_drop); } else { emit_op(s, OP_iterator_close); } @@ -24046,6 +25822,8 @@ static void emit_return(JSParseState *s, BOOL hasval) label_return = -1; } + /* XXX: if this is not initialized, should throw the + ReferenceError in the caller realm */ emit_op(s, OP_scope_get_var); emit_atom(s, JS_ATOM_this); emit_u16(s, 0); @@ -24065,9 +25843,10 @@ static void emit_return(JSParseState *s, BOOL hasval) } #define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */ -#define DECL_MASK_LABEL (1 << 1) /* allow labelled statement */ +/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */ +#define DECL_MASK_FUNC_WITH_LABEL (1 << 1) #define DECL_MASK_OTHER (1 << 2) /* all other declarations */ -#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_LABEL | DECL_MASK_OTHER) +#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER) static __exception int js_parse_statement_or_decl(JSParseState *s, int decl_mask); @@ -24096,7 +25875,8 @@ static __exception int js_parse_block(JSParseState *s) return 0; } -static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, BOOL export_flag) { JSContext *ctx = s->ctx; @@ -24136,15 +25916,15 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, emit_u16(s, fd->scope_level); if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0) goto var_error; - if (js_parse_assign_expr(s, in_accepted)) { + if (js_parse_assign_expr2(s, parse_flags)) { JS_FreeAtom(ctx, name1); goto var_error; } set_object_name(s, name); - put_lvalue(s, opcode, scope, name1, label, FALSE); - emit_op(s, OP_drop); + put_lvalue(s, opcode, scope, name1, label, + PUT_LVALUE_NOKEEP, FALSE); } else { - if (js_parse_assign_expr(s, in_accepted)) + if (js_parse_assign_expr2(s, parse_flags)) goto var_error; set_object_name(s, name); emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? @@ -24171,7 +25951,7 @@ static __exception int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, if ((s->token.val == '[' || s->token.val == '{') && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { emit_op(s, OP_undefined); - if (js_parse_destructing_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) + if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { return js_parse_error(s, "variable name expected"); @@ -24300,7 +26080,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { - if (js_parse_destructing_element(s, tok, 0, TRUE, -1, FALSE)) + if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0) return -1; has_destructuring = TRUE; } else { @@ -24326,32 +26106,17 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, int skip_bits; if ((s->token.val == '[' || s->token.val == '{') && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) { - if (js_parse_destructing_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE)) + if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) return -1; } else { - int lvalue_label, depth; - if (js_parse_postfix_expr(s, TRUE)) + int lvalue_label; + if (js_parse_left_hand_side_expr(s)) return -1; if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, - &depth, FALSE, TOK_FOR)) + NULL, FALSE, TOK_FOR)) return -1; - /* swap value and lvalue object and store it into lvalue object */ - /* enum_rec val [depth] -- enum_rec */ - switch(depth) { - case 1: - emit_op(s, OP_swap); - break; - case 2: - emit_op(s, OP_rot3l); - break; - case 3: - emit_op(s, OP_rot4l); - break; - default: - abort(); - } - put_lvalue_nokeep(s, opcode, scope, var_name, lvalue_label, - TOK_FOR /* not used */); + put_lvalue(s, opcode, scope, var_name, lvalue_label, + PUT_LVALUE_NOKEEP_BOTTOM, FALSE); } var_name = JS_ATOM_NULL; } @@ -24365,7 +26130,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, /* parse and evaluate initializer prior to evaluating the object (only used with "for in" with a non lexical variable in non strict mode */ - if (next_token(s) || js_parse_assign_expr(s, FALSE)) { + if (next_token(s) || js_parse_assign_expr2(s, 0)) { JS_FreeAtom(ctx, var_name); return -1; } @@ -24398,7 +26163,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (next_token(s)) return -1; if (is_for_of) { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; } else { if (js_parse_expr(s)) @@ -24451,7 +26216,11 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (is_for_of) { if (is_async) { /* call the next method */ - emit_op(s, OP_for_await_of_next); + /* stack: iter_obj next catch_offset */ + emit_op(s, OP_dup3); + emit_op(s, OP_drop); + emit_op(s, OP_call_method); + emit_u16(s, 0); /* get the result of the promise */ emit_op(s, OP_await); /* unwrap the value and done values */ @@ -24502,10 +26271,6 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, if (is_label(s)) { BlockEnv *be; - if (!(decl_mask & DECL_MASK_LABEL)) { - js_parse_error(s, "functions can only be labelled inside blocks"); - goto fail; - } label_name = JS_DupAtom(ctx, s->token.u.ident.atom); for (be = s->cur_func->top_break; be; be = be->prev) { @@ -24529,11 +26294,12 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, label_break = new_label(s); push_break_entry(s->cur_func, &break_entry, label_name, label_break, -1, 0); - if (s->cur_func->js_mode & JS_MODE_STRICT) + if (!(s->cur_func->js_mode & JS_MODE_STRICT) && + (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) { + mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL; + } else { mask = 0; - else - mask = DECL_MASK_FUNC; - mask |= DECL_MASK_LABEL; + } if (js_parse_statement_or_decl(s, mask)) goto fail; emit_label(s, label_break); @@ -25012,7 +26778,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { /* XXX: TOK_LET is not completely correct */ - if (js_parse_destructing_element(s, TOK_LET, 0, TRUE, -1, TRUE)) + if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0) goto fail; } else { js_parse_error(s, "identifier expected"); @@ -25078,18 +26844,37 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, } emit_label(s, label_finally); if (s->token.val == TOK_FINALLY) { - int saved_eval_ret_idx; + int saved_eval_ret_idx = 0; /* avoid warning */ + if (next_token(s)) goto fail; /* on the stack: ret_value gosub_ret_value */ push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, -1, -1, 2); - saved_eval_ret_idx = s->cur_func->eval_ret_idx; - s->cur_func->eval_ret_idx = -1; - /* 'finally' does not update eval_ret */ + + if (s->cur_func->eval_ret_idx >= 0) { + /* 'finally' updates eval_ret only if not a normal + termination */ + saved_eval_ret_idx = + add_var(s->ctx, s->cur_func, JS_ATOM__ret_); + if (saved_eval_ret_idx < 0) + goto fail; + emit_op(s, OP_get_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + emit_op(s, OP_put_loc); + emit_u16(s, saved_eval_ret_idx); + set_eval_ret_undefined(s); + } + if (js_parse_block(s)) goto fail; - s->cur_func->eval_ret_idx = saved_eval_ret_idx; + + if (s->cur_func->eval_ret_idx >= 0) { + emit_op(s, OP_get_loc); + emit_u16(s, saved_eval_ret_idx); + emit_op(s, OP_put_loc); + emit_u16(s, s->cur_func->eval_ret_idx); + } pop_break_entry(s->cur_func); } emit_op(s, OP_ret); @@ -25235,6 +27020,25 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) return m; } +static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, + JS_MarkFunc *mark_func) +{ + int i; + + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL && + me->u.local.var_ref) { + mark_func(rt, &me->u.local.var_ref->header); + } + } + + JS_MarkValue(rt, m->module_ns, mark_func); + JS_MarkValue(rt, m->func_obj, mark_func); + JS_MarkValue(rt, m->eval_exception, mark_func); + JS_MarkValue(rt, m->meta_obj, mark_func); +} + static void js_free_module_def(JSContext *ctx, JSModuleDef *m) { int i; @@ -25272,26 +27076,6 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m) js_free(ctx, m); } -static int js_resize_array(JSContext *ctx, void **parray, int elem_size, - int *psize, int *pcount, int new_count) -{ - if (unlikely(new_count > *psize)) { - int new_size; - size_t slack; - void *new_array; - /* XXX: potential arithmetic overflow */ - new_size = max_int(new_count, *psize * 3 / 2); - new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); - if (!new_array) - return -1; - new_size += slack / elem_size; - *psize = new_size; - *parray = new_array; - } - *pcount = new_count; - return 0; -} - static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, JSAtom module_name) { @@ -25307,11 +27091,10 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, if (js_resize_array(ctx, (void **)&m->req_module_entries, sizeof(JSReqModuleEntry), - &m->req_module_entries_size, &m->req_module_entries_count, + &m->req_module_entries_size, m->req_module_entries_count + 1)) return -1; - i = m->req_module_entries_count - 1; - rme = &m->req_module_entries[i]; + rme = &m->req_module_entries[m->req_module_entries_count++]; rme->module_name = JS_DupAtom(ctx, module_name); rme->module = NULL; return i; @@ -25343,18 +27126,17 @@ static JSExportEntry *add_export_entry2(JSContext *ctx, js_parse_error(s, "duplicate exported name '%s'", JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name)); } else { - JS_ThrowSyntaxError(ctx, "duplicate exported name '%s'", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name)); + JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name); } return NULL; } if (js_resize_array(ctx, (void **)&m->export_entries, sizeof(JSExportEntry), - &m->export_entries_size, &m->export_entries_count, + &m->export_entries_size, m->export_entries_count + 1)) return NULL; - me = &m->export_entries[m->export_entries_count - 1]; + me = &m->export_entries[m->export_entries_count++]; memset(me, 0, sizeof(*me)); me->local_name = JS_DupAtom(ctx, local_name); me->export_name = JS_DupAtom(ctx, export_name); @@ -25377,10 +27159,10 @@ static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, if (js_resize_array(ctx, (void **)&m->star_export_entries, sizeof(JSStarExportEntry), - &m->star_export_entries_size, &m->star_export_entries_count, + &m->star_export_entries_size, m->star_export_entries_count + 1)) return -1; - se = &m->star_export_entries[m->star_export_entries_count - 1]; + se = &m->star_export_entries[m->star_export_entries_count++]; se->req_module_idx = req_module_idx; return 0; } @@ -25517,32 +27299,20 @@ static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) /* return NULL in case of exception (e.g. module could not be loaded) */ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, - JSAtom base_module_name, - JSAtom module_name1) + const char *base_cname, + const char *cname1) { JSRuntime *rt = ctx->rt; JSModuleDef *m; char *cname; - const char *base_cname, *cname1; JSAtom module_name; - base_cname = JS_AtomToCString(ctx, base_module_name); - if (!base_cname) - return NULL; - cname1 = JS_AtomToCString(ctx, module_name1); - if (!cname1) { - JS_FreeCString(ctx, base_cname); - return NULL; - } - if (!rt->module_normalize_func) { cname = js_default_module_normalize_name(ctx, base_cname, cname1); } else { cname = rt->module_normalize_func(ctx, base_cname, cname1, rt->module_loader_opaque); } - JS_FreeCString(ctx, base_cname); - JS_FreeCString(ctx, cname1); if (!cname) return NULL; @@ -25576,6 +27346,27 @@ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, return m; } +static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, + JSAtom base_module_name, + JSAtom module_name1) +{ + const char *base_cname, *cname; + JSModuleDef *m; + + base_cname = JS_AtomToCString(ctx, base_module_name); + if (!base_cname) + return NULL; + cname = JS_AtomToCString(ctx, module_name1); + if (!cname) { + JS_FreeCString(ctx, base_cname); + return NULL; + } + m = js_host_resolve_imported_module(ctx, base_cname, cname); + JS_FreeCString(ctx, base_cname); + JS_FreeCString(ctx, cname); + return m; +} + typedef struct JSResolveEntry { JSModuleDef *module; JSAtom name; @@ -25606,10 +27397,9 @@ static int add_resolve_entry(JSContext *ctx, JSResolveState *s, if (js_resize_array(ctx, (void **)&s->array, sizeof(JSResolveEntry), - &s->size, &s->count, - s->count + 1)) + &s->size, s->count + 1)) return -1; - re = &s->array[s->count - 1]; + re = &s->array[s->count++]; re->module = m; re->name = JS_DupAtom(ctx, name); return 0; @@ -25734,20 +27524,20 @@ static void js_resolve_export_throw_error(JSContext *ctx, break; default: case JS_RESOLVE_RES_NOT_FOUND: - JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), - JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); - break; - case JS_RESOLVE_RES_CIRCULAR: JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); break; - case JS_RESOLVE_RES_AMBIGUOUS: + case JS_RESOLVE_RES_CIRCULAR: JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); break; + case JS_RESOLVE_RES_AMBIGUOUS: + JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", + JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), + JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); + break; } } @@ -25801,9 +27591,9 @@ static __exception int get_exported_names(JSContext *ctx, return 0; } if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]), - &s->modules_size, &s->modules_count, s->modules_count + 1)) + &s->modules_size, s->modules_count + 1)) return -1; - s->modules[s->modules_count - 1] = m; + s->modules[s->modules_count++] = m; for(i = 0; i < m->export_entries_count; i++) { JSExportEntry *me = &m->export_entries[i]; @@ -25812,9 +27602,10 @@ static __exception int get_exported_names(JSContext *ctx, j = find_exported_name(s, me->export_name); if (j < 0) { if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]), - &s->exported_names_size, &s->exported_names_count, s->exported_names_count + 1)) + &s->exported_names_size, + s->exported_names_count + 1)) return -1; - en = &s->exported_names[s->exported_names_count - 1]; + en = &s->exported_names[s->exported_names_count++]; en->export_name = me->export_name; /* avoid a second lookup for simple module exports */ if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL) @@ -25871,19 +27662,11 @@ static int exported_names_cmp(const void *p1, const void *p2, void *opaque) static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m); -static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, - void *opaque) +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque) { JSModuleDef *m = opaque; - JSValue val, this_val = JS_MKPTR(JS_TAG_OBJECT, p); - - val = js_get_module_ns(ctx, m); - if (JS_IsException(val)) - return -1; - if (JS_DefinePropertyValue(ctx, this_val, atom, val, - JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) - return -1; - return 0; + return js_get_module_ns(ctx, m); } static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) @@ -25966,7 +27749,7 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) /* the exported namespace must be created on demand */ if (JS_DefineAutoInitProperty(ctx, obj, en->export_name, - js_module_ns_autoinit, + JS_AUTOINIT_ID_MODULE_NS, en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) goto fail; break; @@ -26019,8 +27802,8 @@ static int js_resolve_module(JSContext *ctx, JSModuleDef *m) /* resolve each requested module */ for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; - m1 = js_host_resolve_imported_module(ctx, m->module_name, - rme->module_name); + m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, + rme->module_name); if (!m1) return -1; rme->module = m1; @@ -26039,19 +27822,18 @@ static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical) if (!var_ref) return NULL; var_ref->header.ref_count = 1; - var_ref->gc_header.mark = 0; - var_ref->link.prev = NULL; - var_ref->link.next = NULL; if (is_lexical) var_ref->value = JS_UNINITIALIZED; else var_ref->value = JS_UNDEFINED; var_ref->pvalue = &var_ref->value; + var_ref->is_detached = TRUE; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); return var_ref; } /* Create the function associated with the module */ -static int js_create_module_function(JSContext *ctx, JSModuleDef *m) +static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) { JSFunctionBytecode *b; int i; @@ -26102,20 +27884,15 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) return -1; } -/* Prepare a module to be executed by resolving all the imported - variables. */ -static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) +/* must be done before js_link_module() because of cyclic references */ +static int js_create_module_function(JSContext *ctx, JSModuleDef *m) { - int i; - JSImportEntry *mi; - JSModuleDef *m1; - JSVarRef **var_refs, *var_ref; - JSObject *p; BOOL is_c_module; - - if (m->instantiated) + int i; + JSVarRef *var_ref; + + if (m->func_created) return 0; - m->instantiated = TRUE; is_c_module = (m->init_func != NULL); @@ -26126,18 +27903,54 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) if (me->export_type == JS_EXPORT_TYPE_LOCAL) { var_ref = js_create_module_var(ctx, FALSE); if (!var_ref) - goto fail; + return -1; me->u.local.var_ref = var_ref; } } } else { - if (js_create_module_function(ctx, m) < 0) - goto fail; + if (js_create_module_bytecode_function(ctx, m)) + return -1; } + m->func_created = TRUE; + + /* do it on the dependencies */ + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + if (js_create_module_function(ctx, rme->module) < 0) + return -1; + } + + return 0; +} + + +/* Prepare a module to be executed by resolving all the imported + variables. */ +static int js_link_module(JSContext *ctx, JSModuleDef *m) +{ + int i; + JSImportEntry *mi; + JSModuleDef *m1; + JSVarRef **var_refs, *var_ref; + JSObject *p; + BOOL is_c_module; + JSValue ret_val; + + if (m->instantiated) + return 0; + m->instantiated = TRUE; + +#ifdef DUMP_MODULE_RESOLVE + { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (js_instantiate_module(ctx, rme->module) < 0) + if (js_link_module(ctx, rme->module) < 0) goto fail; } @@ -26176,6 +27989,8 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) } #endif + is_c_module = (m->init_func != NULL); + if (!is_c_module) { p = JS_VALUE_GET_OBJ(m->func_obj); var_refs = p->u.func.var_refs; @@ -26254,6 +28069,12 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) me->u.local.var_ref = var_ref; } } + + /* initialize the global variables */ + ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL); + if (JS_IsException(ret_val)) + goto fail; + JS_FreeValue(ctx, ret_val); } #ifdef DUMP_MODULE_RESOLVE @@ -26264,8 +28085,9 @@ static int js_instantiate_module(JSContext *ctx, JSModuleDef *m) return -1; } -/* warning: the returned atom is not allocated */ -static JSAtom js_get_script_or_module_name(JSContext *ctx) +/* return JS_ATOM_NULL if the name cannot be found. Only works with + not striped bytecode functions. */ +JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) { JSStackFrame *sf; JSFunctionBytecode *b; @@ -26273,15 +28095,23 @@ static JSAtom js_get_script_or_module_name(JSContext *ctx) /* XXX: currently we just use the filename of the englobing function. It does not work for eval(). Need to add a ScriptOrModule info in JSFunctionBytecode */ - sf = ctx->current_stack_frame; - assert(sf != NULL); - assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT); + sf = ctx->rt->current_stack_frame; + if (!sf) + return JS_ATOM_NULL; + while (n_stack_levels-- > 0) { + sf = sf->prev_frame; + if (!sf) + return JS_ATOM_NULL; + } + if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) + return JS_ATOM_NULL; p = JS_VALUE_GET_OBJ(sf->cur_func); - assert(js_class_has_bytecode(p->class_id)); + if (!js_class_has_bytecode(p->class_id)) + return JS_ATOM_NULL; b = p->u.func.function_bytecode; if (!b->has_debug) return JS_ATOM_NULL; - return b->debug.filename; + return JS_DupAtom(ctx, b->debug.filename); } JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) @@ -26308,13 +28138,14 @@ static JSValue js_import_meta(JSContext *ctx) JSAtom filename; JSModuleDef *m; - filename = js_get_script_or_module_name(ctx); + filename = JS_GetScriptOrModuleName(ctx, 0); if (filename == JS_ATOM_NULL) goto fail; /* XXX: inefficient, need to add a module or script pointer in JSFunctionBytecode */ m = js_find_loaded_module(ctx, filename); + JS_FreeAtom(ctx, filename); if (!m) { fail: JS_ThrowTypeError(ctx, "import.meta not supported in this context"); @@ -26323,68 +28154,109 @@ static JSValue js_import_meta(JSContext *ctx) return JS_GetImportMeta(ctx, m); } -static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) +/* used by os.Worker() and import() */ +JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, + const char *filename) { JSModuleDef *m; - JSAtom basename, filename; - JSValue promise, resolving_funcs[2]; - JSValue specifierString, ret, func_obj, err, ns; + JSValue ret, func_obj; - promise = JS_NewPromiseCapability(ctx, resolving_funcs); - if (JS_IsException(promise)) - return promise; - - basename = js_get_script_or_module_name(ctx); - if (basename == JS_ATOM_NULL) { - JS_ThrowTypeError(ctx, "no function filename for import()"); - goto exception; - } - - specifierString = JS_ToString(ctx, specifier); - if (JS_IsException(specifierString)) - goto exception; - filename = JS_ValueToAtom(ctx, specifierString); - JS_FreeValue(ctx, specifierString); - if (filename == JS_ATOM_NULL) - goto exception; - m = js_host_resolve_imported_module(ctx, basename, filename); - JS_FreeAtom(ctx, filename); - if (!m) { - goto exception; - } + if (!m) + return NULL; if (js_resolve_module(ctx, m) < 0) { js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); - goto exception; + return NULL; } /* Evaluate the module code */ func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); ret = JS_EvalFunction(ctx, func_obj); if (JS_IsException(ret)) - goto exception; + return NULL; JS_FreeValue(ctx, ret); + return m; +} + +static JSValue js_dynamic_import_job(JSContext *ctx, + int argc, JSValueConst *argv) +{ + JSValueConst *resolving_funcs = argv; + JSValueConst basename_val = argv[2]; + JSValueConst specifier = argv[3]; + JSModuleDef *m; + const char *basename = NULL, *filename; + JSValue ret, err, ns; + + if (!JS_IsString(basename_val)) { + JS_ThrowTypeError(ctx, "no function filename for import()"); + goto exception; + } + basename = JS_ToCString(ctx, basename_val); + if (!basename) + goto exception; + + filename = JS_ToCString(ctx, specifier); + if (!filename) + goto exception; + + m = JS_RunModule(ctx, basename, filename); + JS_FreeCString(ctx, filename); + if (!m) + goto exception; /* return the module namespace */ ns = js_get_module_ns(ctx, m); - if (JS_IsException(ret)) + if (JS_IsException(ns)) goto exception; ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, 1, (JSValueConst *)&ns); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, ns); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; - + JS_FreeCString(ctx, basename); + return JS_UNDEFINED; exception: + err = JS_GetException(ctx); ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, (JSValueConst *)&err); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, err); + JS_FreeCString(ctx, basename); + return JS_UNDEFINED; +} + +static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) +{ + JSAtom basename; + JSValue promise, resolving_funcs[2], basename_val; + JSValueConst args[4]; + + basename = JS_GetScriptOrModuleName(ctx, 0); + if (basename == JS_ATOM_NULL) + basename_val = JS_NULL; + else + basename_val = JS_AtomToValue(ctx, basename); + JS_FreeAtom(ctx, basename); + if (JS_IsException(basename_val)) + return basename_val; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) { + JS_FreeValue(ctx, basename_val); + return promise; + } + + args[0] = resolving_funcs[0]; + args[1] = resolving_funcs[1]; + args[2] = basename_val; + args[3] = specifier; + + JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args); + + JS_FreeValue(ctx, basename_val); JS_FreeValue(ctx, resolving_funcs[0]); JS_FreeValue(ctx, resolving_funcs[1]); return promise; @@ -26439,7 +28311,7 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) if (JS_IsException(ret_val)) { /* save the thrown exception value */ m->eval_has_exception = TRUE; - m->eval_exception = JS_DupValue(ctx, ctx->current_exception); + m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception); } m->eval_mark = FALSE; m->evaluated = TRUE; @@ -26602,7 +28474,7 @@ static __exception int js_parse_export(JSParseState *s) s->token.ptr, s->token.line_num, JS_PARSE_EXPORT_DEFAULT, NULL); } else { - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) return -1; } /* set the name of anonymous functions */ @@ -26617,8 +28489,8 @@ static __exception int js_parse_export(JSParseState *s) emit_atom(s, local_name); emit_u16(s, 0); - if (add_export_entry(s, m, local_name, JS_ATOM_default, - JS_EXPORT_TYPE_LOCAL) < 0) + if (!add_export_entry(s, m, local_name, JS_ATOM_default, + JS_EXPORT_TYPE_LOCAL)) return -1; break; case TOK_VAR: @@ -26663,10 +28535,10 @@ static int add_import(JSParseState *s, JSModuleDef *m, return -1; if (js_resize_array(ctx, (void **)&m->import_entries, sizeof(JSImportEntry), - &m->import_entries_size, &m->import_entries_count, + &m->import_entries_size, m->import_entries_count + 1)) return -1; - mi = &m->import_entries[m->import_entries_count - 1]; + mi = &m->import_entries[m->import_entries_count++]; mi->import_name = JS_DupAtom(ctx, import_name); mi->var_idx = var_idx; return 0; @@ -26719,6 +28591,10 @@ static __exception int js_parse_import(JSParseState *s) return js_parse_error(s, "expecting 'as'"); if (next_token(s)) return -1; + if (!token_is_ident(s->token.val)) { + js_parse_error(s, "identifier expected"); + return -1; + } local_name = JS_DupAtom(ctx, s->token.u.ident.atom); import_name = JS_ATOM__star_; if (next_token(s)) @@ -26841,7 +28717,9 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, fd->last_opcode_pos = -1; fd->func_name = JS_ATOM_NULL; fd->var_object_idx = -1; + fd->arg_var_object_idx = -1; fd->arguments_var_idx = -1; + fd->arguments_arg_idx = -1; fd->func_var_idx = -1; fd->eval_ret_idx = -1; fd->this_var_idx = -1; @@ -26850,13 +28728,14 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx, fd->home_object_var_idx = -1; /* XXX: should distinguish arg, var and var object and body scopes */ - fd->scope_level = 0; /* 0: var/arg scope, 1:body scope */ - fd->scope_first = -1; fd->scopes = fd->def_scope_array; fd->scope_size = countof(fd->def_scope_array); fd->scope_count = 1; fd->scopes[0].first = -1; fd->scopes[0].parent = -1; + fd->scope_level = 0; /* 0: var/arg scope */ + fd->scope_first = -1; + fd->body_scope = -1; fd->filename = JS_NewAtom(ctx, filename); fd->line_num = line_num; @@ -26937,10 +28816,10 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) } js_free(ctx, fd->args); - for(i = 0; i < fd->hoisted_def_count; i++) { - JS_FreeAtom(ctx, fd->hoisted_def[i].var_name); + for(i = 0; i < fd->global_var_count; i++) { + JS_FreeAtom(ctx, fd->global_vars[i].var_name); } - js_free(ctx, fd->hoisted_def); + js_free(ctx, fd->global_vars); for(i = 0; i < fd->closure_var_count; i++) { JSClosureVar *cv = &fd->closure_var[i]; @@ -27393,20 +29272,10 @@ static int add_closure_var(JSContext *ctx, JSFunctionDef *s, return -1; } - if (s->closure_var_count >= s->closure_var_size) { - JSClosureVar *new_tab; - int new_size; - size_t slack; - new_size = max_int(s->closure_var_count + 1, - s->closure_var_size * 3 / 2); - new_tab = js_realloc2(ctx, s->closure_var, - new_size * sizeof(JSClosureVar), &slack); - if (!new_tab) - return -1; - new_size += slack / sizeof(*new_tab); - s->closure_var = new_tab; - s->closure_var_size = new_size; - } + if (js_resize_array(ctx, (void **)&s->closure_var, + sizeof(s->closure_var[0]), + &s->closure_var_size, s->closure_var_count + 1)) + return -1; cv = &s->closure_var[s->closure_var_count++]; cv->is_local = is_local; cv->is_arg = is_arg; @@ -27484,7 +29353,7 @@ static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos) return (bc_buf[pos + 1] == OP_put_ref_value && (opcode == OP_insert3 || opcode == OP_perm4 || - //opcode == OP_nop || + opcode == OP_nop || opcode == OP_rot3l)); } @@ -27494,7 +29363,7 @@ static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos) return (bc_buf[pos + 1] == OP_put_ref_value && (opcode == OP_insert3 || opcode == OP_perm4 || - //opcode == OP_nop || + opcode == OP_nop || opcode == OP_rot3l)); } @@ -27576,20 +29445,22 @@ static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s, end_pos = label_pos + 2; op = bc_buf[label_pos]; if (is_strict) { - switch(op) { - case OP_insert3: - op = OP_insert2; - break; - case OP_perm4: - op = OP_perm3; - break; - case OP_rot3l: - op = OP_swap; - break; - default: - abort(); + if (op != OP_nop) { + switch(op) { + case OP_insert3: + op = OP_insert2; + break; + case OP_perm4: + op = OP_perm3; + break; + case OP_rot3l: + op = OP_swap; + break; + default: + abort(); + } + bc_buf[pos++] = op; } - bc_buf[pos++] = op; } else { if (op == OP_insert3) bc_buf[pos++] = OP_dup; @@ -27632,19 +29503,27 @@ static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, switch(var_name) { case JS_ATOM_home_object: /* 'home_object' pseudo variable */ - var_idx = s->home_object_var_idx = add_var(ctx, s, var_name); + if (s->home_object_var_idx < 0) + s->home_object_var_idx = add_var(ctx, s, var_name); + var_idx = s->home_object_var_idx; break; case JS_ATOM_this_active_func: /* 'this.active_func' pseudo variable */ - var_idx = s->this_active_func_var_idx = add_var(ctx, s, var_name); + if (s->this_active_func_var_idx < 0) + s->this_active_func_var_idx = add_var(ctx, s, var_name); + var_idx = s->this_active_func_var_idx; break; case JS_ATOM_new_target: /* 'new.target' pseudo variable */ - var_idx = s->new_target_var_idx = add_var(ctx, s, var_name); + if (s->new_target_var_idx < 0) + s->new_target_var_idx = add_var(ctx, s, var_name); + var_idx = s->new_target_var_idx; break; case JS_ATOM_this: /* 'this' pseudo variable */ - var_idx = s->this_var_idx = add_var_this(ctx, s); + if (s->this_var_idx < 0) + s->this_var_idx = add_var_this(ctx, s); + var_idx = s->this_var_idx; break; default: var_idx = -1; @@ -27653,18 +29532,32 @@ static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, return var_idx; } +/* test if 'var_name' is in the variable object on the stack. If is it + the case, handle it and jump to 'label_done' */ +static void var_object_test(JSContext *ctx, JSFunctionDef *s, + JSAtom var_name, int op, DynBuf *bc, + int *plabel_done, BOOL is_with) +{ + dbuf_putc(bc, get_with_scope_opcode(op)); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + *plabel_done = new_label_fd(s, *plabel_done); + dbuf_put_u32(bc, *plabel_done); + dbuf_putc(bc, is_with); + update_label(s, *plabel_done, 1); + s->jump_size++; +} + /* return the position of the next opcode */ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, JSAtom var_name, int scope_level, int op, DynBuf *bc, uint8_t *bc_buf, - LabelSlot *ls, int pos_next, int arg_valid) + LabelSlot *ls, int pos_next) { int idx, var_idx, is_put; int label_done; - BOOL is_func_var = FALSE; JSFunctionDef *fd; JSVarDef *vd; - BOOL is_pseudo_var; + BOOL is_pseudo_var, is_arg_scope; label_done = -1; @@ -27682,12 +29575,11 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (vd->var_name == var_name) { if (op == OP_scope_put_var || op == OP_scope_make_ref) { if (vd->is_const) { - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); goto done; } - is_func_var = vd->is_func_var; } var_idx = idx; break; @@ -27695,33 +29587,16 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { dbuf_putc(bc, OP_get_loc); dbuf_put_u16(bc, idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, 1); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, 1); } idx = vd->scope_next; } + is_arg_scope = (idx == ARG_SCOPE_END); if (var_idx < 0) { - /* XXX: scoping issues: - should not resolve vars from the function body during argument parse, - `arguments` and function-name should not be hidden by later vars. - */ - var_idx = find_var(ctx, s, var_name); - if (var_idx >= 0) { - if (scope_level == 0 - && (var_idx & ARGUMENT_VAR_OFFSET) - && (var_idx - ARGUMENT_VAR_OFFSET) >= arg_valid) { - /* referring to an uninitialized argument */ - dbuf_putc(bc, OP_throw_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - dbuf_putc(bc, JS_THROW_VAR_UNINITIALIZED); - } - if (!(var_idx & ARGUMENT_VAR_OFFSET)) - is_func_var = s->vars[var_idx].is_func_var; + /* argument scope: variables are not visible but pseudo + variables are visible */ + if (!is_arg_scope) { + var_idx = find_var(ctx, s, var_name); } if (var_idx < 0 && is_pseudo_var) @@ -27730,21 +29605,31 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (var_idx < 0 && var_name == JS_ATOM_arguments && s->has_arguments_binding) { /* 'arguments' pseudo variable */ - var_idx = add_arguments_var(ctx, s, var_name); + var_idx = add_arguments_var(ctx, s); } if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) { /* add a new variable with the function name */ var_idx = add_func_var(ctx, s, var_name); - is_func_var = TRUE; } } if (var_idx >= 0) { + if ((op == OP_scope_put_var || op == OP_scope_make_ref) && + !(var_idx & ARGUMENT_VAR_OFFSET) && + s->vars[var_idx].is_const) { + /* only happens when assigning a function expression name + in strict mode */ + dbuf_putc(bc, OP_throw_error); + dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); + dbuf_putc(bc, JS_THROW_VAR_RO); + goto done; + } /* OP_scope_put_var_init is only used to initialize a lexical variable, so it is never used in a with or var object. It can be used with a closure (module global variable case). */ switch (op) { case OP_scope_make_ref: - if (is_func_var) { + if (!(var_idx & ARGUMENT_VAR_OFFSET) && + s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { /* Create a dummy object reference for the func_var */ dbuf_putc(bc, OP_object); dbuf_putc(bc, OP_get_loc); @@ -27792,7 +29677,6 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (var_idx & ARGUMENT_VAR_OFFSET) { dbuf_putc(bc, OP_get_arg + is_put); dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); - /* XXX: should test if argument reference needs TDZ check */ } else { if (is_put) { if (s->vars[var_idx].is_lexical) { @@ -27825,41 +29709,32 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, goto done; } /* check eval object */ - if (s->var_object_idx >= 0 && !is_pseudo_var) { + if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) { dbuf_putc(bc, OP_get_loc); dbuf_put_u16(bc, s->var_object_idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, 0); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); } + /* check eval object in argument scope */ + if (s->arg_var_object_idx >= 0 && !is_pseudo_var) { + dbuf_putc(bc, OP_get_loc); + dbuf_put_u16(bc, s->arg_var_object_idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); + } + /* check parent scopes */ for (fd = s; fd->parent;) { scope_level = fd->parent_scope_level; fd = fd->parent; - if (scope_level == 0) { - /* function is defined as part of the argument parsing: hide vars - from the function body. - XXX: variables created from argument destructuring might need - to be visible, should refine this method. - */ - var_idx = find_arg(ctx, fd, var_name); - goto check_idx; - } for (idx = fd->scopes[scope_level].first; idx >= 0;) { vd = &fd->vars[idx]; if (vd->var_name == var_name) { if (op == OP_scope_put_var || op == OP_scope_make_ref) { if (vd->is_const) { - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); goto done; } - is_func_var = vd->is_func_var; } var_idx = idx; break; @@ -27869,26 +29744,19 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, if (idx >= 0) { dbuf_putc(bc, OP_get_var_ref); dbuf_put_u16(bc, idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, 1); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, 1); } } idx = vd->scope_next; } + is_arg_scope = (idx == ARG_SCOPE_END); if (var_idx >= 0) break; - - var_idx = find_var(ctx, fd, var_name); - check_idx: - if (var_idx >= 0) { - if (!(var_idx & ARGUMENT_VAR_OFFSET)) - is_func_var = fd->vars[var_idx].is_func_var; - break; + + if (!is_arg_scope) { + var_idx = find_var(ctx, fd, var_name); + if (var_idx >= 0) + break; } if (is_pseudo_var) { var_idx = resolve_pseudo_var(ctx, fd, var_name); @@ -27896,33 +29764,39 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, break; } if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) { - var_idx = add_arguments_var(ctx, fd, var_name); + var_idx = add_arguments_var(ctx, fd); break; } if (fd->is_func_expr && fd->func_name == var_name) { /* add a new variable with the function name */ var_idx = add_func_var(ctx, fd, var_name); - is_func_var = TRUE; break; } /* check eval object */ - if (fd->var_object_idx >= 0 && !is_pseudo_var) { - fd->vars[fd->var_object_idx].is_captured = 1; + if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { + vd = &fd->vars[fd->var_object_idx]; + vd->is_captured = 1; idx = get_closure_var(ctx, s, fd, FALSE, - fd->var_object_idx, JS_ATOM__var_, + fd->var_object_idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); dbuf_putc(bc, OP_get_var_ref); dbuf_put_u16(bc, idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, 0); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); } + /* check eval object in argument scope */ + if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { + vd = &fd->vars[fd->arg_var_object_idx]; + vd->is_captured = 1; + idx = get_closure_var(ctx, s, fd, FALSE, + fd->arg_var_object_idx, vd->var_name, + FALSE, FALSE, JS_VAR_NORMAL); + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + var_object_test(ctx, s, var_name, op, bc, &label_done, 0); + } + if (fd->is_eval) break; /* it it necessarily the top level function */ } @@ -27947,6 +29821,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } goto has_idx; } else if ((cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_ || cv->var_name == JS_ATOM__with_) && !is_pseudo_var) { int is_with = (cv->var_name == JS_ATOM__with_); if (fd != s) { @@ -27960,13 +29835,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } dbuf_putc(bc, OP_get_var_ref); dbuf_put_u16(bc, idx); - dbuf_putc(bc, get_with_scope_opcode(op)); - dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); - label_done = new_label_fd(s, label_done); - dbuf_put_u32(bc, label_done); - dbuf_putc(bc, is_with); - update_label(s, label_done, 1); - s->jump_size++; + var_object_test(ctx, s, var_name, op, bc, &label_done, is_with); } } } @@ -27991,14 +29860,14 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, has_idx: if ((op == OP_scope_put_var || op == OP_scope_make_ref) && s->closure_var[idx].is_const) { - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); goto done; } switch (op) { case OP_scope_make_ref: - if (is_func_var) { + if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { /* Create a dummy object reference for the func_var */ dbuf_putc(bc, OP_object); dbuf_putc(bc, OP_get_var_ref); @@ -28128,12 +29997,12 @@ static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd, static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx) { - /* Note: the private field can be uninitialized, so the _check is - necessary */ + /* if the field is not initialized, the error is catched when + accessing it */ if (is_ref) - dbuf_putc(bc, OP_get_var_ref_check); + dbuf_putc(bc, OP_get_var_ref); else - dbuf_putc(bc, OP_get_loc_check); + dbuf_putc(bc, OP_get_loc); dbuf_put_u16(bc, idx); } @@ -28162,8 +30031,6 @@ static int resolve_scope_private_field1(JSContext *ctx, } scope_level = fd->parent_scope_level; if (!fd->parent) { - char buf[ATOM_GET_STR_BUF_SIZE]; - if (fd->is_eval) { /* closure of the eval function (top level) */ for (idx = 0; idx < fd->closure_var_count; idx++) { @@ -28186,8 +30053,8 @@ static int resolve_scope_private_field1(JSContext *ctx, } } /* XXX: no line number info */ - JS_ThrowSyntaxError(ctx, "undefined private field %s", - JS_AtomGetStr(ctx, buf, sizeof(buf), var_name)); + JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'", + var_name); return -1; } else { fd = fd->parent; @@ -28240,7 +30107,7 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, break; case JS_VAR_PRIVATE_SETTER: /* XXX: add clearer error message */ - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); break; @@ -28257,7 +30124,7 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, case JS_VAR_PRIVATE_METHOD: case JS_VAR_PRIVATE_GETTER: /* XXX: add clearer error message */ - dbuf_putc(bc, OP_throw_var); + dbuf_putc(bc, OP_throw_error); dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); dbuf_putc(bc, JS_THROW_VAR_RO); break; @@ -28309,17 +30176,33 @@ static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, } } +/* XXX: should handle the argument scope generically */ +static BOOL is_var_in_arg_scope(const JSVarDef *vd) +{ + return (vd->var_name == JS_ATOM_home_object || + vd->var_name == JS_ATOM_this_active_func || + vd->var_name == JS_ATOM_new_target || + vd->var_name == JS_ATOM_this || + vd->var_name == JS_ATOM__arg_var_ || + vd->var_kind == JS_VAR_FUNCTION_NAME); +} + static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) { JSFunctionDef *fd; JSVarDef *vd; int i, scope_level, scope_idx; - BOOL has_arguments_binding, has_this_binding; + BOOL has_arguments_binding, has_this_binding, is_arg_scope; /* in non strict mode, variables are created in the caller's environment object */ if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) { s->var_object_idx = add_var(ctx, s, JS_ATOM__var_); + if (s->has_parameter_expressions) { + /* an additional variable object is needed for the + argument scope */ + s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_); + } } /* eval can potentially use 'arguments' so we must define it */ @@ -28335,8 +30218,14 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object); } has_arguments_binding = s->has_arguments_binding; - if (has_arguments_binding) - add_arguments_var(ctx, s, JS_ATOM_arguments); + if (has_arguments_binding) { + add_arguments_var(ctx, s); + /* also add an arguments binding in the argument scope to + raise an error if a direct eval in the argument scope tries + to redefine it */ + if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT)) + add_arguments_arg(ctx, s); + } if (s->is_func_expr && s->func_name != JS_ATOM_NULL) add_func_var(ctx, s, s->func_name); @@ -28353,7 +30242,6 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) fd = fd->parent; if (!fd) break; - scope_idx = fd->scopes[scope_level].first; /* add 'this' if it was not previously added */ if (!has_this_binding && fd->has_this_binding) { if (fd->this_var_idx < 0) @@ -28368,7 +30256,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) } /* add 'arguments' if it was not previously added */ if (!has_arguments_binding && fd->has_arguments_binding) { - add_arguments_var(ctx, fd, JS_ATOM_arguments); + add_arguments_var(ctx, fd); has_arguments_binding = TRUE; } /* add function name */ @@ -28376,6 +30264,7 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) add_func_var(ctx, fd, fd->func_name); /* add lexical variables */ + scope_idx = fd->scopes[scope_level].first; while (scope_idx >= 0) { vd = &fd->vars[scope_idx]; vd->is_captured = 1; @@ -28383,24 +30272,37 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind); scope_idx = vd->scope_next; } - /* add unscoped variables */ - for(i = 0; i < fd->arg_count; i++) { - vd = &fd->args[i]; - if (vd->var_name != JS_ATOM_NULL) { - get_closure_var(ctx, s, fd, - TRUE, i, vd->var_name, FALSE, FALSE, - JS_VAR_NORMAL); + is_arg_scope = (scope_idx == ARG_SCOPE_END); + if (!is_arg_scope) { + /* add unscoped variables */ + for(i = 0; i < fd->arg_count; i++) { + vd = &fd->args[i]; + if (vd->var_name != JS_ATOM_NULL) { + get_closure_var(ctx, s, fd, + TRUE, i, vd->var_name, FALSE, FALSE, + JS_VAR_NORMAL); + } } - } - for(i = 0; i < fd->var_count; i++) { - vd = &fd->vars[i]; - /* do not close top level last result */ - if (vd->scope_level == 0 && - vd->var_name != JS_ATOM__ret_ && - vd->var_name != JS_ATOM_NULL) { - get_closure_var(ctx, s, fd, - FALSE, i, vd->var_name, FALSE, FALSE, - JS_VAR_NORMAL); + for(i = 0; i < fd->var_count; i++) { + vd = &fd->vars[i]; + /* do not close top level last result */ + if (vd->scope_level == 0 && + vd->var_name != JS_ATOM__ret_ && + vd->var_name != JS_ATOM_NULL) { + get_closure_var(ctx, s, fd, + FALSE, i, vd->var_name, FALSE, FALSE, + JS_VAR_NORMAL); + } + } + } else { + for(i = 0; i < fd->var_count; i++) { + vd = &fd->vars[i]; + /* do not close top level last result */ + if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { + get_closure_var(ctx, s, fd, + FALSE, i, vd->var_name, FALSE, FALSE, + JS_VAR_NORMAL); + } } } if (fd->is_eval) { @@ -28418,6 +30320,18 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) } } +static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, + JSVarDef *vd, int var_idx) +{ + cv->is_local = TRUE; + cv->is_arg = FALSE; + cv->is_const = vd->is_const; + cv->is_lexical = vd->is_lexical; + cv->var_kind = vd->var_kind; + cv->var_idx = var_idx; + cv->var_name = JS_DupAtom(ctx, vd->var_name); +} + /* for direct eval compilation: add references to the variables of the calling function */ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, @@ -28425,7 +30339,8 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, { int i, count; JSVarDef *vd; - + BOOL is_arg_scope; + count = b->arg_count + b->var_count + b->closure_var_count; s->closure_var = NULL; s->closure_var_count = 0; @@ -28440,41 +30355,41 @@ static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, vd = &b->vardefs[b->arg_count + i]; if (vd->scope_level > 0) { JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - cv->is_local = TRUE; - cv->is_arg = FALSE; - cv->is_const = vd->is_const; - cv->is_lexical = vd->is_lexical; - cv->var_kind = vd->var_kind; - cv->var_idx = i; - cv->var_name = JS_DupAtom(ctx, vd->var_name); + set_closure_from_var(ctx, cv, vd, i); } i = vd->scope_next; } - /* Add argument variables */ - for(i = 0; i < b->arg_count; i++) { - JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; - vd = &b->vardefs[i]; - cv->is_local = TRUE; - cv->is_arg = TRUE; - cv->is_const = FALSE; - cv->is_lexical = FALSE; - cv->var_kind = JS_VAR_NORMAL; - cv->var_idx = i; - cv->var_name = JS_DupAtom(ctx, vd->var_name); - } - /* Add local non lexical variables */ - for(i = 0; i < b->var_count; i++) { - vd = &b->vardefs[b->arg_count + i]; - if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) { + is_arg_scope = (i == ARG_SCOPE_END); + if (!is_arg_scope) { + /* Add argument variables */ + for(i = 0; i < b->arg_count; i++) { JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + vd = &b->vardefs[i]; cv->is_local = TRUE; - cv->is_arg = FALSE; + cv->is_arg = TRUE; cv->is_const = FALSE; cv->is_lexical = FALSE; cv->var_kind = JS_VAR_NORMAL; cv->var_idx = i; cv->var_name = JS_DupAtom(ctx, vd->var_name); } + /* Add local non lexical variables */ + for(i = 0; i < b->var_count; i++) { + vd = &b->vardefs[b->arg_count + i]; + if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) { + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + set_closure_from_var(ctx, cv, vd, i); + } + } + } else { + /* only add pseudo variables */ + for(i = 0; i < b->var_count; i++) { + vd = &b->vardefs[b->arg_count + i]; + if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { + JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; + set_closure_from_var(ctx, cv, vd, i); + } + } } for(i = 0; i < b->closure_var_count; i++) { JSClosureVar *cv0 = &b->closure_var[i]; @@ -28632,59 +30547,93 @@ static BOOL code_match(CodeContext *s, int pos, ...) static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc) { - int i, idx, var_idx; + int i, idx, label_next = -1; - /* add the hoisted functions and variables */ - for(i = 0; i < s->hoisted_def_count; i++) { - JSHoistedDef *hf = &s->hoisted_def[i]; + /* add the hoisted functions in arguments and local variables */ + for(i = 0; i < s->arg_count; i++) { + JSVarDef *vd = &s->args[i]; + if (vd->func_pool_idx >= 0) { + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, vd->func_pool_idx); + dbuf_putc(bc, OP_put_arg); + dbuf_put_u16(bc, i); + } + } + for(i = 0; i < s->var_count; i++) { + JSVarDef *vd = &s->vars[i]; + if (vd->scope_level == 0 && vd->func_pool_idx >= 0) { + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, vd->func_pool_idx); + dbuf_putc(bc, OP_put_loc); + dbuf_put_u16(bc, i); + } + } + + /* the module global variables must be initialized before + evaluating the module so that the exported functions are + visible if there are cyclic module references */ + if (s->module) { + label_next = new_label_fd(s, -1); + + /* if 'this' is true, initialize the global variables and return */ + dbuf_putc(bc, OP_push_this); + dbuf_putc(bc, OP_if_false); + dbuf_put_u32(bc, label_next); + update_label(s, label_next, 1); + s->jump_size++; + } + + /* add the global variables (only happens if s->is_global_var is + true) */ + for(i = 0; i < s->global_var_count; i++) { + JSGlobalVar *hf = &s->global_vars[i]; int has_closure = 0; BOOL force_init = hf->force_init; - if (s->is_global_var && hf->var_name != JS_ATOM_NULL) { - /* we are in an eval, so the closure contains all the - enclosing variables */ - /* If the outer function has a variable environment, - create a property for the variable there */ - for(idx = 0; idx < s->closure_var_count; idx++) { - JSClosureVar *cv = &s->closure_var[idx]; - if (cv->var_name == hf->var_name) { - has_closure = 2; - force_init = FALSE; - break; - } - if (cv->var_name == JS_ATOM__var_) { - dbuf_putc(bc, OP_get_var_ref); - dbuf_put_u16(bc, idx); - has_closure = 1; - force_init = TRUE; - break; - } + /* we are in an eval, so the closure contains all the + enclosing variables */ + /* If the outer function has a variable environment, + create a property for the variable there */ + for(idx = 0; idx < s->closure_var_count; idx++) { + JSClosureVar *cv = &s->closure_var[idx]; + if (cv->var_name == hf->var_name) { + has_closure = 2; + force_init = FALSE; + break; } - if (!has_closure) { - int flags; - - flags = 0; - if (s->eval_type != JS_EVAL_TYPE_GLOBAL) - flags |= JS_PROP_CONFIGURABLE; - if (hf->cpool_idx >= 0 && !hf->is_lexical) { - /* global function definitions need a specific handling */ - dbuf_putc(bc, OP_fclosure); - dbuf_put_u32(bc, hf->cpool_idx); - - dbuf_putc(bc, OP_define_func); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, flags); - - goto done_hoisted_def; - } else { - if (hf->is_lexical) { - flags |= DEFINE_GLOBAL_LEX_VAR; - if (!hf->is_const) - flags |= JS_PROP_WRITABLE; - } - dbuf_putc(bc, OP_define_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, flags); + if (cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_) { + dbuf_putc(bc, OP_get_var_ref); + dbuf_put_u16(bc, idx); + has_closure = 1; + force_init = TRUE; + break; + } + } + if (!has_closure) { + int flags; + + flags = 0; + if (s->eval_type != JS_EVAL_TYPE_GLOBAL) + flags |= JS_PROP_CONFIGURABLE; + if (hf->cpool_idx >= 0 && !hf->is_lexical) { + /* global function definitions need a specific handling */ + dbuf_putc(bc, OP_fclosure); + dbuf_put_u32(bc, hf->cpool_idx); + + dbuf_putc(bc, OP_define_func); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, flags); + + goto done_global_var; + } else { + if (hf->is_lexical) { + flags |= DEFINE_GLOBAL_LEX_VAR; + if (!hf->is_const) + flags |= JS_PROP_WRITABLE; } + dbuf_putc(bc, OP_define_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, flags); } } if (hf->cpool_idx >= 0 || force_init) { @@ -28699,37 +30648,35 @@ static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, Dy } else { dbuf_putc(bc, OP_undefined); } - if (s->is_global_var) { - if (has_closure == 2) { - dbuf_putc(bc, OP_put_var_ref); - dbuf_put_u16(bc, idx); - } else if (has_closure == 1) { - dbuf_putc(bc, OP_define_field); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(bc, OP_drop); - } else { - /* XXX: Check if variable is writable and enumerable */ - dbuf_putc(bc, OP_put_var); - dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); - } + if (has_closure == 2) { + dbuf_putc(bc, OP_put_var_ref); + dbuf_put_u16(bc, idx); + } else if (has_closure == 1) { + dbuf_putc(bc, OP_define_field); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(bc, OP_drop); } else { - var_idx = hf->var_idx; - if (var_idx & ARGUMENT_VAR_OFFSET) { - dbuf_putc(bc, OP_put_arg); - dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); - } else { - dbuf_putc(bc, OP_put_loc); - dbuf_put_u16(bc, var_idx); - } + /* XXX: Check if variable is writable and enumerable */ + dbuf_putc(bc, OP_put_var); + dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); } } - done_hoisted_def: + done_global_var: JS_FreeAtom(ctx, hf->var_name); } - js_free(ctx, s->hoisted_def); - s->hoisted_def = NULL; - s->hoisted_def_count = 0; - s->hoisted_def_size = 0; + + if (s->module) { + dbuf_putc(bc, OP_return_undef); + + dbuf_putc(bc, OP_label); + dbuf_put_u32(bc, label_next); + s->label_slots[label_next].pos2 = bc->size; + } + + js_free(ctx, s->global_vars); + s->global_vars = NULL; + s->global_var_count = 0; + s->global_var_size = 0; } static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len, @@ -28809,7 +30756,7 @@ static int get_label_pos(JSFunctionDef *s, int label) variables when necessary */ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) { - int pos, pos_next, bc_len, op, len, i, idx, arg_valid, line_num; + int pos, pos_next, bc_len, op, len, i, idx, line_num; uint8_t *bc_buf; JSAtom var_name; DynBuf bc_out; @@ -28822,47 +30769,43 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) /* first pass for runtime checks (must be done before the variables are created) */ - if (s->is_global_var) { - for(i = 0; i < s->hoisted_def_count; i++) { - JSHoistedDef *hf = &s->hoisted_def[i]; - int flags; - - if (hf->var_name != JS_ATOM_NULL) { - /* check if global variable (XXX: simplify) */ - for(idx = 0; idx < s->closure_var_count; idx++) { - JSClosureVar *cv = &s->closure_var[idx]; - if (cv->var_name == hf->var_name) { - if (s->eval_type == JS_EVAL_TYPE_DIRECT && - cv->is_lexical) { - /* Check if a lexical variable is - redefined as 'var'. XXX: Could abort - compilation here, but for consistency - with the other checks, we delay the - error generation. */ - dbuf_putc(&bc_out, OP_throw_var); - dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); - dbuf_putc(&bc_out, JS_THROW_VAR_REDECL); - } - goto next; - } - if (cv->var_name == JS_ATOM__var_) - goto next; + for(i = 0; i < s->global_var_count; i++) { + JSGlobalVar *hf = &s->global_vars[i]; + int flags; + + /* check if global variable (XXX: simplify) */ + for(idx = 0; idx < s->closure_var_count; idx++) { + JSClosureVar *cv = &s->closure_var[idx]; + if (cv->var_name == hf->var_name) { + if (s->eval_type == JS_EVAL_TYPE_DIRECT && + cv->is_lexical) { + /* Check if a lexical variable is + redefined as 'var'. XXX: Could abort + compilation here, but for consistency + with the other checks, we delay the + error generation. */ + dbuf_putc(&bc_out, OP_throw_error); + dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); + dbuf_putc(&bc_out, JS_THROW_VAR_REDECL); } - - dbuf_putc(&bc_out, OP_check_define_var); - dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); - flags = 0; - if (hf->is_lexical) - flags |= DEFINE_GLOBAL_LEX_VAR; - if (hf->cpool_idx >= 0) - flags |= DEFINE_GLOBAL_FUNC_VAR; - dbuf_putc(&bc_out, flags); + goto next; } - next: ; + if (cv->var_name == JS_ATOM__var_ || + cv->var_name == JS_ATOM__arg_var_) + goto next; } + + dbuf_putc(&bc_out, OP_check_define_var); + dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); + flags = 0; + if (hf->is_lexical) + flags |= DEFINE_GLOBAL_LEX_VAR; + if (hf->cpool_idx >= 0) + flags |= DEFINE_GLOBAL_FUNC_VAR; + dbuf_putc(&bc_out, flags); + next: ; } - arg_valid = 0; line_num = 0; /* avoid warning */ for (pos = 0; pos < bc_len; pos = pos_next) { op = bc_buf[pos]; @@ -28874,9 +30817,6 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) s->line_number_size++; goto no_change; - case OP_set_arg_valid_upto: - arg_valid = get_u16(bc_buf + pos + 1); - break; case OP_eval: /* convert scope index to adjusted variable index */ { int call_argc = get_u16(bc_buf + pos + 1); @@ -28902,7 +30842,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) var_name = get_u32(bc_buf + pos + 1); scope = get_u16(bc_buf + pos + 5); pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, - NULL, NULL, pos_next, arg_valid); + NULL, NULL, pos_next); JS_FreeAtom(ctx, var_name); break; case OP_scope_make_ref: @@ -28915,7 +30855,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) ls = &s->label_slots[label]; ls->ref_count--; /* always remove label reference */ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, - bc_buf, ls, pos_next, arg_valid); + bc_buf, ls, pos_next); JS_FreeAtom(ctx, var_name); } break; @@ -28996,7 +30936,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) case OP_return: case OP_return_undef: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_ret: if (OPTIMIZE) { /* remove dead code */ @@ -29030,25 +30970,27 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) { int scope_idx, scope = get_u16(bc_buf + pos + 1); - if (scope == 1) { + if (scope == s->body_scope) { instantiate_hoisted_definitions(ctx, s, &bc_out); } for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { JSVarDef *vd = &s->vars[scope_idx]; if (vd->scope_level == scope) { - if (vd->var_kind == JS_VAR_FUNCTION_DECL || - vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) { - /* Initialize lexical variable upon entering scope */ - dbuf_putc(&bc_out, OP_fclosure); - dbuf_put_u32(&bc_out, vd->func_pool_or_scope_idx); - dbuf_putc(&bc_out, OP_put_loc); - dbuf_put_u16(&bc_out, scope_idx); - } else { - /* XXX: should check if variable can be used - before initialization */ - dbuf_putc(&bc_out, OP_set_loc_uninitialized); - dbuf_put_u16(&bc_out, scope_idx); + if (scope_idx != s->arguments_arg_idx) { + if (vd->var_kind == JS_VAR_FUNCTION_DECL || + vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) { + /* Initialize lexical variable upon entering scope */ + dbuf_putc(&bc_out, OP_fclosure); + dbuf_put_u32(&bc_out, vd->func_pool_idx); + dbuf_putc(&bc_out, OP_put_loc); + dbuf_put_u16(&bc_out, scope_idx); + } else { + /* XXX: should check if variable can be used + before initialization */ + dbuf_putc(&bc_out, OP_set_loc_uninitialized); + dbuf_put_u16(&bc_out, scope_idx); + } } scope_idx = vd->scope_next; } else { @@ -29451,6 +31393,8 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) dbuf_putc(&bc_out, OP_special_object); dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS); } + if (s->arguments_arg_idx >= 0) + put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx); put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx); } /* initialize a reference to the current function if needed */ @@ -29465,6 +31409,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); put_short_code(&bc_out, OP_put_loc, s->var_object_idx); } + if (s->arg_var_object_idx >= 0) { + dbuf_putc(&bc_out, OP_special_object); + dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); + put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx); + } for (pos = 0; pos < bc_len; pos = pos_next) { int val; @@ -29472,20 +31421,6 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) len = opcode_info[op].size; pos_next = pos + len; switch(op) { - case OP_close_var_object: - { - if (s->var_object_idx >= 0) { - /* close the var object and create a new one */ - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_close_loc); - dbuf_put_u16(&bc_out, s->var_object_idx); - dbuf_putc(&bc_out, OP_special_object); - dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); - put_short_code(&bc_out, OP_put_loc, s->var_object_idx); - } - } - break; - case OP_line_num: /* line number info (for debug). We put it in a separate compressed table to reduce memory usage and get better @@ -29546,7 +31481,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); goto no_change; @@ -30110,20 +32045,20 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) } goto no_change; +#if SHORT_OPCODES case OP_typeof: if (OPTIMIZE) { /* simplify typeof tests */ if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) { if (cc.line_num >= 0) line_num = cc.line_num; int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq; -#if SHORT_OPCODES int op2 = -1; switch (cc.atom) { case JS_ATOM_undefined: - op2 = OP_is_undefined; + op2 = OP_typeof_is_undefined; break; case JS_ATOM_function: - op2 = OP_is_function; + op2 = OP_typeof_is_function; break; } if (op2 >= 0) { @@ -30147,19 +32082,10 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) goto has_label; } } -#endif - if (cc.atom == JS_ATOM_undefined) { - /* transform typeof(s) == "undefined" into s === void 0 */ - add_pc2line_info(s, bc_out.size, line_num); - dbuf_putc(&bc_out, OP_undefined); - dbuf_putc(&bc_out, op1); - JS_FreeAtom(ctx, cc.atom); - pos_next = cc.pos; - break; - } } } goto no_change; +#endif default: no_change: @@ -30276,56 +32202,91 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) /* compute the maximum stack size needed by the function */ typedef struct StackSizeState { + int bc_len; int stack_len_max; uint16_t *stack_level_tab; + int *pc_stack; + int pc_stack_len; + int pc_stack_size; } StackSizeState; -static __exception int compute_stack_size_rec(JSContext *ctx, - JSFunctionDef *fd, - StackSizeState *s, - int pos, int op, int stack_len) +/* 'op' is only used for error indication */ +static __exception int ss_check(JSContext *ctx, StackSizeState *s, + int pos, int op, int stack_len) { - int bc_len, diff, n_pop, pos_next; + if ((unsigned)pos >= s->bc_len) { + JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); + return -1; + } + if (stack_len > s->stack_len_max) { + s->stack_len_max = stack_len; + if (s->stack_len_max > JS_STACK_SIZE_MAX) { + JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); + return -1; + } + } + if (s->stack_level_tab[pos] != 0xffff) { + /* already explored: check that the stack size is consistent */ + if (s->stack_level_tab[pos] != stack_len) { + JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)", + s->stack_level_tab[pos], stack_len, pos); + return -1; + } else { + return 0; + } + } + + /* mark as explored and store the stack size */ + s->stack_level_tab[pos] = stack_len; + + /* queue the new PC to explore */ + if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]), + &s->pc_stack_size, s->pc_stack_len + 1)) + return -1; + s->pc_stack[s->pc_stack_len++] = pos; + return 0; +} + +static __exception int compute_stack_size(JSContext *ctx, + JSFunctionDef *fd, + int *pstack_size) +{ + StackSizeState s_s, *s = &s_s; + int i, diff, n_pop, pos_next, stack_len, pos, op; const JSOpCode *oi; const uint8_t *bc_buf; - if (stack_len > s->stack_len_max) { - s->stack_len_max = stack_len; - if (s->stack_len_max > JS_STACK_SIZE_MAX) - goto stack_overflow; - } bc_buf = fd->byte_code.buf; - bc_len = fd->byte_code.size; - for(;;) { - if ((unsigned)pos >= bc_len) - goto buf_overflow; -#if 0 - printf("%5d: %d\n", pos, stack_len); -#endif - if (s->stack_level_tab[pos] != 0xffff) { - /* already explored: check that the stack size is consistent */ - if (s->stack_level_tab[pos] != stack_len) { - JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)", - s->stack_level_tab[pos], stack_len, pos); - return -1; - } else { - return 0; - } - } else { - s->stack_level_tab[pos] = stack_len; - } + s->bc_len = fd->byte_code.size; + /* bc_len > 0 */ + s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * + s->bc_len); + if (!s->stack_level_tab) + return -1; + for(i = 0; i < s->bc_len; i++) + s->stack_level_tab[i] = 0xffff; + s->stack_len_max = 0; + s->pc_stack = NULL; + s->pc_stack_len = 0; + s->pc_stack_size = 0; + /* breadth-first graph exploration */ + if (ss_check(ctx, s, 0, OP_invalid, 0)) + goto fail; + + while (s->pc_stack_len > 0) { + pos = s->pc_stack[--s->pc_stack_len]; + stack_len = s->stack_level_tab[pos]; op = bc_buf[pos]; if (op == 0 || op >= OP_COUNT) { JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos); - return -1; + goto fail; } oi = &short_opcode_info(op); pos_next = pos + oi->size; - if (pos_next > bc_len) { - buf_overflow: + if (pos_next > s->bc_len) { JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); - return -1; + goto fail; } n_pop = oi->n_pop; /* call pops a variable number of arguments */ @@ -30341,13 +32302,15 @@ static __exception int compute_stack_size_rec(JSContext *ctx, if (stack_len < n_pop) { JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos); - return -1; + goto fail; } stack_len += oi->n_push - n_pop; if (stack_len > s->stack_len_max) { s->stack_len_max = stack_len; - if (s->stack_len_max > JS_STACK_SIZE_MAX) - goto stack_overflow; + if (s->stack_len_max > JS_STACK_SIZE_MAX) { + JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); + goto fail; + } } switch(op) { case OP_tail_call: @@ -30356,9 +32319,9 @@ static __exception int compute_stack_size_rec(JSContext *ctx, case OP_return_undef: case OP_return_async: case OP_throw: - case OP_throw_var: + case OP_throw_error: case OP_ret: - goto done; + goto done_insn; case OP_goto: diff = get_u32(bc_buf + pos + 1); pos_next = pos + 1 + diff; @@ -30375,73 +32338,57 @@ static __exception int compute_stack_size_rec(JSContext *ctx, case OP_if_true8: case OP_if_false8: diff = (int8_t)bc_buf[pos + 1]; - if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len)) - return -1; + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len)) + goto fail; break; #endif case OP_if_true: case OP_if_false: case OP_catch: diff = get_u32(bc_buf + pos + 1); - if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len)) - return -1; + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len)) + goto fail; break; case OP_gosub: diff = get_u32(bc_buf + pos + 1); - if (compute_stack_size_rec(ctx, fd, s, pos + 1 + diff, op, stack_len + 1)) - return -1; + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1)) + goto fail; break; case OP_with_get_var: case OP_with_delete_var: diff = get_u32(bc_buf + pos + 5); - if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 1)) - return -1; + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1)) + goto fail; break; case OP_with_make_ref: case OP_with_get_ref: case OP_with_get_ref_undef: diff = get_u32(bc_buf + pos + 5); - if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len + 2)) - return -1; + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2)) + goto fail; break; case OP_with_put_var: diff = get_u32(bc_buf + pos + 5); - if (compute_stack_size_rec(ctx, fd, s, pos + 5 + diff, op, stack_len - 1)) - return -1; + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1)) + goto fail; break; default: break; } - pos = pos_next; + if (ss_check(ctx, s, pos_next, op, stack_len)) + goto fail; + done_insn: ; } - done: - return 0; - - stack_overflow: - JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); - return -1; -} - -static __exception int compute_stack_size(JSContext *ctx, - JSFunctionDef *fd, - int *pstack_size) -{ - StackSizeState s_s, *s = &s_s; - int bc_len, i, ret; - - bc_len = fd->byte_code.size; - /* bc_len > 0 */ - s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * bc_len); - if (!s->stack_level_tab) - return -1; - for(i = 0; i < bc_len; i++) - s->stack_level_tab[i] = 0xffff; - s->stack_len_max = 0; - ret = compute_stack_size_rec(ctx, fd, s, 0, OP_invalid, 0); js_free(ctx, s->stack_level_tab); + js_free(ctx, s->pc_stack); *pstack_size = s->stack_len_max; - return ret; + return 0; + fail: + js_free(ctx, s->stack_level_tab); + js_free(ctx, s->pc_stack); + *pstack_size = 0; + return -1; } static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) @@ -30449,14 +32396,14 @@ static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) int i, idx; JSModuleDef *m = fd->module; JSExportEntry *me; - JSHoistedDef *hf; + JSGlobalVar *hf; /* The imported global variables were added as closure variables in js_parse_import(). We add here the module global variables. */ - for(i = 0; i < fd->hoisted_def_count; i++) { - hf = &fd->hoisted_def[i]; + for(i = 0; i < fd->global_var_count; i++) { + hf = &fd->global_vars[i]; if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const, hf->is_lexical, FALSE) < 0) return -1; @@ -30468,9 +32415,8 @@ static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) if (me->export_type == JS_EXPORT_TYPE_LOCAL) { idx = find_closure_var(ctx, fd, me->local_name); if (idx < 0) { - char buf1[ATOM_GET_STR_BUF_SIZE]; - JS_ThrowSyntaxError(ctx, "exported variable '%s' does not exist", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), me->local_name)); + JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist", + me->local_name); return -1; } me->u.local.var_idx = idx; @@ -30495,6 +32441,10 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) for (scope = 0; scope < fd->scope_count; scope++) { fd->scopes[scope].first = -1; } + if (fd->has_parameter_expressions) { + /* special end of variable list marker for the argument scope */ + fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END; + } for (idx = 0; idx < fd->var_count; idx++) { JSVarDef *vd = &fd->vars[idx]; vd->scope_next = fd->scopes[vd->scope_level].first; @@ -30502,12 +32452,12 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) } for (scope = 2; scope < fd->scope_count; scope++) { JSVarScope *sd = &fd->scopes[scope]; - if (sd->first == -1) + if (sd->first < 0) sd->first = fd->scopes[sd->parent].first; } for (idx = 0; idx < fd->var_count; idx++) { JSVarDef *vd = &fd->vars[idx]; - if (vd->scope_next == -1 && vd->scope_level > 1) { + if (vd->scope_next < 0 && vd->scope_level > 1) { scope = fd->scopes[vd->scope_level].parent; vd->scope_next = fd->scopes[scope].first; } @@ -30594,7 +32544,6 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) if (!b) goto fail; b->header.ref_count = 1; - b->gc_header.mark = 0; b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset); b->byte_code_len = fd->byte_code.size; @@ -30681,7 +32630,11 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) b->super_call_allowed = fd->super_call_allowed; b->super_allowed = fd->super_allowed; b->arguments_allowed = fd->arguments_allowed; + b->backtrace_barrier = fd->backtrace_barrier; + b->realm = JS_DupContext(ctx); + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1) if (!(fd->js_mode & JS_MODE_STRIP)) { js_dump_function_bytecode(ctx, b); @@ -30725,6 +32678,8 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) JSClosureVar *cv = &b->closure_var[i]; JS_FreeAtomRT(rt, cv->var_name); } + if (b->realm) + JS_FreeContext(b->realm); JS_FreeAtomRT(rt, b->func_name); if (b->has_debug) { @@ -30732,7 +32687,13 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) js_free_rt(rt, b->debug.pc2line_buf); js_free_rt(rt, b->debug.source); } - js_free_rt(rt, b); + + remove_gc_object(&b->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { + list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, b); + } } static __exception int js_parse_directives(JSParseState *s) @@ -30823,11 +32784,8 @@ static __exception int js_parse_directives(JSParseState *s) } #endif #ifdef CONFIG_BIGNUM - else if (!strcmp(str, "use bigint")) { - s->cur_func->js_mode |= JS_MODE_BIGINT; - } - else if (!strcmp(str, "use math")) { - s->cur_func->js_mode |= JS_MODE_BIGINT | JS_MODE_MATH; + else if (s->ctx->bignum_ext && !strcmp(str, "use math")) { + s->cur_func->js_mode |= JS_MODE_MATH; } #endif } @@ -30871,7 +32829,8 @@ static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, /* Check if argument name duplicates a destructuring parameter */ /* XXX: should have a flag for such variables */ for (i = 0; i < fd->var_count; i++) { - if (fd->vars[i].var_name == name) + if (fd->vars[i].var_name == name && + fd->vars[i].scope_level == 0) goto duplicate; } } @@ -30980,8 +32939,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE && (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) { - JSHoistedDef *hf; - hf = find_hoisted_def(fd, func_name); + JSGlobalVar *hf; + hf = find_global_var(fd, func_name); /* XXX: should check scope chain */ if (hf && hf->scope_level == fd->scope_level) { js_parse_error(s, "invalid redefinition of global identifier in module code"); @@ -30991,21 +32950,23 @@ static __exception int js_parse_function_decl2(JSParseState *s, } if (func_type == JS_PARSE_FUNC_VAR) { - /* Create lexical name here so function closure contains it */ if (!(fd->js_mode & JS_MODE_STRICT) + && func_kind == JS_FUNC_NORMAL && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0 && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET)) && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) { create_func_var = TRUE; } + /* Create the lexical name here so that the function closure + contains it */ if (fd->is_eval && (fd->eval_type == JS_EVAL_TYPE_GLOBAL || fd->eval_type == JS_EVAL_TYPE_MODULE) && - fd->scope_level == 1) { + fd->scope_level == fd->body_scope) { /* avoid creating a lexical variable in the global scope. XXX: check annex B */ - JSHoistedDef *hf; - hf = find_hoisted_def(fd, func_name); + JSGlobalVar *hf; + hf = find_global_var(fd, func_name); /* XXX: should check scope chain */ if (hf && hf->scope_level == fd->scope_level) { js_parse_error(s, "invalid redefinition of global identifier"); @@ -31080,6 +33041,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, /* parse arguments */ fd->has_simple_parameter_list = TRUE; + fd->has_parameter_expressions = FALSE; has_opt_arg = FALSE; if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) { JSAtom name; @@ -31092,13 +33054,30 @@ static __exception int js_parse_function_decl2(JSParseState *s, goto fail; fd->defined_arg_count = 1; } else { - if (js_parse_expect(s, '(')) - goto fail; + if (s->token.val == '(') { + int skip_bits; + /* if there is an '=' inside the parameter list, we + consider there is a parameter expression inside */ + js_parse_skip_parens_token(s, &skip_bits, FALSE); + if (skip_bits & SKIP_HAS_ASSIGNMENT) + fd->has_parameter_expressions = TRUE; + if (next_token(s)) + goto fail; + } else { + if (js_parse_expect(s, '(')) + goto fail; + } + if (fd->has_parameter_expressions) { + fd->scope_level = -1; /* force no parent scope */ + if (push_scope(s) < 0) + return -1; + } + while (s->token.val != ')') { JSAtom name; BOOL rest = FALSE; - int idx; + int idx, has_initializer; if (s->token.val == TOK_ELLIPSIS) { fd->has_simple_parameter_list = FALSE; @@ -31117,15 +33096,13 @@ static __exception int js_parse_function_decl2(JSParseState *s, emit_op(s, OP_get_arg); emit_u16(s, idx); } - if (js_parse_destructing_element(s, TOK_VAR, 1, TRUE, -1, TRUE)) + has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE); + if (has_initializer < 0) goto fail; - /* Close var object: necessary if direct eval call - occurred in the assignment expression or if any - variable was captured and a later direct eval call - may instantiate it in the var object. - The next pass will generate the capture if required. - */ - emit_op(s, OP_close_var_object); + if (has_initializer) + has_opt_arg = TRUE; + if (!has_opt_arg) + fd->defined_arg_count++; } else if (s->token.val == TOK_IDENT) { if (s->token.u.ident.is_reserved) { js_parse_error_reserved_identifier(s); @@ -31136,6 +33113,11 @@ static __exception int js_parse_function_decl2(JSParseState *s, js_parse_error_reserved_identifier(s); goto fail; } + if (fd->has_parameter_expressions) { + if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0) + goto fail; + } + /* XXX: could avoid allocating an argument if rest is true */ idx = add_arg(ctx, fd, name); if (idx < 0) goto fail; @@ -31144,72 +33126,55 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (rest) { emit_op(s, OP_rest); emit_u16(s, idx); + if (fd->has_parameter_expressions) { + emit_op(s, OP_dup); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } emit_op(s, OP_put_arg); emit_u16(s, idx); fd->has_simple_parameter_list = FALSE; has_opt_arg = TRUE; } else if (s->token.val == '=') { + int label; + fd->has_simple_parameter_list = FALSE; has_opt_arg = TRUE; if (next_token(s)) goto fail; - /* optimize `x = void 0` default value: no code needed */ - if (s->token.val == TOK_VOID) { - JSParsePos pos; - js_parse_get_pos(s, &pos); - if (next_token(s)) - goto fail; - if (s->token.val == TOK_NUMBER) { - if (next_token(s)) - goto fail; - if (s->token.val == ',') { - if (next_token(s)) - goto fail; - continue; - } - if (s->token.val == ')') { - continue; - } - } - if (js_parse_seek_token(s, &pos)) - goto fail; + label = new_label(s); + emit_op(s, OP_get_arg); + emit_u16(s, idx); + emit_op(s, OP_dup); + emit_op(s, OP_undefined); + emit_op(s, OP_strict_eq); + emit_goto(s, OP_if_false, label); + emit_op(s, OP_drop); + if (js_parse_assign_expr(s)) + goto fail; + set_object_name(s, name); + emit_op(s, OP_dup); + emit_op(s, OP_put_arg); + emit_u16(s, idx); + emit_label(s, label); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); + } else { + if (!has_opt_arg) { + fd->defined_arg_count++; } -#if 0 - /* XXX: not correct for eval code */ - /* Check for a default value of `undefined` - to omit default argument processing */ - if (s->token.val == TOK_IDENT && - s->token.u.ident.atom == JS_ATOM_undefined && - fd->parent == NULL && - ((tok = peek_token(s, FALSE)) == ',' || tok == ')')) { - if (next_token(s)) /* ignore undefined token */ - goto fail; - } else -#endif - { - int label = new_label(s); - if (idx > 0) { - emit_op(s, OP_set_arg_valid_upto); - emit_u16(s, idx); - } + if (fd->has_parameter_expressions) { + /* copy the argument to the argument scope */ emit_op(s, OP_get_arg); emit_u16(s, idx); - emit_op(s, OP_undefined); - emit_op(s, OP_strict_eq); - emit_goto(s, OP_if_false, label); - if (js_parse_assign_expr(s, TRUE)) - goto fail; - set_object_name(s, name); - emit_op(s, OP_put_arg); - emit_u16(s, idx); - /* Close var object: see above comment. */ - emit_op(s, OP_close_var_object); - emit_label(s, label); + emit_op(s, OP_scope_put_var_init); + emit_atom(s, name); + emit_u16(s, fd->scope_level); } - } else if (!has_opt_arg) { - fd->defined_arg_count++; } } else { js_parse_error(s, "missing formal parameter"); @@ -31219,10 +33184,10 @@ static __exception int js_parse_function_decl2(JSParseState *s, js_parse_expect(s, ')'); goto fail; } - if (s->token.val == ',') { - if (next_token(s)) - goto fail; - } + if (s->token.val == ')') + break; + if (js_parse_expect(s, ',')) + goto fail; } if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) || (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) { @@ -31231,6 +33196,41 @@ static __exception int js_parse_function_decl2(JSParseState *s, } } + if (fd->has_parameter_expressions) { + int idx; + + /* Copy the variables in the argument scope to the variable + scope (see FunctionDeclarationInstantiation() in spec). The + normal arguments are already present, so no need to copy + them. */ + idx = fd->scopes[fd->scope_level].first; + while (idx >= 0) { + JSVarDef *vd = &fd->vars[idx]; + if (vd->scope_level != fd->scope_level) + break; + if (find_var(ctx, fd, vd->var_name) < 0) { + if (add_var(ctx, fd, vd->var_name) < 0) + goto fail; + vd = &fd->vars[idx]; /* fd->vars may have been reallocated */ + emit_op(s, OP_scope_get_var); + emit_atom(s, vd->var_name); + emit_u16(s, fd->scope_level); + emit_op(s, OP_scope_put_var); + emit_atom(s, vd->var_name); + emit_u16(s, 0); + } + idx = vd->scope_next; + } + + /* the argument scope has no parent, hence we don't use pop_scope(s) */ + emit_op(s, OP_leave_scope); + emit_u16(s, fd->scope_level); + + /* set the variable scope as the current scope */ + fd->scope_level = 0; + fd->scope_first = fd->scopes[fd->scope_level].first; + } + if (next_token(s)) goto fail; @@ -31242,7 +33242,8 @@ static __exception int js_parse_function_decl2(JSParseState *s, /* in generators, yield expression is forbidden during the parsing of the arguments */ fd->in_function_body = TRUE; - push_scope(s); /* enter body scope: fd->scope_level = 1 */ + push_scope(s); /* enter body scope */ + fd->body_scope = fd->scope_level; if (s->token.val == TOK_ARROW) { if (next_token(s)) @@ -31252,7 +33253,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, if (js_parse_function_check_names(s, fd, func_name)) goto fail; - if (js_parse_assign_expr(s, TRUE)) + if (js_parse_assign_expr(s)) goto fail; if (func_kind != JS_FUNC_NORMAL) @@ -31334,10 +33335,10 @@ done: emit_u32(s, idx); if (create_func_var) { if (s->cur_func->is_global_var) { - JSHoistedDef *hf; + JSGlobalVar *hf; /* the global variable must be defined at the start of the function */ - hf = add_hoisted_def(ctx, s->cur_func, -1, func_name, -1, FALSE); + hf = add_global_var(ctx, s->cur_func, func_name); if (!hf) goto fail; /* it is considered as defined at the top level @@ -31367,7 +33368,7 @@ done: } if (lexical_func_idx >= 0) { /* lexical variable will be initialized upon entering scope */ - s->cur_func->vars[lexical_func_idx].func_pool_or_scope_idx = idx; + s->cur_func->vars[lexical_func_idx].func_pool_idx = idx; emit_op(s, OP_drop); } else { /* store function object into its lexical name */ @@ -31383,17 +33384,23 @@ done: if (var_idx < 0) goto fail; /* the variable will be assigned at the top of the function */ - if (!add_hoisted_def(ctx, s->cur_func, idx, JS_ATOM_NULL, var_idx, FALSE)) - goto fail; + if (var_idx & ARGUMENT_VAR_OFFSET) { + s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx; + } else { + s->cur_func->vars[var_idx].func_pool_idx = idx; + } } else { JSAtom func_var_name; + JSGlobalVar *hf; if (func_name == JS_ATOM_NULL) func_var_name = JS_ATOM__default_; /* export default */ else func_var_name = func_name; /* the variable will be assigned at the top of the function */ - if (!add_hoisted_def(ctx, s->cur_func, idx, func_var_name, -1, FALSE)) + hf = add_global_var(ctx, s->cur_func, func_var_name); + if (!hf) goto fail; + hf->cpool_idx = idx; if (export_flag != JS_PARSE_EXPORT_NONE) { if (!add_export_entry(s, s->cur_func->module, func_var_name, export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL)) @@ -31493,7 +33500,9 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, m = JS_VALUE_GET_PTR(fun_obj); /* the module refcount should be >= 2 */ JS_FreeValue(ctx, fun_obj); - if (js_instantiate_module(ctx, m) < 0) + if (js_create_module_function(ctx, m) < 0) + goto fail; + if (js_link_module(ctx, m) < 0) goto fail; ret_val = js_evaluate_module(ctx, m); if (JS_IsException(ret_val)) { @@ -31525,8 +33534,11 @@ static void skip_shebang(JSParseState *s) break; } else if (*p >= 0x80) { c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (c == CP_LS || c == CP_PS) + if (c == CP_LS || c == CP_PS) { break; + } else if (c == -1) { + p++; /* skip invalid UTF-8 */ + } } else { p++; } @@ -31556,7 +33568,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, m = NULL; if (eval_type == JS_EVAL_TYPE_DIRECT) { JSObject *p; - sf = ctx->current_stack_frame; + sf = ctx->rt->current_stack_frame; assert(sf != NULL); assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT); p = JS_VALUE_GET_OBJ(sf->cur_func); @@ -31589,6 +33601,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, s->cur_func = fd; fd->eval_type = eval_type; fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT); + fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0); if (eval_type == JS_EVAL_TYPE_DIRECT) { fd->new_target_allowed = b->new_target_allowed; fd->super_call_allowed = b->super_call_allowed; @@ -31611,7 +33624,8 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, s->allow_html_comments = !s->is_module; push_scope(s); /* body scope */ - + fd->body_scope = fd->scope_level; + err = js_parse_program(s); if (err) { fail: @@ -31674,19 +33688,27 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, } -JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, - const char *filename, int eval_flags) +JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int eval_flags) { int eval_type = eval_flags & JS_EVAL_TYPE_MASK; JSValue ret; assert(eval_type == JS_EVAL_TYPE_GLOBAL || eval_type == JS_EVAL_TYPE_MODULE); - ret = JS_EvalInternal(ctx, ctx->global_obj, input, input_len, filename, + ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, eval_flags, -1); return ret; } +JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags) +{ + return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, + eval_flags); +} + int JS_ResolveModule(JSContext *ctx, JSValueConst obj) { if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { @@ -31699,6 +33721,110 @@ int JS_ResolveModule(JSContext *ctx, JSValueConst obj) return 0; } +/*******************************************************************/ +/* object list */ + +typedef struct { + JSObject *obj; + uint32_t hash_next; /* -1 if no next entry */ +} JSObjectListEntry; + +/* XXX: reuse it to optimize weak references */ +typedef struct { + JSObjectListEntry *object_tab; + int object_count; + int object_size; + uint32_t *hash_table; + uint32_t hash_size; +} JSObjectList; + +static void js_object_list_init(JSObjectList *s) +{ + memset(s, 0, sizeof(*s)); +} + +static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size) +{ + return ((uintptr_t)p * 3163) & (hash_size - 1); +} + +static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s, + uint32_t new_hash_size) +{ + JSObjectListEntry *e; + uint32_t i, h, *new_hash_table; + + new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size); + if (!new_hash_table) + return -1; + js_free(ctx, s->hash_table); + s->hash_table = new_hash_table; + s->hash_size = new_hash_size; + + for(i = 0; i < s->hash_size; i++) { + s->hash_table[i] = -1; + } + for(i = 0; i < s->object_count; i++) { + e = &s->object_tab[i]; + h = js_object_list_get_hash(e->obj, s->hash_size); + e->hash_next = s->hash_table[h]; + s->hash_table[h] = i; + } + return 0; +} + +/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if + memory error */ +static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) +{ + JSObjectListEntry *e; + uint32_t h, new_hash_size; + + if (js_resize_array(ctx, (void *)&s->object_tab, + sizeof(s->object_tab[0]), + &s->object_size, s->object_count + 1)) + return -1; + if (unlikely((s->object_count + 1) >= s->hash_size)) { + new_hash_size = max_uint32(s->hash_size, 4); + while (new_hash_size <= s->object_count) + new_hash_size *= 2; + if (js_object_list_resize_hash(ctx, s, new_hash_size)) + return -1; + } + e = &s->object_tab[s->object_count++]; + h = js_object_list_get_hash(obj, s->hash_size); + e->obj = obj; + e->hash_next = s->hash_table[h]; + s->hash_table[h] = s->object_count - 1; + return 0; +} + +/* return -1 if not present or the object index */ +static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj) +{ + JSObjectListEntry *e; + uint32_t h, p; + + /* must test empty size because there is no hash table */ + if (s->object_count == 0) + return -1; + h = js_object_list_get_hash(obj, s->hash_size); + p = s->hash_table[h]; + while (p != -1) { + e = &s->object_tab[p]; + if (e->obj == obj) + return p; + p = e->hash_next; + } + return -1; +} + +static void js_object_list_end(JSContext *ctx, JSObjectList *s) +{ + js_free(ctx, s->object_tab); + js_free(ctx, s->hash_table); +} + /*******************************************************************/ /* binary object writer & reader */ @@ -31714,9 +33840,16 @@ typedef enum BCTagEnum { BC_TAG_ARRAY, BC_TAG_BIG_INT, BC_TAG_BIG_FLOAT, + BC_TAG_BIG_DECIMAL, BC_TAG_TEMPLATE_OBJECT, BC_TAG_FUNCTION_BYTECODE, BC_TAG_MODULE, + BC_TAG_TYPED_ARRAY, + BC_TAG_ARRAY_BUFFER, + BC_TAG_SHARED_ARRAY_BUFFER, + BC_TAG_DATE, + BC_TAG_OBJECT_VALUE, + BC_TAG_OBJECT_REFERENCE, } BCTagEnum; #ifdef CONFIG_BIGNUM @@ -31734,14 +33867,21 @@ typedef enum BCTagEnum { typedef struct BCWriterState { JSContext *ctx; DynBuf dbuf; - BOOL byte_swap; - BOOL allow_bytecode; + BOOL byte_swap : 8; + BOOL allow_bytecode : 8; + BOOL allow_sab : 8; + BOOL allow_reference : 8; uint32_t first_atom; uint32_t *atom_to_idx; int atom_to_idx_size; JSAtom *idx_to_atom; int idx_to_atom_count; int idx_to_atom_size; + uint8_t **sab_tab; + int sab_tab_len; + int sab_tab_size; + /* list of referenced objects (used if allow_reference = TRUE) */ + JSObjectList object_list; } BCWriterState; #ifdef DUMP_READ_OBJECT @@ -31756,11 +33896,18 @@ static const char * const bc_tag_str[] = { "string", "object", "array", - "big_int", - "big_float", + "bigint", + "bigfloat", + "bigdecimal", "template", "function", "module", + "TypedArray", + "ArrayBuffer", + "SharedArrayBuffer", + "Date", + "ObjectValue", + "ObjectReference", }; #endif @@ -31820,36 +33967,20 @@ static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) return 0; } if (atom >= s->atom_to_idx_size) { - size_t new_size, i, slack; - uint32_t *new_tab; - /* XXX: potential arithmetic overflow */ - new_size = s->atom_to_idx_size * 3 / 2; - if ((atom + 1) > new_size) - new_size = atom + 1; - new_tab = js_realloc2(s->ctx, s->atom_to_idx, - new_size * sizeof(s->atom_to_idx[0]), &slack); - if (!new_tab) - goto fail; - new_size += slack / sizeof(*new_tab); - for(i = s->atom_to_idx_size; i < new_size; i++) - new_tab[i] = 0; - s->atom_to_idx = new_tab; - s->atom_to_idx_size = new_size; - } - if ((s->idx_to_atom_count + 1) > s->idx_to_atom_size) { - size_t new_size, slack; - JSAtom *new_tab; - new_size = s->idx_to_atom_size * 3 / 2; - if ((s->idx_to_atom_count + 1) > new_size) - new_size = s->idx_to_atom_count + 1; - new_tab = js_realloc2(s->ctx, s->idx_to_atom, - new_size * sizeof(s->idx_to_atom[0]), &slack); - if (!new_tab) - goto fail; - new_size += slack / sizeof(*new_tab); - s->idx_to_atom = new_tab; - s->idx_to_atom_size = new_size; + int old_size, i; + old_size = s->atom_to_idx_size; + if (js_resize_array(s->ctx, (void **)&s->atom_to_idx, + sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, + atom + 1)) + return -1; + /* XXX: could add a specific js_resize_array() function to do it */ + for(i = old_size; i < s->atom_to_idx_size; i++) + s->atom_to_idx[i] = 0; } + if (js_resize_array(s->ctx, (void **)&s->idx_to_atom, + sizeof(s->idx_to_atom[0]), + &s->idx_to_atom_size, s->idx_to_atom_count + 1)) + goto fail; v = s->idx_to_atom_count++; s->idx_to_atom[v] = atom + s->first_atom; @@ -31995,10 +34126,412 @@ static void JS_WriteString(BCWriterState *s, JSString *p) } } +#ifdef CONFIG_BIGNUM +static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) +{ + uint32_t tag, tag1; + int64_t e; + JSBigFloat *bf = JS_VALUE_GET_PTR(obj); + bf_t *a = &bf->num; + size_t len, i, n1, j; + limb_t v; + + tag = JS_VALUE_GET_TAG(obj); + switch(tag) { + case JS_TAG_BIG_INT: + tag1 = BC_TAG_BIG_INT; + break; + case JS_TAG_BIG_FLOAT: + tag1 = BC_TAG_BIG_FLOAT; + break; + case JS_TAG_BIG_DECIMAL: + tag1 = BC_TAG_BIG_DECIMAL; + break; + default: + abort(); + } + bc_put_u8(s, tag1); + + /* sign + exponent */ + if (a->expn == BF_EXP_ZERO) + e = 0; + else if (a->expn == BF_EXP_INF) + e = 1; + else if (a->expn == BF_EXP_NAN) + e = 2; + else if (a->expn >= 0) + e = a->expn + 3; + else + e = a->expn; + e = (e << 1) | a->sign; + if (e < INT32_MIN || e > INT32_MAX) { + JS_ThrowInternalError(s->ctx, "bignum exponent is too large"); + return -1; + } + bc_put_sleb128(s, e); + + /* mantissa */ + if (a->len != 0) { + if (tag != JS_TAG_BIG_DECIMAL) { + i = 0; + while (i < a->len && a->tab[i] == 0) + i++; + assert(i < a->len); + v = a->tab[i]; + n1 = sizeof(limb_t); + while ((v & 0xff) == 0) { + n1--; + v >>= 8; + } + i++; + len = (a->len - i) * sizeof(limb_t) + n1; + if (len > INT32_MAX) { + JS_ThrowInternalError(s->ctx, "bignum is too large"); + return -1; + } + bc_put_leb128(s, len); + /* always saved in byte based little endian representation */ + for(j = 0; j < n1; j++) { + dbuf_putc(&s->dbuf, v >> (j * 8)); + } + for(; i < a->len; i++) { + limb_t v = a->tab[i]; +#if LIMB_BITS == 32 +#ifdef WORDS_BIGENDIAN + v = bswap32(v); +#endif + dbuf_put_u32(&s->dbuf, v); +#else +#ifdef WORDS_BIGENDIAN + v = bswap64(v); +#endif + dbuf_put_u64(&s->dbuf, v); +#endif + } + } else { + int bpos, d; + uint8_t v8; + size_t i0; + + /* little endian BCD */ + i = 0; + while (i < a->len && a->tab[i] == 0) + i++; + assert(i < a->len); + len = a->len * LIMB_DIGITS; + v = a->tab[i]; + j = 0; + while ((v % 10) == 0) { + j++; + v /= 10; + } + len -= j; + assert(len > 0); + if (len > INT32_MAX) { + JS_ThrowInternalError(s->ctx, "bignum is too large"); + return -1; + } + bc_put_leb128(s, len); + + bpos = 0; + v8 = 0; + i0 = i; + for(; i < a->len; i++) { + if (i != i0) { + v = a->tab[i]; + j = 0; + } + for(; j < LIMB_DIGITS; j++) { + d = v % 10; + v /= 10; + if (bpos == 0) { + v8 = d; + bpos = 1; + } else { + dbuf_putc(&s->dbuf, v8 | (d << 4)); + bpos = 0; + } + } + } + /* flush the last digit */ + if (bpos) { + dbuf_putc(&s->dbuf, v8); + } + } + } + return 0; +} +#endif /* CONFIG_BIGNUM */ + +static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj); + +static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) +{ + JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); + uint32_t flags; + int idx, i; + + bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); + flags = idx = 0; + bc_set_flags(&flags, &idx, b->has_prototype, 1); + bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); + bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); + bc_set_flags(&flags, &idx, b->need_home_object, 1); + bc_set_flags(&flags, &idx, b->func_kind, 2); + bc_set_flags(&flags, &idx, b->new_target_allowed, 1); + bc_set_flags(&flags, &idx, b->super_call_allowed, 1); + bc_set_flags(&flags, &idx, b->super_allowed, 1); + bc_set_flags(&flags, &idx, b->arguments_allowed, 1); + bc_set_flags(&flags, &idx, b->has_debug, 1); + bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); + assert(idx <= 16); + bc_put_u16(s, flags); + bc_put_u8(s, b->js_mode); + bc_put_atom(s, b->func_name); + + bc_put_leb128(s, b->arg_count); + bc_put_leb128(s, b->var_count); + bc_put_leb128(s, b->defined_arg_count); + bc_put_leb128(s, b->stack_size); + bc_put_leb128(s, b->closure_var_count); + bc_put_leb128(s, b->cpool_count); + bc_put_leb128(s, b->byte_code_len); + if (b->vardefs) { + bc_put_leb128(s, b->arg_count + b->var_count); + for(i = 0; i < b->arg_count + b->var_count; i++) { + JSVarDef *vd = &b->vardefs[i]; + bc_put_atom(s, vd->var_name); + bc_put_leb128(s, vd->scope_level); + bc_put_leb128(s, vd->scope_next + 1); + flags = idx = 0; + bc_set_flags(&flags, &idx, vd->var_kind, 4); + bc_set_flags(&flags, &idx, vd->is_const, 1); + bc_set_flags(&flags, &idx, vd->is_lexical, 1); + bc_set_flags(&flags, &idx, vd->is_captured, 1); + assert(idx <= 8); + bc_put_u8(s, flags); + } + } else { + bc_put_leb128(s, 0); + } + + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + bc_put_atom(s, cv->var_name); + bc_put_leb128(s, cv->var_idx); + flags = idx = 0; + bc_set_flags(&flags, &idx, cv->is_local, 1); + bc_set_flags(&flags, &idx, cv->is_arg, 1); + bc_set_flags(&flags, &idx, cv->is_const, 1); + bc_set_flags(&flags, &idx, cv->is_lexical, 1); + bc_set_flags(&flags, &idx, cv->var_kind, 4); + assert(idx <= 8); + bc_put_u8(s, flags); + } + + if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) + goto fail; + + if (b->has_debug) { + bc_put_atom(s, b->debug.filename); + bc_put_leb128(s, b->debug.line_num); + bc_put_leb128(s, b->debug.pc2line_len); + dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); + } + + for(i = 0; i < b->cpool_count; i++) { + if (JS_WriteObjectRec(s, b->cpool[i])) + goto fail; + } + return 0; + fail: + return -1; +} + +static int JS_WriteModule(BCWriterState *s, JSValueConst obj) +{ + JSModuleDef *m = JS_VALUE_GET_PTR(obj); + int i; + + bc_put_u8(s, BC_TAG_MODULE); + bc_put_atom(s, m->module_name); + + bc_put_leb128(s, m->req_module_entries_count); + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + bc_put_atom(s, rme->module_name); + } + + bc_put_leb128(s, m->export_entries_count); + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + bc_put_u8(s, me->export_type); + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + bc_put_leb128(s, me->u.local.var_idx); + } else { + bc_put_leb128(s, me->u.req_module_idx); + bc_put_atom(s, me->local_name); + } + bc_put_atom(s, me->export_name); + } + + bc_put_leb128(s, m->star_export_entries_count); + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + bc_put_leb128(s, se->req_module_idx); + } + + bc_put_leb128(s, m->import_entries_count); + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + bc_put_leb128(s, mi->var_idx); + bc_put_atom(s, mi->import_name); + bc_put_leb128(s, mi->req_module_idx); + } + + if (JS_WriteObjectRec(s, m->func_obj)) + goto fail; + return 0; + fail: + return -1; +} + +static int JS_WriteArray(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + uint32_t i, len; + JSValue val; + int ret; + BOOL is_template; + + if (s->allow_bytecode && !p->extensible) { + /* not extensible array: we consider it is a + template when we are saving bytecode */ + bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); + is_template = TRUE; + } else { + bc_put_u8(s, BC_TAG_ARRAY); + is_template = FALSE; + } + if (js_get_length32(s->ctx, &len, obj)) + goto fail1; + bc_put_leb128(s, len); + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(s->ctx, obj, i); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + if (is_template) { + val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + return 0; + fail1: + return -1; +} + +static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + uint32_t i, prop_count; + JSShape *sh; + JSShapeProperty *pr; + int pass; + JSAtom atom; + + bc_put_u8(s, BC_TAG_OBJECT); + prop_count = 0; + sh = p->shape; + for(pass = 0; pass < 2; pass++) { + if (pass == 1) + bc_put_leb128(s, prop_count); + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + atom = pr->atom; + if (atom != JS_ATOM_NULL && + JS_AtomIsString(s->ctx, atom) && + (pr->flags & JS_PROP_ENUMERABLE)) { + if (pr->flags & JS_PROP_TMASK) { + JS_ThrowTypeError(s->ctx, "only value properties are supported"); + goto fail; + } + if (pass == 0) { + prop_count++; + } else { + bc_put_atom(s, atom); + if (JS_WriteObjectRec(s, p->prop[i].u.value)) + goto fail; + } + } + } + } + return 0; + fail: + return -1; +} + +static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSTypedArray *ta = p->u.typed_array; + + bc_put_u8(s, BC_TAG_TYPED_ARRAY); + bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); + bc_put_leb128(s, p->u.array.count); + bc_put_leb128(s, ta->offset); + if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) + return -1; + return 0; +} + +static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); + return -1; + } + bc_put_u8(s, BC_TAG_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); + return 0; +} + +static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj) +{ + JSObject *p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer *abuf = p->u.array_buffer; + assert(!abuf->detached); /* SharedArrayBuffer are never detached */ + bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + bc_put_u64(s, (uintptr_t)abuf->data); + if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), + &s->sab_tab_size, s->sab_tab_len + 1)) + return -1; + /* keep the SAB pointer so that the user can clone it or free it */ + s->sab_tab[s->sab_tab_len++] = abuf->data; + return 0; +} + static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) { - uint32_t tag = JS_VALUE_GET_NORM_TAG(obj); + uint32_t tag; + if (js_check_stack_overflow(s->ctx->rt, 0)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + + tag = JS_VALUE_GET_NORM_TAG(obj); switch(tag) { case JS_TAG_NULL: bc_put_u8(s, BC_TAG_NULL); @@ -32029,291 +34562,90 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) } break; case JS_TAG_FUNCTION_BYTECODE: - { - JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); - uint32_t flags; - int idx, i; - - if (!s->allow_bytecode) - goto invalid_tag; - bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); - flags = idx = 0; - bc_set_flags(&flags, &idx, b->has_prototype, 1); - bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); - bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); - bc_set_flags(&flags, &idx, b->need_home_object, 1); - bc_set_flags(&flags, &idx, b->func_kind, 2); - bc_set_flags(&flags, &idx, b->new_target_allowed, 1); - bc_set_flags(&flags, &idx, b->super_call_allowed, 1); - bc_set_flags(&flags, &idx, b->super_allowed, 1); - bc_set_flags(&flags, &idx, b->arguments_allowed, 1); - bc_set_flags(&flags, &idx, b->has_debug, 1); - assert(idx <= 16); - bc_put_u16(s, flags); - bc_put_u8(s, b->js_mode); - bc_put_atom(s, b->func_name); - - bc_put_leb128(s, b->arg_count); - bc_put_leb128(s, b->var_count); - bc_put_leb128(s, b->defined_arg_count); - bc_put_leb128(s, b->stack_size); - bc_put_leb128(s, b->closure_var_count); - bc_put_leb128(s, b->cpool_count); - bc_put_leb128(s, b->byte_code_len); - if (b->vardefs) { - bc_put_leb128(s, b->arg_count + b->var_count); - for(i = 0; i < b->arg_count + b->var_count; i++) { - JSVarDef *vd = &b->vardefs[i]; - bc_put_atom(s, vd->var_name); - bc_put_leb128(s, vd->scope_level); - bc_put_leb128(s, vd->scope_next + 1); - flags = idx = 0; - bc_set_flags(&flags, &idx, vd->var_kind, 4); - bc_set_flags(&flags, &idx, vd->is_func_var, 1); - bc_set_flags(&flags, &idx, vd->is_const, 1); - bc_set_flags(&flags, &idx, vd->is_lexical, 1); - bc_set_flags(&flags, &idx, vd->is_captured, 1); - assert(idx <= 8); - bc_put_u8(s, flags); - } - } else { - bc_put_leb128(s, 0); - } - - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - bc_put_atom(s, cv->var_name); - bc_put_leb128(s, cv->var_idx); - flags = idx = 0; - bc_set_flags(&flags, &idx, cv->is_local, 1); - bc_set_flags(&flags, &idx, cv->is_arg, 1); - bc_set_flags(&flags, &idx, cv->is_const, 1); - bc_set_flags(&flags, &idx, cv->is_lexical, 1); - bc_set_flags(&flags, &idx, cv->var_kind, 4); - assert(idx <= 8); - bc_put_u8(s, flags); - } - - if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) - goto fail; - - if (b->has_debug) { - bc_put_atom(s, b->debug.filename); - bc_put_leb128(s, b->debug.line_num); - bc_put_leb128(s, b->debug.pc2line_len); - dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); - } - - for(i = 0; i < b->cpool_count; i++) { - if (JS_WriteObjectRec(s, b->cpool[i])) - goto fail; - } - } + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteFunctionTag(s, obj)) + goto fail; break; case JS_TAG_MODULE: - { - JSModuleDef *m = JS_VALUE_GET_PTR(obj); - int i; - - if (!s->allow_bytecode) - goto invalid_tag; - bc_put_u8(s, BC_TAG_MODULE); - bc_put_atom(s, m->module_name); - - bc_put_leb128(s, m->req_module_entries_count); - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - bc_put_atom(s, rme->module_name); - } - - bc_put_leb128(s, m->export_entries_count); - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - bc_put_u8(s, me->export_type); - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - bc_put_leb128(s, me->u.local.var_idx); - } else { - bc_put_leb128(s, me->u.req_module_idx); - bc_put_atom(s, me->local_name); - } - bc_put_atom(s, me->export_name); - } - - bc_put_leb128(s, m->star_export_entries_count); - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - bc_put_leb128(s, se->req_module_idx); - } - - bc_put_leb128(s, m->import_entries_count); - for(i = 0; i < m->import_entries_count; i++) { - JSImportEntry *mi = &m->import_entries[i]; - bc_put_leb128(s, mi->var_idx); - bc_put_atom(s, mi->import_name); - bc_put_leb128(s, mi->req_module_idx); - } - - if (JS_WriteObjectRec(s, m->func_obj)) - goto fail; - } + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteModule(s, obj)) + goto fail; break; case JS_TAG_OBJECT: { JSObject *p = JS_VALUE_GET_OBJ(obj); - uint32_t i, prop_count, len; - JSShape *sh; - JSShapeProperty *pr; - JSValue val; - int ret, pass; - BOOL is_template; - JSAtom atom; - - if (p->class_id != JS_CLASS_ARRAY && - p->class_id != JS_CLASS_OBJECT) { - JS_ThrowTypeError(s->ctx, "unsupported object class"); - goto fail; - } - if (p->tmp_mark) { - JS_ThrowTypeError(s->ctx, "circular reference"); - goto fail; - } - p->tmp_mark = 1; - if (p->class_id == JS_CLASS_ARRAY) { - if (s->allow_bytecode && !p->extensible) { - /* not extensible array: we consider it is a - template when we are saving bytecode */ - bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); - is_template = TRUE; + int ret, idx; + + if (s->allow_reference) { + idx = js_object_list_find(s->ctx, &s->object_list, p); + if (idx >= 0) { + bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); + bc_put_leb128(s, idx); + break; } else { - bc_put_u8(s, BC_TAG_ARRAY); - is_template = FALSE; - } - if (js_get_length32(s->ctx, &len, obj)) - goto fail1; - bc_put_leb128(s, len); - for(i = 0; i < len; i++) { - val = JS_GetPropertyUint32(s->ctx, obj, i); - if (JS_IsException(val)) - goto fail1; - ret = JS_WriteObjectRec(s, val); - JS_FreeValue(s->ctx, val); - if (ret) - goto fail1; - } - if (is_template) { - val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); - if (JS_IsException(val)) - goto fail1; - ret = JS_WriteObjectRec(s, val); - JS_FreeValue(s->ctx, val); - if (ret) - goto fail1; + if (js_object_list_add(s->ctx, &s->object_list, p)) + goto fail; } } else { - bc_put_u8(s, BC_TAG_OBJECT); - prop_count = 0; - sh = p->shape; - for(pass = 0; pass < 2; pass++) { - if (pass == 1) - bc_put_leb128(s, prop_count); - for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { - atom = pr->atom; - if (atom != JS_ATOM_NULL && - JS_AtomIsString(s->ctx, atom) && - (pr->flags & JS_PROP_ENUMERABLE)) { - if (pr->flags & JS_PROP_TMASK) { - JS_ThrowTypeError(s->ctx, "only value properties are supported"); - goto fail; - } - if (pass == 0) { - prop_count++; - } else { - bc_put_atom(s, atom); - if (JS_WriteObjectRec(s, p->prop[i].u.value)) { - fail1: - p->tmp_mark = 0; - goto fail; - } - } - } - } + if (p->tmp_mark) { + JS_ThrowTypeError(s->ctx, "circular reference"); + goto fail; } + p->tmp_mark = 1; + } + switch(p->class_id) { + case JS_CLASS_ARRAY: + ret = JS_WriteArray(s, obj); + break; + case JS_CLASS_OBJECT: + ret = JS_WriteObjectTag(s, obj); + break; + case JS_CLASS_ARRAY_BUFFER: + ret = JS_WriteArrayBuffer(s, obj); + break; + case JS_CLASS_SHARED_ARRAY_BUFFER: + if (!s->allow_sab) + goto invalid_tag; + ret = JS_WriteSharedArrayBuffer(s, obj); + break; + case JS_CLASS_DATE: + bc_put_u8(s, BC_TAG_DATE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: + case JS_CLASS_BIG_FLOAT: + case JS_CLASS_BIG_DECIMAL: +#endif + bc_put_u8(s, BC_TAG_OBJECT_VALUE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + default: + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_WriteTypedArray(s, obj); + } else { + JS_ThrowTypeError(s->ctx, "unsupported object class"); + ret = -1; + } + break; } p->tmp_mark = 0; + if (ret) + goto fail; } break; #ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: case JS_TAG_BIG_FLOAT: - { - int32_t e; - JSBigFloat *bf = JS_VALUE_GET_PTR(obj); - bf_t *a = &bf->num; - size_t len, i, n1, j; - limb_t v; - - bc_put_u8(s, tag == JS_TAG_BIG_INT ? BC_TAG_BIG_INT : - BC_TAG_BIG_FLOAT); - if (a->len != 0) { - i = 0; - while (i < a->len && a->tab[i] == 0) - i++; - assert(i < a->len); - v = a->tab[i]; - n1 = sizeof(limb_t); - while ((v & 0xff) == 0) { - n1--; - v >>= 8; - } - i++; - len = (a->len - i) * sizeof(limb_t) + n1; - if (len > INT32_MAX) { - JS_ThrowInternalError(s->ctx, "bignum is too large"); - goto fail; - } - /* XXX: it would be slightly more efficient to put the - sign in the MSB of the mantissa */ - bc_put_leb128(s, len * 2 + a->sign); - /* always saved in byte based little endian representation */ - for(j = 0; j < n1; j++) { - dbuf_putc(&s->dbuf, v >> (j * 8)); - } - for(; i < a->len; i++) { - limb_t v = a->tab[i]; -#if LIMB_BITS == 32 -#ifdef WORDS_BIGENDIAN - v = bswap32(v); -#endif - dbuf_put_u32(&s->dbuf, v); -#else -#ifdef WORDS_BIGENDIAN - v = bswap64(v); -#endif - dbuf_put_u64(&s->dbuf, v); -#endif - } -#if LIMB_BITS != 32 - if (a->expn <= INT32_MIN || a->expn >= (INT32_MAX - 1)) { - JS_ThrowInternalError(s->ctx, "bignum exponent is too large"); - goto fail; - } -#endif - bc_put_sleb128(s, a->expn); - } else { - bc_put_leb128(s, a->sign); /* length + sign */ - if (tag == JS_TAG_BIG_FLOAT) { - /* special numbers */ - if (a->expn == BF_EXP_ZERO) { - e = 0; - } else if (a->expn == BF_EXP_INF) { - e = 1; - } else { - e = 2; - } - bc_put_u8(s, e); - } - } - } + case JS_TAG_BIG_DECIMAL: + if (JS_WriteBigNum(s, obj)) + goto fail; break; #endif default: @@ -32367,8 +34699,8 @@ static int JS_WriteObjectAtoms(BCWriterState *s) return -1; } -uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, - int flags) +uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags, uint8_t ***psab_tab, size_t *psab_tab_len) { BCWriterState ss, *s = &ss; @@ -32377,29 +34709,48 @@ uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, /* XXX: byte swapped output is untested */ s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0); s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); + s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); /* XXX: could use a different version when bytecode is included */ if (s->allow_bytecode) s->first_atom = JS_ATOM_END; else s->first_atom = 1; js_dbuf_init(ctx, &s->dbuf); - + js_object_list_init(&s->object_list); + if (JS_WriteObjectRec(s, obj)) goto fail; if (JS_WriteObjectAtoms(s)) goto fail; + js_object_list_end(ctx, &s->object_list); js_free(ctx, s->atom_to_idx); js_free(ctx, s->idx_to_atom); *psize = s->dbuf.size; + if (psab_tab) + *psab_tab = s->sab_tab; + if (psab_tab_len) + *psab_tab_len = s->sab_tab_len; return s->dbuf.buf; fail: + js_object_list_end(ctx, &s->object_list); js_free(ctx, s->atom_to_idx); js_free(ctx, s->idx_to_atom); dbuf_free(&s->dbuf); *psize = 0; + if (psab_tab) + *psab_tab = NULL; + if (psab_tab_len) + *psab_tab_len = 0; return NULL; } +uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags) +{ + return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL); +} + typedef struct BCReaderState { JSContext *ctx; const uint8_t *buf_start, *ptr, *buf_end; @@ -32407,8 +34758,15 @@ typedef struct BCReaderState { uint32_t idx_to_atom_count; JSAtom *idx_to_atom; int error_state; - BOOL allow_bytecode; - BOOL is_rom_data; + BOOL allow_sab : 8; + BOOL allow_bytecode : 8; + BOOL is_rom_data : 8; + BOOL allow_reference : 8; + /* object references */ + JSObject **objects; + int objects_count; + int objects_size; + #ifdef DUMP_READ_OBJECT const uint8_t *ptr_last; int level; @@ -32612,7 +34970,7 @@ static JSString *JS_ReadString(BCReaderState *s) p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ } #ifdef DUMP_READ_OBJECT - bc_read_trace(s, "string: "); JS_DumpString(s->ctx->rt, p); printf("\n"); + JS_DumpString(s->ctx->rt, p); printf("\n"); #endif return p; } @@ -32681,28 +35039,693 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, return 0; } +#ifdef CONFIG_BIGNUM +static JSValue JS_ReadBigNum(BCReaderState *s, int tag) +{ + JSValue obj = JS_UNDEFINED; + uint8_t v8; + int32_t e; + uint32_t len; + limb_t l, i, n, j; + JSBigFloat *p; + limb_t v; + bf_t *a; + int bpos, d; + + p = js_new_bf(s->ctx); + if (!p) + goto fail; + switch(tag) { + case BC_TAG_BIG_INT: + obj = JS_MKPTR(JS_TAG_BIG_INT, p); + break; + case BC_TAG_BIG_FLOAT: + obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p); + break; + case BC_TAG_BIG_DECIMAL: + obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p); + break; + default: + abort(); + } + + /* sign + exponent */ + if (bc_get_sleb128(s, &e)) + goto fail; + + a = &p->num; + a->sign = e & 1; + e >>= 1; + if (e == 0) + a->expn = BF_EXP_ZERO; + else if (e == 1) + a->expn = BF_EXP_INF; + else if (e == 2) + a->expn = BF_EXP_NAN; + else if (e >= 3) + a->expn = e - 3; + else + a->expn = e; + + /* mantissa */ + if (a->expn != BF_EXP_ZERO && + a->expn != BF_EXP_INF && + a->expn != BF_EXP_NAN) { + if (bc_get_leb128(s, &len)) + goto fail; + bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); + if (len == 0) { + JS_ThrowInternalError(s->ctx, "invalid bignum length"); + goto fail; + } + if (tag != BC_TAG_BIG_DECIMAL) + l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); + else + l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS; + if (bf_resize(a, l)) { + JS_ThrowOutOfMemory(s->ctx); + goto fail; + } + if (tag != BC_TAG_BIG_DECIMAL) { + n = len & (sizeof(limb_t) - 1); + if (n != 0) { + v = 0; + for(i = 0; i < n; i++) { + if (bc_get_u8(s, &v8)) + goto fail; + v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); + } + a->tab[0] = v; + i = 1; + } else { + i = 0; + } + for(; i < l; i++) { +#if LIMB_BITS == 32 + if (bc_get_u32(s, &v)) + goto fail; +#ifdef WORDS_BIGENDIAN + v = bswap32(v); +#endif +#else + if (bc_get_u64(s, &v)) + goto fail; +#ifdef WORDS_BIGENDIAN + v = bswap64(v); +#endif +#endif + a->tab[i] = v; + } + } else { + bpos = 0; + for(i = 0; i < l; i++) { + if (i == 0 && (n = len % LIMB_DIGITS) != 0) { + j = LIMB_DIGITS - n; + } else { + j = 0; + } + v = 0; + for(; j < LIMB_DIGITS; j++) { + if (bpos == 0) { + if (bc_get_u8(s, &v8)) + goto fail; + d = v8 & 0xf; + bpos = 1; + } else { + d = v8 >> 4; + bpos = 0; + } + if (d >= 10) { + JS_ThrowInternalError(s->ctx, "invalid digit"); + goto fail; + } + v += mp_pow_dec[j] * d; + } + a->tab[i] = v; + } + } + } + bc_read_trace(s, "}\n"); + return obj; + fail: + JS_FreeValue(s->ctx, obj); + return JS_EXCEPTION; +} +#endif /* CONFIG_BIGNUM */ + +static JSValue JS_ReadObjectRec(BCReaderState *s); + +static int BC_add_object_ref1(BCReaderState *s, JSObject *p) +{ + if (s->allow_reference) { + if (js_resize_array(s->ctx, (void *)&s->objects, + sizeof(s->objects[0]), + &s->objects_size, s->objects_count + 1)) + return -1; + s->objects[s->objects_count++] = p; + } + return 0; +} + +static int BC_add_object_ref(BCReaderState *s, JSValueConst obj) +{ + return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj)); +} + +static JSValue JS_ReadFunctionTag(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSFunctionBytecode bc, *b; + JSValue obj = JS_UNDEFINED; + uint16_t v16; + uint8_t v8; + int idx, i, local_count; + int function_size, cpool_offset, byte_code_offset; + int closure_var_offset, vardefs_offset; + + memset(&bc, 0, sizeof(bc)); + bc.header.ref_count = 1; + //bc.gc_header.mark = 0; + + if (bc_get_u16(s, &v16)) + goto fail; + idx = 0; + bc.has_prototype = bc_get_flags(v16, &idx, 1); + bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); + bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); + bc.need_home_object = bc_get_flags(v16, &idx, 1); + bc.func_kind = bc_get_flags(v16, &idx, 2); + bc.new_target_allowed = bc_get_flags(v16, &idx, 1); + bc.super_call_allowed = bc_get_flags(v16, &idx, 1); + bc.super_allowed = bc_get_flags(v16, &idx, 1); + bc.arguments_allowed = bc_get_flags(v16, &idx, 1); + bc.has_debug = bc_get_flags(v16, &idx, 1); + bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); + bc.read_only_bytecode = s->is_rom_data; + if (bc_get_u8(s, &v8)) + goto fail; + bc.js_mode = v8; + if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure + goto fail; + if (bc_get_leb128_u16(s, &bc.arg_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.var_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.defined_arg_count)) + goto fail; + if (bc_get_leb128_u16(s, &bc.stack_size)) + goto fail; + if (bc_get_leb128_int(s, &bc.closure_var_count)) + goto fail; + if (bc_get_leb128_int(s, &bc.cpool_count)) + goto fail; + if (bc_get_leb128_int(s, &bc.byte_code_len)) + goto fail; + if (bc_get_leb128_int(s, &local_count)) + goto fail; + + if (bc.has_debug) { + function_size = sizeof(*b); + } else { + function_size = offsetof(JSFunctionBytecode, debug); + } + cpool_offset = function_size; + function_size += bc.cpool_count * sizeof(*bc.cpool); + vardefs_offset = function_size; + function_size += local_count * sizeof(*bc.vardefs); + closure_var_offset = function_size; + function_size += bc.closure_var_count * sizeof(*bc.closure_var); + byte_code_offset = function_size; + if (!bc.read_only_bytecode) { + function_size += bc.byte_code_len; + } + + b = js_mallocz(ctx, function_size); + if (!b) + return JS_EXCEPTION; + + memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); + b->header.ref_count = 1; + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + + obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); + +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n"); +#endif + bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", + b->arg_count, b->var_count, b->defined_arg_count, + b->closure_var_count, b->cpool_count); + bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", + b->stack_size, b->byte_code_len, local_count); + + if (local_count != 0) { + bc_read_trace(s, "vars {\n"); + b->vardefs = (void *)((uint8_t*)b + vardefs_offset); + for(i = 0; i < local_count; i++) { + JSVarDef *vd = &b->vardefs[i]; + if (bc_get_atom(s, &vd->var_name)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_level)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_next)) + goto fail; + vd->scope_next--; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + vd->var_kind = bc_get_flags(v8, &idx, 4); + vd->is_const = bc_get_flags(v8, &idx, 1); + vd->is_lexical = bc_get_flags(v8, &idx, 1); + vd->is_captured = bc_get_flags(v8, &idx, 1); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); +#endif + } + bc_read_trace(s, "}\n"); + } + if (b->closure_var_count != 0) { + bc_read_trace(s, "closure vars {\n"); + b->closure_var = (void *)((uint8_t*)b + closure_var_offset); + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + int var_idx; + if (bc_get_atom(s, &cv->var_name)) + goto fail; + if (bc_get_leb128_int(s, &var_idx)) + goto fail; + cv->var_idx = var_idx; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + cv->is_local = bc_get_flags(v8, &idx, 1); + cv->is_arg = bc_get_flags(v8, &idx, 1); + cv->is_const = bc_get_flags(v8, &idx, 1); + cv->is_lexical = bc_get_flags(v8, &idx, 1); + cv->var_kind = bc_get_flags(v8, &idx, 4); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n"); +#endif + } + bc_read_trace(s, "}\n"); + } + { + bc_read_trace(s, "bytecode {\n"); + if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) + goto fail; + bc_read_trace(s, "}\n"); + } + if (b->has_debug) { + /* read optional debug information */ + bc_read_trace(s, "debug {\n"); + if (bc_get_atom(s, &b->debug.filename)) + goto fail; + if (bc_get_leb128_int(s, &b->debug.line_num)) + goto fail; + if (bc_get_leb128_int(s, &b->debug.pc2line_len)) + goto fail; + if (b->debug.pc2line_len) { + b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); + if (!b->debug.pc2line_buf) + goto fail; + if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) + goto fail; + } +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); +#endif + bc_read_trace(s, "}\n"); + } + if (b->cpool_count != 0) { + bc_read_trace(s, "cpool {\n"); + b->cpool = (void *)((uint8_t*)b + cpool_offset); + for(i = 0; i < b->cpool_count; i++) { + JSValue val; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + b->cpool[i] = val; + } + bc_read_trace(s, "}\n"); + } + b->realm = JS_DupContext(ctx); + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadModule(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj; + JSModuleDef *m = NULL; + JSAtom module_name; + int i; + uint8_t v8; + + if (bc_get_atom(s, &module_name)) + goto fail; +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n"); +#endif + m = js_new_module_def(ctx, module_name); + if (!m) + goto fail; + obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + if (bc_get_leb128_int(s, &m->req_module_entries_count)) + goto fail; + if (m->req_module_entries_count != 0) { + m->req_module_entries_size = m->req_module_entries_count; + m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); + if (!m->req_module_entries) + goto fail; + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + if (bc_get_atom(s, &rme->module_name)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->export_entries_count)) + goto fail; + if (m->export_entries_count != 0) { + m->export_entries_size = m->export_entries_count; + m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); + if (!m->export_entries) + goto fail; + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (bc_get_u8(s, &v8)) + goto fail; + me->export_type = v8; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + if (bc_get_leb128_int(s, &me->u.local.var_idx)) + goto fail; + } else { + if (bc_get_leb128_int(s, &me->u.req_module_idx)) + goto fail; + if (bc_get_atom(s, &me->local_name)) + goto fail; + } + if (bc_get_atom(s, &me->export_name)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->star_export_entries_count)) + goto fail; + if (m->star_export_entries_count != 0) { + m->star_export_entries_size = m->star_export_entries_count; + m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); + if (!m->star_export_entries) + goto fail; + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + if (bc_get_leb128_int(s, &se->req_module_idx)) + goto fail; + } + } + + if (bc_get_leb128_int(s, &m->import_entries_count)) + goto fail; + if (m->import_entries_count != 0) { + m->import_entries_size = m->import_entries_count; + m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); + if (!m->import_entries) + goto fail; + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + if (bc_get_leb128_int(s, &mi->var_idx)) + goto fail; + if (bc_get_atom(s, &mi->import_name)) + goto fail; + if (bc_get_leb128_int(s, &mi->req_module_idx)) + goto fail; + } + } + + m->func_obj = JS_ReadObjectRec(s); + if (JS_IsException(m->func_obj)) + goto fail; + return obj; + fail: + if (m) { + js_free_module_def(ctx, m); + } + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectTag(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj; + uint32_t prop_count, i; + JSAtom atom; + JSValue val; + int ret; + + obj = JS_NewObject(ctx); + if (BC_add_object_ref(s, obj)) + goto fail; + if (bc_get_leb128(s, &prop_count)) + goto fail; + for(i = 0; i < prop_count; i++) { + if (bc_get_atom(s, &atom)) + goto fail; +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n"); +#endif + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) { + JS_FreeAtom(ctx, atom); + goto fail; + } + ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) + goto fail; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadArray(BCReaderState *s, int tag) +{ + JSContext *ctx = s->ctx; + JSValue obj; + uint32_t len, i; + JSValue val; + int ret, prop_flags; + BOOL is_template; + + obj = JS_NewArray(ctx); + if (BC_add_object_ref(s, obj)) + goto fail; + is_template = (tag == BC_TAG_TEMPLATE_OBJECT); + if (bc_get_leb128(s, &len)) + goto fail; + for(i = 0; i < len; i++) { + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (is_template) + prop_flags = JS_PROP_ENUMERABLE; + else + prop_flags = JS_PROP_C_W_E; + ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, + prop_flags); + if (ret < 0) + goto fail; + } + if (is_template) { + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (!JS_IsUndefined(val)) { + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); + if (ret < 0) + goto fail; + } + JS_PreventExtensions(ctx, obj); + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadTypedArray(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED; + uint8_t array_tag; + JSValueConst args[3]; + uint32_t offset, len, idx; + + if (bc_get_u8(s, &array_tag)) + return JS_EXCEPTION; + if (array_tag >= JS_TYPED_ARRAY_COUNT) + return JS_ThrowTypeError(ctx, "invalid typed array"); + if (bc_get_leb128(s, &len)) + return JS_EXCEPTION; + if (bc_get_leb128(s, &offset)) + return JS_EXCEPTION; + /* XXX: this hack could be avoided if the typed array could be + created before the array buffer */ + idx = s->objects_count; + if (BC_add_object_ref1(s, NULL)) + goto fail; + array_buffer = JS_ReadObjectRec(s); + if (JS_IsException(array_buffer)) + return JS_EXCEPTION; + if (!js_get_array_buffer(ctx, array_buffer)) { + JS_FreeValue(ctx, array_buffer); + return JS_EXCEPTION; + } + args[0] = array_buffer; + args[1] = JS_NewInt64(ctx, offset); + args[2] = JS_NewInt64(ctx, len); + obj = js_typed_array_constructor(ctx, JS_UNDEFINED, + 3, args, + JS_CLASS_UINT8C_ARRAY + array_tag); + if (JS_IsException(obj)) + goto fail; + if (s->allow_reference) { + s->objects[idx] = JS_VALUE_GET_OBJ(obj); + } + JS_FreeValue(ctx, array_buffer); + return obj; + fail: + JS_FreeValue(ctx, array_buffer); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadArrayBuffer(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint32_t byte_length; + JSValue obj; + + if (bc_get_leb128(s, &byte_length)) + return JS_EXCEPTION; + if (unlikely(s->buf_end - s->ptr < byte_length)) { + bc_read_error_end(s); + return JS_EXCEPTION; + } + obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + s->ptr += byte_length; + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + uint32_t byte_length; + uint8_t *data_ptr; + JSValue obj; + uint64_t u64; + + if (bc_get_leb128(s, &byte_length)) + return JS_EXCEPTION; + if (bc_get_u64(s, &u64)) + return JS_EXCEPTION; + data_ptr = (uint8_t *)(uintptr_t)u64; + /* the SharedArrayBuffer is cloned */ + obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length, + JS_CLASS_SHARED_ARRAY_BUFFER, + data_ptr, + NULL, NULL, FALSE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadDate(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue val, obj = JS_UNDEFINED; + + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + if (!JS_IsNumber(val)) { + JS_ThrowTypeError(ctx, "Number tag expected for date"); + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE], + JS_CLASS_DATE); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + JS_SetObjectData(ctx, obj, val); + return obj; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadObjectValue(BCReaderState *s) +{ + JSContext *ctx = s->ctx; + JSValue val, obj = JS_UNDEFINED; + + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + obj = JS_ToObject(ctx, val); + if (JS_IsException(obj)) + goto fail; + if (BC_add_object_ref(s, obj)) + goto fail; + JS_FreeValue(ctx, val); + return obj; + fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + static JSValue JS_ReadObjectRec(BCReaderState *s) { JSContext *ctx = s->ctx; uint8_t tag; JSValue obj = JS_UNDEFINED; - JSModuleDef *m = NULL; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); if (bc_get_u8(s, &tag)) return JS_EXCEPTION; + bc_read_trace(s, "%s {\n", bc_tag_str[tag]); + switch(tag) { case BC_TAG_NULL: - bc_read_trace(s, "null\n"); obj = JS_NULL; break; case BC_TAG_UNDEFINED: - bc_read_trace(s, "undefined\n"); obj = JS_UNDEFINED; break; case BC_TAG_BOOL_FALSE: case BC_TAG_BOOL_TRUE: - bc_read_trace(s, "%s\n", bc_tag_str[tag]); obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE); break; case BC_TAG_INT32: @@ -32710,7 +35733,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) int32_t val; if (bc_get_sleb128(s, &val)) return JS_EXCEPTION; - bc_read_trace(s, "int32 %d\n", val); + bc_read_trace(s, "%d\n", val); obj = JS_NewInt32(ctx, val); } break; @@ -32719,7 +35742,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) JSFloat64Union u; if (bc_get_u64(s, &u.u64)) return JS_EXCEPTION; - bc_read_trace(s, "float64 %g\n", u.d); + bc_read_trace(s, "%g\n", u.d); obj = __JS_NewFloat64(ctx, u.d); } break; @@ -32733,450 +35756,68 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) } break; case BC_TAG_FUNCTION_BYTECODE: - { - JSFunctionBytecode bc, *b; - uint16_t v16; - uint8_t v8; - int idx, i, local_count; - int function_size, cpool_offset, byte_code_offset; - int closure_var_offset, vardefs_offset; - - if (!s->allow_bytecode) - goto invalid_tag; - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - memset(&bc, 0, sizeof(bc)); - bc.header.ref_count = 1; - //bc.gc_header.mark = 0; - - if (bc_get_u16(s, &v16)) - goto fail; - idx = 0; - bc.has_prototype = bc_get_flags(v16, &idx, 1); - bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); - bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); - bc.need_home_object = bc_get_flags(v16, &idx, 1); - bc.func_kind = bc_get_flags(v16, &idx, 2); - bc.new_target_allowed = bc_get_flags(v16, &idx, 1); - bc.super_call_allowed = bc_get_flags(v16, &idx, 1); - bc.super_allowed = bc_get_flags(v16, &idx, 1); - bc.arguments_allowed = bc_get_flags(v16, &idx, 1); - bc.has_debug = bc_get_flags(v16, &idx, 1); - bc.read_only_bytecode = s->is_rom_data; - if (bc_get_u8(s, &v8)) - goto fail; - bc.js_mode = v8; - if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure - goto fail; - if (bc_get_leb128_u16(s, &bc.arg_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.var_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.defined_arg_count)) - goto fail; - if (bc_get_leb128_u16(s, &bc.stack_size)) - goto fail; - if (bc_get_leb128_int(s, &bc.closure_var_count)) - goto fail; - if (bc_get_leb128_int(s, &bc.cpool_count)) - goto fail; - if (bc_get_leb128_int(s, &bc.byte_code_len)) - goto fail; - if (bc_get_leb128_int(s, &local_count)) - goto fail; - - if (bc.has_debug) { - function_size = sizeof(*b); - } else { - function_size = offsetof(JSFunctionBytecode, debug); - } - cpool_offset = function_size; - function_size += bc.cpool_count * sizeof(*bc.cpool); - vardefs_offset = function_size; - function_size += local_count * sizeof(*bc.vardefs); - closure_var_offset = function_size; - function_size += bc.closure_var_count * sizeof(*bc.closure_var); - byte_code_offset = function_size; - if (!bc.read_only_bytecode) { - function_size += bc.byte_code_len; - } - - b = js_mallocz(ctx, function_size); - if (!b) - return JS_EXCEPTION; - - memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); - obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); - -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n"); -#endif - bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", - b->arg_count, b->var_count, b->defined_arg_count, - b->closure_var_count, b->cpool_count); - bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", - b->stack_size, b->byte_code_len, local_count); - - if (local_count != 0) { - bc_read_trace(s, "vars {\n"); - b->vardefs = (void *)((uint8_t*)b + vardefs_offset); - for(i = 0; i < local_count; i++) { - JSVarDef *vd = &b->vardefs[i]; - if (bc_get_atom(s, &vd->var_name)) - goto fail; - if (bc_get_leb128_int(s, &vd->scope_level)) - goto fail; - if (bc_get_leb128_int(s, &vd->scope_next)) - goto fail; - vd->scope_next--; - if (bc_get_u8(s, &v8)) - goto fail; - idx = 0; - vd->var_kind = bc_get_flags(v8, &idx, 4); - vd->is_func_var = bc_get_flags(v8, &idx, 1); - vd->is_const = bc_get_flags(v8, &idx, 1); - vd->is_lexical = bc_get_flags(v8, &idx, 1); - vd->is_captured = bc_get_flags(v8, &idx, 1); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); -#endif - } - bc_read_trace(s, "}\n"); - } - if (b->closure_var_count != 0) { - bc_read_trace(s, "closure vars {\n"); - b->closure_var = (void *)((uint8_t*)b + closure_var_offset); - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - int var_idx; - if (bc_get_atom(s, &cv->var_name)) - goto fail; - if (bc_get_leb128_int(s, &var_idx)) - goto fail; - cv->var_idx = var_idx; - if (bc_get_u8(s, &v8)) - goto fail; - idx = 0; - cv->is_local = bc_get_flags(v8, &idx, 1); - cv->is_arg = bc_get_flags(v8, &idx, 1); - cv->is_const = bc_get_flags(v8, &idx, 1); - cv->is_lexical = bc_get_flags(v8, &idx, 1); - cv->var_kind = bc_get_flags(v8, &idx, 4); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n"); -#endif - } - bc_read_trace(s, "}\n"); - } - { - bc_read_trace(s, "bytecode {\n"); - if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) - goto fail; - bc_read_trace(s, "}\n"); - } - if (b->has_debug) { - /* read optional debug information */ - bc_read_trace(s, "debug {\n"); - if (bc_get_atom(s, &b->debug.filename)) - goto fail; - if (bc_get_leb128_int(s, &b->debug.line_num)) - goto fail; - if (bc_get_leb128_int(s, &b->debug.pc2line_len)) - goto fail; - if (b->debug.pc2line_len) { - b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); - if (!b->debug.pc2line_buf) - goto fail; - if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) - goto fail; - } -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); -#endif - bc_read_trace(s, "}\n"); - } - if (b->cpool_count != 0) { - bc_read_trace(s, "cpool {\n"); - b->cpool = (void *)((uint8_t*)b + cpool_offset); - for(i = 0; i < b->cpool_count; i++) { - JSValue val; - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - b->cpool[i] = val; - } - bc_read_trace(s, "}\n"); - } - bc_read_trace(s, "}\n"); - } + if (!s->allow_bytecode) + goto invalid_tag; + obj = JS_ReadFunctionTag(s); break; case BC_TAG_MODULE: - { - JSAtom module_name; - int i; - uint8_t v8; - - if (!s->allow_bytecode) - goto invalid_tag; - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - if (bc_get_atom(s, &module_name)) - goto fail; -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n"); -#endif - m = js_new_module_def(ctx, module_name); - if (!m) - goto fail; - obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); - if (bc_get_leb128_int(s, &m->req_module_entries_count)) - goto fail; - if (m->req_module_entries_count != 0) { - m->req_module_entries_size = m->req_module_entries_count; - m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); - if (!m->req_module_entries) - goto fail; - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (bc_get_atom(s, &rme->module_name)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->export_entries_count)) - goto fail; - if (m->export_entries_count != 0) { - m->export_entries_size = m->export_entries_count; - m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); - if (!m->export_entries) - goto fail; - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (bc_get_u8(s, &v8)) - goto fail; - me->export_type = v8; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - if (bc_get_leb128_int(s, &me->u.local.var_idx)) - goto fail; - } else { - if (bc_get_leb128_int(s, &me->u.req_module_idx)) - goto fail; - if (bc_get_atom(s, &me->local_name)) - goto fail; - } - if (bc_get_atom(s, &me->export_name)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->star_export_entries_count)) - goto fail; - if (m->star_export_entries_count != 0) { - m->star_export_entries_size = m->star_export_entries_count; - m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); - if (!m->star_export_entries) - goto fail; - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - if (bc_get_leb128_int(s, &se->req_module_idx)) - goto fail; - } - } - - if (bc_get_leb128_int(s, &m->import_entries_count)) - goto fail; - if (m->import_entries_count != 0) { - m->import_entries_size = m->import_entries_count; - m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); - if (!m->import_entries) - goto fail; - for(i = 0; i < m->import_entries_count; i++) { - JSImportEntry *mi = &m->import_entries[i]; - if (bc_get_leb128_int(s, &mi->var_idx)) - goto fail; - if (bc_get_atom(s, &mi->import_name)) - goto fail; - if (bc_get_leb128_int(s, &mi->req_module_idx)) - goto fail; - } - } - - m->func_obj = JS_ReadObjectRec(s); - if (JS_IsException(m->func_obj)) - goto fail; - bc_read_trace(s, "}\n"); - } + if (!s->allow_bytecode) + goto invalid_tag; + obj = JS_ReadModule(s); break; case BC_TAG_OBJECT: - { - uint32_t prop_count, i; - JSAtom atom; - JSValue val; - int ret; - - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - obj = JS_NewObject(ctx); - if (bc_get_leb128(s, &prop_count)) - goto fail; - for(i = 0; i < prop_count; i++) { - if (bc_get_atom(s, &atom)) - goto fail; -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n"); -#endif - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) { - JS_FreeAtom(ctx, atom); - goto fail; - } - ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); - JS_FreeAtom(ctx, atom); - if (ret < 0) - goto fail; - } - bc_read_trace(s, "}\n"); - } + obj = JS_ReadObjectTag(s); break; case BC_TAG_ARRAY: case BC_TAG_TEMPLATE_OBJECT: - { - uint32_t len, i; - JSValue val; - int ret, prop_flags; - BOOL is_template; - - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - obj = JS_NewArray(ctx); - is_template = (tag == BC_TAG_TEMPLATE_OBJECT); - if (bc_get_leb128(s, &len)) - goto fail; - for(i = 0; i < len; i++) { - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - if (is_template) - prop_flags = JS_PROP_ENUMERABLE; - else - prop_flags = JS_PROP_C_W_E; - ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, - prop_flags); - if (ret < 0) - goto fail; - } - if (is_template) { - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - if (!JS_IsUndefined(val)) { - ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); - if (ret < 0) - goto fail; - } - JS_PreventExtensions(ctx, obj); - } - bc_read_trace(s, "}\n"); - } + obj = JS_ReadArray(s, tag); + break; + case BC_TAG_TYPED_ARRAY: + obj = JS_ReadTypedArray(s); + break; + case BC_TAG_ARRAY_BUFFER: + obj = JS_ReadArrayBuffer(s); + break; + case BC_TAG_SHARED_ARRAY_BUFFER: + if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) + goto invalid_tag; + obj = JS_ReadSharedArrayBuffer(s); + break; + case BC_TAG_DATE: + obj = JS_ReadDate(s); + break; + case BC_TAG_OBJECT_VALUE: + obj = JS_ReadObjectValue(s); break; #ifdef CONFIG_BIGNUM case BC_TAG_BIG_INT: case BC_TAG_BIG_FLOAT: - { - uint8_t sign, v8; - int32_t e; - uint32_t len; - limb_t l, i, n; - JSBigFloat *p; - bf_t *a; - - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - if (bc_get_leb128(s, &len)) - goto fail; - sign = len & 1; - len >>= 1; - p = js_new_bf(s->ctx); - if (!p) - goto fail; - if (tag == BC_TAG_BIG_INT) - obj = JS_MKPTR(JS_TAG_BIG_INT, p); - else - obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p); - l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); - a = &p->num; - bf_resize(a, l); - a->sign = sign; - if (len == 0) { - if (tag == BC_TAG_BIG_INT) { - a->expn = BF_EXP_ZERO; - } else { - if (bc_get_u8(s, &v8)) - goto fail; - switch(v8) { - case 0: - a->expn = BF_EXP_ZERO; - break; - case 1: - a->expn = BF_EXP_INF; - break; - default: - case 2: - a->expn = BF_EXP_NAN; - break; - } - } - } else { - n = len & (sizeof(limb_t) - 1); - if (n != 0) { - limb_t v; - v = 0; - for(i = 0; i < n; i++) { - if (bc_get_u8(s, &v8)) - goto fail; - v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); - } - a->tab[0] = v; - i = 1; - } else { - i = 0; - } - for(; i < l; i++) { - limb_t v; -#if LIMB_BITS == 32 - if (bc_get_u32(s, &v)) - goto fail; -#ifdef WORDS_BIGENDIAN - v = bswap32(v); -#endif -#else - if (bc_get_u64(s, &v)) - goto fail; -#ifdef WORDS_BIGENDIAN - v = bswap64(v); -#endif -#endif - a->tab[i] = v; - } - if (bc_get_sleb128(s, &e)) - goto fail; - a->expn = e; - } - bc_read_trace(s, "}\n"); - } + case BC_TAG_BIG_DECIMAL: + obj = JS_ReadBigNum(s, tag); break; #endif + case BC_TAG_OBJECT_REFERENCE: + { + uint32_t val; + if (!s->allow_reference) + return JS_ThrowSyntaxError(ctx, "object references are not allowed"); + if (bc_get_leb128(s, &val)) + return JS_EXCEPTION; + bc_read_trace(s, "%u\n", val); + if (val >= s->objects_count) { + return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)", + val, s->objects_count); + } + obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val])); + } + break; default: invalid_tag: return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)", tag, (unsigned int)(s->ptr - s->buf_start)); } + bc_read_trace(s, "}\n"); return obj; - fail: - if (m) { - js_free_module_def(ctx, m); - } - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; } static int JS_ReadObjectAtoms(BCReaderState *s) @@ -33229,10 +35870,11 @@ static void bc_reader_free(BCReaderState *s) } js_free(s->ctx, s->idx_to_atom); } + js_free(s->ctx, s->objects); } JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags) + int flags) { BCReaderState ss, *s = &ss; JSValue obj; @@ -33247,6 +35889,8 @@ JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, s->ptr = buf; s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0); + s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); if (s->allow_bytecode) s->first_atom = JS_ATOM_END; else @@ -33307,16 +35951,39 @@ static JSAtom find_atom(JSContext *ctx, const char *name) return atom; } -static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, - JSAtom atom, void *opaque) +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque) { const JSCFunctionListEntry *e = opaque; - JSValueConst obj = JS_MKPTR(JS_TAG_OBJECT, p); + JSValue val; + + switch(e->def_type) { + case JS_DEF_CFUNC: + val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, + e->name, e->u.func.length, e->u.func.cproto, e->magic); + break; + case JS_DEF_PROP_STRING: + val = JS_NewAtomString(ctx, e->u.str); + break; + case JS_DEF_OBJECT: + val = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); + break; + default: + abort(); + } + return val; +} + +static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, + JSAtom atom, + const JSCFunctionListEntry *e) +{ JSValue val; int prop_flags = e->prop_flags; switch(e->def_type) { - case JS_DEF_ALIAS: + case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */ { JSAtom atom1 = find_atom(ctx, e->u.alias.name); switch (e->u.alias.base) { @@ -33333,12 +36000,16 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, abort(); } JS_FreeAtom(ctx, atom1); - goto setval; + if (atom == JS_ATOM_Symbol_toPrimitive) { + /* Symbol.toPrimitive functions are not writable */ + prop_flags = JS_PROP_CONFIGURABLE; + } else if (atom == JS_ATOM_Symbol_hasInstance) { + /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ + prop_flags = 0; + } } + break; case JS_DEF_CFUNC: - val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, - e->name, e->u.func.length, e->u.func.cproto, e->magic); - setval: if (atom == JS_ATOM_Symbol_toPrimitive) { /* Symbol.toPrimitive functions are not writable */ prop_flags = JS_PROP_CONFIGURABLE; @@ -33346,8 +36017,10 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ prop_flags = 0; } - break; - case JS_DEF_CGETSET: + JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, + (void *)e, prop_flags); + return 0; + case JS_DEF_CGETSET: /* XXX: use autoinit again ? */ case JS_DEF_CGETSET_MAGIC: { JSValue getter, setter; @@ -33371,9 +36044,6 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, return 0; } break; - case JS_DEF_PROP_STRING: - val = JS_NewAtomString(ctx, e->u.str); - break; case JS_DEF_PROP_INT32: val = JS_NewInt32(ctx, e->u.i32); break; @@ -33386,10 +36056,11 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, case JS_DEF_PROP_UNDEFINED: val = JS_UNDEFINED; break; + case JS_DEF_PROP_STRING: case JS_DEF_OBJECT: - val = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); - break; + JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, + (void *)e, prop_flags); + return 0; default: abort(); } @@ -33400,36 +36071,12 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p, void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, const JSCFunctionListEntry *tab, int len) { - int i, prop_flags; + int i; for (i = 0; i < len; i++) { const JSCFunctionListEntry *e = &tab[i]; JSAtom atom = find_atom(ctx, e->name); - - switch (e->def_type) { - case JS_DEF_CFUNC: - case JS_DEF_CGETSET: - case JS_DEF_CGETSET_MAGIC: - case JS_DEF_PROP_STRING: - case JS_DEF_ALIAS: - case JS_DEF_OBJECT: - prop_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE | (e->prop_flags & JS_PROP_ENUMERABLE); - JS_DefineAutoInitProperty(ctx, obj, atom, - JS_InstantiateFunctionListItem, - (void *)e, prop_flags); - break; - case JS_DEF_PROP_INT32: - case JS_DEF_PROP_INT64: - case JS_DEF_PROP_DOUBLE: - case JS_DEF_PROP_UNDEFINED: - { - JSObject *p = JS_VALUE_GET_OBJ(obj); - JS_InstantiateFunctionListItem(ctx, p, atom, (void *)e); - } - break; - default: - abort(); - } + JS_InstantiateFunctionListItem(ctx, obj, atom, e); JS_FreeAtom(ctx, atom); } } @@ -33470,6 +36117,10 @@ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, case JS_DEF_PROP_DOUBLE: val = __JS_NewFloat64(ctx, e->u.f64); break; + case JS_DEF_OBJECT: + val = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); + break; default: abort(); } @@ -33494,9 +36145,8 @@ static void JS_SetConstructor2(JSContext *ctx, set_cycle_flag(ctx, proto); } -static void JS_SetConstructor(JSContext *ctx, - JSValueConst func_obj, - JSValueConst proto) +void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst proto) { JS_SetConstructor2(ctx, func_obj, proto, 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); @@ -33551,49 +36201,14 @@ static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val, return JS_NewBool(ctx, isnan(d)); } -#ifdef CONFIG_BIGNUM -static BOOL js_number_is_finite(JSContext *ctx, JSValueConst val) -{ - BOOL res; - uint32_t tag; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_FLOAT64: - res = isfinite(JS_VALUE_GET_FLOAT64(val)); - break; - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - res = bf_is_finite(&p->num); - } - break; - default: - res = TRUE; - break; - } - return res; -} -#endif - static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { BOOL res; -#ifdef CONFIG_BIGNUM - JSValue val; - - val = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(val)) - return val; - res = js_number_is_finite(ctx, val); - JS_FreeValue(ctx, val); -#else double d; if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) return JS_EXCEPTION; res = isfinite(d); -#endif return JS_NewBool(ctx, res); } @@ -33613,27 +36228,20 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) case JS_TAG_EXCEPTION: return JS_DupValue(ctx, val); #ifdef CONFIG_BIGNUM - case JS_TAG_INT: - if (is_bignum_mode(ctx)) - obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); - else - obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); - goto set_value; case JS_TAG_BIG_INT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); goto set_value; - case JS_TAG_FLOAT64: - obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); - goto set_value; case JS_TAG_BIG_FLOAT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); goto set_value; -#else + case JS_TAG_BIG_DECIMAL: + obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL); + goto set_value; +#endif case JS_TAG_INT: case JS_TAG_FLOAT64: obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); goto set_value; -#endif case JS_TAG_STRING: /* XXX: should call the string constructor */ { @@ -33775,7 +36383,7 @@ static __exception int JS_ObjectDefineProperties(JSContext *ctx, if (JS_IsException(props)) return -1; p = JS_VALUE_GET_OBJ(props); - if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK) < 0) + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0) goto exception; for(i = 0; i < len; i++) { JS_FreeValue(ctx, desc); @@ -33852,7 +36460,7 @@ static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val, JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) return JS_ThrowTypeErrorNotAnObject(ctx); } - return JS_DupValue(ctx, JS_GetPrototype(ctx, val)); + return JS_GetPrototype(ctx, val); } static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val, @@ -34206,10 +36814,13 @@ static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val ret = JS_PreventExtensions(ctx, obj); if (ret < 0) return JS_EXCEPTION; - if (reflect) + if (reflect) { return JS_NewBool(ctx, ret); - else + } else { + if (!ret) + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); return JS_DupValue(ctx, obj); + } } static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, @@ -34345,6 +36956,13 @@ static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, if (!JS_IsObject(obj)) return JS_DupValue(ctx, obj); + res = JS_PreventExtensions(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + if (!res) { + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); + } + p = JS_VALUE_GET_OBJ(obj); flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) @@ -34370,7 +36988,7 @@ static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, goto exception; } js_free_prop_enum(ctx, props, len); - return js_object_preventExtensions(ctx, JS_UNDEFINED, 1, argv, 0); + return JS_DupValue(ctx, obj); exception: js_free_prop_enum(ctx, props, len); @@ -34636,7 +37254,7 @@ static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val) val = JS_ToObject(ctx, this_val); if (JS_IsException(val)) return val; - ret = JS_DupValue(ctx, JS_GetPrototype(ctx, val)); + ret = JS_GetPrototype(ctx, val); JS_FreeValue(ctx, val); return ret; } @@ -34657,9 +37275,9 @@ static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val, static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - JSValue obj; + JSValue obj, v1; JSValueConst v; - int max_depth = 1000, res = -1; + int res; v = argv[0]; if (!JS_IsObject(v)) @@ -34667,27 +37285,29 @@ static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, obj = JS_ToObject(ctx, this_val); if (JS_IsException(obj)) return JS_EXCEPTION; - while (--max_depth > 0) { - v = JS_GetPrototype(ctx, v); - if (JS_IsException(v)) + v1 = JS_DupValue(ctx, v); + for(;;) { + v1 = JS_GetPrototypeFree(ctx, v1); + if (JS_IsException(v1)) goto exception; - if (JS_IsNull(v)) { + if (JS_IsNull(v1)) { res = FALSE; break; } - if (js_strict_eq2(ctx, JS_DupValue(ctx, obj), JS_DupValue(ctx, v), - JS_EQ_STRICT)) { + if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { res = TRUE; break; } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; } + JS_FreeValue(ctx, v1); JS_FreeValue(ctx, obj); - if (res < 0) - return JS_ThrowInternalError(ctx, "prototype chain cycle"); - else - return JS_NewBool(ctx, res); + return JS_NewBool(ctx, res); exception: + JS_FreeValue(ctx, v1); JS_FreeValue(ctx, obj); return JS_EXCEPTION; } @@ -34727,7 +37347,6 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int setter) { JSValue obj, res = JS_EXCEPTION; - JSValueConst v; JSAtom prop = JS_ATOM_NULL; JSPropertyDescriptor desc; int has_prop; @@ -34739,8 +37358,8 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, if (unlikely(prop == JS_ATOM_NULL)) goto exception; - for (v = obj;;) { - has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(v), prop); + for (;;) { + has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); if (has_prop < 0) goto exception; if (has_prop) { @@ -34751,13 +37370,16 @@ static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, js_free_desc(ctx, &desc); break; } - v = JS_GetPrototype(ctx, v); - if (JS_IsException(v)) + obj = JS_GetPrototypeFree(ctx, obj); + if (JS_IsException(obj)) goto exception; - if (JS_IsNull(v)) { + if (JS_IsNull(obj)) { res = JS_UNDEFINED; break; } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; } exception: @@ -34823,59 +37445,12 @@ static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val, return JS_UNDEFINED; } -/* insert a '\n' at unicode character position 'pos' in the source of - 'func_obj' */ -static int patch_function_constructor_source(JSContext *ctx, - JSValueConst func_obj, - size_t pos) -{ - JSObject *p; - JSFunctionBytecode *b; - char *r, *r_end, *new_source; - int c; - size_t idx, len; - - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) - return 0; - p = JS_VALUE_GET_OBJ(func_obj); - if (!js_class_has_bytecode(p->class_id)) - return 0; - b = p->u.func.function_bytecode; - if (!b->has_debug) - return 0; - r = b->debug.source; - r_end = b->debug.source + b->debug.source_len; - idx = 0; - while (r < r_end) { - if (idx == pos) { - /* add the '\n' */ - new_source = js_realloc(ctx, b->debug.source, - b->debug.source_len + 2); - if (!new_source) - return -1; - len = r - b->debug.source; - memmove(new_source + len + 1, new_source + len, - b->debug.source_len + 1 - len); - new_source[len] = '\n'; - b->debug.source = new_source; - b->debug.source_len++; - break; - } - c = unicode_from_utf8((const uint8_t *)r, UTF8_CHAR_LEN_MAX, - (const uint8_t **)&r); - if (c < 0) - break; - idx++; - } - return 0; -} - /* XXX: add a specific eval mode so that Function("}), ({") is rejected */ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic) { JSFunctionKindEnum func_kind = magic; - int i, n, ret, func_start_pos; + int i, n, ret; JSValue s, proto, obj = JS_UNDEFINED; StringBuffer b_s, *b = &b_s; @@ -34900,13 +37475,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, if (string_buffer_concat_value(b, argv[i])) goto fail; } - string_buffer_puts8(b, "\n) {"); - /* Annex B HTML comments: We don't add a '\n' after "{" so that - "-->" 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 */ + string_buffer_puts8(b, "\n) {\n"); if (n >= 0) { if (string_buffer_concat_value(b, argv[n])) goto fail; @@ -34920,19 +37489,23 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, 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); + proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); if (JS_IsException(proto)) goto fail1; - if (!JS_IsUndefined(proto)) { - ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); + if (!JS_IsObject(proto)) { + JSContext *realm; JS_FreeValue(ctx, proto); - if (ret < 0) + realm = JS_GetFunctionRealm(ctx, new_target); + if (!realm) goto fail1; + proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]); } + ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); + JS_FreeValue(ctx, proto); + if (ret < 0) + goto fail1; } return obj; @@ -34955,8 +37528,7 @@ static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, return JS_ToUint32Free(ctx, pres, len_val); } -static __exception int js_get_length64(JSContext *ctx, int64_t *pres, - JSValueConst obj) +int js_get_length64(JSContext *ctx, int64_t *pres, JSValueConst obj) { JSValue len_val; len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); @@ -35015,6 +37587,8 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, return tab; } +/* magic value: 0 = normal apply, 1 = apply for constructor, 2 = + Reflect.apply */ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { @@ -35026,14 +37600,14 @@ static JSValue js_function_apply(JSContext *ctx, JSValueConst 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) { + if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) { return JS_Call(ctx, this_val, this_arg, 0, NULL); } tab = build_arg_list(ctx, &len, array_arg); if (!tab) return JS_EXCEPTION; - if (magic) { + if (magic & 1) { ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab); } else { ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab); @@ -35056,10 +37630,9 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSBoundFunction *bf; - JSValue func_obj, name1; + JSValue func_obj, name1, len_val; JSObject *p; - int arg_count, i; - uint32_t len1; + int arg_count, i, ret; if (check_function(ctx, this_val)) return JS_EXCEPTION; @@ -35082,6 +37655,44 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, } p->u.bound_function = bf; + /* XXX: the spec could be simpler by only using GetOwnProperty */ + ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length); + if (ret < 0) + goto exception; + if (!ret) { + len_val = JS_NewInt32(ctx, 0); + } else { + len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length); + if (JS_IsException(len_val)) + goto exception; + if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { + /* most common case */ + int len1 = JS_VALUE_GET_INT(len_val); + if (len1 <= arg_count) + len1 = 0; + else + len1 -= arg_count; + len_val = JS_NewInt32(ctx, len1); + } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) { + double d = JS_VALUE_GET_FLOAT64(len_val); + if (isnan(d)) { + d = 0.0; + } else { + d = trunc(d); + if (d <= (double)arg_count) + d = 0.0; + else + d -= (double)arg_count; /* also converts -0 to +0 */ + } + len_val = JS_NewFloat64(ctx, d); + } else { + JS_FreeValue(ctx, len_val); + len_val = JS_NewInt32(ctx, 0); + } + } + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, + len_val, JS_PROP_CONFIGURABLE); + name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); if (JS_IsException(name1)) goto exception; @@ -35094,15 +37705,6 @@ static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, 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); @@ -35130,27 +37732,22 @@ static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val, 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}"; + 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); @@ -35181,34 +37778,102 @@ static const JSCFunctionListEntry js_function_proto_funcs[] = { /* Error class */ +static JSValue iterator_to_array(JSContext *ctx, JSValueConst items) +{ + JSValue iter, next_method = JS_UNDEFINED; + JSValue v, r = JS_UNDEFINED; + int64_t k; + BOOL done; + + iter = JS_GetIterator(ctx, items, FALSE); + if (JS_IsException(iter)) + goto exception; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto exception; + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + for (k = 0;; k++) { + v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(v)) + goto exception_close; + if (done) + break; + if (JS_DefinePropertyValueInt64(ctx, r, k, v, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception_close; + } + done: + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + return r; + exception_close: + JS_IteratorClose(ctx, iter, TRUE); + exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; + goto done; +} + static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic) { JSValue obj, msg, proto; - JSValueConst proto1; + JSValueConst message; 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); + proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); if (JS_IsException(proto)) return proto; + if (!JS_IsObject(proto)) { + JSContext *realm; + JSValueConst proto1; + + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, new_target); + if (!realm) + return JS_EXCEPTION; + if (magic < 0) { + proto1 = realm->class_proto[JS_CLASS_ERROR]; + } else { + proto1 = realm->native_error_proto[magic]; + } + proto = JS_DupValue(ctx, proto1); + } obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR); JS_FreeValue(ctx, proto); - if (!JS_IsException(obj) && !JS_IsUndefined(argv[0])) { - msg = JS_ToString(ctx, argv[0]); - if (unlikely(JS_IsException(msg))) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } + if (JS_IsException(obj)) + return obj; + if (magic == JS_AGGREGATE_ERROR) { + message = argv[1]; + } else { + message = argv[0]; + } + + if (!JS_IsUndefined(message)) { + msg = JS_ToString(ctx, message); + if (unlikely(JS_IsException(msg))) + goto exception; JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } + + if (magic == JS_AGGREGATE_ERROR) { + JSValue error_list = iterator_to_array(ctx, argv[0]); + if (JS_IsException(error_list)) + goto exception; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + + /* skip the Error() function in the backtrace */ + build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); return obj; + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; } static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, @@ -35216,6 +37881,8 @@ static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, { 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); @@ -35244,6 +37911,24 @@ static const JSCFunctionListEntry js_error_proto_funcs[] = { JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), }; +/* AggregateError */ + +/* used by C code. */ +static JSValue js_aggregate_error_constructor(JSContext *ctx, + JSValueConst errors) +{ + JSValue obj; + + obj = JS_NewObjectProtoClass(ctx, + ctx->native_error_proto[JS_AGGREGATE_ERROR], + JS_CLASS_ERROR); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + return obj; +} + /* Array */ static int JS_CopySubArray(JSContext *ctx, @@ -35292,7 +37977,7 @@ static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, return obj; if (argc == 1 && JS_IsNumber(argv[0])) { uint32_t len; - if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) + if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) goto fail; if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) goto fail; @@ -35469,22 +38154,47 @@ static JSValue js_get_this(JSContext *ctx, static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, JSValueConst len_val) { - JSValue ctor, ret; + JSValue ctor, ret, species; int res; - + JSContext *realm; + res = JS_IsArray(ctx, obj); if (res < 0) return JS_EXCEPTION; if (!res) return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); - ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED); + ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); if (JS_IsException(ctor)) - return JS_EXCEPTION; - if (JS_IsUndefined(ctor)) + return ctor; + if (JS_IsConstructor(ctx, ctor)) { + /* legacy web compatibility */ + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) { + JS_FreeValue(ctx, ctor); + return JS_EXCEPTION; + } + if (realm != ctx && + js_same_value(ctx, ctor, realm->array_ctor)) { + JS_FreeValue(ctx, ctor); + ctor = JS_UNDEFINED; + } + } + if (JS_IsObject(ctor)) { + species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); + JS_FreeValue(ctx, ctor); + if (JS_IsException(species)) + return species; + ctor = species; + if (JS_IsNull(ctor)) + ctor = JS_UNDEFINED; + } + if (JS_IsUndefined(ctor)) { return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); - ret = JS_CallConstructor(ctx, ctor, 1, &len_val); - JS_FreeValue(ctx, ctor); - return ret; + } else { + ret = JS_CallConstructor(ctx, ctor, 1, &len_val); + JS_FreeValue(ctx, ctor); + return ret; + } } static const JSCFunctionListEntry js_array_funcs[] = { @@ -35537,7 +38247,7 @@ static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, if (res) { if (js_get_length64(ctx, &len, e)) goto exception; - if (n + len >= MAX_SAFE_INTEGER) { + if (n + len > MAX_SAFE_INTEGER) { JS_ThrowTypeError(ctx, "Array loo long"); goto exception; } @@ -36473,7 +39183,8 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, JS_ThrowTypeError(ctx, "Array too long"); goto fail; } - if (JS_SetPropertyInt64(ctx, target, targetIndex, element) < 0) + if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) return -1; targetIndex++; } @@ -36870,7 +39581,6 @@ static const JSCFunctionListEntry js_array_proto_funcs[] = { 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 ), @@ -36888,17 +39598,14 @@ 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_bignum_mode(ctx)) - val = __JS_NewFloat64(ctx, 0); - else - val = JS_NewInt32(ctx, 0); + val = JS_NewInt32(ctx, 0); } else { val = JS_ToNumeric(ctx, argv[0]); if (JS_IsException(val)) return val; switch(JS_VALUE_GET_TAG(val)) { +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: case JS_TAG_BIG_FLOAT: { @@ -36909,25 +39616,19 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, val = __JS_NewFloat64(ctx, d); } break; - case JS_TAG_INT: - if (is_bignum_mode(ctx)) { - /* always return a number in bignum mode */ - val = __JS_NewFloat64(ctx, JS_VALUE_GET_INT(val)); - } + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + return val; + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; break; +#endif default: break; } } -#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)) @@ -36958,7 +39659,6 @@ static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val, 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); @@ -36991,8 +39691,7 @@ static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, 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); + return JS_NewBool(ctx, is_safe_integer(d)); } static const JSCFunctionListEntry js_number_funcs[] = { @@ -37036,11 +39735,24 @@ static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisNumberValue(ctx, this_val); } +static int js_get_radix(JSContext *ctx, JSValueConst val) +{ + int radix; + if (JS_ToInt32Sat(ctx, &radix, val)) + return -1; + if (radix < 2 || radix > 36) { + JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + return -1; + } + return radix; +} + static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { JSValue val; int base; + double d; val = js_thisNumberValue(ctx, this_val); if (JS_IsException(val)) @@ -37048,198 +39760,18 @@ static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, if (magic || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) + base = js_get_radix(ctx, argv[0]); + if (base < 0) goto fail; - if (base < 2 || base > 36) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - goto fail; - } } -#ifdef CONFIG_BIGNUM - { - JSValue ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); - JS_FreeValue(ctx, val); - return ret; - } -#else - { - double d; - if (JS_ToFloat64Free(ctx, &d, val)) - return JS_EXCEPTION; - return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT); - } -#endif + 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; } -#ifdef CONFIG_BIGNUM - -static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val); - -static int64_t js_number_get_prec_max(JSContext *ctx, int magic) -{ - if (magic) - return BF_PREC_MAX; - else - return 100; -} - -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_number_toFixed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue val, ret; - int64_t f; - int res, rnd_mode; - bf_t a_s, *a, b; - BOOL is_float; - - if (magic) - val = js_thisBigFloatValue(ctx, this_val); - else - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (f < 0 || f > js_number_get_prec_max(ctx, magic)) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (magic && argc > 1) { - rnd_mode = get_rnd_mode(ctx, argv[1]); - if (rnd_mode < 0) - goto fail; - } - - a = JS_ToBigFloat(ctx, &is_float, &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 JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue val, ret; - int64_t f; - int rnd_mode; - - if (magic) - val = js_thisBigFloatValue(ctx, this_val); - else - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToInt64Sat(ctx, &f, argv[0])) - goto fail; - if (!js_number_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 > js_number_get_prec_max(ctx, magic)) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (magic && 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_number_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue val, ret; - int64_t p; - int rnd_mode; - - if (magic) - val = js_thisBigFloatValue(ctx, this_val); - else - val = js_thisNumberValue(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_number_is_finite(ctx, val)) { - to_string: - ret = JS_ToString(ctx, this_val); - } else { - if (p < 1 || p > js_number_get_prec_max(ctx, magic)) { - JS_ThrowRangeError(ctx, "invalid number of digits"); - goto fail; - } - rnd_mode = BF_RNDNA; - if (magic && 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_number_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("toExponential", 1, js_number_toExponential, 0 ), - JS_CFUNC_MAGIC_DEF("toFixed", 1, js_number_toFixed, 0 ), - JS_CFUNC_MAGIC_DEF("toPrecision", 1, js_number_toPrecision, 0 ), - 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 ), -}; - -#else /* !CONFIG_BIGNUM */ - static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -37326,14 +39858,11 @@ static const JSCFunctionListEntry js_number_proto_funcs[] = { JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ), }; -#endif /* !CONFIG_BIGNUM */ - - static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *str; - int radix; + const char *str, *p; + int radix, flags; JSValue ret; str = JS_ToCString(ctx, argv[0]); @@ -37346,15 +39875,10 @@ static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, if (radix != 0 && (radix < 2 || radix > 36)) { ret = JS_NAN; } else { -#ifdef CONFIG_BIGNUM - int flags; - flags = BF_ATOF_INT_ONLY | BF_ATOF_NAN_IF_EMPTY | BF_ATOF_FLOAT64; - if (is_bignum_mode(ctx)) - flags |= BF_ATOF_INT_PREC_INF; - ret = js_atof(ctx, str, NULL, radix, flags); -#else - ret = js_atod(ctx, str, NULL, radix, ATOD_INT_ONLY | ATOD_NAN_IF_EMPTY); -#endif + p = str; + p += skip_spaces(p); + flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; + ret = js_atof(ctx, p, NULL, radix, flags); } JS_FreeCString(ctx, str); return ret; @@ -37363,18 +39887,15 @@ static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - const char *str; + const char *str, *p; JSValue ret; str = JS_ToCString(ctx, argv[0]); if (!str) return JS_EXCEPTION; -#ifdef CONFIG_BIGNUM - ret = js_atof(ctx, str, NULL, 10, - BF_ATOF_JS_QUIRKS | BF_ATOF_NAN_IF_EMPTY | BF_ATOF_FLOAT64); -#else - ret = js_atod(ctx, str, NULL, 10, ATOD_NAN_IF_EMPTY); -#endif + p = str; + p += skip_spaces(p); + ret = js_atof(ctx, p, NULL, 10, 0); JS_FreeCString(ctx, str); return ret; } @@ -37465,46 +39986,6 @@ static int js_string_get_own_property(JSContext *ctx, 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, @@ -37512,14 +39993,31 @@ static int js_string_define_own_property(JSContext *ctx, JSValueConst setter, int flags) { uint32_t idx; - + JSObject *p; + JSString *p1, *p2; + if (__JS_AtomIsTaggedInt(prop)) { idx = __JS_AtomToUInt32(prop); - if (idx >= js_string_obj_get_length(ctx, this_obj)) + p = JS_VALUE_GET_OBJ(this_obj); + if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) + goto def; + p1 = JS_VALUE_GET_STRING(p->u.object_data); + if (idx >= p1->len) goto def; if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags)) - return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); - /* XXX: should check if same value is configured */ + goto fail; + /* check that the same value is configured */ + if (flags & JS_PROP_HAS_VALUE) { + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + goto fail; + p2 = JS_VALUE_GET_STRING(val); + if (p2->len != 1) + goto fail; + if (string_get(p1, idx) != string_get(p2, 0)) { + fail: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + } return TRUE; } else { def: @@ -37544,7 +40042,6 @@ static int js_string_delete_property(JSContext *ctx, 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, }; @@ -37869,12 +40366,14 @@ static int string_indexof(JSString *p1, JSString *p2, int from) return -1; } -static int string_advance_index(JSString *p, int index, BOOL unicode) +static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode) { - if (!unicode || (unsigned)index >= p->len || !p->is_wide_char) { + if (!unicode || index >= p->len || !p->is_wide_char) { index++; } else { - string_getc(p, &index); + int index32 = (int)index; + string_getc(p, &index32); + index = index32; } return index; } @@ -37971,22 +40470,25 @@ static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, p1 = JS_VALUE_GET_STRING(v); len = p->len; v_len = p1->len; - pos = (magic & 2) ? len : 0; + 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; + ret = 0; + if (magic == 0) { + start = pos; + stop = len; + } else { + if (magic == 1) { + if (pos > len) + goto done; + } else { + pos -= v_len; + } start = stop = pos; } - ret = 0; if (start >= 0 && start <= stop) { for (i = start;; i++) { if (!string_cmp(p, p1, i, 0, v_len)) { @@ -37997,6 +40499,7 @@ static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, break; } } + done: JS_FreeValue(ctx, str); JS_FreeValue(ctx, v); return JS_NewBool(ctx, ret); @@ -38007,6 +40510,35 @@ fail: 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) { @@ -38023,6 +40555,12 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, 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); } @@ -38107,15 +40645,17 @@ static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val 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 (j < len) { + c1 = string_get(rp, j); + if (c1 >= '0' && c1 <= '9') { + /* This behavior is specified in ES6 and refined in ECMA 2019 */ + /* ECMA 2019 does not have the extra test, but + Test262 S15.5.4.11_A3_T1..3 require this behavior */ + k1 = k * 10 + c1 - '0'; + if (k1 >= 1 && k1 < captures_len) { + k = k1; + j++; + } } } if (k >= 1 && k < captures_len) { @@ -38158,7 +40698,8 @@ exception: } static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, + int is_replaceAll) { // replace(rx, rep) JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1]; @@ -38166,7 +40707,8 @@ static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, JSValue str, search_str, replaceValue_str, repl_str; JSString *sp, *searchp; StringBuffer b_s, *b = &b_s; - int pos, functionalReplace; + int pos, functionalReplace, endOfLastMatch; + BOOL is_first; if (JS_IsUndefined(O) || JS_IsNull(O)) return JS_ThrowTypeError(ctx, "cannot convert to object"); @@ -38177,10 +40719,14 @@ static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, 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)) { + if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) { args[0] = O; args[1] = replaceValue; return JS_CallFree(ctx, replacer, searchValue, 2, args); @@ -38203,34 +40749,54 @@ static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, sp = JS_VALUE_GET_STRING(str); searchp = JS_VALUE_GET_STRING(search_str); - - pos = string_indexof(sp, searchp, 0); - if (pos < 0) { - string_buffer_free(b); - JS_FreeValue(ctx, search_str); - JS_FreeValue(ctx, replaceValue_str); - return 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; } - 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, 0, pos); - string_buffer_concat_value_free(b, repl_str); - string_buffer_concat(b, sp, pos + searchp->len, sp->len); + string_buffer_concat(b, sp, endOfLastMatch, sp->len); JS_FreeValue(ctx, search_str); JS_FreeValue(ctx, replaceValue_str); JS_FreeValue(ctx, str); @@ -38441,10 +41007,6 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, 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)) @@ -38459,6 +41021,10 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, p1 = NULL; } } + if (n > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + goto fail2; + } if (string_buffer_init(ctx, b, n)) goto fail3; n -= len; @@ -38975,7 +41541,8 @@ static const JSCFunctionListEntry js_string_proto_funcs[] = { 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_DEF("replace", 2, js_string_replace ), + 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 ), @@ -39057,351 +41624,6 @@ static double js_fmax(double a, double 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_math_fop(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, r_s, *r = &r_s; - BOOL is_float; - JSFloatEnv *fe; - int rnd_mode; - JSValue op1; - - op1 = JS_ToNumber(ctx, argv[0]); - if (JS_IsException(op1)) - return op1; - a = JS_ToBigFloat(ctx, &is_float, &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, fe->prec, - (fe->flags & ~BF_RND_MASK) | 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_math_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; - BOOL is_float; - 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, &is_float, &a_s, op1); - b = JS_ToBigFloat(ctx, &is_float, &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; - BOOL is_float; - int res; - - r = JS_ToBigFloat(ctx, &is_float, &r_s, ret); - if (!bf_is_nan(r)) { - a = JS_ToBigFloat(ctx, &is_float, &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); - } - } - } - 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) { @@ -39455,8 +41677,6 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, } } -#endif /* !CONFIG_BIGNUM */ - static double js_math_sign(double a) { if (isnan(a) || a == 0.0) @@ -39498,24 +41718,23 @@ static double js_math_round(double a) static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - double r, a, b; + double r, a; int i; - if (argc == 2) { - /* use the more precise built-in function when possible */ - if (JS_ToFloat64(ctx, &a, argv[0])) + r = 0; + if (argc > 0) { + if (JS_ToFloat64(ctx, &r, 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; + if (argc == 1) { + r = fabs(r); + } else { + /* use the built-in function to minimize precision loss */ + for (i = 1; i < argc; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + r = hypot(r, a); + } } - r = sqrt(r); } return JS_NewFloat64(ctx, r); } @@ -39589,11 +41808,7 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, 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 ), @@ -39720,6 +41935,20 @@ static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd)); } +static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor, + JSValueConst def_proto) +{ + JSValue proto; + proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(proto)) { + JS_FreeValue(ctx, proto); + proto = JS_DupValue(ctx, def_proto); + } + return proto; +} + /* create a new date object */ static JSValue js___date_create(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -40089,13 +42318,14 @@ static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mas 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; - + re = js_get_regexp(ctx, this_val, FALSE); + if (!re) { + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) + return JS_UNDEFINED; + else + return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); + } + flags = lre_get_flags(re->bytecode->u.str8); return JS_NewBool(ctx, (flags & mask) != 0); } @@ -40172,7 +42402,7 @@ fail: BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) { JSContext *ctx = opaque; - return js_check_stack_overflow(ctx, alloca_size); + return js_check_stack_overflow(ctx->rt, alloca_size); } void *lre_realloc(void *opaque, void *ptr, size_t size) @@ -40254,14 +42484,11 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, 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; + group_name_ptr = lre_get_groupnames(re_bytecode); + if (group_name_ptr) { 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++) { @@ -40311,7 +42538,7 @@ fail1: return JS_EXCEPTION; } -/* delete partions of a string that match a given regex */ +/* delete portions of a string that match a given regex */ static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg) { JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); @@ -40510,9 +42737,7 @@ static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, 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); + nextIndex = string_advance_index(p, thisIndex, fullUnicode); if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) goto exception; } @@ -40601,7 +42826,7 @@ static JSValue js_regexp_string_iterator_next(JSContext *ctx, 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) + JS_NewInt64(ctx, nextIndex)) < 0) goto exception; } JS_FreeValue(ctx, matchStr); @@ -40654,7 +42879,7 @@ static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, JS_GetProperty(ctx, R, JS_ATOM_lastIndex))) goto exception; if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex, - JS_NewInt32(ctx, lastIndex)) < 0) + JS_NewInt64(ctx, lastIndex)) < 0) goto exception; iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); @@ -40842,7 +43067,7 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, 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) + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) goto exception; } } @@ -40868,7 +43093,8 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, tab = JS_NewArray(ctx); if (JS_IsException(tab)) goto exception; - if (JS_SetPropertyInt64(ctx, tab, 0, JS_DupValue(ctx, matched)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; for(n = 1; n < nCaptures; n++) { JSValue capN; @@ -40880,7 +43106,8 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, if (JS_IsException(capN)) goto exception; } - if (JS_SetPropertyInt64(ctx, tab, n, capN) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; } JS_FreeValue(ctx, namedCaptures); @@ -40888,12 +43115,12 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, if (JS_IsException(namedCaptures)) goto exception; if (functionalReplace) { - if (JS_SetPropertyInt64(ctx, tab, n++, JS_NewInt32(ctx, position)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; - if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, str)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (!JS_IsUndefined(namedCaptures)) { - if (JS_SetPropertyInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures)) < 0) + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; } args[0] = JS_UNDEFINED; @@ -40901,14 +43128,23 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, rep_str); rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0)); } else { + JSValue namedCaptures1; + if (!JS_IsUndefined(namedCaptures)) { + namedCaptures1 = JS_ToObject(ctx, namedCaptures); + if (JS_IsException(namedCaptures1)) + goto exception; + } else { + namedCaptures1 = JS_UNDEFINED; + } args[0] = matched; args[1] = str; args[2] = JS_NewInt32(ctx, position); args[3] = tab; - args[4] = namedCaptures; + args[4] = namedCaptures1; args[5] = rep_val; JS_FreeValue(ctx, rep_str); rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); + JS_FreeValue(ctx, namedCaptures1); } if (JS_IsException(rep_str)) goto exception; @@ -40971,8 +43207,10 @@ static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { JS_FreeValue(ctx, previousLastIndex); } else { - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { + previousLastIndex = JS_UNDEFINED; goto exception; + } } JS_FreeValue(ctx, str); JS_FreeValue(ctx, currentLastIndex); @@ -41077,7 +43315,8 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, sub = js_sub_string(ctx, strp, p, q); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (lengthA == lim) goto done; @@ -41088,7 +43327,7 @@ static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; if (lengthA == lim) goto done; @@ -41103,7 +43342,7 @@ add_tail: sub = js_sub_string(ctx, strp, p, size); if (JS_IsException(sub)) goto exception; - if (JS_SetPropertyInt64(ctx, A, lengthA++, sub) < 0) + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) goto exception; goto done; exception: @@ -41179,58 +43418,69 @@ void JS_AddIntrinsicRegExp(JSContext *ctx) /* 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 int json_parse_expect(JSParseState *s, int tok) +{ + if (s->token.val != tok) { + /* XXX: dump token correctly in all cases */ + return js_parse_error(s, "expecting '%c'", tok); + } + return json_next_token(s); +} + static JSValue json_parse_value(JSParseState *s) { JSContext *ctx = s->ctx; JSValue val = JS_NULL; - BOOL is_neg; int ret; switch(s->token.val) { case '{': { - JSValue prop_val, prop_str; - - if (next_token(s)) + JSValue prop_val; + JSAtom prop_name; + + if (json_next_token(s)) goto fail; val = JS_NewObject(ctx); if (JS_IsException(val)) goto fail; if (s->token.val != '}') { for(;;) { - if (s->token.val != TOK_STRING) { + if (s->token.val == TOK_STRING) { + prop_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (prop_name == JS_ATOM_NULL) + goto fail; + } else if (s->ext_json && s->token.val == TOK_IDENT) { + prop_name = JS_DupAtom(ctx, s->token.u.ident.atom); + } else { js_parse_error(s, "expecting property name"); goto fail; } - 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; - } + if (json_next_token(s)) + goto fail1; + if (json_parse_expect(s, ':')) + goto fail1; prop_val = json_parse_value(s); if (JS_IsException(prop_val)) { - JS_FreeValue(ctx, prop_str); + fail1: + JS_FreeAtom(ctx, prop_name); goto fail; } - ret = JS_DefinePropertyValueValue(ctx, val, prop_str, - prop_val, JS_PROP_C_W_E); + ret = JS_DefinePropertyValue(ctx, val, prop_name, + prop_val, JS_PROP_C_W_E); + JS_FreeAtom(ctx, prop_name); if (ret < 0) goto fail; if (s->token.val != ',') break; - if (next_token(s)) + if (json_next_token(s)) goto fail; + if (s->ext_json && s->token.val == '}') + break; } } - if (js_parse_expect(s, '}')) + if (json_parse_expect(s, '}')) goto fail; } break; @@ -41239,7 +43489,7 @@ static JSValue json_parse_value(JSParseState *s) JSValue el; uint32_t idx; - if (next_token(s)) + if (json_next_token(s)) goto fail; val = JS_NewArray(ctx); if (JS_IsException(val)) @@ -41255,86 +43505,41 @@ static JSValue json_parse_value(JSParseState *s) goto fail; if (s->token.val != ',') break; - if (next_token(s)) + if (json_next_token(s)) goto fail; idx++; + if (s->ext_json && s->token.val == ']') + break; } } - if (js_parse_expect(s, ']')) + if (json_parse_expect(s, ']')) goto fail; } break; case TOK_STRING: val = JS_DupValue(ctx, s->token.u.str.str); - if (next_token(s)) + if (json_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_bignum_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); + if (json_next_token(s)) + goto fail; + break; + case TOK_IDENT: + if (s->token.u.ident.atom == JS_ATOM_false || + s->token.u.ident.atom == JS_ATOM_true) { + val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true)); + } else if (s->token.u.ident.atom == JS_ATOM_null) { + val = JS_NULL; + } else { + goto def_token; } -#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)) + if (json_next_token(s)) goto fail; break; default: + def_token: if (s->token.val == TOK_EOF) { js_parse_error(s, "unexpected end of input"); } else { @@ -41349,15 +43554,15 @@ static JSValue json_parse_value(JSParseState *s) return JS_EXCEPTION; } -JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename) +JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename, int flags) { JSParseState s1, *s = &s1; - JSValue val; + JSValue val = JS_UNDEFINED; js_parse_init(ctx, s, buf, buf_len, filename); - - if (next_token(s)) + s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0); + if (json_next_token(s)) goto fail; val = json_parse_value(s); if (JS_IsException(val)) @@ -41368,10 +43573,17 @@ JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, } return val; fail: + JS_FreeValue(ctx, val); free_token(s, &s->token); return JS_EXCEPTION; } +JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename) +{ + return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); +} + static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, JSAtom name, JSValueConst reviver) { @@ -41382,7 +43594,7 @@ static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, JSAtom prop; JSPropertyEnum *atoms = NULL; - if (js_check_stack_overflow(ctx, 0)) { + if (js_check_stack_overflow(ctx->rt, 0)) { return JS_ThrowStackOverflow(ctx); } @@ -41490,10 +43702,6 @@ static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue 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) { @@ -41752,13 +43960,11 @@ exception: return -1; } -static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, + JSValueConst replacer, JSValueConst space0) { - // stringify(val, replacer, space) StringBuffer b_s; JSONStringifyContext jsc_s, *jsc = &jsc_s; - JSValueConst replacer = argv[1]; JSValue val, v, space, ret, wrapper; int res; int64_t i, j, n; @@ -41827,7 +44033,7 @@ static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val, } } } - space = JS_DupValue(ctx, argv[2]); + space = JS_DupValue(ctx, space0); if (JS_IsObject(space)) { JSObject *p = JS_VALUE_GET_OBJ(space); if (p->class_id == JS_CLASS_NUMBER) { @@ -41858,9 +44064,9 @@ static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val, if (JS_IsException(wrapper)) goto exception; if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string, - JS_DupValue(ctx, argv[0]), JS_PROP_C_W_E) < 0) + JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0) goto exception; - val = JS_DupValue(ctx, argv[0]); + val = JS_DupValue(ctx, obj); val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); if (JS_IsException(val)) @@ -41888,6 +44094,13 @@ done: 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 ), @@ -41909,7 +44122,7 @@ void JS_AddIntrinsicJSON(JSContext *ctx) 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); + return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); } static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, @@ -42065,6 +44278,7 @@ static const JSCFunctionListEntry js_reflect_funcs[] = { JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ), JS_CFUNC_DEF("set", 3, js_reflect_set ), JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ), }; static const JSCFunctionListEntry js_reflect_obj[] = { @@ -42079,7 +44293,6 @@ static void js_proxy_finalizer(JSRuntime *rt, JSValue val) if (s) { JS_FreeValueRT(rt, s->target); JS_FreeValueRT(rt, s->handler); - JS_FreeValueRT(rt, s->proto); js_free_rt(rt, s); } } @@ -42091,7 +44304,6 @@ static void js_proxy_mark(JSRuntime *rt, JSValueConst val, if (s) { JS_MarkValue(rt, s->target, mark_func); JS_MarkValue(rt, s->handler, mark_func); - JS_MarkValue(rt, s->proto, mark_func); } } @@ -42107,7 +44319,7 @@ static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, JSValue method; /* safer to test recursion in all proxy methods */ - if (js_check_stack_overflow(ctx, 0)) { + if (js_check_stack_overflow(ctx->rt, 0)) { JS_ThrowStackOverflow(ctx); return NULL; } @@ -42126,17 +44338,12 @@ static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, return s; } -static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) { JSProxyData *s; - JSValue method, ret; - JSValueConst proto1; + JSValue method, ret, 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; @@ -42162,22 +44369,22 @@ static JSValueConst js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) return JS_EXCEPTION; } if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) { + JS_FreeValue(ctx, proto1); fail: JS_FreeValue(ctx, ret); return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); } + JS_FreeValue(ctx, proto1); } - /* store the prototype in the proxy so that its refcount is at least 1 */ - set_value(ctx, &s->proto, ret); - return (JSValueConst)ret; + return 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; + JSValue method, ret, proto1; + JSValueConst args[2]; BOOL res; int res2; @@ -42208,9 +44415,11 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, if (JS_IsException(proto1)) return -1; if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) { + JS_FreeValue(ctx, proto1); JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); return -1; } + JS_FreeValue(ctx, proto1); } return TRUE; } @@ -42746,11 +44955,11 @@ static int js_proxy_get_own_property_names(JSContext *ctx, len2 = 0; if (js_get_length32(ctx, &len, prop_array)) goto fail; - if (len == 0) - goto done; - tab = js_mallocz(ctx, sizeof(tab[0]) * len); - if (!tab) - 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)) @@ -42822,7 +45031,6 @@ static int js_proxy_get_own_property_names(JSContext *ctx, } } - done: js_free_prop_enum(ctx, tab2, len2); JS_FreeValue(ctx, prop_array); *ptab = tab; @@ -42835,38 +45043,6 @@ static int js_proxy_get_own_property_names(JSContext *ctx, return -1; } -static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - JSProxyData *s; - JSValue method, arg_array, ret; - JSValueConst args[3]; - - 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 JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, JSValueConst new_target, int argc, JSValueConst *argv) @@ -42901,6 +45077,41 @@ static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, 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); @@ -42935,14 +45146,6 @@ static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, 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)) @@ -42954,7 +45157,6 @@ static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, } 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); @@ -44024,6 +46226,14 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc, 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) { @@ -44039,7 +46249,14 @@ static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, #ifdef DUMP_PROMISE printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject); #endif - /* Note: could call HostPromiseRejectTracker */ + 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]; @@ -44163,7 +46380,8 @@ static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, static JSValue js_promise_resolve_function_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, + int flags) { JSObject *p = JS_VALUE_GET_OBJ(func_obj); JSPromiseFunctionData *s; @@ -44445,14 +46663,18 @@ static __exception int remainingElementsCount_add(JSContext *ctx, return (remainingElementsCount == 0); } +#define PROMISE_MAGIC_all 0 +#define PROMISE_MAGIC_allSettled 1 +#define PROMISE_MAGIC_any 2 + static JSValue js_promise_all_resolve_element(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data) { - int is_allSettled = magic & 1; - int is_reject = magic & 2; + int resolve_type = magic & 3; + int is_reject = magic & 4; BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]); JSValueConst values = func_data[2]; JSValueConst resolve = func_data[3]; @@ -44466,7 +46688,7 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, return JS_UNDEFINED; func_data[0] = JS_NewBool(ctx, TRUE); - if (is_allSettled) { + if (resolve_type == PROMISE_MAGIC_allSettled) { JSValue str; obj = JS_NewObject(ctx); @@ -44498,7 +46720,16 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, if (is_zero < 0) return JS_EXCEPTION; if (is_zero) { - ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); + if (resolve_type == PROMISE_MAGIC_any) { + JSValue error; + error = js_aggregate_error_constructor(ctx, values); + if (JS_IsException(error)) + return JS_EXCEPTION; + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + } else { + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); + } if (JS_IsException(ret)) return ret; JS_FreeValue(ctx, ret); @@ -44510,19 +46741,23 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, 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 result_promise, resolving_funcs[2], item, next_promise, ret; JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; - JSValue promise_resolve = JS_UNDEFINED; + JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; JSValueConst then_args[2], resolve_element_data[5]; - BOOL done, is_allSettled = magic; - int index, is_zero; + BOOL done; + int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); if (!JS_IsObject(this_val)) return JS_ThrowTypeErrorNotAnObject(ctx); result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); if (JS_IsException(result_promise)) return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; iter = JS_GetIterator(ctx, argv[0], FALSE); if (JS_IsException(iter)) { JSValue error; @@ -44550,11 +46785,6 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, 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' @@ -44575,24 +46805,30 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, 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[3] = resolving_funcs[is_promise_any]; resolve_element_data[4] = resolve_element_env; resolve_element = JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, - is_allSettled, 5, resolve_element_data); + magic, 5, resolve_element_data); if (JS_IsException(resolve_element)) { JS_FreeValue(ctx, next_promise); goto fail_reject1; } - if (is_allSettled) { + if (magic == PROMISE_MAGIC_allSettled) { reject_element = JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, - is_allSettled | 2, 5, resolve_element_data); + magic | 4, 5, resolve_element_data); if (JS_IsException(reject_element)) { JS_FreeValue(ctx, next_promise); goto fail_reject1; } + } else if (magic == PROMISE_MAGIC_any) { + if (JS_DefinePropertyValueUint32(ctx, values, index, + JS_UNDEFINED, JS_PROP_C_W_E) < 0) + goto fail_reject1; + reject_element = resolve_element; + resolve_element = JS_DupValue(ctx, resolving_funcs[0]); } else { reject_element = JS_DupValue(ctx, resolving_funcs[1]); } @@ -44618,7 +46854,15 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, if (is_zero < 0) goto fail_reject; if (is_zero) { - ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, + if (magic == PROMISE_MAGIC_any) { + JSValue error; + error = js_aggregate_error_constructor(ctx, values); + if (JS_IsException(error)) + goto fail_reject; + JS_FreeValue(ctx, values); + values = error; + } + ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, 1, (JSValueConst *)&values); if (check_exception_free(ctx, ret)) goto fail_reject; @@ -44642,8 +46886,8 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, 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 result_promise, resolving_funcs[2], item, next_promise, ret; + JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; JSValue promise_resolve = JS_UNDEFINED; BOOL done; @@ -44652,6 +46896,10 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); if (JS_IsException(result_promise)) return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; iter = JS_GetIterator(ctx, argv[0], FALSE); if (JS_IsException(iter)) { JSValue error; @@ -44668,11 +46916,6 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, 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 */ @@ -44742,6 +46985,13 @@ static __exception int perform_promise_then(JSContext *ctx, 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]; @@ -44815,31 +47065,20 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va { JSValueConst ctor = func_data[0]; JSValueConst onFinally = func_data[1]; - JSValue res, promise, resolving_funcs[2], ret, then_func; + JSValue res, promise, 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); + promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); 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 (JS_IsException(promise)) + return promise; if (magic == 0) { - then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 1, + then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, 0, 1, argv); } else { - then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 1, + then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0, 0, 1, argv); } if (JS_IsException(then_func)) { @@ -44889,8 +47128,9 @@ static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val, 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_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), + JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), + JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), JS_CFUNC_DEF("race", 1, js_promise_race ), //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), @@ -45025,7 +47265,8 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi goto done_resolve; } } - value = JS_IteratorNext2(ctx, s->sync_iter, method, 1, argv, &done); + value = JS_IteratorNext2(ctx, s->sync_iter, method, + argc >= 1 ? 1 : 0, argv, &done); JS_FreeValue(ctx, method); if (JS_IsException(value)) goto reject; @@ -45304,7 +47545,8 @@ static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, } c = (c << 6) | (c1 & 0x3f); } - if (c < c_min || c > 0x10FFFF) { + if (c < c_min || c > 0x10FFFF || + (c >= 0xd800 && c < 0xe000)) { js_throw_URIError(ctx, "malformed UTF-8"); goto fail; } @@ -45431,7 +47673,7 @@ static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, JSString *p; int i, len, c; - str = JS_ToStringCheckObject(ctx, argv[0]); + str = JS_ToString(ctx, argv[0]); if (JS_IsException(str)) return str; @@ -45457,7 +47699,7 @@ static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, JSString *p; int i, len, c, n; - str = JS_ToStringCheckObject(ctx, argv[0]); + str = JS_ToString(ctx, argv[0]); if (JS_IsException(str)) return str; @@ -45543,7 +47785,7 @@ static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double 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); + p->u.object_data = JS_NewFloat64(ctx, v); return JS_DupValue(ctx, p->u.object_data); } } @@ -45587,7 +47829,7 @@ 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 fields[9], int is_local, int force) { double dval; int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; @@ -45646,12 +47888,18 @@ static double time_clip(double t) { 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; +/* The spec mandates the use of 'double' and it fixes the order + of the operations */ +static double set_date_fields(double fields[], int is_local) { + int64_t y; + double days, d, h, m1; + int i, m, md; + + m1 = fields[1]; + m = fmod(m1, 12); + if (m < 0) + m += 12; + y = (int64_t)(fields[0] + floor(m1 / 12)); days = days_from_year(y); for(i = 0; i < m; i++) { @@ -45661,7 +47909,8 @@ static double set_date_fields(int64_t fields[], int is_local) { days += md; } days += fields[2] - 1; - h = ((fields[3] * 60 + fields[4]) * 60 + fields[5]) * 1000 + fields[6]; + h = fields[3] * 3600000 + fields[4] * 60000 + + fields[5] * 1000 + fields[6]; d = days * 86400000 + h; if (is_local) d += getTimezoneOffset(d) * 60000; @@ -45672,7 +47921,7 @@ 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]; + double fields[9]; int res, n, is_local; is_local = magic & 0x0F; @@ -45686,14 +47935,14 @@ static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, if (magic & 0x100) { // getYear fields[0] -= 1900; } - return JS_NewInt64(ctx, fields[n]); + return JS_NewFloat64(ctx, fields[n]); } static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { // _field(obj, first_field, end_field, args, is_local) - int64_t fields[9]; + double fields[9]; int res, first_field, end_field, is_local, i, n; double d, a; @@ -45735,7 +47984,7 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, { // _string(obj, fmt, part) char buf[64]; - int64_t fields[9]; + double fields[9]; int res, fmt, part, pos; int y, mon, d, h, m, s, ms, wd, tz; @@ -45883,7 +48132,7 @@ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, } val = time_clip(val); } else { - int64_t fields[] = { 0, 0, 1, 0, 0, 0, 0 }; + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; if (n > 7) n = 7; for(i = 0; i < n; i++) { @@ -45902,12 +48151,12 @@ has_val: JSValueConst args[3]; args[0] = new_target; args[1] = ctx->class_proto[JS_CLASS_DATE]; - args[2] = __JS_NewFloat64(ctx, val); + 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)); + 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(); */ @@ -45923,7 +48172,7 @@ 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 }; + double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; int i, n; double a; @@ -45941,7 +48190,7 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, if (i == 0 && fields[0] >= 0 && fields[0] < 100) fields[0] += 1900; } - return __JS_NewFloat64(ctx, set_date_fields(fields, 0)); + return JS_NewFloat64(ctx, set_date_fields(fields, 0)); } static void string_skip_spaces(JSString *sp, int *pp) { @@ -46051,11 +48300,13 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, // parse(s) JSValue s, rv; int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 }; + double fields1[7]; int64_t tz, hh, mm; double d; int p, i, c, sgn; JSString *sp; - + BOOL is_local; + rv = JS_NAN; s = JS_ToString(ctx, argv[0]); @@ -46071,11 +48322,15 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, if (string_get_signed_field(sp, &p, &fields[0])) goto done; + is_local = TRUE; 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) == '.') { + if (i <= 3) { + /* no time: UTC by default */ + is_local = FALSE; + } else 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; @@ -46093,14 +48348,20 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, /* 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; + if (p < sp->len) { + sgn = string_get(sp, p); + if (sgn == '+' || 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; + is_local = FALSE; + } else if (sgn == 'Z') { + is_local = FALSE; + } } } else { /* toString or toUTCString format */ @@ -46135,6 +48396,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, // XXX: parse optional milliseconds? /* parse the time zone offset if present: [+-]HHmm */ + is_local = FALSE; tz = 0; for (tz = 0; p < sp->len; p++) { sgn = string_get(sp, p); @@ -46151,8 +48413,10 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, } } } - d = set_date_fields(fields, 0) - tz * 60000; - rv = __JS_NewFloat64(ctx, d); + for(i = 0; i < 7; i++) + fields1[i] = fields[i]; + d = set_date_fields(fields1, is_local) - tz * 60000; + rv = JS_NewFloat64(ctx, d); done: JS_FreeValue(ctx, s); @@ -46222,7 +48486,7 @@ static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, if (JS_ThisTimeValue(ctx, &v, this_val)) return JS_EXCEPTION; - return __JS_NewFloat64(ctx, v); + return JS_NewFloat64(ctx, v); } static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, @@ -46251,7 +48515,7 @@ static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, if (y >= 0 && y < 100) y += 1900; } - args[0] = __JS_NewFloat64(ctx, y); + args[0] = JS_NewFloat64(ctx, y); return set_date_field(ctx, this_val, 1, args, 0x011); } @@ -46371,62 +48635,364 @@ void JS_AddIntrinsicEval(JSContext *ctx) #ifdef CONFIG_BIGNUM +/* Operators */ + +static void js_operator_set_finalizer(JSRuntime *rt, JSValue val) +{ + JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); + int i, j; + JSBinaryOperatorDefEntry *ent; + + if (opset) { + for(i = 0; i < JS_OVOP_COUNT; i++) { + if (opset->self_ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i])); + } + for(j = 0; j < opset->left.count; j++) { + ent = &opset->left.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); + } + } + js_free_rt(rt, opset->left.tab); + for(j = 0; j < opset->right.count; j++) { + ent = &opset->right.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); + } + } + js_free_rt(rt, opset->right.tab); + js_free_rt(rt, opset); + } +} + +static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); + int i, j; + JSBinaryOperatorDefEntry *ent; + + if (opset) { + for(i = 0; i < JS_OVOP_COUNT; i++) { + if (opset->self_ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]), + mark_func); + } + for(j = 0; j < opset->left.count; j++) { + ent = &opset->left.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), + mark_func); + } + } + for(j = 0; j < opset->right.count; j++) { + ent = &opset->right.tab[j]; + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + if (ent->ops[i]) + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), + mark_func); + } + } + } +} + + +/* create an OperatorSet object */ +static JSValue js_operators_create_internal(JSContext *ctx, + int argc, JSValueConst *argv, + BOOL is_primitive) +{ + JSValue opset_obj, prop, obj; + JSOperatorSetData *opset, *opset1; + JSBinaryOperatorDef *def; + JSValueConst arg; + int i, j; + JSBinaryOperatorDefEntry *new_tab; + JSBinaryOperatorDefEntry *ent; + uint32_t op_count; + + if (ctx->rt->operator_count == UINT32_MAX) { + return JS_ThrowTypeError(ctx, "too many operators"); + } + opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET); + if (JS_IsException(opset_obj)) + goto fail; + opset = js_mallocz(ctx, sizeof(*opset)); + if (!opset) + goto fail; + JS_SetOpaque(opset_obj, opset); + if (argc >= 1) { + arg = argv[0]; + /* self operators */ + for(i = 0; i < JS_OVOP_COUNT; i++) { + prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + opset->self_ops[i] = JS_VALUE_GET_OBJ(prop); + } + } + } + /* left & right operators */ + for(j = 1; j < argc; j++) { + arg = argv[j]; + prop = JS_GetPropertyStr(ctx, arg, "left"); + if (JS_IsException(prop)) + goto fail; + def = &opset->right; + if (JS_IsUndefined(prop)) { + prop = JS_GetPropertyStr(ctx, arg, "right"); + if (JS_IsException(prop)) + goto fail; + if (JS_IsUndefined(prop)) { + JS_ThrowTypeError(ctx, "left or right property must be present"); + goto fail; + } + def = &opset->left; + } + /* get the operator set */ + obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype); + JS_FreeValue(ctx, prop); + if (JS_IsException(obj)) + goto fail; + prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); + JS_FreeValue(ctx, obj); + if (JS_IsException(prop)) + goto fail; + opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET); + if (!opset1) { + JS_FreeValue(ctx, prop); + goto fail; + } + op_count = opset1->operator_counter; + JS_FreeValue(ctx, prop); + + /* we assume there are few entries */ + new_tab = js_realloc(ctx, def->tab, + (def->count + 1) * sizeof(def->tab[0])); + if (!new_tab) + goto fail; + def->tab = new_tab; + def->count++; + ent = def->tab + def->count - 1; + memset(ent, 0, sizeof(def->tab[0])); + ent->operator_index = op_count; + + for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { + prop = JS_GetPropertyStr(ctx, arg, + js_overloadable_operator_names[i]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + ent->ops[i] = JS_VALUE_GET_OBJ(prop); + } + } + } + opset->is_primitive = is_primitive; + opset->operator_counter = ctx->rt->operator_count++; + return opset_obj; + fail: + JS_FreeValue(ctx, opset_obj); + return JS_EXCEPTION; +} + +static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_operators_create_internal(ctx, argc, argv, FALSE); +} + +static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue opset_obj, prop; + JSOperatorSetData *opset; + const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW }; + JSOverloadableOperatorEnum op; + int i; + + opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT], + JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset_obj)) + goto fail; + opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET); + if (!opset) + goto fail; + for(i = 0; i < countof(ops); i++) { + op = ops[i]; + prop = JS_GetPropertyStr(ctx, argv[0], + js_overloadable_operator_names[op]); + if (JS_IsException(prop)) + goto fail; + if (!JS_IsUndefined(prop)) { + if (!JS_IsNull(prop) && check_function(ctx, prop)) { + JS_FreeValue(ctx, prop); + goto fail; + } + if (opset->self_ops[op]) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op])); + if (JS_IsNull(prop)) { + opset->self_ops[op] = NULL; + } else { + opset->self_ops[op] = JS_VALUE_GET_PTR(prop); + } + } + } + JS_FreeValue(ctx, opset_obj); + return JS_UNDEFINED; + fail: + JS_FreeValue(ctx, opset_obj); + return JS_EXCEPTION; +} + +static int js_operators_set_default(JSContext *ctx, JSValueConst obj) +{ + JSValue opset_obj; + + if (!JS_IsObject(obj)) /* in case the prototype is not defined */ + return 0; + opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE); + if (JS_IsException(opset_obj)) + return -1; + /* cannot be modified by the user */ + JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet, + opset_obj, 0); + return 0; +} + +static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); +} + +static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue func_obj, proto, opset_obj; + + func_obj = JS_UNDEFINED; + proto = JS_NewObject(ctx); + if (JS_IsException(proto)) + return JS_EXCEPTION; + opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE); + if (JS_IsException(opset_obj)) + goto fail; + JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet, + opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators", + 0, JS_CFUNC_constructor, 0); + if (JS_IsException(func_obj)) + goto fail; + JS_SetConstructor2(ctx, func_obj, proto, + 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, proto); + return func_obj; + fail: + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_operators_funcs[] = { + JS_CFUNC_DEF("create", 1, js_operators_create ), + JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ), +}; + +/* must be called after all overloadable base types are initialized */ +void JS_AddIntrinsicOperators(JSContext *ctx) +{ + JSValue obj; + + ctx->allow_operator_overloading = TRUE; + obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1); + JS_SetPropertyFunctionList(ctx, obj, + js_operators_funcs, + countof(js_operators_funcs)); + JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators, + obj, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + /* add default operatorSets */ + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]); + js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]); +} + +/* BigInt */ + static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) { uint32_t tag; - BOOL is_legacy; - int ret; - is_legacy = is_bignum_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)); - } + val = JS_NewBigInt64(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; - BOOL is_float; - a = JS_ToBigFloat(ctx, &is_float, &a_s, val); - bf_init(ctx->bf_ctx, r); + bf_t *a, a_s; + + a = JS_ToBigFloat(ctx, &a_s, val); 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_PREC_INF, BF_RNDZ); + JSValue val1 = JS_NewBigInt(ctx); + bf_t *r; + int ret; + if (JS_IsException(val1)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + r = JS_GetBigInt(val1); + ret = bf_set(r, a); + ret |= bf_rint(r, BF_RNDZ); JS_FreeValue(ctx, val); - if (is_legacy && (ret & BF_ST_INEXACT)) { - bf_delete(r); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val1); + val = JS_ThrowOutOfMemory(ctx); + } else if (ret & BF_ST_INEXACT) { + JS_FreeValue(ctx, val1); val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer"); } else { - val = JS_NewBigInt2(ctx, r, is_legacy); + val = JS_CompactBigInt(ctx, val1); } } if (a == &a_s) bf_delete(a); } break; - case JS_TAG_STRING: - val = JS_StringToBigIntErr(ctx, val); - if (JS_IsException(val)) + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) break; goto redo; + 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); + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); if (JS_IsException(val)) break; goto redo; @@ -46440,23 +49006,14 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) } static JSValue js_bigint_constructor(JSContext *ctx, - JSValueConst this_val, + JSValueConst new_target, int argc, JSValueConst *argv) { + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); } -static 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_bignum_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)) @@ -46465,8 +49022,6 @@ static JSValue js_thisBigIntValue(JSContext *ctx, 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_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); } @@ -46487,14 +49042,11 @@ static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, if (argc == 0 || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) + base = js_get_radix(ctx, argv[0]); + if (base < 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); + ret = js_bigint_to_string1(ctx, val, base); JS_FreeValue(ctx, val); return ret; fail: @@ -46508,79 +49060,103 @@ static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisBigIntValue(ctx, this_val); } -static JSValue js_integer_div(JSContext *ctx, +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; + bf_t a_s, b_s, *a, *b, *r, *q; int status; - + JSValue q_val, r_val; + + q_val = JS_NewBigInt(ctx); + if (JS_IsException(q_val)) + return JS_EXCEPTION; + r_val = JS_NewBigInt(ctx); + if (JS_IsException(r_val)) + goto fail; b = NULL; a = JS_ToBigInt(ctx, &a_s, argv[0]); if (!a) - return JS_EXCEPTION; + goto fail; b = JS_ToBigInt(ctx, &b_s, argv[1]); if (!b) { JS_FreeBigInt(ctx, a, &a_s); - return JS_EXCEPTION; + goto fail; } - bf_init(ctx->bf_ctx, q); - bf_init(ctx->bf_ctx, r); + q = JS_GetBigInt(q_val); + r = JS_GetBigInt(r_val); status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf); JS_FreeBigInt(ctx, a, &a_s); JS_FreeBigInt(ctx, b, &b_s); if (unlikely(status)) { - bf_delete(q); - bf_delete(r); throw_bf_exception(ctx, status); - return JS_EXCEPTION; + goto fail; } + q_val = JS_CompactBigInt(ctx, q_val); 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)); + if (JS_IsException(ret)) + goto fail; + JS_SetPropertyUint32(ctx, ret, 0, q_val); + JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val)); return ret; } else { - bf_delete(r); - return JS_NewBigInt(ctx, q); + JS_FreeValue(ctx, r_val); + return q_val; } + fail: + JS_FreeValue(ctx, q_val); + JS_FreeValue(ctx, r_val); + return JS_EXCEPTION; } -static JSValue js_integer_sqrt(JSContext *ctx, +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; + bf_t a_s, *a, *r, *rem; int status; + JSValue r_val, rem_val; + + r_val = JS_NewBigInt(ctx); + if (JS_IsException(r_val)) + return JS_EXCEPTION; + rem_val = JS_NewBigInt(ctx); + if (JS_IsException(rem_val)) + return JS_EXCEPTION; + r = JS_GetBigInt(r_val); + rem = JS_GetBigInt(rem_val); a = JS_ToBigInt(ctx, &a_s, argv[0]); if (!a) - return JS_EXCEPTION; - bf_init(ctx->bf_ctx, r); - bf_init(ctx->bf_ctx, rem); + goto fail; 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); + throw_bf_exception(ctx, status); + goto fail; } + r_val = JS_CompactBigInt(ctx, r_val); 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)); + if (JS_IsException(ret)) + goto fail; + JS_SetPropertyUint32(ctx, ret, 0, r_val); + JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val)); return ret; } else { - bf_delete(rem); - return JS_NewBigInt(ctx, r); + JS_FreeValue(ctx, rem_val); + return r_val; } + fail: + JS_FreeValue(ctx, r_val); + JS_FreeValue(ctx, rem_val); + return JS_EXCEPTION; } -static JSValue js_integer_op1(JSContext *ctx, +static JSValue js_bigint_op1(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) @@ -46610,24 +49186,30 @@ static JSValue js_integer_op1(JSContext *ctx, abort(); } JS_FreeBigInt(ctx, a, &a_s); - return JS_NewInt64(ctx, res); + return JS_NewBigInt64(ctx, res); } -static JSValue js_integer_asUintN(JSContext *ctx, +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; - BOOL is_legacy; - + bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; + JSValue res; + if (JS_ToIndex(ctx, &bits, argv[0])) return JS_EXCEPTION; - a = JS_ToBigInt(ctx, &a_s, argv[1]); - if (!a) + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) return JS_EXCEPTION; + r = JS_GetBigInt(res); + a = JS_ToBigInt(ctx, &a_s, argv[1]); + if (!a) { + JS_FreeValue(ctx, res); + return JS_EXCEPTION; + } /* XXX: optimize */ - bf_init(ctx->bf_ctx, r); + r = JS_GetBigInt(res); bf_init(ctx->bf_ctx, mask); bf_set_ui(mask, 1); bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); @@ -46644,25 +49226,25 @@ static JSValue js_integer_asUintN(JSContext *ctx, } bf_delete(mask); JS_FreeBigInt(ctx, a, &a_s); - is_legacy = is_bignum_mode(ctx) ^ 1; - return JS_NewBigInt2(ctx, r, is_legacy); + return JS_CompactBigInt(ctx, res); } -static const JSCFunctionListEntry js_integer_funcs[] = { - JS_CFUNC_MAGIC_DEF("tdiv", 2, js_integer_div, BF_RNDZ ), - JS_CFUNC_MAGIC_DEF("fdiv", 2, js_integer_div, BF_RNDD ), - JS_CFUNC_MAGIC_DEF("cdiv", 2, js_integer_div, BF_RNDU ), - JS_CFUNC_MAGIC_DEF("ediv", 2, js_integer_div, BF_DIVREM_EUCLIDIAN ), - JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_integer_div, BF_RNDZ | 0x10 ), - JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_integer_div, BF_RNDD | 0x10 ), - JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_integer_div, BF_RNDU | 0x10 ), - JS_CFUNC_MAGIC_DEF("edivrem", 2, js_integer_div, BF_DIVREM_EUCLIDIAN | 0x10 ), - JS_CFUNC_MAGIC_DEF("sqrt", 1, js_integer_sqrt, 0 ), - JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_integer_sqrt, 1 ), - JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_integer_op1, 0 ), - JS_CFUNC_MAGIC_DEF("ctz", 1, js_integer_op1, 1 ), - JS_CFUNC_MAGIC_DEF("asUintN", 2, js_integer_asUintN, 0 ), - JS_CFUNC_MAGIC_DEF("asIntN", 2, js_integer_asUintN, 1 ), +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[] = { @@ -46671,6 +49253,29 @@ static const JSCFunctionListEntry js_bigint_proto_funcs[] = { JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), }; +void JS_AddIntrinsicBigInt(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValueConst obj1; + + rt->bigint_ops.to_string = js_bigint_to_string; + rt->bigint_ops.from_string = js_string_to_bigint; + rt->bigint_ops.unary_arith = js_unary_arith_bigint; + rt->bigint_ops.binary_arith = js_binary_arith_bigint; + rt->bigint_ops.compare = js_compare_bigfloat; + + ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], + js_bigint_proto_funcs, + countof(js_bigint_proto_funcs)); + obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_INT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, + countof(js_bigint_funcs)); +} + +/* BigFloat */ + static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) { if (JS_IsBigFloat(this_val)) @@ -46699,12 +49304,9 @@ static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, if (argc == 0 || JS_IsUndefined(argv[0])) { base = 10; } else { - if (JS_ToInt32Sat(ctx, &base, argv[0])) + base = js_get_radix(ctx, argv[0]); + if (base < 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); @@ -46720,24 +49322,184 @@ static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisBigFloatValue(ctx, this_val); } +static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val) +{ + int rnd_mode; + if (JS_ToInt32Sat(ctx, &rnd_mode, val)) + return -1; + if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) { + JS_ThrowRangeError(ctx, "invalid rounding mode"); + return -1; + } + return rnd_mode; +} + +static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode, radix; + + val = js_thisBigFloatValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (f < 0 || f > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + radix = 10; + /* XXX: swap parameter order for rounding mode and radix */ + if (argc > 1) { + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val) +{ + BOOL res; + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + res = bf_is_finite(&p->num); + } + break; + default: + res = FALSE; + break; + } + return res; +} + +static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode, radix; + + val = js_thisBigFloatValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (!js_bigfloat_is_finite(ctx, val)) { + ret = JS_ToString(ctx, val); + } else if (JS_IsUndefined(argv[0])) { + ret = js_ftoa(ctx, val, 10, 0, + BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); + } else { + if (f < 0 || f > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + radix = 10; + if (argc > 1) { + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, f + 1, + rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); + } + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t p; + int rnd_mode, radix; + + val = js_thisBigFloatValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_IsUndefined(argv[0])) + goto to_string; + if (JS_ToInt64Sat(ctx, &p, argv[0])) + goto fail; + if (!js_bigfloat_is_finite(ctx, val)) { + to_string: + ret = JS_ToString(ctx, this_val); + } else { + if (p < 1 || p > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + radix = 10; + if (argc > 1) { + rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + if (argc > 2) { + radix = js_get_radix(ctx, argv[2]); + if (radix < 0) + goto fail; + } + ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED); + } + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ), JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ), - JS_CFUNC_MAGIC_DEF("toPrecision", 1, js_number_toPrecision, 1 ), - JS_CFUNC_MAGIC_DEF("toFixed", 1, js_number_toFixed, 1 ), - JS_CFUNC_MAGIC_DEF("toExponential", 1, js_number_toExponential, 1 ), + 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, + JSValueConst new_target, int argc, JSValueConst *argv) { JSValue val; + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); if (argc == 0) { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); bf_set_zero(r, 0); - val = JS_NewBigFloat(ctx, r); } else { val = JS_DupValue(ctx, argv[0]); redo: @@ -46746,18 +49508,26 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, 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); + bf_t *r; + double d = JS_VALUE_GET_FLOAT64(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_float64(r, d)) + goto fail; } break; case JS_TAG_INT: { - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - val = JS_NewBigFloat(ctx, r); + bf_t *r; + int32_t v = JS_VALUE_GET_INT(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_si(r, v)) + goto fail; } break; case JS_TAG_BIG_INT: @@ -46767,20 +49537,42 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, val = JS_MKPTR(JS_TAG_BIG_FLOAT, p); } break; + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + break; + goto redo; case JS_TAG_STRING: { const char *str, *p; + size_t len; int err; - str = JS_ToCString(ctx, val); + str = JS_ToCStringLen(ctx, &len, val); JS_FreeValue(ctx, val); if (!str) return JS_EXCEPTION; - val = js_atof(ctx, str, &p, 0, BF_ATOF_BIN_OCT | - BF_ATOF_NO_PREFIX_AFTER_SIGN | - BF_ATOF_JS_QUIRKS); + p = str; p += skip_spaces(p); - err = (*p != '\0'); + if ((p - str) == len) { + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + bf_set_zero(r, 0); + err = 0; + } else { + val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | + ATOD_TYPE_BIG_FLOAT | + ATOD_ACCEPT_PREFIX_AFTER_SIGN); + if (JS_IsException(val)) { + JS_FreeCString(ctx, str); + return JS_EXCEPTION; + } + p += skip_spaces(p); + err = ((p - str) != len); + } JS_FreeCString(ctx, str); if (err) { JS_FreeValue(ctx, val); @@ -46801,13 +49593,20 @@ static JSValue js_bigfloat_constructor(JSContext *ctx, } } return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; } -static JSValue js_float_get_const(JSContext *ctx, - JSValueConst this_val, int magic) +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); + bf_t *r; + JSValue val; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); switch(magic) { case 0: /* PI */ bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); @@ -46843,13 +49642,13 @@ static JSValue js_float_get_const(JSContext *ctx, default: abort(); } - return JS_NewBigFloat(ctx, r); + return val; } -static JSValue js_float_parseFloat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) { - bf_t a_s, *a = &a_s; + bf_t *a; const char *str; JSValue ret; int radix; @@ -46873,44 +49672,268 @@ static JSValue js_float_parseFloat(JSContext *ctx, JSValueConst this_val, if (!fe) goto fail; } - bf_init(ctx->bf_ctx, a); - bf_atof(a, str, NULL, radix, fe->prec, BF_ATOF_JS_QUIRKS | fe->flags); - ret = JS_NewBigFloat(ctx, a); + ret = JS_NewBigFloat(ctx); + if (JS_IsException(ret)) + goto done; + a = JS_GetBigFloat(ret); + /* XXX: use js_atof() */ + bf_atof(a, str, NULL, radix, fe->prec, fe->flags); + done: JS_FreeCString(ctx, str); return ret; } +static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst val = argv[0]; + JSBigFloat *p; + + if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) + return JS_FALSE; + p = JS_VALUE_GET_PTR(val); + return JS_NewBool(ctx, bf_is_finite(&p->num)); +} + +static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst val = argv[0]; + JSBigFloat *p; + + if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) + return JS_FALSE; + p = JS_VALUE_GET_PTR(val); + return JS_NewBool(ctx, bf_is_nan(&p->num)); +} + +enum { + MATH_OP_ABS, + MATH_OP_FLOOR, + MATH_OP_CEIL, + MATH_OP_ROUND, + MATH_OP_TRUNC, + MATH_OP_SQRT, + MATH_OP_FPROUND, + MATH_OP_ACOS, + MATH_OP_ASIN, + MATH_OP_ATAN, + MATH_OP_ATAN2, + MATH_OP_COS, + MATH_OP_EXP, + MATH_OP_LOG, + MATH_OP_POW, + MATH_OP_SIN, + MATH_OP_TAN, + MATH_OP_FMOD, + MATH_OP_REM, + MATH_OP_SIGN, + + MATH_OP_ADD, + MATH_OP_SUB, + MATH_OP_MUL, + MATH_OP_DIV, +}; + +static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, *r; + JSFloatEnv *fe; + int rnd_mode; + JSValue op1, res; + + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + a = JS_ToBigFloat(ctx, &a_s, op1); + fe = &ctx->fp_env; + if (argc > 1) { + fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + fail: + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + r = JS_GetBigFloat(res); + switch (magic) { + case MATH_OP_ABS: + bf_set(r, a); + r->sign = 0; + break; + case MATH_OP_FLOOR: + rnd_mode = BF_RNDD; + goto rint; + case MATH_OP_CEIL: + rnd_mode = BF_RNDU; + goto rint; + case MATH_OP_ROUND: + rnd_mode = BF_RNDNA; + goto rint; + case MATH_OP_TRUNC: + rnd_mode = BF_RNDZ; + rint: + bf_set(r, a); + fe->status |= bf_rint(r, rnd_mode); + break; + case MATH_OP_SQRT: + fe->status |= bf_sqrt(r, a, fe->prec, fe->flags); + break; + case MATH_OP_FPROUND: + bf_set(r, a); + fe->status |= bf_round(r, fe->prec, fe->flags); + break; + case MATH_OP_ACOS: + fe->status |= bf_acos(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ASIN: + fe->status |= bf_asin(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ATAN: + fe->status |= bf_atan(r, a, fe->prec, fe->flags); + break; + case MATH_OP_COS: + fe->status |= bf_cos(r, a, fe->prec, fe->flags); + break; + case MATH_OP_EXP: + fe->status |= bf_exp(r, a, fe->prec, fe->flags); + break; + case MATH_OP_LOG: + fe->status |= bf_log(r, a, fe->prec, fe->flags); + break; + case MATH_OP_SIN: + fe->status |= bf_sin(r, a, fe->prec, fe->flags); + break; + case MATH_OP_TAN: + fe->status |= bf_tan(r, a, fe->prec, fe->flags); + break; + case MATH_OP_SIGN: + if (bf_is_nan(a) || bf_is_zero(a)) { + bf_set(r, a); + } else { + bf_set_si(r, 1 - 2 * a->sign); + } + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return res; +} + +static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; + JSFloatEnv *fe; + JSValue op1, op2, res; + + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + op2 = JS_ToNumeric(ctx, argv[1]); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return op2; + } + a = JS_ToBigFloat(ctx, &a_s, op1); + 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) + goto fail; + } + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) { + fail: + 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; + } + r = JS_GetBigFloat(res); + switch (magic) { + case MATH_OP_ATAN2: + fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_POW: + fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS); + break; + case MATH_OP_FMOD: + fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); + break; + case MATH_OP_REM: + fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN); + break; + case MATH_OP_ADD: + fe->status |= bf_add(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_SUB: + fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_MUL: + fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_DIV: + fe->status |= bf_div(r, a, b, fe->prec, fe->flags); + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; +} + static const JSCFunctionListEntry js_bigfloat_funcs[] = { - JS_CGETSET_MAGIC_DEF("PI", js_float_get_const, NULL, 0 ), - JS_CGETSET_MAGIC_DEF("LN2", js_float_get_const, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_float_get_const, NULL, 2 ), - JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_float_get_const, NULL, 3 ), - JS_CGETSET_MAGIC_DEF("EPSILON", js_float_get_const, NULL, 4 ), - JS_CFUNC_DEF("parseFloat", 1, js_float_parseFloat ), - JS_CFUNC_MAGIC_DEF("abs", 1, js_math_fop, MATH_OP_ABS ), - JS_CFUNC_MAGIC_DEF("fpRound", 1, js_math_fop, MATH_OP_FPROUND ), - JS_CFUNC_MAGIC_DEF("floor", 1, js_math_fop, MATH_OP_FLOOR ), - JS_CFUNC_MAGIC_DEF("ceil", 1, js_math_fop, MATH_OP_CEIL ), - JS_CFUNC_MAGIC_DEF("round", 1, js_math_fop, MATH_OP_ROUND ), - JS_CFUNC_MAGIC_DEF("trunc", 1, js_math_fop, MATH_OP_TRUNC ), - JS_CFUNC_MAGIC_DEF("sqrt", 1, js_math_fop, MATH_OP_SQRT ), - JS_CFUNC_MAGIC_DEF("acos", 1, js_math_fop, MATH_OP_ACOS ), - JS_CFUNC_MAGIC_DEF("asin", 1, js_math_fop, MATH_OP_ASIN ), - JS_CFUNC_MAGIC_DEF("atan", 1, js_math_fop, MATH_OP_ATAN ), - JS_CFUNC_MAGIC_DEF("atan2", 2, js_math_fop2, MATH_OP_ATAN2 ), - JS_CFUNC_MAGIC_DEF("cos", 1, js_math_fop, MATH_OP_COS ), - JS_CFUNC_MAGIC_DEF("exp", 1, js_math_fop, MATH_OP_EXP ), - JS_CFUNC_MAGIC_DEF("log", 1, js_math_fop, MATH_OP_LOG ), - JS_CFUNC_MAGIC_DEF("pow", 2, js_math_fop2, MATH_OP_POW ), - JS_CFUNC_MAGIC_DEF("sin", 1, js_math_fop, MATH_OP_SIN ), - JS_CFUNC_MAGIC_DEF("tan", 1, js_math_fop, MATH_OP_TAN ), - JS_CFUNC_MAGIC_DEF("sign", 1, js_math_fop, MATH_OP_SIGN ), - JS_CFUNC_MAGIC_DEF("add", 2, js_math_fop2, MATH_OP_ADD ), - JS_CFUNC_MAGIC_DEF("sub", 2, js_math_fop2, MATH_OP_SUB ), - JS_CFUNC_MAGIC_DEF("mul", 2, js_math_fop2, MATH_OP_MUL ), - JS_CFUNC_MAGIC_DEF("div", 2, js_math_fop2, MATH_OP_DIV ), - JS_CFUNC_MAGIC_DEF("fmod", 2, js_math_fop2, MATH_OP_FMOD ), - JS_CFUNC_MAGIC_DEF("remainder", 2, js_math_fop2, MATH_OP_REM ), + 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 */ @@ -46970,9 +49993,6 @@ 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) @@ -46988,18 +50008,16 @@ static JSValue js_float_env_setPrec(JSContext *ctx, return JS_EXCEPTION; if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) return JS_ThrowRangeError(ctx, "invalid precision"); - exp_bits = BF_EXP_BITS_MAX1; + exp_bits = BF_EXP_BITS_MAX; if (argc > 2 && !JS_IsUndefined(argv[2])) { if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) return JS_EXCEPTION; - if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX1) + if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); } - flags = BF_RNDN | bf_set_exp_bits(exp_bits); - if (exp_bits != BF_EXP_BITS_MAX1) - flags |= BF_FLAG_SUBNORMAL; + flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); saved_prec = ctx->fp_env.prec; saved_flags = ctx->fp_env.flags; @@ -47039,7 +50057,7 @@ static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_v } } -static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) { JSFloatEnv *fe; int b; @@ -47059,24 +50077,20 @@ static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_v case FE_EXP: if (JS_ToInt32Sat(ctx, &b, val)) return JS_EXCEPTION; - if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX1) + if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) 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); + b = bigfloat_get_rnd_mode(ctx, val); if (b < 0) return JS_EXCEPTION; fe->flags = (fe->flags & ~BF_RND_MASK) | b; break; case FE_SUBNORMAL: b = JS_ToBool(ctx, val); - if (bf_get_exp_bits(fe->flags) != BF_EXP_BITS_MAX1) { - fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); - } + fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); break; default: b = JS_ToBool(ctx, val); @@ -47106,12 +50120,12 @@ static const JSCFunctionListEntry js_float_env_funcs[] = { 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("RNDA", BF_RNDA, 0 ), JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), - JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX1, 0 ), + JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), }; static const JSCFunctionListEntry js_float_env_proto_funcs[] = { @@ -47136,12 +50150,529 @@ static const JSCFunctionListEntry js_float_env_proto_funcs[] = { JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ), }; +void JS_AddIntrinsicBigFloat(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValueConst obj1; + + rt->bigfloat_ops.to_string = js_bigfloat_to_string; + rt->bigfloat_ops.from_string = js_string_to_bigfloat; + rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat; + rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat; + rt->bigfloat_ops.compare = js_compare_bigfloat; + rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64; + rt->bigfloat_ops.mul_pow10 = js_mul_pow10; + + ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT], + js_bigfloat_proto_funcs, + countof(js_bigfloat_proto_funcs)); + obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_FLOAT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs, + countof(js_bigfloat_funcs)); + + ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV], + js_float_env_proto_funcs, + countof(js_float_env_proto_funcs)); + obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", + js_float_env_constructor, 1, + ctx->class_proto[JS_CLASS_FLOAT_ENV]); + JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, + countof(js_float_env_funcs)); +} + +/* BigDecimal */ + +static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, + BOOL allow_null_or_undefined) +{ + redo: + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_BIG_DECIMAL: + break; + case JS_TAG_NULL: + if (!allow_null_or_undefined) + goto fail; + /* fall thru */ + case JS_TAG_BOOL: + case JS_TAG_INT: + { + bfdec_t *r; + int32_t v = JS_VALUE_GET_INT(val); + + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); + if (bfdec_set_si(r, v)) { + JS_FreeValue(ctx, val); + val = JS_EXCEPTION; + break; + } + } + break; + case JS_TAG_FLOAT64: + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + break; + goto redo; + case JS_TAG_STRING: + { + const char *str, *p; + size_t len; + int err; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + bfdec_t *r; + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); + bfdec_set_zero(r, 0); + err = 0; + } else { + val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL); + if (JS_IsException(val)) { + JS_FreeCString(ctx, str); + return JS_EXCEPTION; + } + p += skip_spaces(p); + err = ((p - str) != len); + } + JS_FreeCString(ctx, str); + if (err) { + JS_FreeValue(ctx, val); + return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal"); + } + } + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + break; + goto redo; + case JS_TAG_UNDEFINED: + { + bfdec_t *r; + if (!allow_null_or_undefined) + goto fail; + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigDecimal(val); + bfdec_set_nan(r); + } + break; + default: + fail: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal"); + } + return val; +} + +static JSValue js_bigdecimal_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue val; + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (argc == 0) { + bfdec_t *r; + val = JS_NewBigDecimal(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigDecimal(val); + bfdec_set_zero(r, 0); + } else { + val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE); + } + return val; +} + +static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_IsBigDecimal(this_val)) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_BIG_DECIMAL) { + if (JS_IsBigDecimal(p->u.object_data)) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a bigdecimal"); +} + +static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + return JS_ToStringFree(ctx, val); +} + +static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisBigDecimalValue(ctx, this_val); +} + +static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj) +{ + const char *str; + size_t size; + int rnd_mode; + + str = JS_ToCStringLen(ctx, &size, obj); + if (!str) + return -1; + if (strlen(str) != size) + goto invalid_rounding_mode; + if (!strcmp(str, "floor")) { + rnd_mode = BF_RNDD; + } else if (!strcmp(str, "ceiling")) { + rnd_mode = BF_RNDU; + } else if (!strcmp(str, "down")) { + rnd_mode = BF_RNDZ; + } else if (!strcmp(str, "up")) { + rnd_mode = BF_RNDA; + } else if (!strcmp(str, "half-even")) { + rnd_mode = BF_RNDN; + } else if (!strcmp(str, "half-up")) { + rnd_mode = BF_RNDNA; + } else { + invalid_rounding_mode: + JS_FreeCString(ctx, str); + JS_ThrowTypeError(ctx, "invalid rounding mode"); + return -1; + } + JS_FreeCString(ctx, str); + return rnd_mode; +} + +typedef struct { + int64_t prec; + bf_flags_t flags; +} BigDecimalEnv; + +static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, + JSValueConst obj) +{ + JSValue prop; + int64_t val; + BOOL has_prec; + int rnd_mode; + + if (!JS_IsObject(obj)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode); + if (JS_IsException(prop)) + return -1; + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop); + JS_FreeValue(ctx, prop); + if (rnd_mode < 0) + return -1; + fe->flags = rnd_mode; + + prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits); + if (JS_IsException(prop)) + return -1; + has_prec = FALSE; + if (!JS_IsUndefined(prop)) { + if (JS_ToInt64SatFree(ctx, &val, prop)) + return -1; + if (val < 1 || val > BF_PREC_MAX) + goto invalid_precision; + fe->prec = val; + has_prec = TRUE; + } + + prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits); + if (JS_IsException(prop)) + return -1; + if (!JS_IsUndefined(prop)) { + if (has_prec) { + JS_FreeValue(ctx, prop); + JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits"); + return -1; + } + if (JS_ToInt64SatFree(ctx, &val, prop)) + return -1; + if (val < 0 || val > BF_PREC_MAX) { + invalid_precision: + JS_ThrowTypeError(ctx, "invalid precision"); + return -1; + } + fe->prec = val; + fe->flags |= BF_FLAG_RADPNT_PREC; + has_prec = TRUE; + } + if (!has_prec) { + JS_ThrowTypeError(ctx, "precision must be present"); + return -1; + } + return 0; +} + + +static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bfdec_t *a, *b, r_s, *r = &r_s; + JSValue op1, op2, res; + BigDecimalEnv fe_s, *fe = &fe_s; + int op_count, ret; + + if (magic == MATH_OP_SQRT || + magic == MATH_OP_ROUND) + op_count = 1; + else + op_count = 2; + + op1 = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(op1)) + return op1; + a = JS_ToBigDecimal(ctx, op1); + if (!a) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + if (op_count >= 2) { + op2 = JS_ToNumeric(ctx, argv[1]); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return op2; + } + b = JS_ToBigDecimal(ctx, op2); + if (!b) + goto fail; + } else { + op2 = JS_UNDEFINED; + b = NULL; + } + fe->flags = BF_RNDZ; + fe->prec = BF_PREC_INF; + if (op_count < argc) { + if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) + goto fail; + } + + res = JS_NewBigDecimal(ctx); + if (JS_IsException(res)) { + fail: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + r = JS_GetBigDecimal(res); + switch (magic) { + case MATH_OP_ADD: + ret = bfdec_add(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_SUB: + ret = bfdec_sub(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_MUL: + ret = bfdec_mul(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_DIV: + ret = bfdec_div(r, a, b, fe->prec, fe->flags); + break; + case MATH_OP_FMOD: + ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); + break; + case MATH_OP_SQRT: + ret = bfdec_sqrt(r, a, fe->prec, fe->flags); + break; + case MATH_OP_ROUND: + ret = bfdec_set(r, a); + if (!(ret & BF_ST_MEM_ERROR)) + ret = bfdec_round(r, fe->prec, fe->flags); + break; + default: + abort(); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP | + BF_ST_OVERFLOW; + if (ret != 0) { + JS_FreeValue(ctx, res); + return throw_bf_exception(ctx, ret); + } else { + return res; + } +} + +static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (f < 0 || f > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t f; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToInt64Sat(ctx, &f, argv[0])) + goto fail; + if (JS_IsUndefined(argv[0])) { + ret = js_bigdecimal_to_string1(ctx, val, 0, + BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); + } else { + if (f < 0 || f > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, f + 1, + rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); + } + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + int64_t p; + int rnd_mode; + + val = js_thisBigDecimalValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_IsUndefined(argv[0])) { + return JS_ToStringFree(ctx, val); + } + if (JS_ToInt64Sat(ctx, &p, argv[0])) + goto fail; + if (p < 1 || p > BF_PREC_MAX) { + JS_ThrowRangeError(ctx, "invalid number of digits"); + goto fail; + } + rnd_mode = BF_RNDNA; + if (argc > 1) { + rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); + if (rnd_mode < 0) + goto fail; + } + ret = js_bigdecimal_to_string1(ctx, val, p, + rnd_mode | BF_FTOA_FORMAT_FIXED); + JS_FreeValue(ctx, val); + return ret; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ), + JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ), + JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ), + JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ), +}; + +static const JSCFunctionListEntry js_bigdecimal_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ), + JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ), + JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ), + JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ), + JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ), + JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ), + JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ), +}; + +void JS_AddIntrinsicBigDecimal(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValueConst obj1; + + rt->bigdecimal_ops.to_string = js_bigdecimal_to_string; + rt->bigdecimal_ops.from_string = js_string_to_bigdecimal; + rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal; + rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal; + rt->bigdecimal_ops.compare = js_compare_bigdecimal; + + ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL], + js_bigdecimal_proto_funcs, + countof(js_bigdecimal_proto_funcs)); + obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal", + js_bigdecimal_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_DECIMAL]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs, + countof(js_bigdecimal_funcs)); +} + +void JS_EnableBignumExt(JSContext *ctx, BOOL enable) +{ + ctx->bignum_ext = enable; +} + #endif /* CONFIG_BIGNUM */ static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = { "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError", - "InternalError", + "InternalError", "AggregateError", }; /* Minimum amount of objects to be able to compile code and display @@ -47209,16 +50740,16 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); /* add caller and arguments properties to throw a TypeError */ - obj1 = JS_NewCFunction(ctx, js_function_proto_caller, "get caller", 0); + 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_FreeValue(ctx, obj1); JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED, - ctx->throw_type_error, ctx->throw_type_error, + 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); @@ -47246,9 +50777,12 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) "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); + JSValue func_obj; + int n_args; + n_args = 1 + (i == JS_AGGREGATE_ERROR); + func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor, + native_error_name[i], n_args, + JS_CFUNC_constructor_or_func_magic, i, obj1); JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i], ctx->native_error_proto[i]); } @@ -47266,6 +50800,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1, ctx->class_proto[JS_CLASS_ARRAY]); + ctx->array_ctor = JS_DupValue(ctx, obj); JS_SetPropertyFunctionList(ctx, obj, js_array_funcs, countof(js_array_funcs)); @@ -47293,6 +50828,12 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) js_array_iterator_proto_funcs, countof(js_array_iterator_proto_funcs)); + /* parseFloat and parseInteger must be defined before Number + because of the Number.parseFloat and Number.parseInteger + aliases */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs, + countof(js_global_funcs)); + /* Number */ ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_NUMBER); @@ -47376,46 +50917,11 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) 0, JS_PROP_CONFIGURABLE); JS_FreeValue(ctx, obj1); -#ifdef CONFIG_BIGNUM - 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_integer_funcs, - countof(js_integer_funcs)); - - 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)); - obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", - js_float_env_constructor, 1, - ctx->class_proto[JS_CLASS_FLOAT_ENV]); - JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, - countof(js_float_env_funcs)); -#endif /* global properties */ -#ifdef ALLOW_UNSAFE_JS 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); -#endif - 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), @@ -47439,6 +50945,7 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx, JSFreeArrayBufferDataFunc *free_func, void *opaque, BOOL alloc_flag) { + JSRuntime *rt = ctx->rt; JSValue obj; JSArrayBuffer *abuf = NULL; @@ -47455,11 +50962,24 @@ static JSValue js_array_buffer_constructor3(JSContext *ctx, 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; + if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && + rt->sab_funcs.sab_alloc) { + abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, + max_int(len, 1)); + if (!abuf->data) + goto fail; + memset(abuf->data, 0, len); + } else { + /* the allocation must be done after the object creation */ + abuf->data = js_mallocz(ctx, max_int(len, 1)); + if (!abuf->data) + goto fail; + } } else { + if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && + rt->sab_funcs.sab_dup) { + rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf); + } abuf->data = buf; } init_list_head(&abuf->array_list); @@ -47545,14 +51065,16 @@ 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) { - /* During the GC sweep phase the ArrayBuffer finalizer may be - called before the typed array finalizers using it, so - abuf->array_list is not necessarily empty. */ - if (!JS_IsInGCSweep(rt)) { - assert(list_empty(&abuf->array_list)); + /* 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->shared && rt->sab_funcs.sab_free) { + rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); + } else { + if (abuf->free_func) + abuf->free_func(rt, abuf->opaque, abuf->data); } - if (abuf->free_func) - abuf->free_func(rt, abuf->opaque, abuf->data); js_free_rt(rt, abuf); } } @@ -47591,11 +51113,9 @@ static JSValue js_array_buffer_get_byteLength(JSContext *ctx, JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); if (!abuf) return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + /* return 0 if detached */ return JS_NewUint32(ctx, abuf->byte_length); } - void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj) { JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); @@ -47857,6 +51377,32 @@ static JSValue js_typed_array_get_byteOffset(JSContext *ctx, 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) { @@ -48002,11 +51548,6 @@ static JSValue js_typed_array___getLength(JSContext *ctx, } #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) { @@ -48210,6 +51751,8 @@ static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val, count = min_int(final - from, len - to); if (count > 0) { p = JS_VALUE_GET_OBJ(this_val); + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); shift = typed_array_size_log2(p->class_id); memmove(p->u.array.u.uint8_ptr + (to << shift), p->u.array.u.uint8_ptr + (from << shift), @@ -48277,6 +51820,9 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + shift = typed_array_size_log2(p->class_id); switch(shift) { case 0: @@ -48365,7 +51911,7 @@ 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; + int len, tag, is_int, is_bigint, k, stop, inc, res = -1; int64_t v64; double d; float f; @@ -48408,7 +51954,11 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, inc = 1; } - is_big = 0; + if (validate_typed_array(ctx, this_val)) + goto exception; + p = JS_VALUE_GET_OBJ(this_val); + + is_bigint = 0; is_int = 0; /* avoid warning */ v64 = 0; /* avoid warning */ tag = JS_VALUE_GET_NORM_TAG(argv[0]); @@ -48423,10 +51973,20 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, 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; + if (tag == JS_TAG_BIG_INT) { + JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]); + + if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { + if (bf_get_int64(&v64, &p1->num, 0) != 0) + goto done; + } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) { + if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0) + goto done; + } else { + goto done; + } + d = 0; + is_bigint = 1; } else #endif { @@ -48500,7 +52060,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, } break; case JS_CLASS_FLOAT32_ARRAY: - if (is_big) + if (is_bigint) break; if (isnan(d)) { const float *pv = p->u.array.u.float_ptr; @@ -48524,7 +52084,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, } break; case JS_CLASS_FLOAT64_ARRAY: - if (is_big) + if (is_bigint) break; if (isnan(d)) { const double *pv = p->u.array.u.double_ptr; @@ -48549,20 +52109,22 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, break; #ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT64_ARRAY: + if (is_bigint || (is_math_mode(ctx) && is_int && + v64 >= -MAX_SAFE_INTEGER && + v64 <= MAX_SAFE_INTEGER)) { + goto scan64; + } + break; case JS_CLASS_BIG_UINT64_ARRAY: - if (is_big || is_strict_mode(ctx)) { - /* generic loop for bignums, argv[0] is a bignum != NaN */ - /* XXX: optimize with explicit values */ + if (is_bigint || (is_math_mode(ctx) && is_int && + v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) { + const uint64_t *pv; + uint64_t v; + scan64: + pv = p->u.array.u.uint64_ptr; + v = v64; for (; k != stop; k += inc) { - 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; + if (pv[k] == v) { res = k; break; } @@ -48917,7 +52479,7 @@ struct TA_sort_context { JSValueConst arr; JSValueConst cmp; JSValue (*getfun)(JSContext *ctx, const void *a); - void *array_ptr; /* cannot change unless the array is detached */ + uint8_t *array_ptr; /* cannot change unless the array is detached */ int elt_size; }; @@ -49740,9 +53302,10 @@ typedef enum AtomicsOpEnum { } AtomicsOpEnum; static void *js_atomics_get_ptr(JSContext *ctx, + JSArrayBuffer **pabuf, int *psize_log2, JSClassID *pclass_id, JSValueConst obj, JSValueConst idx_val, - BOOL is_waitable) + int is_waitable) { JSObject *p; JSTypedArray *ta; @@ -49777,18 +53340,27 @@ static void *js_atomics_get_ptr(JSContext *ctx, ta = p->u.typed_array; abuf = ta->buffer->u.array_buffer; if (!abuf->shared) { - JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); - return NULL; + if (is_waitable == 2) { + JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); + return NULL; + } + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return NULL; + } } if (JS_ToIndex(ctx, &idx, idx_val)) { return NULL; } + /* if the array buffer is detached, p->u.array.count = 0 */ if (idx >= p->u.array.count) { JS_ThrowRangeError(ctx, "out-of-bound access"); return NULL; } size_log2 = typed_array_size_log2(p->class_id); ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); + if (pabuf) + *pabuf = abuf; if (psize_log2) *psize_log2 = size_log2; if (pclass_id) @@ -49809,17 +53381,18 @@ static JSValue js_atomics_op(JSContext *ctx, void *ptr; JSValue ret; JSClassID class_id; + JSArrayBuffer *abuf; - ptr = js_atomics_get_ptr(ctx, &size_log2, &class_id, - argv[0], argv[1], FALSE); + ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id, + argv[0], argv[1], 0); if (!ptr) return JS_EXCEPTION; rep_val = 0; if (op == ATOMICS_OP_LOAD) { v = 0; - } else + } else { #ifdef CONFIG_BIGNUM - if (size_log2 == 3) { + if (size_log2 == 3) { int64_t v64; if (JS_ToBigInt64(ctx, &v64, argv[2])) return JS_EXCEPTION; @@ -49829,19 +53402,23 @@ static JSValue js_atomics_op(JSContext *ctx, return JS_EXCEPTION; rep_val = v64; } - } else + } 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; - } + { + uint32_t v32; + if (JS_ToUint32(ctx, &v32, argv[2])) + return JS_EXCEPTION; + v = v32; + if (op == ATOMICS_OP_COMPARE_EXCHANGE) { + if (JS_ToUint32(ctx, &v32, argv[3])) + return JS_EXCEPTION; + rep_val = v32; + } + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); } + switch(op | (size_log2 << 3)) { #ifdef CONFIG_BIGNUM @@ -49968,8 +53545,10 @@ static JSValue js_atomics_store(JSContext *ctx, int size_log2; void *ptr; JSValue ret; + JSArrayBuffer *abuf; - ptr = js_atomics_get_ptr(ctx, &size_log2, NULL, argv[0], argv[1], FALSE); + ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL, + argv[0], argv[1], 0); if (!ptr) return JS_EXCEPTION; #ifdef CONFIG_BIGNUM @@ -49982,6 +53561,8 @@ static JSValue js_atomics_store(JSContext *ctx, JS_FreeValue(ctx, ret); return JS_EXCEPTION; } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); atomic_store((_Atomic(uint64_t) *)ptr, v64); } else #endif @@ -49995,6 +53576,8 @@ static JSValue js_atomics_store(JSContext *ctx, JS_FreeValue(ctx, ret); return JS_EXCEPTION; } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); switch(size_log2) { case 0: atomic_store((_Atomic(uint8_t) *)ptr, v); @@ -50051,7 +53634,8 @@ static JSValue js_atomics_wait(JSContext *ctx, int ret, size_log2, res; double d; - ptr = js_atomics_get_ptr(ctx, &size_log2, NULL, argv[0], argv[1], TRUE); + ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL, + argv[0], argv[1], 2); if (!ptr) return JS_EXCEPTION; #ifdef CONFIG_BIGNUM @@ -50130,8 +53714,9 @@ static JSValue js_atomics_notify(JSContext *ctx, int32_t count, n; void *ptr; JSAtomicsWaiter *waiter; + JSArrayBuffer *abuf; - ptr = js_atomics_get_ptr(ctx, NULL, NULL, argv[0], argv[1], TRUE); + ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1); if (!ptr) return JS_EXCEPTION; @@ -50141,9 +53726,11 @@ static JSValue js_atomics_notify(JSContext *ctx, if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0)) return JS_EXCEPTION; } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); n = 0; - if (count > 0) { + if (abuf->shared && count > 0) { pthread_mutex_lock(&js_atomics_mutex); init_list_head(&waiter_list); list_for_each_safe(el, el1, &js_atomics_waiter_list) { @@ -50365,18 +53952,16 @@ static int JS_UpdateBreakPoint(JSContext *ctx, JSFunctionBytecode *byte_code, static JSFunctionBytecode *JS_GetByteCode(JSContext *ctx) { - if (ctx == NULL || ctx->current_stack_frame == NULL) { + if (ctx == NULL || ctx->rt->current_stack_frame == NULL) { return NULL; } - JSObject *func = JS_VALUE_GET_OBJ(ctx->current_stack_frame->cur_func); + JSObject *func = JS_VALUE_GET_OBJ(ctx->rt->current_stack_frame->cur_func); if (func == NULL || !js_class_has_bytecode(func->class_id)) { - DEBUGGER_LOGE("JS_GetByteCode get func fail"); return NULL; } JSFunctionBytecode *byte_code = func->u.func.function_bytecode; if (byte_code == NULL || !byte_code->has_debug || !byte_code->debug.filename) { - DEBUGGER_LOGE("JS_GetByteCode get byte_code fail"); return NULL; } return byte_code; @@ -50444,7 +54029,6 @@ int JS_HitBreakpoint(JSContext *ctx, const uint8_t *cur_pc) } JSFunctionBytecode *byte_code = JS_GetByteCode(ctx); if (byte_code == NULL) { - DEBUGGER_LOGE("JS_HitBreakpoint get byte_code fail"); return 0; } @@ -50484,7 +54068,7 @@ int JS_HitBreakpoint(JSContext *ctx, const uint8_t *cur_pc) DEBUGGER_LOGE("JS_HitBreakpoint fail debugger.breakpoints is false"); return 0; } - int pc = (cur_pc ? cur_pc : ctx->current_stack_frame->cur_pc) - byte_code->byte_code_buf - 1; + int pc = (cur_pc ? cur_pc : ctx->rt->current_stack_frame->cur_pc) - byte_code->byte_code_buf - 1; if (pc < 0 || pc > byte_code->byte_code_len) { DEBUGGER_LOGE("JS_HitBreakpoint get pc fail"); return 0; @@ -50509,7 +54093,7 @@ static JSValue JS_BuildFileInfo(JSContext *ctx, JSStackFrame *sf, } JSFunctionBytecode *byte_code = cur_func->u.func.function_bytecode; if (byte_code && byte_code->has_debug) { - if (sf == ctx->current_stack_frame) { + if (sf == ctx->rt->current_stack_frame) { if (cur_pc == NULL) { line_num = byte_code->debug.line_num; } else { @@ -50537,7 +54121,7 @@ JSValue JS_BuildStackTrace(JSContext *ctx, const uint8_t *cur_pc) JSValue stack_trace = JS_NewArray(ctx); uint32_t stack_index = 0; - JSStackFrame *sf = ctx->current_stack_frame; + JSStackFrame *sf = ctx->rt->current_stack_frame; while (sf != NULL) { JSValue cur_frame = JS_NewObject(ctx); @@ -50570,7 +54154,7 @@ uint32_t JS_GetStackDepth(const JSContext *ctx) return 0; } uint32_t count = 0; - JSStackFrame *stack_frame = ctx->current_stack_frame; + JSStackFrame *stack_frame = ctx->rt->current_stack_frame; while (stack_frame != NULL) { count++; stack_frame = stack_frame->prev_frame; @@ -50583,7 +54167,7 @@ LocInfo JS_GetCurrentLocation(JSContext *ctx, const uint8_t *pc) { LocInfo loc; (void)memset_s(&loc, sizeof(loc), 0, sizeof(loc)); - JSStackFrame *stack_frame = ctx->current_stack_frame; + JSStackFrame *stack_frame = ctx->rt->current_stack_frame; if (stack_frame == NULL) { loc.filename = 0; DEBUGGER_LOGE("JS_GetCurrentLocation get stack_frame fail"); @@ -50643,7 +54227,7 @@ static JSStackFrame *JS_FindCurStackFrame(JSContext *ctx, int stack_index) } int cur_index = 0; - JSStackFrame *sf = ctx->current_stack_frame; + JSStackFrame *sf = ctx->rt->current_stack_frame; if (sf == NULL) { DEBUGGER_LOGE("JS_FindCurStackFrame fail sf = NULL"); return NULL; @@ -50798,13 +54382,13 @@ JSValue JS_GetLocalVariables(JSContext *ctx, int stack_index) if (JS_IsException(ret)) { return JS_NULL; } - if (stack_index == 0 && !JS_IsNull(ctx->current_exception) && - !JS_IsUndefined(ctx->current_exception)) { - JS_SetPropertyStr(ctx, ret, "", JS_DupValue(ctx, ctx->current_exception)); + if (stack_index == 0 && !JS_IsNull(ctx->rt->current_exception) && + !JS_IsUndefined(ctx->rt->current_exception)) { + JS_SetPropertyStr(ctx, ret, "", JS_DupValue(ctx, ctx->rt->current_exception)); } int cur_index = 0; - for (JSStackFrame *sf = ctx->current_stack_frame; sf != NULL; sf = sf->prev_frame) { + for (JSStackFrame *sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) { if (cur_index == stack_index - 1) { ret = JS_SetThisObject(ctx, sf, ret); } @@ -50895,7 +54479,7 @@ JSValue JS_JsonStringify(JSContext *ctx, JSValueConst this_val, int argc, JSValu #endif /* for ace 2.0 */ -int JS_CountClassInstances(JSRuntime* rt, uint16_t class_id) +/*int JS_CountClassInstances(JSRuntime* rt, uint16_t class_id) { int count = 0; struct list_head* el; @@ -50907,7 +54491,7 @@ int JS_CountClassInstances(JSRuntime* rt, uint16_t class_id) } } return count; -} +}*/ void *JS_GetOpaqueA(JSValueConst obj, JSClassID* class_ids, size_t class_id_len) diff --git a/quickjs.h b/quickjs.h index 7ed9cd3..f96c6c8 100644 --- a/quickjs.h +++ b/quickjs.h @@ -1,8 +1,8 @@ /* * QuickJS Javascript Engine * - * Copyright (c) 2017-2019 Fabrice Bellard - * Copyright (c) 2017-2019 Charlie Gordon + * 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 @@ -53,7 +53,7 @@ typedef struct JSClass JSClass; typedef uint32_t JSClassID; typedef uint32_t JSAtom; -#if defined(__x86_64__) || defined(__aarch64__) +#if INTPTR_MAX >= INT64_MAX #define JS_PTR64 #define JS_PTR64_DEF(a) a #else @@ -66,14 +66,12 @@ typedef uint32_t JSAtom; enum { /* all tags with a reference count are negative */ - JS_TAG_FIRST = -10, /* first negative tag */ + 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_SHAPE = -6, /* used internally during GC */ - JS_TAG_ASYNC_FUNCTION = -5, /* used internally during GC */ - JS_TAG_VAR_REF = -4, /* used internally during GC */ JS_TAG_MODULE = -3, /* used internally */ JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ JS_TAG_OBJECT = -1, @@ -124,6 +122,11 @@ 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; @@ -182,6 +185,13 @@ static inline int JS_VALUE_GET_NORM_TAG(JSValue v) 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 { @@ -220,6 +230,18 @@ static inline JSValue __JS_NewFloat64(JSContext *ctx, double 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) @@ -283,6 +305,8 @@ static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) 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); @@ -302,6 +326,8 @@ typedef struct JSMallocFunctions { 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); @@ -309,20 +335,22 @@ 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, JSValueConst val); +void *JS_GetRuntimeOpaque(JSRuntime *rt); +void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); +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); -JS_BOOL JS_IsInGCSweep(JSRuntime *rt); JSContext *JS_NewContext(JSRuntime *rt); void JS_FreeContext(JSContext *s); +JSContext *JS_DupContext(JSContext *ctx); void *JS_GetContextOpaque(JSContext *ctx); void JS_SetContextOpaque(JSContext *ctx, void *opaque); JSRuntime *JS_GetRuntime(JSContext *ctx); -void JS_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); +void JS_SetMaxStackSize(JSContext *ctx, size_t stack_size); /* the following functions are used to select the intrinsic object to save memory */ @@ -338,6 +366,14 @@ void JS_AddIntrinsicProxy(JSContext *ctx); void JS_AddIntrinsicMapSet(JSContext *ctx); void JS_AddIntrinsicTypedArrays(JSContext *ctx); void JS_AddIntrinsicPromise(JSContext *ctx); +void JS_AddIntrinsicBigInt(JSContext *ctx); +void JS_AddIntrinsicBigFloat(JSContext *ctx); +void JS_AddIntrinsicBigDecimal(JSContext *ctx); +/* enable operator overloading */ +void JS_AddIntrinsicOperators(JSContext *ctx); +/* enable "use math" */ +void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); + JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); @@ -376,6 +412,8 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); /* atom support */ +#define JS_ATOM_NULL 0 + JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); JSAtom JS_NewAtom(JSContext *ctx, const char *str); JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); @@ -436,13 +474,20 @@ typedef struct 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); + 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 */ @@ -457,7 +502,7 @@ int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) { - return JS_MKVAL(JS_TAG_BOOL, val); + return JS_MKVAL(JS_TAG_BOOL, (val != 0)); } static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) @@ -470,7 +515,28 @@ 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); +static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) +{ + JSValue v; + if (val == (int32_t)val) { + v = JS_NewInt32(ctx, val); + } else { + v = __JS_NewFloat64(ctx, val); + } + return v; +} + +static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) +{ + JSValue v; + if (val <= 0x7fffffff) { + v = JS_NewInt32(ctx, val); + } else { + v = __JS_NewFloat64(ctx, val); + } + return v; +} + JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); @@ -495,7 +561,11 @@ static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) return v; } -JS_BOOL JS_IsNumber(JSValueConst v); +static inline JS_BOOL JS_IsNumber(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); +} static inline JS_BOOL JS_IsInteger(JSValueConst v) { @@ -503,12 +573,24 @@ static inline JS_BOOL JS_IsInteger(JSValueConst v) return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT; } +static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_INT; +} + static inline JS_BOOL JS_IsBigFloat(JSValueConst v) { int tag = JS_VALUE_GET_TAG(v); return tag == JS_TAG_BIG_FLOAT; } +static inline JS_BOOL JS_IsBigDecimal(JSValueConst v) +{ + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_DECIMAL; +} + static inline JS_BOOL JS_IsBool(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; @@ -552,7 +634,6 @@ static inline JS_BOOL JS_IsObject(JSValueConst v) JSValue JS_Throw(JSContext *ctx, JSValue obj); JSValue JS_GetException(JSContext *ctx); JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); -void JS_EnableIsErrorProperty(JSContext *ctx, JS_BOOL enable); void JS_ResetUncatchableError(JSContext *ctx); JSValue JS_NewError(JSContext *ctx); JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); @@ -567,7 +648,7 @@ 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) { + if (--p->ref_count == 0) { __JS_FreeValue(ctx, v); } } @@ -577,7 +658,7 @@ 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) { + if (--p->ref_count == 0) { __JS_FreeValueRT(rt, v); } } @@ -603,14 +684,17 @@ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst 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) +static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) { return JS_ToInt32(ctx, (int32_t*)pres, val); } int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val); int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val); int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val); +/* return an exception if 'val' is a Number */ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); +/* same as JS_ToInt64() but allow BigInt */ +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); JSValue JS_NewString(JSContext *ctx, const char *str); @@ -635,6 +719,7 @@ 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); @@ -647,6 +732,8 @@ static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_ { return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0); } + +JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx); JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, const char *prop); JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, @@ -671,7 +758,7 @@ 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); +JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); #define JS_GPN_STRING_MASK (1 << 0) #define JS_GPN_SYMBOL_MASK (1 << 1) @@ -686,8 +773,6 @@ int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj, JSAtom prop); -JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename); 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, @@ -698,9 +783,13 @@ 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); +/* same as JS_Eval() but with an explicit 'this_obj' parameter */ +JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, + const char *input, size_t input_len, + const char *filename, int eval_flags); JSValue JS_GetGlobalObject(JSContext *ctx); int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, @@ -719,21 +808,51 @@ void JS_SetOpaque(JSValue obj, void *opaque); void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); +/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ +JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename); +#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */ +JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename, int flags); +JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, + JSValueConst replacer, JSValueConst space0); + typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, JSFreeArrayBufferDataFunc *free_func, void *opaque, JS_BOOL is_shared); JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); +int js_get_length64(JSContext *ctx, int64_t *pres, JSValueConst obj); 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); +typedef struct { + void *(*sab_alloc)(void *opaque, size_t size); + void (*sab_free)(void *opaque, void *ptr); + void (*sab_dup)(void *opaque, void *ptr); + void *sab_opaque; +} JSSharedArrayBufferFunctions; +void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, + const JSSharedArrayBufferFunctions *sf); 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); +/* set the [IsHTMLDDA] internal slot */ +void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj); typedef struct JSModuleDef JSModuleDef; @@ -761,20 +880,40 @@ int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *a JS_BOOL JS_IsJobPending(JSRuntime *rt); int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); +JSValue JS_GetCurrentException(JSRuntime *rt); +uint8_t *JS_GetStringFromObject(JSValue obj); /* Object Writer/Reader (currently only used to handle precompiled code) */ -#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ -#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ +#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ +#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ +#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to + encode arbitrary object + graph */ uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, int flags); +uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, + int flags, uint8_t ***psab_tab, size_t *psab_tab_len); + #define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */ #define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */ +#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */ +#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */ JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, int flags); +/* instantiate and evaluate a bytecode function. Only used when + reading a script or module with JS_ReadObject() */ +JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj); /* load the dependencies of the module 'obj'. Useful when JS_ReadObject() returns a module. */ int JS_ResolveModule(JSContext *ctx, JSValueConst obj); +/* only exported for os.Worker() */ +JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels); +/* only exported for os.Worker() */ +JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, + const char *filename); + /* C function definition */ typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ JS_CFUNC_generic, @@ -827,6 +966,8 @@ static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *fun { return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); } +void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst proto); /* C property definition */ @@ -867,24 +1008,25 @@ typedef struct JSCFunctionListEntry { #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_PROP_UNDEFINED 7 #define JS_DEF_OBJECT 8 #define JS_DEF_ALIAS 9 -#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, .u.getset.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, .u.getset.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 } } +/* 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, diff --git a/release.sh b/release.sh index 7ff0272..6dc89fb 100644 --- a/release.sh +++ b/release.sh @@ -6,62 +6,32 @@ set -e version=`cat VERSION` if [ "$1" = "-h" ] ; then - echo "release.sh [all]" + echo "release.sh [release_list]" echo "" - echo "all: build all the archives. Otherwise only build the quickjs source archive." + echo "release_list: extras binary win_binary quickjs" + exit 1 fi -unicode="no" -tests="no" -binary="no" -quickjs="no" +release_list="extras binary win_binary quickjs" -if [ "$1" = "all" ] ; then - unicode="yes" - tests="yes" - binary="yes" - quickjs="yes" -elif [ "$1" = "binary" ] ; then - binary="yes" -else - quickjs="yes" +if [ "$1" != "" ] ; then + release_list="$1" fi #################################################" -# unicode data +# extras -if [ "$unicode" = "yes" ] ; then +if echo $release_list | grep -w -q extras ; then d="quickjs-${version}" -name="quickjs-unicode-data-${version}" +name="quickjs-extras-${version}" outdir="/tmp/${d}" rm -rf $outdir -mkdir -p $outdir $outdir/unicode +mkdir -p $outdir $outdir/unicode $outdir/tests cp unicode/* $outdir/unicode - -( cd /tmp && tar Jcvf /tmp/${name}.tar.xz ${d} ) - -fi - -#################################################" -# all tests - -if [ "$tests" = "yes" ] ; then - -d="quickjs-${version}" -name="quickjs-tests-${version}" -outdir="/tmp/${d}" - -rm -rf $outdir -mkdir -p $outdir $outdir/test262o $outdir/test262 $outdir/tests - -cp -a test262o/test $outdir/test262o - -cp -a test262/test test262/harness $outdir/test262 - cp -a tests/bench-v8 $outdir/tests ( cd /tmp && tar Jcvf /tmp/${name}.tar.xz ${d} ) @@ -69,32 +39,89 @@ cp -a tests/bench-v8 $outdir/tests fi #################################################" -# binary release +# Windows binary release -if [ "$binary" = "yes" ] ; then +if echo $release_list | grep -w -q win_binary ; then -d="quickjs-linux-x86_64-${version}" -name="quickjs-linux-x86_64-${version}" +# win64 + +dlldir=/usr/x86_64-w64-mingw32/sys-root/mingw/bin +cross_prefix="x86_64-w64-mingw32-" +d="quickjs-win-x86_64-${version}" outdir="/tmp/${d}" rm -rf $outdir mkdir -p $outdir -files="qjs qjsbn run-test262 run-test262-bn" +make CONFIG_WIN32=y qjs.exe +cp qjs.exe $outdir +${cross_prefix}strip $outdir/qjs.exe +cp $dlldir/libwinpthread-1.dll $outdir -make -j4 $files +( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) -strip $files -cp $files $outdir +make CONFIG_WIN32=y clean -( cd /tmp/$d && rm -f ../${name}.zip && zip -r ../${name}.zip . ) +# win32 + +dlldir=/usr/i686-w64-mingw32/sys-root/mingw/bin +cross_prefix="i686-w64-mingw32-" +d="quickjs-win-i686-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir + +make clean +make CONFIG_WIN32=y clean + +make CONFIG_WIN32=y CONFIG_M32=y qjs.exe +cp qjs.exe $outdir +${cross_prefix}strip $outdir/qjs.exe +cp $dlldir/libwinpthread-1.dll $outdir + +( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) + +fi + +#################################################" +# Linux binary release + +if echo $release_list | grep -w -q binary ; then + +make clean +make CONFIG_WIN32=y clean +make -j4 qjs run-test262 +make -j4 CONFIG_M32=y qjs32 run-test262-32 +strip qjs run-test262 qjs32 run-test262-32 + +d="quickjs-linux-x86_64-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir + +cp qjs run-test262 $outdir + +( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) + +d="quickjs-linux-i686-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir + +cp qjs32 $outdir/qjs +cp run-test262-32 $outdir/run-test262 + +( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) fi #################################################" # quickjs -if [ "$quickjs" = "yes" ] ; then +if echo $release_list | grep -w -q quickjs ; then make build_doc @@ -112,13 +139,12 @@ cp Makefile VERSION TODO Changelog readme.txt release.sh unicode_download.sh \ 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 \ - bjson.c \ - run-test262.c test262o.conf test262.conf test262bn.conf \ - test262o_errors.txt test262_errors.txt test262bn_errors.txt \ + 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 $outdir/tests +cp tests/*.js tests/*.patch tests/bjson.c $outdir/tests cp examples/*.js examples/*.c $outdir/examples diff --git a/repl.js b/repl.js index b51d2d6..484269e 100644 --- a/repl.js +++ b/repl.js @@ -1,8 +1,8 @@ /* * QuickJS Read Eval Print Loop * - * Copyright (c) 2017-2019 Fabrice Bellard - * Copyright (c) 2017-2019 Charlie Gordon + * 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 @@ -209,6 +209,29 @@ import * as os from "os"; (is_alpha(c) || is_digit(c) || c == '_' || c == '$'); } + function ucs_length(str) { + var len, c, i, str_len = str.length; + len = 0; + /* we never count the trailing surrogate to have the + following property: ucs_length(str) = + ucs_length(str.substring(0, a)) + ucs_length(str.substring(a, + str.length)) for 0 <= a <= str.length */ + for(i = 0; i < str_len; i++) { + c = str.charCodeAt(i); + if (c < 0xdc00 || c >= 0xe000) + len++; + } + return len; + } + + function is_trailing_surrogate(c) { + var d; + if (typeof c !== "string") + return false; + d = c.codePointAt(0); /* can be NaN if empty string */ + return d >= 0xdc00 && d < 0xe000; + } + function is_balanced(a, b) { switch (a + b) { case "()": @@ -235,6 +258,7 @@ import * as os from "os"; std.puts("\x1b[" + ((n != 1) ? n : "") + code); } + /* XXX: handle double-width characters */ function move_cursor(delta) { var i, l; if (delta > 0) { @@ -269,15 +293,16 @@ import * as os from "os"; } function update() { - var i; - + var i, cmd_len; + /* cursor_pos is the position in 16 bit characters inside the + UTF-16 string 'cmd' */ 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); + move_cursor(-ucs_length(last_cmd.substring(0, last_cursor_pos))); if (show_colors) { var str = mexpr ? mexpr + '\n' + cmd : cmd; var start = str.length - cmd.length; @@ -287,8 +312,7 @@ import * as os from "os"; std.puts(cmd); } } - /* Note: assuming no surrogate pairs */ - term_cursor_x = (term_cursor_x + cmd.length) % term_width; + term_cursor_x = (term_cursor_x + ucs_length(cmd)) % term_width; if (term_cursor_x == 0) { /* show the cursor on the next line */ std.puts(" \x08"); @@ -298,7 +322,11 @@ import * as os from "os"; last_cmd = cmd; last_cursor_pos = cmd.length; } - move_cursor(cursor_pos - last_cursor_pos); + if (cursor_pos > last_cursor_pos) { + move_cursor(ucs_length(cmd.substring(last_cursor_pos, cursor_pos))); + } else if (cursor_pos < last_cursor_pos) { + move_cursor(-ucs_length(cmd.substring(cursor_pos, last_cursor_pos))); + } last_cursor_pos = cursor_pos; std.out.flush(); } @@ -333,13 +361,19 @@ import * as os from "os"; } function forward_char() { - if (cursor_pos < cmd.length) + if (cursor_pos < cmd.length) { cursor_pos++; + while (is_trailing_surrogate(cmd.charAt(cursor_pos))) + cursor_pos++; + } } function backward_char() { - if (cursor_pos > 0) + if (cursor_pos > 0) { cursor_pos--; + while (is_trailing_surrogate(cmd.charAt(cursor_pos))) + cursor_pos--; + } } function skip_word_forward(pos) { @@ -419,8 +453,18 @@ import * as os from "os"; } function delete_char_dir(dir) { - var start = cursor_pos - (dir < 0); - var end = start + 1; + var start, end; + + start = cursor_pos; + if (dir < 0) { + start--; + while (is_trailing_surrogate(cmd.charAt(start))) + start--; + } + end = start + 1; + while (is_trailing_surrogate(cmd.charAt(end))) + end++; + if (start >= 0 && start < cmd.length) { if (last_fun === kill_region) { kill_region(start, end, dir); @@ -752,7 +796,7 @@ import * as os from "os"; function readline_print_prompt() { std.puts(prompt); - term_cursor_x = prompt.length % term_width; + term_cursor_x = ucs_length(prompt) % term_width; last_cmd = ""; last_cursor_pos = 0; } @@ -785,7 +829,7 @@ import * as os from "os"; function handle_char(c1) { var c; - c = String.fromCharCode(c1); + c = String.fromCodePoint(c1); switch(readline_state) { case 0: if (c == '\x1b') { /* '^[' - ESC */ @@ -825,7 +869,7 @@ import * as os from "os"; var fun; if (quote_flag) { - if (keys.length === 1) + if (ucs_length(keys) === 1) insert(keys); quote_flag = false; } else if (fun = commands[keys]) { @@ -845,7 +889,7 @@ import * as os from "os"; return; } last_fun = this_fun; - } else if (keys.length === 1 && keys >= ' ') { + } else if (ucs_length(keys) === 1 && keys >= ' ') { insert(keys); last_fun = insert; } else { @@ -860,23 +904,40 @@ import * as os from "os"; 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 */ - if (typeof a === "bigfloat" && eval_mode !== "math") { + 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(); + } + } + 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(); @@ -939,7 +1000,7 @@ import * as os from "os"; function print_rec(a) { var n, i, keys, key, type, s; - type = eval_mode_typeof(a); + type = typeof(a); if (type === "object") { if (a === null) { std.puts(a); @@ -994,10 +1055,14 @@ import * as os from "os"; if (s.length > 79) s = s.substring(0, 75) + "...\""; std.puts(s); - } else if (type === "number" || type === "bigfloat") { + } 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() + "m"); } else if (type === "symbol") { std.puts(String(a)); } else if (type === "function") { @@ -1093,8 +1158,7 @@ import * as os from "os"; param = expr.substring(cmd.length + 1).trim(); if (param === "") { std.puts("Running mode=" + eval_mode + "\n"); - } else if (param === "std" || param === "math" || - param === "bigint") { + } else if (param === "std" || param === "math") { eval_mode = param; } else { std.puts("Invalid mode\n"); @@ -1145,14 +1209,14 @@ import * as os from "os"; "\\t " + sel(show_time) + "toggle timing display\n" + "\\clear clear the terminal\n"); if (has_jscalc) { - std.puts("\\a " + sel(algebraicMode) + "algegraic mode\n" + + 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"); + std.puts("\\mode [std|math] change the running mode (current = " + eval_mode + ")\n"); } } if (!config_numcalc) { @@ -1166,11 +1230,9 @@ import * as os from "os"; 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); + result = std.evalScript(expr, { backtrace_barrier: true }); eval_time = (new Date).getTime() - now; std.puts(colors[styles.result]); print(result); @@ -1202,15 +1264,12 @@ import * as os from "os"; } if (has_bignum) { log2_10 = Math.log(10) / Math.log(2); + prec = 113; + expBits = 15; if (has_jscalc) { - prec = 113; - expBits = 15; eval_mode = "math"; /* XXX: numeric mode should always be the default ? */ g.algebraicMode = config_numcalc; - } else { - prec = 53; - expBits = 11; } } diff --git a/run-test262.c b/run-test262.c index 98f57ed..c860dbb 100644 --- a/run-test262.c +++ b/run-test262.c @@ -1,8 +1,8 @@ /* * ECMA Test 262 Runner for QuickJS * - * Copyright (c) 2017-2018 Fabrice Bellard - * Copyright (c) 2017-2018 Charlie Gordon + * 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 @@ -41,8 +41,6 @@ /* 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" @@ -385,8 +383,11 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val, str = JS_ToCString(ctx, argv[i]); if (!str) return JS_EXCEPTION; - if (!strcmp(str, "Test262:AsyncTestComplete")) + if (!strcmp(str, "Test262:AsyncTestComplete")) { async_done++; + } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) { + async_done = 2; /* force an error */ + } fputs(str, outfile); JS_FreeCString(ctx, str); } @@ -727,23 +728,31 @@ static JSValue js_new_agent(JSContext *ctx) } #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 */ + JSValue ret; + ctx1 = JS_NewContext(JS_GetRuntime(ctx)); if (!ctx1) return JS_ThrowOutOfMemory(ctx); - return add_helpers1(ctx1); + ret = add_helpers1(ctx1); + /* ctx1 has a refcount so it stays alive */ + JS_FreeContext(ctx1); + return ret; +} + +static JSValue js_IsHTMLDDA(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_NULL; } -#endif static JSValue add_helpers1(JSContext *ctx) { JSValue global_obj; - JSValue obj262; + JSValue obj262, obj; global_obj = JS_GetGlobalObject(ctx); @@ -767,12 +776,12 @@ static JSValue add_helpers1(JSContext *ctx) 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 + obj = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0); + JS_SetIsHTMLDDA(ctx, obj); + JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj); JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262)); @@ -1516,10 +1525,6 @@ int run_test_buf(const char *filename, char *harness, namelist_t *ip, add_helpers(ctx); - /* add backtrace if the isError property is present in a thrown - object */ - JS_EnableIsErrorProperty(ctx, TRUE); - for (i = 0; i < ip->count; i++) { if (eval_file(ctx, harness, ip->array[i], JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRIP)) { @@ -1817,10 +1822,6 @@ int run_test262_harness_test(const char *filename, BOOL is_module) add_helpers(ctx); - /* add backtrace if the isError property is present in a thrown - object */ - JS_EnableIsErrorProperty(ctx, TRUE); - buf = load_file(filename, &buf_len); if (is_module) { @@ -1913,6 +1914,7 @@ void help(void) "-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" diff --git a/test262.conf b/test262.conf index a450332..f30a4ac 100644 --- a/test262.conf +++ b/test262.conf @@ -48,16 +48,21 @@ testdir=test262/test # list the features that are included # skipped features are tagged as such to avoid warnings +AggregateError +align-detached-buffer-semantics-with-web-reality +arbitrary-module-namespace-names=skip Array.prototype.flat Array.prototype.flatMap Array.prototype.flatten +Array.prototype.item=skip Array.prototype.values ArrayBuffer arrow-function async-functions async-iteration Atomics -BigInt=skip +Atomics.waitAsync=skip +BigInt caller class class-fields-private @@ -66,9 +71,11 @@ class-methods-private class-static-fields-public class-static-fields-private class-static-methods-private +cleanupSome=skip +coalesce-expression computed-property-names const -cross-realm=skip +cross-realm DataView DataView.prototype.getFloat32 DataView.prototype.getFloat64 @@ -78,26 +85,31 @@ 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 +FinalizationRegistry=skip +FinalizationRegistry.prototype.cleanupSome=skip Float32Array Float64Array +for-in-order for-of generators globalThis hashbang host-gc-required=skip import.meta +Int16Array Int32Array Int8Array -IsHTMLDDA=skip +IsHTMLDDA json-superset +legacy-regexp=skip let +logical-assignment-operators Map new.target numeric-separator-literal @@ -106,8 +118,10 @@ object-spread Object.fromEntries Object.is optional-catch-binding -optional-chaining=skip +optional-chaining +Promise Promise.allSettled +Promise.any Promise.prototype.finally Proxy proxy-missing-checks @@ -117,6 +131,7 @@ Reflect.set Reflect.setPrototypeOf regexp-dotall regexp-lookbehind +regexp-match-indices=skip regexp-named-groups regexp-unicode-property-escapes rest-parameters @@ -126,7 +141,9 @@ string-trimming String.fromCodePoint String.prototype.endsWith String.prototype.includes +String.prototype.item=skip String.prototype.matchAll +String.prototype.replaceAll String.prototype.trimEnd String.prototype.trimStart super @@ -149,14 +166,19 @@ tail-call-optimization=skip template top-level-await=skip TypedArray +TypedArray.prototype.item=skip u180e Uint16Array +Uint32Array Uint8Array Uint8ClampedArray WeakMap WeakRef=skip WeakSet well-formed-json-stringify +__getter__ +__proto__ +__setter__ [exclude] # list excluded tests and directories here @@ -164,11 +186,9 @@ well-formed-json-stringify # intl not supported test262/test/intl402/ -# these builtins are not supported: -test262/test/built-ins/BigInt/ - # 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 diff --git a/test262_errors.txt b/test262_errors.txt index ac2a707..502a650 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -1,2 +1,51 @@ +test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js:20: Test262Error: Expected a ReferenceError but got a ReferenceError +test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js:20: strict mode: Test262Error: Expected a ReferenceError but got a ReferenceError +test262/test/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js:46: SyntaxError: invalid group name +test262/test/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js:46: strict mode: SyntaxError: invalid group name +test262/test/built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer.js:36: Test262Error: throws a TypeError getting a value from the detached buffer Expected a TypeError to be thrown but no exception was thrown at all (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer.js:36: strict mode: Test262Error: throws a TypeError getting a value from the detached buffer Expected a TypeError to be thrown but no exception was thrown at all (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/map/BigInt/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/map/BigInt/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/reduce/BigInt/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/reduce/BigInt/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/reduceRight/BigInt/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/reduceRight/BigInt/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/some/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/some/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer.js:37: Test262Error: (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer.js:37: strict mode: Test262Error: (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js:38: Test262Error: (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js:38: strict mode: Test262Error: (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/BigInt/index-prop-desc.js:21: Test262Error: Expected SameValue(«43», «42») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/BigInt/index-prop-desc.js:21: strict mode: Test262Error: Expected SameValue(«43», «42») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/index-prop-desc.js:22: Test262Error: Expected SameValue(«43», «42») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/index-prop-desc.js:22: strict mode: Test262Error: Expected SameValue(«43», «42») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer-realm.js:36: strict mode: TypeError: out-of-bound numeric index (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js:34: TypeError: cannot convert bigint to number (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js:32: strict mode: TypeError: out-of-bound numeric index (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer-realm.js:36: strict mode: TypeError: out-of-bound numeric index (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer.js:32: strict mode: TypeError: out-of-bound numeric index (Testing with Float64Array.) 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: cannot read property 'c' of undefined +test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: cannot read property '_b' of undefined diff --git a/tests/bjson.c b/tests/bjson.c new file mode 100644 index 0000000..8e52741 --- /dev/null +++ b/tests/bjson.c @@ -0,0 +1,96 @@ +/* + * 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; + int flags; + + 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"); + flags = 0; + if (JS_ToBool(ctx, argv[3])) + flags |= JS_READ_OBJ_REFERENCE; + obj = JS_ReadObject(ctx, buf + pos, len, flags); + return obj; +} + +static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + size_t len; + uint8_t *buf; + JSValue array; + int flags; + + flags = 0; + if (JS_ToBool(ctx, argv[1])) + flags |= JS_WRITE_OBJ_REFERENCE; + buf = JS_WriteObject(ctx, &len, argv[0], flags); + 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", 4, js_bjson_read ), + JS_CFUNC_DEF("write", 2, 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 index 1b19de4..1c5f52d 100644 --- a/tests/microbench.js +++ b/tests/microbench.js @@ -22,8 +22,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ - -"use strict"; +import * as std from "std"; function pad(str, n) { str += ""; @@ -98,13 +97,16 @@ var clocks_per_sec = 1000000; var max_iterations = 100; var clock_threshold = 2000; /* favoring short measuring spans */ var min_n_argument = 1; -var __date_clock; +var get_clock; -if (typeof __date_clock != "function") { +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) { @@ -136,21 +138,18 @@ function bench(f, text) if (f.bench) { ti_n = f(text); } else { - var clock = __date_clock; - if (typeof clock != "function") - clock = Date.now; ti_n = 1000000000; min_ti = clock_threshold / 10; for(i = 0; i < 30; i++) { ti = 1000000000; for (j = 0; j < max_iterations; j++) { - t = clock(); - while ((t1 = clock()) == t) + t = get_clock(); + while ((t1 = get_clock()) == t) continue; nb_its = f(n); if (nb_its < 0) return; // test failure - t1 = clock() - t1; + t1 = get_clock() - t1; if (ti > t1) ti = t1; } @@ -229,6 +228,19 @@ function prop_create(n) return n * 4; } +function prop_delete(n) +{ + var obj, j; + obj = {}; + for(j = 0; j < n; j++) { + obj[j] = 1; + } + for(j = 0; j < n; j++) { + delete obj[j]; + } + return n; +} + function array_read(n) { var tab, len, sum, i, j; @@ -827,9 +839,9 @@ function sort_bench(text) { for (j = 0; j < 100; j++) { arr = new array_type(n); f(arr, n, def); - var t1 = __date_clock(); + var t1 = get_clock(); arr.sort(); - t1 = __date_clock() - t1; + t1 = get_clock() - t1; tx += t1; if (!ti || ti > t1) ti = t1; @@ -867,16 +879,60 @@ function sort_bench(text) { 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) { + f = std.open(filename, "r"); + if (!f) return null; - } str = f.readAsString(); res = JSON.parse(str); f.close(); @@ -902,6 +958,7 @@ function main(argc, argv, g) prop_read, prop_write, prop_create, + prop_delete, array_read, array_write, array_prop_create, @@ -931,6 +988,10 @@ function main(argc, argv, g) //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; diff --git a/tests/test262.patch b/tests/test262.patch index 59f08ec..6576bdc 100644 --- a/tests/test262.patch +++ b/tests/test262.patch @@ -1,8 +1,8 @@ diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js -index 135c16e..b19f2ef 100644 +index 9c1217351e..3c24755558 100644 --- a/harness/atomicsHelper.js +++ b/harness/atomicsHelper.js -@@ -222,10 +222,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) { +@@ -227,10 +227,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) { * } */ $262.agent.timeouts = { @@ -22,11 +22,11 @@ index 135c16e..b19f2ef 100644 /** diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js -index 2abfee3..e7c07b1 100644 +index be7039fda0..7b38abf8df 100644 --- a/harness/regExpUtils.js +++ b/harness/regExpUtils.js -@@ -5,24 +5,27 @@ description: | - Collection of functions used to assert the correctness of RegExp objects. +@@ -6,24 +6,27 @@ description: | + defines: [buildString, testPropertyEscapes, matchValidator] ---*/ +if ($262 && typeof $262.codePointRange === "function") { diff --git a/tests/test_bignum.js b/tests/test_bignum.js index 344aaf9..f4f72a0 100644 --- a/tests/test_bignum.js +++ b/tests/test_bignum.js @@ -1,4 +1,3 @@ -"use math"; "use strict"; function assert(actual, expected, message) { @@ -18,48 +17,104 @@ function assert(actual, expected, message) { (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) +function bigint_pow(a, n) { var r, i; - r = 1; - for(i = 0; i < n; i++) + r = 1n; + for(i = 0n; i < n; i++) r *= a; return r; } -function test_integer() +/* 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; - 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); + test_less(2n, 3n); + test_eq(3n, 3n); - r = 1 << 31; - assert(r, 2147483648, "1 << 31 === 2147483648"); + 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 = 1 << 32; - assert(r, 4294967296, "1 << 32 === 4294967296"); - - r = (1 << 31) < 0; - assert(r, false, "(1 << 31) < 0 === false"); + 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 = Integer[div1]; - divrem = Integer[div1 + "rem"]; + div = BigInt[div1]; + divrem = BigInt[div1 + "rem"]; assert(div(a, b) == q); t = divrem(a, b); assert(t[0] == q); @@ -74,196 +129,198 @@ function test_idiv1(div, a, b, r) test_divrem(div, -a, -b, r[3]); } -function test_idiv() +/* QuickJS BigInt extensions */ +function test_bigint_ext() { - test_idiv1("tdiv", 3, 2, [1, -1, -1, 1]); - test_idiv1("fdiv", 3, 2, [1, -2, -2, 1]); - test_idiv1("cdiv", 3, 2, [2, -1, -1, 2]); - test_idiv1("ediv", 3, 2, [1, -2, -1, 2]); + 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_float() +function test_bigfloat() { var e, a, b, sqrt2; - assert(typeof 1 === "bigint"); - assert(typeof 1.0 === "bigfloat"); - assert(1 == 1.0); - assert(1 !== 1.0); + 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(2, e); - assert(a == BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e)); + a = BigFloat.sqrt(2l, e); + assert(a === BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e)); assert(e.inexact === true); - assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcd); + assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcc908b2fb1366ea95l); b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128); - assert(a == b); -} + assert(a === b); -/* jscalc tests */ + assert(BigFloat.isNaN(BigFloat(NaN))); + assert(BigFloat.isFinite(1l)); + assert(!BigFloat.isFinite(1l/0l)); -function test_modulo() -{ - var i, p, a, b; + assert(BigFloat.abs(-3l) === 3l); + assert(BigFloat.sign(-3l) === -1l); - /* 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; + assert(BigFloat.exp(0.2l) === 1.2214027581601698339210719946396742l); + assert(BigFloat.log(3l) === 1.0986122886681096913952452369225256l); + assert(BigFloat.pow(2.1l, 1.6l) === 3.277561666451861947162828744873745l); - a = Mod(3, 101); - b = Mod(-1, 101); - assert((a + b) == Mod(2, 101)); - assert(a ^ 100 == Mod(1, 101)); + assert(BigFloat.sin(-1l) === -0.841470984807896506652502321630299l); + assert(BigFloat.cos(1l) === 0.5403023058681397174009366074429766l); + assert(BigFloat.tan(0.1l) === 0.10033467208545054505808004578111154l); - p = 2 ^ 607 - 1; /* mersenne prime */ - a = Mod(3, p) ^ (p - 1); - assert(a == Mod(1, p)); + assert(BigFloat.asin(0.3l) === 0.30469265401539750797200296122752915l); + assert(BigFloat.acos(0.4l) === 1.1592794807274085998465837940224159l); + assert(BigFloat.atan(0.7l) === 0.610725964389208616543758876490236l); + assert(BigFloat.atan2(7.1l, -5.1l) === 2.1937053809751415549388104628759813l); + + assert(BigFloat.floor(2.5l) === 2l); + assert(BigFloat.ceil(2.5l) === 3l); + assert(BigFloat.trunc(-2.5l) === -2l); + assert(BigFloat.round(2.5l) === 3l); + + assert(BigFloat.fmod(3l,2l) === 1l); + assert(BigFloat.remainder(3l,2l) === -1l); + + /* string conversion */ + assert((1234.125l).toString(), "1234.125"); + assert((1234.125l).toFixed(2), "1234.13"); + assert((1234.125l).toFixed(2, "down"), "1234.12"); + assert((1234.125l).toExponential(), "1.234125e+3"); + assert((1234.125l).toExponential(5), "1.23413e+3"); + assert((1234.125l).toExponential(5, BigFloatEnv.RNDZ), "1.23412e+3"); + assert((1234.125l).toPrecision(6), "1234.13"); + assert((1234.125l).toPrecision(6, BigFloatEnv.RNDZ), "1234.12"); + + /* string conversion with binary base */ + assert((0x123.438l).toString(16), "123.438"); + assert((0x323.438l).toString(16), "323.438"); + assert((0x723.438l).toString(16), "723.438"); + assert((0xf23.438l).toString(16), "f23.438"); + assert((0x123.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "123.44"); + assert((0x323.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "323.44"); + assert((0x723.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "723.44"); + assert((0xf23.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "f23.44"); + assert((0x0.0000438l).toFixed(6, BigFloatEnv.RNDNA, 16), "0.000044"); + assert((0x1230000000l).toFixed(1, BigFloatEnv.RNDNA, 16), "1230000000.0"); + assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "123.44"); + assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDZ, 16), "123.43"); + assert((0x323.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "323.44"); + assert((0x723.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "723.44"); + assert((-0xf23.438l).toPrecision(5, BigFloatEnv.RNDD, 16), "-f23.44"); + assert((0x123.438l).toExponential(4, BigFloatEnv.RNDNA, 16), "1.2344p+8"); } -function test_polynomial() +function test_bigdecimal() { - var a, b, q, r, t, i; - a = (1 + X) ^ 4; - assert(a == X^4+4*X^3+6*X^2+4*X+1); + assert(1m === 1m); + assert(1m !== 2m); + test_less(1m, 2m); + test_eq(2m, 2m); - 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); + test_less(1, 2m); + test_eq(2, 2m); - a = 1 + 2*X + 3*X^2; - assert(a.apply(0.1) == 1.23); + test_less(1.1, 2m); + test_eq(Math.sqrt(4), 2m); + + test_less(2n, 3m); + test_eq(3n, 3m); + + assert(BigDecimal("1234.1") === 1234.1m); + assert(BigDecimal(" 1234.1") === 1234.1m); + assert(BigDecimal(" 1234.1 ") === 1234.1m); - a = 1-2*X^2+2*X^3; - assert(deriv(a) == (6*X^2-4*X)); - assert(deriv(integ(a)) == a); + assert(BigDecimal(0.1) === 0.1m); + assert(BigDecimal(123) === 123m); + assert(BigDecimal(true) === 1m); - 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); - } + assert(123m + 1m === 124m); + assert(123m - 1m === 122m); + + assert(3.2m * 3m === 9.6m); + assert(10m / 2m === 5m); + assertThrows(RangeError, () => { 10m / 3m } ); + + assert(10m % 3m === 1m); + assert(-10m % 3m === -1m); + + assert(1234.5m ** 3m === 1881365963.625m); + assertThrows(RangeError, () => { 2m ** 3.1m } ); + assertThrows(RangeError, () => { 2m ** -3m } ); + + assert(BigDecimal.sqrt(2m, + { roundingMode: "half-even", + maximumSignificantDigits: 4 }) === 1.414m); + assert(BigDecimal.sqrt(101m, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 10.050m); + assert(BigDecimal.sqrt(0.002m, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 0.045m); + + assert(BigDecimal.round(3.14159m, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 3.142m); + + assert(BigDecimal.add(3.14159m, 0.31212m, + { roundingMode: "half-even", + maximumFractionDigits: 2 }) === 3.45m); + assert(BigDecimal.sub(3.14159m, 0.31212m, + { roundingMode: "down", + maximumFractionDigits: 2 }) === 2.82m); + assert(BigDecimal.mul(3.14159m, 0.31212m, + { roundingMode: "half-even", + maximumFractionDigits: 3 }) === 0.981m); + assert(BigDecimal.mod(3.14159m, 0.31211m, + { roundingMode: "half-even", + maximumFractionDigits: 4 }) === 0.0205m); + assert(BigDecimal.div(20m, 3m, + { roundingMode: "half-even", + maximumSignificantDigits: 3 }) === 6.67m); + assert(BigDecimal.div(20m, 3m, + { roundingMode: "half-even", + maximumFractionDigits: 50 }) === + 6.66666666666666666666666666666666666666666666666667m); + + /* string conversion */ + assert((1234.125m).toString(), "1234.125"); + assert((1234.125m).toFixed(2), "1234.13"); + assert((1234.125m).toFixed(2, "down"), "1234.12"); + assert((1234.125m).toExponential(), "1.234125e+3"); + assert((1234.125m).toExponential(5), "1.23413e+3"); + assert((1234.125m).toExponential(5, "down"), "1.23412e+3"); + assert((1234.125m).toPrecision(6), "1234.13"); + assert((1234.125m).toPrecision(6, "down"), "1234.12"); + assert((-1234.125m).toPrecision(6, "floor"), "-1234.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_idiv(); -test_float(); - -test_modulo(); -test_mod(); -test_polynomial(); -test_poly_mod(); -test_rfunc(); -test_series(); -test_matrix(); -test_trig(); +test_bigint1(); +test_bigint2(); +test_bigint_ext(); +test_bigfloat(); +test_bigdecimal(); diff --git a/tests/test_bjson.js b/tests/test_bjson.js index bd37120..fcbb8e6 100644 --- a/tests/test_bjson.js +++ b/tests/test_bjson.js @@ -1,12 +1,20 @@ -import * as bjson from "../bjson.so"; +import * as bjson from "./bjson.so"; -function assert(b, str) -{ - if (b) { +function assert(actual, expected, message) { + if (arguments.length == 1) + expected = true; + + if (actual === expected) return; - } else { - throw Error("assertion failed: " + str); - } + + 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 toHex(a) @@ -24,6 +32,20 @@ function toHex(a) return s; } +function isArrayLike(a) +{ + return Array.isArray(a) || + (a instanceof Uint8ClampedArray) || + (a instanceof Uint8Array) || + (a instanceof Uint16Array) || + (a instanceof Uint32Array) || + (a instanceof Int8Array) || + (a instanceof Int16Array) || + (a instanceof Int32Array) || + (a instanceof Float32Array) || + (a instanceof Float64Array); +} + function toStr(a) { var s, i, props, prop; @@ -32,7 +54,15 @@ function toStr(a) case "object": if (a === null) return "null"; - if (Array.isArray(a)) { + if (a instanceof Date) { + s = "Date(" + toStr(a.valueOf()) + ")"; + } else if (a instanceof Number) { + s = "Number(" + toStr(a.valueOf()) + ")"; + } else if (a instanceof String) { + s = "String(" + toStr(a.valueOf()) + ")"; + } else if (a instanceof Boolean) { + s = "Boolean(" + toStr(a.valueOf()) + ")"; + } else if (isArrayLike(a)) { s = "["; for(i = 0; i < a.length; i++) { if (i != 0) @@ -85,6 +115,35 @@ function bjson_test(a) } } +/* test multiple references to an object including circular + references */ +function bjson_test_reference() +{ + var array, buf, i, n, array_buffer; + n = 16; + array = []; + for(i = 0; i < n; i++) + array[i] = {}; + array_buffer = new ArrayBuffer(n); + for(i = 0; i < n; i++) { + array[i].next = array[(i + 1) % n]; + array[i].idx = i; + array[i].typed_array = new Uint8Array(array_buffer, i, 1); + } + buf = bjson.write(array, true); + + array = bjson.read(buf, 0, buf.byteLength, true); + + /* check the result */ + for(i = 0; i < n; i++) { + assert(array[i].next, array[(i + 1) % n]); + assert(array[i].idx, i); + assert(array[i].typed_array.buffer, array_buffer); + assert(array[i].typed_array.length, 1); + assert(array[i].typed_array.byteOffset, i); + } +} + function bjson_test_all() { var obj; @@ -104,7 +163,18 @@ function bjson_test_all() BigFloat.MIN_VALUE]); }, 113, 15); } + if (typeof BigDecimal !== "undefined") { + bjson_test([BigDecimal("0"), + BigDecimal("0.8"), BigDecimal("123321312321321e100"), + BigDecimal("-1233213123213214332333223332e100"), + BigDecimal("1.233e-1000")]); + } + bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]); + + bjson_test(new Int32Array([123123, 222111, -32222])); + bjson_test(new Float64Array([123123, 222111.5])); + /* tested with a circular reference */ obj = {}; obj.x = obj; @@ -114,6 +184,8 @@ function bjson_test_all() } catch(e) { assert(e instanceof TypeError); } + + bjson_test_reference(); } bjson_test_all(); diff --git a/tests/test_builtin.js b/tests/test_builtin.js index 2b444df..c044b2c 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -17,6 +17,22 @@ function assert(actual, expected, message) { (message ? " (" + message + ")" : "")); } +function assert_throws(expected_error, func) +{ + var err = false; + try { + func(); + } catch(e) { + err = true; + if (!(e instanceof expected_error)) { + throw Error("unexpected exception type"); + } + } + if (!err) { + throw Error("expected exception"); + } +} + // load more elaborate version of assert if available try { __loadScript("test_assert.js"); } catch(e) {} @@ -39,7 +55,7 @@ function test_function() function constructor1(a) { this.x = a; } - + var r, g; r = my_func.call(null, 1, 2); @@ -48,6 +64,13 @@ function test_function() r = my_func.apply(null, [1, 2]); assert(r, 3, "apply"); + r = (function () { return 1; }).apply(null, undefined); + assert(r, 1); + + assert_throws(TypeError, (function() { + Reflect.apply((function () { return 1; }), null, undefined); + })); + r = new Function("a", "b", "return a + b;"); assert(r(2,3), 5, "function"); @@ -277,6 +300,8 @@ function test_string() assert("aaaa".split("aaaaa", 1), [ "aaaa" ]); assert(eval('"\0"'), "\0"); + + assert("abc".padStart(Infinity, ""), "abc"); } function test_math() @@ -287,6 +312,10 @@ function test_math() assert(Math.ceil(a), 2); assert(Math.imul(0x12345678, 123), -1088058456); assert(Math.fround(0.1), 0.10000000149011612); + assert(Math.hypot() == 0); + assert(Math.hypot(-2) == 2); + assert(Math.hypot(3, 4) == 5); + assert(Math.abs(Math.hypot(3, 4, 5) - 7.0710678118654755) <= 1e-15); } function test_number() @@ -303,6 +332,9 @@ function test_number() 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"); @@ -498,6 +530,10 @@ function test_regexp() a = eval("/\0a/"); assert(a.toString(), "/\0a/"); assert(a.exec("\0a")[0], "\0a"); + + assert(/{1a}/.toString(), "/{1a}/"); + a = /a{1+/.exec("a{11"); + assert(a, ["a{11"] ); } function test_symbol() diff --git a/tests/test_language.js b/tests/test_language.js new file mode 100644 index 0000000..8d13deb --- /dev/null +++ b/tests/test_language.js @@ -0,0 +1,547 @@ +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 assert_throws(expected_error, func) +{ + var err = false; + try { + func(); + } catch(e) { + err = true; + if (!(e instanceof expected_error)) { + throw Error("unexpected exception type"); + } + } + if (!err) { + throw Error("expected exception"); + } +} + +// 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() +{ + var f = function f() { }; + assert(f.prototype.constructor, f, "prototype"); + + var g = function g() { }; + /* QuickJS bug */ + Object.defineProperty(g, "prototype", { writable: false }); + assert(g.prototype.constructor, g, "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"); + + a = "aaa"; + b = "bbb"; + assert(`aaa${a, b}ccc`, "aaabbbccc"); +} + +function test_template_skip() +{ + var a = "Bar"; + var { b = `${a + `a${a}` }baz` } = {}; + assert(b, "BaraBarbaz"); +} + +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; }; +} + +function test_destructuring() +{ + function * g () { return 0; }; + var [x] = g(); + assert(x, void 0); +} + +function test_spread() +{ + var x; + x = [1, 2, ...[3, 4]]; + assert(x.toString(), "1,2,3,4"); + + x = [ ...[ , ] ]; + assert(Object.getOwnPropertyNames(x).toString(), "0,length"); +} + +function test_function_length() +{ + assert( ((a, b = 1, c) => {}).length, 1); + assert( (([a,b]) => {}).length, 1); + assert( (({a,b}) => {}).length, 1); + assert( ((c, [a,b] = 1, d) => {}).length, 1); +} + +function test_argument_scope() +{ + var f; + var c = "global"; + + f = function(a = eval("var arguments")) {}; + assert_throws(SyntaxError, f); + + f = function(a = eval("1"), b = arguments[0]) { return b; }; + assert(f(12), 12); + + f = function(a, b = arguments[0]) { return b; }; + assert(f(12), 12); + + f = function(a, b = () => arguments) { return b; }; + assert(f(12)()[0], 12); + + f = function(a = eval("1"), b = () => arguments) { return b; }; + assert(f(12)()[0], 12); + + (function() { + "use strict"; + f = function(a = this) { return a; }; + assert(f.call(123), 123); + + f = function f(a = f) { return a; }; + assert(f(), f); + + f = function f(a = eval("f")) { return a; }; + assert(f(), f); + })(); + + f = (a = eval("var c = 1"), probe = () => c) => { + var c = 2; + assert(c, 2); + assert(probe(), 1); + } + f(); + + f = (a = eval("var arguments = 1"), probe = () => arguments) => { + var arguments = 2; + assert(arguments, 2); + assert(probe(), 1); + } + f(); + + f = function f(a = eval("var c = 1"), b = c, probe = () => c) { + assert(b, 1); + assert(c, 1); + assert(probe(), 1) + } + f(); + + assert(c, "global"); + f = function f(a, b = c, probe = () => c) { + eval("var c = 1"); + assert(c, 1); + assert(b, "global"); + assert(probe(), "global") + } + f(); + assert(c, "global"); + + f = function f(a = eval("var c = 1"), probe = (d = eval("c")) => d) { + assert(probe(), 1) + } + f(); +} + +function test_function_expr_name() +{ + var f; + + /* non strict mode test : assignment to the function name silently + fails */ + + f = function myfunc() { + myfunc = 1; + return myfunc; + }; + assert(f(), f); + + f = function myfunc() { + myfunc = 1; + (() => { + myfunc = 1; + })(); + return myfunc; + }; + assert(f(), f); + + f = function myfunc() { + eval("myfunc = 1"); + return myfunc; + }; + assert(f(), f); + + /* strict mode test : assignment to the function name raises a + TypeError exception */ + + f = function myfunc() { + "use strict"; + myfunc = 1; + }; + assert_throws(TypeError, f); + + f = function myfunc() { + "use strict"; + (() => { + myfunc = 1; + })(); + }; + assert_throws(TypeError, f); + + f = function myfunc() { + "use strict"; + eval("myfunc = 1"); + }; + assert_throws(TypeError, f); +} + +test_op1(); +test_cvt(); +test_eq(); +test_inc_dec(); +test_op2(); +test_delete(); +test_prototype(); +test_arguments(); +test_class(); +test_template(); +test_template_skip(); +test_object_literal(); +test_regexp_skip(); +test_labels(); +test_destructuring(); +test_spread(); +test_function_length(); +test_argument_scope(); +test_function_expr_name(); diff --git a/tests/test_op_overloading.js b/tests/test_op_overloading.js new file mode 100644 index 0000000..d08a85e --- /dev/null +++ b/tests/test_op_overloading.js @@ -0,0 +1,207 @@ +"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 + ")" : "")); +} + +/* operators overloading with Operators.create() */ +function test_operators_create() { + class Vec2 + { + constructor(x, y) { + this.x = x; + this.y = y; + } + static mul_scalar(p1, a) { + var r = new Vec2(); + r.x = p1.x * a; + r.y = p1.y * a; + return r; + } + toString() { + return "Vec2(" + this.x + "," + this.y + ")"; + } + } + + Vec2.prototype[Symbol.operatorSet] = Operators.create( + { + "+"(p1, p2) { + var r = new Vec2(); + r.x = p1.x + p2.x; + r.y = p1.y + p2.y; + return r; + }, + "-"(p1, p2) { + var r = new Vec2(); + r.x = p1.x - p2.x; + r.y = p1.y - p2.y; + return r; + }, + "=="(a, b) { + return a.x == b.x && a.y == b.y; + }, + "<"(a, b) { + var r; + /* lexicographic order */ + if (a.x == b.x) + r = (a.y < b.y); + else + r = (a.x < b.x); + return r; + }, + "++"(a) { + var r = new Vec2(); + r.x = a.x + 1; + r.y = a.y + 1; + return r; + } + }, + { + left: Number, + "*"(a, b) { + return Vec2.mul_scalar(b, a); + } + }, + { + right: Number, + "*"(a, b) { + return Vec2.mul_scalar(a, b); + } + }); + + var a = new Vec2(1, 2); + var b = new Vec2(3, 4); + var r; + + r = a * 2 + 3 * b; + assert(r.x === 11 && r.y === 16); + assert(a == a, true); + assert(a == b, false); + assert(a != a, false); + assert(a < b, true); + assert(a <= b, true); + assert(b < a, false); + assert(b <= a, false); + assert(a <= a, true); + assert(a >= a, true); + a++; + assert(a.x === 2 && a.y === 3); + r = ++a; + assert(a.x === 3 && a.y === 4); + assert(r === a); +} + +/* operators overloading thru inheritance */ +function test_operators() +{ + var Vec2; + + function mul_scalar(p1, a) { + var r = new Vec2(); + r.x = p1.x * a; + r.y = p1.y * a; + return r; + } + + var vec2_ops = Operators({ + "+"(p1, p2) { + var r = new Vec2(); + r.x = p1.x + p2.x; + r.y = p1.y + p2.y; + return r; + }, + "-"(p1, p2) { + var r = new Vec2(); + r.x = p1.x - p2.x; + r.y = p1.y - p2.y; + return r; + }, + "=="(a, b) { + return a.x == b.x && a.y == b.y; + }, + "<"(a, b) { + var r; + /* lexicographic order */ + if (a.x == b.x) + r = (a.y < b.y); + else + r = (a.x < b.x); + return r; + }, + "++"(a) { + var r = new Vec2(); + r.x = a.x + 1; + r.y = a.y + 1; + return r; + } + }, + { + left: Number, + "*"(a, b) { + return mul_scalar(b, a); + } + }, + { + right: Number, + "*"(a, b) { + return mul_scalar(a, b); + } + }); + + Vec2 = class Vec2 extends vec2_ops + { + constructor(x, y) { + super(); + this.x = x; + this.y = y; + } + toString() { + return "Vec2(" + this.x + "," + this.y + ")"; + } + } + + var a = new Vec2(1, 2); + var b = new Vec2(3, 4); + var r; + + r = a * 2 + 3 * b; + assert(r.x === 11 && r.y === 16); + assert(a == a, true); + assert(a == b, false); + assert(a != a, false); + assert(a < b, true); + assert(a <= b, true); + assert(b < a, false); + assert(b <= a, false); + assert(a <= a, true); + assert(a >= a, true); + a++; + assert(a.x === 2 && a.y === 3); + r = ++a; + assert(a.x === 3 && a.y === 4); + assert(r === a); +} + +function test_default_op() +{ + assert(Object(1) + 2, 3); + assert(Object(1) + true, 2); + assert(-Object(1), -1); +} + +test_operators_create(); +test_operators(); +test_default_op(); diff --git a/tests/test_qjscalc.js b/tests/test_qjscalc.js new file mode 100644 index 0000000..1483466 --- /dev/null +++ b/tests/test_qjscalc.js @@ -0,0 +1,256 @@ +"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"); + + assert(typeof 1 === "number"); + assert(typeof 9007199254740991 === "number"); + assert(typeof 9007199254740992 === "bigint"); +} + +function test_float() +{ + 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_fraction() +{ + assert((1/3 + 1).toString(), "4/3") + assert((2/3)^30, 1073741824/205891132094649); + assert(1/3 < 2/3); + assert(1/3 < 1); + assert(1/3 == 1.0/3); + assert(1.0/3 < 2/3); +} + +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_fraction(); +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 index b5b1bd0..3ea6e34 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -26,6 +26,12 @@ try { std.loadScript("test_assert.js"); } catch(e) {} function test_printf() { assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc"); + assert(std.sprintf("%010d", 123), "0000000123"); + assert(std.sprintf("%x", -2), "fffffffe"); + assert(std.sprintf("%lx", -2), "fffffffffffffffe"); + assert(std.sprintf("%10.1f", 2.1), " 2.1"); + assert(std.sprintf("%*.*f", 10, 2, -2.13), " -2.13"); + assert(std.sprintf("%#lx", 0x7fffffffffffffffn), "0x7fffffffffffffff"); } function test_file1() @@ -106,6 +112,9 @@ function test_popen() f.puts(content); f.close(); + /* test loadFile */ + assert(std.loadFile(fname), content); + /* execute the 'cat' shell command */ f = std.popen("cat " + fname, "r"); str = f.readAsString(); @@ -116,6 +125,20 @@ function test_popen() os.remove(fname); } +function test_ext_json() +{ + var expected, input, obj; + expected = '{"x":false,"y":true,"z2":null,"a":[1,8,160],"s":"str"}'; + input = `{ "x":false, /*comments are allowed */ + "y":true, // also a comment + z2:null, // unquoted property names + "a":[+1,0o10,0xa0,], // plus prefix, octal, hexadecimal + "s":"str",} // trailing comma in objects and arrays + `; + obj = std.parseExtJSON(input); + assert(JSON.stringify(obj), expected); +} + function test_os() { var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path; @@ -142,13 +165,19 @@ function test_os() buf[i] = i; assert(os.write(fd, buf.buffer, 0, buf.length) === buf.length); - assert(os.seek(fd, 0, os.SEEK_SET) === 0); + assert(os.seek(fd, 0, std.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]); + if (typeof BigInt !== "undefined") { + assert(os.seek(fd, BigInt(6), std.SEEK_SET), BigInt(6)); + assert(os.read(fd, buf2.buffer, 0, 1) === 1); + assert(buf[6] == buf2[0]); + } + assert(os.close(fd) === 0); [files, err] = os.readdir(fdir); @@ -205,7 +234,11 @@ function test_os_exec() assert(ret, 1); fds = os.pipe(); - pid = os.exec(["echo", "hello"], { stdout: fds[1], block: false } ); + pid = os.exec(["sh", "-c", "echo $FOO"], { + stdout: fds[1], + block: false, + env: { FOO: "hello" }, + } ); assert(pid >= 0); os.close(fds[1]); /* close the write end (as it is only in the child) */ f = std.fdopen(fds[0], "r"); @@ -245,3 +278,4 @@ test_popen(); test_os(); test_os_exec(); test_timer(); +test_ext_json(); diff --git a/tests/test_worker.js b/tests/test_worker.js new file mode 100644 index 0000000..4b52bf8 --- /dev/null +++ b/tests/test_worker.js @@ -0,0 +1,62 @@ +/* os.Worker API test */ +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 + ")" : "")); +} + +var worker; + +function test_worker() +{ + var counter; + + worker = new os.Worker("./test_worker_module.js"); + + counter = 0; + worker.onmessage = function (e) { + var ev = e.data; +// print("recv", JSON.stringify(ev)); + switch(ev.type) { + case "num": + assert(ev.num, counter); + counter++; + if (counter == 10) { + /* test SharedArrayBuffer modification */ + let sab = new SharedArrayBuffer(10); + let buf = new Uint8Array(sab); + worker.postMessage({ type: "sab", buf: buf }); + } + break; + case "sab_done": + { + let buf = ev.buf; + /* check that the SharedArrayBuffer was modified */ + assert(buf[2], 10); + worker.postMessage({ type: "abort" }); + } + break; + case "done": + /* terminate */ + worker.onmessage = null; + break; + } + }; +} + + +test_worker(); diff --git a/tests/test_worker_module.js b/tests/test_worker_module.js new file mode 100644 index 0000000..c783e1f --- /dev/null +++ b/tests/test_worker_module.js @@ -0,0 +1,31 @@ +/* Worker code for test_worker.js */ +import * as std from "std"; +import * as os from "os"; + +var parent = os.Worker.parent; + +function handle_msg(e) { + var ev = e.data; + // print("child_recv", JSON.stringify(ev)); + switch(ev.type) { + case "abort": + parent.postMessage({ type: "done" }); + break; + case "sab": + /* modify the SharedArrayBuffer */ + ev.buf[2] = 10; + parent.postMessage({ type: "sab_done", buf: ev.buf }); + break; + } +} + +function worker_main() { + var i; + + parent.onmessage = handle_msg; + for(i = 0; i < 10; i++) { + parent.postMessage({ type: "num", num: i }); + } +} + +worker_main(); diff --git a/unicode_download.sh b/unicode_download.sh index a72d9ae..87bb295 100644 --- a/unicode_download.sh +++ b/unicode_download.sh @@ -1,8 +1,8 @@ #!/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" +url="ftp://ftp.unicode.org/Public/13.0.0/ucd" +emoji_url="${url}/emoji/emoji-data.txt" files="CaseFolding.txt DerivedNormalizationProps.txt PropList.txt \ SpecialCasing.txt CompositionExclusions.txt ScriptExtensions.txt \ @@ -11,9 +11,9 @@ PropertyValueAliases.txt" mkdir -p unicode -for f in $files; do - g="${url}/${f}" - wget $g -O unicode/$f -done +#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 index f99908e..f18aaa0 100644 --- a/unicode_gen.c +++ b/unicode_gen.c @@ -296,6 +296,7 @@ void parse_unicode_data(const char *filename) exit(1); } + last_code = 0; for(;;) { if (!get_line(line, sizeof(line), f)) break; diff --git a/unicode_gen_def.h b/unicode_gen_def.h index 08dd8d7..47b0e39 100644 --- a/unicode_gen_def.h +++ b/unicode_gen_def.h @@ -66,6 +66,7 @@ DEF(Caucasian_Albanian, "Aghb") DEF(Chakma, "Cakm") DEF(Cham, "Cham") DEF(Cherokee, "Cher") +DEF(Chorasmian, "Chrs") DEF(Common, "Zyyy") DEF(Coptic, "Copt,Qaac") DEF(Cuneiform, "Xsux") @@ -73,6 +74,7 @@ DEF(Cypriot, "Cprt") DEF(Cyrillic, "Cyrl") DEF(Deseret, "Dsrt") DEF(Devanagari, "Deva") +DEF(Dives_Akuru, "Diak") DEF(Dogra, "Dogr") DEF(Duployan, "Dupl") DEF(Egyptian_Hieroglyphs, "Egyp") @@ -106,6 +108,7 @@ DEF(Kayah_Li, "Kali") DEF(Kharoshthi, "Khar") DEF(Khmer, "Khmr") DEF(Khojki, "Khoj") +DEF(Khitan_Small_Script, "Kits") DEF(Khudawadi, "Sind") DEF(Lao, "Laoo") DEF(Latin, "Latn") @@ -193,6 +196,7 @@ DEF(Ugaritic, "Ugar") DEF(Vai, "Vaii") DEF(Wancho, "Wcho") DEF(Warang_Citi, "Wara") +DEF(Yezidi, "Yezi") DEF(Yi, "Yiii") DEF(Zanabazar_Square, "Zanb") #endif @@ -244,11 +248,11 @@ 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(Emoji_Component, "EComp") +DEF(Emoji_Modifier, "EMod") +DEF(Emoji_Modifier_Base, "EBase") +DEF(Emoji_Presentation, "EPres") +DEF(Extended_Pictographic, "ExtPict") DEF(Default_Ignorable_Code_Point, "DI") DEF(ID_Start, "IDS") DEF(Case_Ignorable, "CI")