From 501bb66666431d091bfccabded8bdd4a1368c615 Mon Sep 17 00:00:00 2001 From: ErosZy Date: Sun, 18 Sep 2022 11:58:50 +0800 Subject: [PATCH 01/15] feat: add qjs/qjsc CMakeLists for dev debug and release --- .gitignore | 9 + CMakeLists.txt | 56 + Changelog | 296 +- LICENSE | 44 +- Makefile | 470 - TODO | 140 +- VERSION | 2 +- doc/jsbignum.texi | 1178 +- doc/quickjs.texi | 2194 +- examples/fib.c | 144 +- examples/fib_module.js | 20 +- examples/hello.js | 2 +- examples/hello_module.js | 12 +- examples/pi_bigdecimal.js | 136 +- examples/pi_bigfloat.js | 132 +- examples/pi_bigint.js | 236 +- examples/point.c | 302 +- examples/test_fib.js | 12 +- examples/test_point.js | 80 +- include/quickjs/cutils.h | 594 +- include/quickjs/libbf.h | 1070 +- include/quickjs/libregexp-opcode.h | 116 +- include/quickjs/libregexp.h | 184 +- include/quickjs/libunicode-table.h | 8736 ++++---- include/quickjs/libunicode.h | 248 +- include/quickjs/list.h | 204 +- include/quickjs/quickjs-atom.h | 546 +- include/quickjs/quickjs-opcode.h | 730 +- include/quickjs/quickjs.h | 1902 +- qjs.c | 1140 +- qjsc.c | 1524 +- qjscalc.js | 5314 ++--- quickjs-libc.c | 7854 ++++---- quickjs-libc.h | 118 +- readme.txt | 2 +- release.sh | 316 +- repl.js | 3132 +-- run-test262.c | 4214 ++-- src/CMakeLists.txt | 54 + src/core/base.h | 236 +- src/core/builtins/js-array.c | 3506 ++-- src/core/builtins/js-array.h | 198 +- src/core/builtins/js-async-function.c | 620 +- src/core/builtins/js-async-function.h | 116 +- src/core/builtins/js-async-generator.c | 880 +- src/core/builtins/js-async-generator.h | 214 +- src/core/builtins/js-atomics.c | 1052 +- src/core/builtins/js-atomics.h | 70 +- src/core/builtins/js-big-num.c | 9272 ++++----- src/core/builtins/js-big-num.h | 246 +- src/core/builtins/js-boolean.c | 134 +- src/core/builtins/js-boolean.h | 70 +- src/core/builtins/js-closures.c | 388 +- src/core/builtins/js-closures.h | 96 +- src/core/builtins/js-date.c | 1938 +- src/core/builtins/js-date.h | 160 +- src/core/builtins/js-function.c | 1288 +- src/core/builtins/js-function.h | 226 +- src/core/builtins/js-generator.c | 374 +- src/core/builtins/js-generator.h | 130 +- src/core/builtins/js-json.c | 1474 +- src/core/builtins/js-json.h | 60 +- src/core/builtins/js-map.c | 1620 +- src/core/builtins/js-map.h | 64 +- src/core/builtins/js-math.c | 476 +- src/core/builtins/js-math.h | 106 +- src/core/builtins/js-number.c | 614 +- src/core/builtins/js-number.h | 126 +- src/core/builtins/js-object.c | 2180 +- src/core/builtins/js-object.h | 158 +- src/core/builtins/js-operator.c | 1624 +- src/core/builtins/js-operator.h | 156 +- src/core/builtins/js-promise.c | 2640 +-- src/core/builtins/js-promise.h | 78 +- src/core/builtins/js-proxy.c | 1976 +- src/core/builtins/js-proxy.h | 154 +- src/core/builtins/js-reflect.c | 352 +- src/core/builtins/js-reflect.h | 94 +- src/core/builtins/js-regexp.c | 2982 +-- src/core/builtins/js-regexp.h | 70 +- src/core/builtins/js-string.c | 2994 +-- src/core/builtins/js-string.h | 180 +- src/core/builtins/js-symbol.c | 252 +- src/core/builtins/js-symbol.h | 88 +- src/core/builtins/js-typed-array.c | 4536 ++--- src/core/builtins/js-typed-array.h | 78 +- src/core/bytecode.c | 4184 ++-- src/core/bytecode.h | 74 +- src/core/convertion.c | 3348 ++-- src/core/convertion.h | 334 +- src/core/exception.c | 512 +- src/core/exception.h | 154 +- src/core/function.c | 5758 +++--- src/core/function.h | 306 +- src/core/gc.c | 1610 +- src/core/gc.h | 240 +- src/core/malloc.c | 482 +- src/core/malloc.h | 116 +- src/core/memory.c | 1058 +- src/core/memory.h | 62 +- src/core/module.c | 2596 +-- src/core/module.h | 168 +- src/core/object.c | 4432 ++-- src/core/object.h | 338 +- src/core/parser.c | 24416 +++++++++++------------ src/core/parser.h | 846 +- src/core/runtime.c | 6116 +++--- src/core/runtime.h | 502 +- src/core/shape.c | 1172 +- src/core/shape.h | 182 +- src/core/string.c | 3260 +-- src/core/string.h | 526 +- src/core/types.h | 1892 +- src/cutils.c | 1262 +- src/libbf.c | 16910 ++++++++-------- src/libregexp.c | 5220 ++--- src/libunicode.c | 3112 +-- test262.conf | 398 +- test262_errors.txt | 35 - test262o.conf | 820 +- test262o_errors.txt | 0 tests/bjson.c | 192 +- tests/microbench.js | 2130 +- tests/test262.patch | 142 +- tests/test_bignum.js | 652 +- tests/test_bjson.js | 382 +- tests/test_builtin.js | 1370 +- tests/test_closure.js | 442 +- tests/test_language.js | 1094 +- tests/test_loop.js | 736 +- tests/test_op_overloading.js | 414 +- tests/test_qjscalc.js | 512 +- tests/test_std.js | 562 +- tests/test_worker.js | 124 +- tests/test_worker_module.js | 62 +- unicode_download.sh | 38 +- unicode_gen.c | 6114 +++--- unicode_gen_def.h | 568 +- 138 files changed, 98699 insertions(+), 99085 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt delete mode 100644 Makefile create mode 100644 src/CMakeLists.txt delete mode 100644 test262_errors.txt delete mode 100644 test262o_errors.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..612b6c654 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +build +bin +.idea +.vscode +lib +repl.c +qjscalc.c +cmake-build-debug +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..e63c7739b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.2) +project(QUICKJS) +set(C_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) +set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/include) +add_subdirectory(src) + +set(COMPILE_FLAGS -Wall -MMD -Wno-array-bounds -Wno-format-truncation) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(COMPILE_FLAGS ${COMPILE_FLAGS} -g) +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(COMPILE_FLAGS ${COMPILE_FLAGS} -O2) +endif() + +if (UNIX) + set(LINK_LIBRARIES m pthread dl) +elseif(WIN32) + set(LINK_LIBRARIES m pthread) +endif() + +add_compile_options(${COMPILE_FLAGS}) +add_compile_definitions( + _GNU_SOURCE + CONFIG_BIGNUM + CONFIG_VERSION=${QUICKJS_VERSION} +) + +set(QJSC_CONFIG -DCONFIG_PREFIX="/usr/local" -DCONFIG_LTO) +set(QJSC_EXE "${EXECUTABLE_OUTPUT_PATH}/qjsc") +if(UNIX OR WIN32) + set(QJS_CONFIG ${QJSC_CONFIG} -DCONFIG_CC="gcc") +else() + set(QJS_CONFIG ${QJSC_CONFIG} -DCONFIG_CC="clang") +endif() + +if(WIN32) + set(QJSC_EXE "${QJSC_EXE}.exe") +endif() + +add_executable(qjsc qjsc.c quickjs-libc.c) +target_link_libraries(qjsc quickjs ${LINK_LIBRARIES}) +target_compile_definitions(qjsc PUBLIC ${QJSC_CONFIG}) + +add_custom_command( + TARGET qjsc POST_BUILD + COMMAND ${QJSC_EXE} -c -o ${PROJECT_SOURCE_DIR}/repl.c -m ${PROJECT_SOURCE_DIR}/repl.js + COMMAND ${QJSC_EXE} -fbignum -c -o ${PROJECT_SOURCE_DIR}/qjscalc.c ${PROJECT_SOURCE_DIR}/qjscalc.js +) + +add_executable(qjs qjs.c quickjs-libc.c repl.c qjscalc.c) +target_link_libraries(qjs quickjs ${LINK_LIBRARIES}) diff --git a/Changelog b/Changelog index c09af91cb..2fe098e30 100644 --- a/Changelog +++ b/Changelog @@ -1,148 +1,148 @@ -2021-03-27: - -- faster Array.prototype.push and Array.prototype.unshift -- added JS_UpdateStackTop() -- fixed Windows console -- misc bug fixes - -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 -- exported JS_ValueToAtom() -- qjsc: added 'qjsc_' prefix to the generated C identifiers -- added cross-compilation support -- misc bug fixes - -2019-09-01: - -- added globalThis -- documented JS_EVAL_FLAG_COMPILE_ONLY -- added import.meta.url and import.meta.main -- added 'debugger' statement -- misc bug fixes - -2019-08-18: - -- added os.realpath, os.getcwd, os.mkdir, os.stat, os.lstat, - os.readlink, os.readdir, os.utimes, std.popen -- module autodetection -- added import.meta -- misc bug fixes - -2019-08-10: - -- added public class fields and private class fields, methods and - accessors (TC39 proposal) -- changed JS_ToCStringLen() prototype -- qjsc: handle '-' in module names and modules with the same filename -- added std.urlGet -- exported JS_GetOwnPropertyNames() and JS_GetOwnProperty() -- exported some bigint C functions -- added support for eshost in run-test262 -- misc bug fixes - -2019-07-28: - -- added dynamic import -- added Promise.allSettled -- added String.prototype.matchAll -- added Object.fromEntries -- reduced number of ticks in await -- added BigInt support in Atomics -- exported JS_NewPromiseCapability() -- misc async function and async generator fixes -- enabled hashbang support by default - -2019-07-21: - -- updated test262 tests -- updated to Unicode version 12.1.0 -- fixed missing Date object in qjsc -- fixed multi-context creation -- misc ES2020 related fixes -- simplified power and division operators in bignum extension -- fixed several crash conditions - -2019-07-09: - -- first public release +2021-03-27: + +- faster Array.prototype.push and Array.prototype.unshift +- added JS_UpdateStackTop() +- fixed Windows console +- misc bug fixes + +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 +- exported JS_ValueToAtom() +- qjsc: added 'qjsc_' prefix to the generated C identifiers +- added cross-compilation support +- misc bug fixes + +2019-09-01: + +- added globalThis +- documented JS_EVAL_FLAG_COMPILE_ONLY +- added import.meta.url and import.meta.main +- added 'debugger' statement +- misc bug fixes + +2019-08-18: + +- added os.realpath, os.getcwd, os.mkdir, os.stat, os.lstat, + os.readlink, os.readdir, os.utimes, std.popen +- module autodetection +- added import.meta +- misc bug fixes + +2019-08-10: + +- added public class fields and private class fields, methods and + accessors (TC39 proposal) +- changed JS_ToCStringLen() prototype +- qjsc: handle '-' in module names and modules with the same filename +- added std.urlGet +- exported JS_GetOwnPropertyNames() and JS_GetOwnProperty() +- exported some bigint C functions +- added support for eshost in run-test262 +- misc bug fixes + +2019-07-28: + +- added dynamic import +- added Promise.allSettled +- added String.prototype.matchAll +- added Object.fromEntries +- reduced number of ticks in await +- added BigInt support in Atomics +- exported JS_NewPromiseCapability() +- misc async function and async generator fixes +- enabled hashbang support by default + +2019-07-21: + +- updated test262 tests +- updated to Unicode version 12.1.0 +- fixed missing Date object in qjsc +- fixed multi-context creation +- misc ES2020 related fixes +- simplified power and division operators in bignum extension +- fixed several crash conditions + +2019-07-09: + +- first public release diff --git a/LICENSE b/LICENSE index 2c8fdebaf..9d05efecb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,22 @@ -QuickJS Javascript Engine - -Copyright (c) 2017-2021 Fabrice Bellard -Copyright (c) 2017-2021 Charlie Gordon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +QuickJS Javascript Engine + +Copyright (c) 2017-2021 Fabrice Bellard +Copyright (c) 2017-2021 Charlie Gordon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile deleted file mode 100644 index 49b1f6fa7..000000000 --- a/Makefile +++ /dev/null @@ -1,470 +0,0 @@ -# -# QuickJS Javascript Engine -# -# Copyright (c) 2017-2021 Fabrice Bellard -# Copyright (c) 2017-2021 Charlie Gordon -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -ifeq ($(shell uname -s),Darwin) -CONFIG_DARWIN=y -endif -# Windows cross compilation from Linux -#CONFIG_WIN32=y -# use link time optimization (smaller and faster executables but slower build) -CONFIG_LTO=y -# consider warnings as errors (for development) -#CONFIG_WERROR=y -# force 32 bit build for some utilities -#CONFIG_M32=y - -ifdef CONFIG_DARWIN -# use clang instead of gcc -CONFIG_CLANG=y -CONFIG_DEFAULT_AR=y -endif - -# installation directory -prefix=/usr/local - -# use the gprof profiler -#CONFIG_PROFILE=y -# use address sanitizer -#CONFIG_ASAN=y -# include the code for BigInt/BigFloat/BigDecimal and math mode -CONFIG_BIGNUM=y - -OBJDIR=.obj - -ifdef CONFIG_WIN32 - ifdef CONFIG_M32 - CROSS_PREFIX=i686-w64-mingw32- - else - CROSS_PREFIX=x86_64-w64-mingw32- - endif - EXE=.exe -else - CROSS_PREFIX= - EXE= -endif -ifdef CONFIG_CLANG - HOST_CC=clang - CC=$(CROSS_PREFIX)clang - CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d - CFLAGS += -Wextra - CFLAGS += -Wno-sign-compare - CFLAGS += -Wno-missing-field-initializers - CFLAGS += -Wundef -Wuninitialized - CFLAGS += -Wunused -Wno-unused-parameter - CFLAGS += -Wwrite-strings - CFLAGS += -Wchar-subscripts -funsigned-char - CFLAGS += -MMD -MF $(OBJDIR)/$(@F).d - ifdef CONFIG_DEFAULT_AR - AR=$(CROSS_PREFIX)ar - else - ifdef CONFIG_LTO - AR=$(CROSS_PREFIX)llvm-ar - else - AR=$(CROSS_PREFIX)ar - endif - endif -else - HOST_CC=gcc - CC=$(CROSS_PREFIX)gcc - CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d - CFLAGS += -Wno-array-bounds -Wno-format-truncation - ifdef CONFIG_LTO - AR=$(CROSS_PREFIX)gcc-ar - else - AR=$(CROSS_PREFIX)ar - endif -endif -STRIP=$(CROSS_PREFIX)strip -ifdef CONFIG_WERROR -CFLAGS+=-Werror -endif -DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\" -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 -CFLAGS_OPT=$(CFLAGS) -O2 -CFLAGS_NOLTO:=$(CFLAGS_OPT) -LDFLAGS=-g -ifdef CONFIG_LTO -CFLAGS_SMALL+=-flto -CFLAGS_OPT+=-flto -LDFLAGS+=-flto -endif -ifdef CONFIG_PROFILE -CFLAGS+=-p -LDFLAGS+=-p -endif -ifdef CONFIG_ASAN -CFLAGS+=-fsanitize=address -fno-omit-frame-pointer -LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -endif -ifdef CONFIG_WIN32 -LDEXPORT= -else -LDEXPORT=-rdynamic -endif - -PROGS=qjs$(EXE) qjsc$(EXE) run-test262 -ifneq ($(CROSS_PREFIX),) -QJSC_CC=gcc -QJSC=./host-qjsc -PROGS+=$(QJSC) -else -QJSC_CC=$(CC) -QJSC=./qjsc$(EXE) -endif -ifndef CONFIG_WIN32 -PROGS+=qjscalc -endif -ifdef CONFIG_M32 -PROGS+=qjs32 qjs32_s -endif -PROGS+=libquickjs.a -ifdef CONFIG_LTO -PROGS+=libquickjs.lto.a -endif - -# examples -ifeq ($(CROSS_PREFIX),) -ifdef CONFIG_ASAN -PROGS+= -else -PROGS+=examples/hello examples/hello_module examples/test_fib -ifndef CONFIG_DARWIN -PROGS+=examples/fib.so examples/point.so -endif -endif -endif - -all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) - -QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.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 - -HOST_LIBS=-lm -ldl -lpthread -LIBS=-lm -ifndef CONFIG_WIN32 -LIBS+=-ldl -lpthread -endif -LIBS+=$(EXTRA_LIBS) - -$(OBJDIR): - mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests - -qjs$(EXE): $(QJS_OBJS) - $(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) - -qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS)) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -ifneq ($(CROSS_PREFIX),) - -$(QJSC): $(OBJDIR)/qjsc.host.o \ - $(patsubst %.o, %.host.o, $(QJS_LIB_OBJS)) - $(HOST_CC) $(LDFLAGS) -o $@ $^ $(HOST_LIBS) - -endif #CROSS_PREFIX - -QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(prefix)\" -ifdef CONFIG_LTO -QJSC_DEFINES+=-DCONFIG_LTO -endif -QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(prefix)\" - -$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES) -$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES) - -qjs32: $(patsubst %.o, %.m32.o, $(QJS_OBJS)) - $(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) - -qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS)) - $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) - @size $@ - -qjscalc: qjs - ln -sf $< $@ - -ifdef CONFIG_LTO -LTOEXT=.lto -else -LTOEXT= -endif - -libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS) - $(AR) rcs $@ $^ - -ifdef CONFIG_LTO -libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS)) - $(AR) rcs $@ $^ -endif # CONFIG_LTO - -repl.c: $(QJSC) repl.js - $(QJSC) -c -o $@ -m repl.js - -qjscalc.c: $(QJSC) qjscalc.js - $(QJSC) -fbignum -c -o $@ qjscalc.js - -ifneq ($(wildcard unicode/UnicodeData.txt),) -$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o \ - $(OBJDIR)/libunicode.nolto.o: libunicode-table.h - -libunicode-table.h: unicode_gen - ./unicode_gen unicode $@ -endif - -run-test262: $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) - $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) - -# object suffix order: nolto, [m32|m32s] - -$(OBJDIR)/%.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_OPT) -c -o $@ $< - -$(OBJDIR)/%.host.o: %.c | $(OBJDIR) - $(HOST_CC) $(CFLAGS_OPT) -c -o $@ $< - -$(OBJDIR)/%.pic.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $< - -$(OBJDIR)/%.nolto.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_NOLTO) -c -o $@ $< - -$(OBJDIR)/%.m32.o: %.c | $(OBJDIR) - $(CC) -m32 $(CFLAGS_OPT) -c -o $@ $< - -$(OBJDIR)/%.m32s.o: %.c | $(OBJDIR) - $(CC) -m32 $(CFLAGS_SMALL) -c -o $@ $< - -$(OBJDIR)/%.debug.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS_DEBUG) -c -o $@ $< - -$(OBJDIR)/%.check.o: %.c | $(OBJDIR) - $(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $< - -regexp_test: libregexp.c libunicode.c cutils.c - $(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS) - -unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c unicode_gen_def.h - $(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o - -clean: - rm -f repl.c qjscalc.c out.c - rm -f *.a *.o *.d *~ 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 qjsc - install -m755 qjs qjsc "$(DESTDIR)$(prefix)/bin" - ln -sf qjs "$(DESTDIR)$(prefix)/bin/qjscalc" - mkdir -p "$(DESTDIR)$(prefix)/lib/quickjs" - install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs" -ifdef CONFIG_LTO - install -m644 libquickjs.lto.a "$(DESTDIR)$(prefix)/lib/quickjs" -endif - mkdir -p "$(DESTDIR)$(prefix)/include/quickjs" - install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(prefix)/include/quickjs" - -############################################################################### -# examples - -# example of static JS compilation -HELLO_SRCS=examples/hello.js -HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ - -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ - -fno-date -fno-module-loader -ifdef CONFIG_BIGNUM -HELLO_OPTS+=-fno-bigint -endif - -hello.c: $(QJSC) $(HELLO_SRCS) - $(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS) - -ifdef CONFIG_M32 -examples/hello: $(OBJDIR)/hello.m32s.o $(patsubst %.o, %.m32s.o, $(QJS_LIB_OBJS)) - $(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -else -examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -endif - -# example of static JS compilation with modules -HELLO_MODULE_SRCS=examples/hello_module.js -HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ - -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ - -fno-date -m -examples/hello_module: $(QJSC) libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS) - $(QJSC) $(HELLO_MODULE_OPTS) -o $@ $(HELLO_MODULE_SRCS) - -# use of an external C module (static compilation) - -test_fib.c: $(QJSC) examples/test_fib.js - $(QJSC) -e -M examples/fib.so,fib -m -o $@ examples/test_fib.js - -examples/test_fib: $(OBJDIR)/test_fib.o $(OBJDIR)/examples/fib.o libquickjs$(LTOEXT).a - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) - -examples/fib.so: $(OBJDIR)/examples/fib.pic.o - $(CC) $(LDFLAGS) -shared -o $@ $^ - -examples/point.so: $(OBJDIR)/examples/point.pic.o - $(CC) $(LDFLAGS) -shared -o $@ $^ - -############################################################################### -# documentation - -DOCS=doc/quickjs.pdf doc/quickjs.html doc/jsbignum.pdf doc/jsbignum.html - -build_doc: $(DOCS) - -clean_doc: - rm -f $(DOCS) - -doc/%.pdf: doc/%.texi - texi2pdf --clean -o $@ -q $< - -doc/%.html.pre: doc/%.texi - makeinfo --html --no-headers --no-split --number-sections -o $@ $< - -doc/%.html: doc/%.html.pre - sed -e 's||\n|' < $< > $@ - -############################################################################### -# tests - -ifndef CONFIG_DARWIN -test: tests/bjson.so examples/point.so -endif -ifdef CONFIG_M32 -test: qjs32 -endif - -test: qjs - ./qjs tests/test_closure.js - ./qjs tests/test_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 - ./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_language.js - ./qjs32 tests/test_builtin.js - ./qjs32 tests/test_loop.js - ./qjs32 tests/test_std.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 tests/microbench.js - -microbench-32: qjs32 - ./qjs32 tests/microbench.js - -# ES5 tests (obsolete) -test2o: run-test262 - time ./run-test262 -m -c test262o.conf - -test2o-32: run-test262-32 - time ./run-test262-32 -m -c test262o.conf - -test2o-update: run-test262 - ./run-test262 -u -c test262o.conf - -# Test262 tests -test2-default: run-test262 - time ./run-test262 -m -c test262.conf - -test2: run-test262 - time ./run-test262 -m -c test262.conf -a - -test2-32: run-test262-32 - time ./run-test262-32 -m -c test262.conf -a - -test2-update: run-test262 - ./run-test262 -u -c test262.conf -a - -test2-check: run-test262 - time ./run-test262 -m -c test262.conf -E -a - -testall: all test microbench test2o test2 - -testall-32: all test-32 microbench-32 test2o-32 test2-32 - -testall-complete: testall testall-32 - -bench-v8: qjs - make -C tests/bench-v8 - ./qjs -d tests/bench-v8/combined.js - -tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o - $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) - --include $(wildcard $(OBJDIR)/*.d) diff --git a/TODO b/TODO index 2a3b3c311..2424070bd 100644 --- a/TODO +++ b/TODO @@ -1,70 +1,70 @@ -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 -- 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 -- improve JS_ComputeMemoryUsage() with more info - -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 -- add heuristic to avoid some cycles in closures -- small String (0-2 charcodes) with immediate storage -- perform static string concatenation at compile time -- optimize string concatenation with ropes or miniropes? -- add implicit numeric strings for Uint32 numbers? -- optimize `s += a + b`, `s += a.b` and similar simple expressions -- ensure string canonical representation and optimise comparisons and hashes? -- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references -- 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 -- convert slow array to fast array when all properties != length are numeric -- optimize destructuring assignments for global and local variables -- implement some form of tail-call-optimization -- optimize OP_apply -- optimize f(...b) - -Test262o: 0/11262 errors, 463 excluded -Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) - -Result: 35/75280 errors, 909 excluded, 585 skipped -Test262 commit: 31126581e7290f9233c29cefd93f66c6ac78f1c9 +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 +- 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 +- improve JS_ComputeMemoryUsage() with more info + +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 +- add heuristic to avoid some cycles in closures +- small String (0-2 charcodes) with immediate storage +- perform static string concatenation at compile time +- optimize string concatenation with ropes or miniropes? +- add implicit numeric strings for Uint32 numbers? +- optimize `s += a + b`, `s += a.b` and similar simple expressions +- ensure string canonical representation and optimise comparisons and hashes? +- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references +- 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 +- convert slow array to fast array when all properties != length are numeric +- optimize destructuring assignments for global and local variables +- implement some form of tail-call-optimization +- optimize OP_apply +- optimize f(...b) + +Test262o: 0/11262 errors, 463 excluded +Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) + +Result: 35/75280 errors, 909 excluded, 585 skipped +Test262 commit: 31126581e7290f9233c29cefd93f66c6ac78f1c9 diff --git a/VERSION b/VERSION index 22ffec184..39c85170c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2021-03-27 +2021-03-27 diff --git a/doc/jsbignum.texi b/doc/jsbignum.texi index 079d92017..177b024ed 100644 --- a/doc/jsbignum.texi +++ b/doc/jsbignum.texi @@ -1,589 +1,589 @@ -\input texinfo - -@iftex -@afourpaper -@headings double -@end iftex - -@titlepage -@afourpaper -@sp 7 -@center @titlefont{Javascript Bignum Extensions} -@sp 3 -@center Version 2020-01-11 -@sp 3 -@center Author: Fabrice Bellard -@end titlepage - -@setfilename jsbignum.info -@settitle Javascript Bignum Extensions - -@contents - -@chapter Introduction - -The Bignum extensions add the following features to the Javascript -language while being 100% backward compatible: - -@itemize - -@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 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 BigFloat and operator overloading. - -@chapter Operator overloading - -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. - -More precisely, the following modifications were made: - -@itemize - -@item @code{with operators from} is not supported. Operator overloading is always enabled. - -@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 - -@chapter BigInt extensions - -A few properties are added to the BigInt object: - -@table @code - -@item tdiv(a, b) -Return @math{trunc(a/b)}. @code{b = 0} raises a RangeError -exception. - -@item fdiv(a, b) -Return @math{\lfloor a/b \rfloor}. @code{b = 0} raises a RangeError -exception. - -@item cdiv(a, b) -Return @math{\lceil a/b \rceil}. @code{b = 0} raises a RangeError -exception. - -@item ediv(a, b) -Return @math{sgn(b) \lfloor a/{|b|} \rfloor} (Euclidian -division). @code{b = 0} raises a RangeError exception. - -@item tdivrem(a, b) -@item fdivrem(a, b) -@item cdivrem(a, b) -@item edivrem(a, b) -Return an array of two elements. The first element is the quotient, -the second is the remainder. The same rounding is done as the -corresponding division operation. - -@item sqrt(a) -Return @math{\lfloor \sqrt(a) \rfloor}. A RangeError exception is -raised if @math{a < 0}. - -@item sqrtrem(a) -Return an array of two elements. The first element is @math{\lfloor -\sqrt{a} \rfloor}. The second element is @math{a-\lfloor \sqrt{a} -\rfloor^2}. A RangeError exception is raised if @math{a < 0}. - -@item floorLog2(a) -Return -1 if @math{a \leq 0} otherwise return @math{\lfloor \log2(a) \rfloor}. - -@item ctz(a) -Return the number of trailing zeros in the two's complement binary representation of a. Return -1 if @math{a=0}. - -@end table - -@chapter BigFloat - -@section Introduction - -This extension adds the @code{BigFloat} primitive type. The -@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} -are supported. The mantissa and exponent can have any bit length with -an implementation specific minimum and maximum. - -@section Floating point rounding - -Each floating point operation operates with infinite precision and -then rounds the result according to the specified floating point -environment (@code{BigFloatEnv} object). The status flags of the -environment are also set according to the result of the operation. - -If no floating point environment is provided, the global floating -point environment is used. - -The rounding mode of the global floating point environment is always -@code{RNDN} (``round to nearest with ties to even'')@footnote{The -rationale is that the rounding mode changes must always be -explicit.}. The status flags of the global environment cannot be -read@footnote{The rationale is to avoid side effects for the built-in -operators.}. The precision of the global environment is -@code{BigFloatEnv.prec}. The number of exponent bits of the global -environment is @code{BigFloatEnv.expBits}. The global environment -subnormal flag is set to @code{true}. - -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 -function can change the global floating point environment for its -callees but not for its caller. - -@section Operators - -The builtin operators are extended so that a BigFloat is returned if -at least one operand is a BigFloat. The computations are always done -with infinite precision and rounded according to the global floating -point environment. - -@code{typeof} applied on a @code{BigFloat} returns @code{bigfloat}. - -BigFloat can be compared with all the other numeric types and the -result follows the expected mathematical relations. - -However, since BigFloat and Number are different types they are never -equal when using the strict comparison operators (e.g. @code{0.0 === -0.0l} is false). - -@section BigFloat literals - -BigFloat literals are floating point numbers with a trailing @code{l} -suffix. BigFloat literals have an infinite precision. They are rounded -according to the global floating point environment when they are -evaluated.@footnote{Base 10 floating point literals cannot usually be -exactly represented as base 2 floating point number. In order to -ensure that the literal is represented accurately with the current -precision, it must be evaluated at runtime.} - -@section Builtin Object changes - -@subsection @code{BigFloat} function - -The @code{BigFloat} function cannot be invoked as a constructor. When -invoked as a function: the parameter is converted to a primitive -type. If the result is a numeric type, it is converted to BigFloat -without rounding. If the result is a string, it is converted to -BigFloat using the precision of the global floating point environment. - -@code{BigFloat} properties: - -@table @code - -@item LN2 -@item PI -Getter. Return the value of the corresponding mathematical constant -rounded to nearest, ties to even with the current global -precision. The constant values are cached for small precisions. - -@item MIN_VALUE -@item MAX_VALUE -@item EPSILON -Getter. Return the minimum, maximum and epsilon @code{BigFloat} values -(same definition as the corresponding @code{Number} constants). - -@item fpRound(a[, e]) -Round the floating point number @code{a} according to the floating -point environment @code{e} or the global environment if @code{e} is -undefined. - -@item parseFloat(a[, radix[, e]]) -Parse the string @code{a} as a floating point number in radix -@code{radix}. The radix is 0 (default) or from 2 to 36. The radix 0 -means radix 10 unless there is a hexadecimal or binary prefix. The -result is rounded according to the floating point environment @code{e} -or the global environment if @code{e} is undefined. - -@item isFinite(a) -Return true if @code{a} is a finite bigfloat. - -@item isNaN(a) -Return true if @code{a} is a NaN bigfloat. - -@item add(a, b[, e]) -@item sub(a, b[, e]) -@item mul(a, b[, e]) -@item div(a, b[, e]) -Perform the specified floating point operation and round the floating -point number @code{a} according to the floating point environment -@code{e} or the global environment if @code{e} is undefined. If -@code{e} is specified, the floating point status flags are updated. - -@item floor(x) -@item ceil(x) -@item round(x) -@item trunc(x) -Round to an integer. No additional rounding is performed. - -@item abs(x) -Return the absolute value of x. No additional rounding is performed. - -@item fmod(x, y[, e]) -@item remainder(x, y[, e]) -Floating point remainder. The quotient is truncated to zero (fmod) or -to the nearest integer with ties to even (remainder). @code{e} is an -optional floating point environment. - -@item sqrt(x[, e]) -Square root. Return a rounded floating point number. @code{e} is an -optional floating point environment. - -@item sin(x[, e]) -@item cos(x[, e]) -@item tan(x[, e]) -@item asin(x[, e]) -@item acos(x[, e]) -@item atan(x[, e]) -@item atan2(x, y[, e]) -@item exp(x[, e]) -@item log(x[, e]) -@item pow(x, y[, e]) -Transcendental operations. Return a rounded floating point -number. @code{e} is an optional floating point environment. - -@end table - -@subsection @code{BigFloat.prototype} - -The following properties are modified: - -@table @code -@item valueOf() -Return the bigfloat primitive value corresponding to @code{this}. - -@item toString(radix) - -For floating point numbers: - -@itemize -@item -If the radix is a power of two, the conversion is done with infinite -precision. -@item -Otherwise, the number is rounded to nearest with ties to even using -the global precision. It is then converted to string using the minimum -number of digits so that its conversion back to a floating point using -the global precision and round to nearest gives the same number. - -@end itemize - -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 and radix can be optionally specified. The radix must be -between 2 and 36. - -@end table - -@subsection @code{BigFloatEnv} constructor - -The @code{BigFloatEnv([p, [,rndMode]]} constructor cannot be invoked as a -function. The floating point environment contains: - -@itemize -@item the mantissa precision in bits - -@item the exponent size in bits assuming an IEEE 754 representation; - -@item the subnormal flag (if true, subnormal floating point numbers can -be generated by the floating point operations). - -@item the rounding mode - -@item the floating point status. The status flags can only be set by the floating point operations. They can be reset with @code{BigFloatEnv.prototype.clearStatus()} or with the various status flag setters. - -@end itemize - -@code{new BigFloatEnv([p, [,rndMode]]} creates a new floating point -environment. The status flags are reset. If no parameter is given the -precision, exponent bits and subnormal flags are copied from the -global floating point environment. Otherwise, the precision is set to -@code{p}, the number of exponent bits is set to @code{expBitsMax} and the -subnormal flags is set to @code{false}. If @code{rndMode} is -@code{undefined}, the rounding mode is set to @code{RNDN}. - -@code{BigFloatEnv} properties: - -@table @code - -@item prec -Getter. Return the precision in bits of the global floating point -environment. The initial value is @code{113}. - -@item expBits -Getter. Return the exponent size in bits of the global floating point -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} -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}. - -@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 113. - -@item expBitsMin -Read-only integer. Return the minimum allowed exponent size in -bits. Must be at least 3. - -@item expBitsMax -Read-only integer. Return the maximum allowed exponent size in -bits. Must be at least 15. - -@item RNDN -Read-only integer. Round to nearest, with ties to even rounding mode. - -@item RNDZ -Read-only integer. Round to zero rounding mode. - -@item RNDD -Read-only integer. Round to -Infinity rounding mode. - -@item RNDU -Read-only integer. Round to +Infinity rounding mode. - -@item RNDNA -Read-only integer. Round to nearest, with ties away from zero rounding mode. - -@item RNDA -Read-only integer. Round away from zero rounding mode. - -@item RNDF@footnote{Could be removed in case a deterministic behavior for floating point operations is required.} -Read-only integer. Faithful rounding mode. The result is -non-deterministically rounded to -Infinity or +Infinity. This rounding -mode usually gives a faster and deterministic running time for the -floating point operations. - -@end table - -@code{BigFloatEnv.prototype} properties: - -@table @code - -@item prec -Getter and setter (Integer). Return or set the precision in bits. - -@item expBits -Getter and setter (Integer). Return or set the exponent size in bits -assuming an IEEE 754 representation. - -@item rndMode -Getter and setter (Integer). Return or set the rounding mode. - -@item subnormal -Getter and setter (Boolean). subnormal flag. It is false when -@code{expBits = expBitsMax}. - -@item clearStatus() -Clear the status flags. - -@item invalidOperation -@item divideByZero -@item overflow -@item underflow -@item inexact -Getter and setter (Boolean). Status flags. - -@end table - -@chapter BigDecimal - -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 + -@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 - -A new @emph{math mode} is enabled with the @code{"use math"} -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. - -The following changes are made to the Javascript semantics: - -@itemize - -@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 modulo operator (@code{%}) returns the Euclidian remainder (always positive) instead of the truncated remainder. - -@item The integer division operator can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}. - -@item The integer power operator with a non zero negative exponent can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}. - -@end itemize - -@bye +\input texinfo + +@iftex +@afourpaper +@headings double +@end iftex + +@titlepage +@afourpaper +@sp 7 +@center @titlefont{Javascript Bignum Extensions} +@sp 3 +@center Version 2020-01-11 +@sp 3 +@center Author: Fabrice Bellard +@end titlepage + +@setfilename jsbignum.info +@settitle Javascript Bignum Extensions + +@contents + +@chapter Introduction + +The Bignum extensions add the following features to the Javascript +language while being 100% backward compatible: + +@itemize + +@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 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 BigFloat and operator overloading. + +@chapter Operator overloading + +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. + +More precisely, the following modifications were made: + +@itemize + +@item @code{with operators from} is not supported. Operator overloading is always enabled. + +@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 + +@chapter BigInt extensions + +A few properties are added to the BigInt object: + +@table @code + +@item tdiv(a, b) +Return @math{trunc(a/b)}. @code{b = 0} raises a RangeError +exception. + +@item fdiv(a, b) +Return @math{\lfloor a/b \rfloor}. @code{b = 0} raises a RangeError +exception. + +@item cdiv(a, b) +Return @math{\lceil a/b \rceil}. @code{b = 0} raises a RangeError +exception. + +@item ediv(a, b) +Return @math{sgn(b) \lfloor a/{|b|} \rfloor} (Euclidian +division). @code{b = 0} raises a RangeError exception. + +@item tdivrem(a, b) +@item fdivrem(a, b) +@item cdivrem(a, b) +@item edivrem(a, b) +Return an array of two elements. The first element is the quotient, +the second is the remainder. The same rounding is done as the +corresponding division operation. + +@item sqrt(a) +Return @math{\lfloor \sqrt(a) \rfloor}. A RangeError exception is +raised if @math{a < 0}. + +@item sqrtrem(a) +Return an array of two elements. The first element is @math{\lfloor +\sqrt{a} \rfloor}. The second element is @math{a-\lfloor \sqrt{a} +\rfloor^2}. A RangeError exception is raised if @math{a < 0}. + +@item floorLog2(a) +Return -1 if @math{a \leq 0} otherwise return @math{\lfloor \log2(a) \rfloor}. + +@item ctz(a) +Return the number of trailing zeros in the two's complement binary representation of a. Return -1 if @math{a=0}. + +@end table + +@chapter BigFloat + +@section Introduction + +This extension adds the @code{BigFloat} primitive type. The +@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} +are supported. The mantissa and exponent can have any bit length with +an implementation specific minimum and maximum. + +@section Floating point rounding + +Each floating point operation operates with infinite precision and +then rounds the result according to the specified floating point +environment (@code{BigFloatEnv} object). The status flags of the +environment are also set according to the result of the operation. + +If no floating point environment is provided, the global floating +point environment is used. + +The rounding mode of the global floating point environment is always +@code{RNDN} (``round to nearest with ties to even'')@footnote{The +rationale is that the rounding mode changes must always be +explicit.}. The status flags of the global environment cannot be +read@footnote{The rationale is to avoid side effects for the built-in +operators.}. The precision of the global environment is +@code{BigFloatEnv.prec}. The number of exponent bits of the global +environment is @code{BigFloatEnv.expBits}. The global environment +subnormal flag is set to @code{true}. + +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 +function can change the global floating point environment for its +callees but not for its caller. + +@section Operators + +The builtin operators are extended so that a BigFloat is returned if +at least one operand is a BigFloat. The computations are always done +with infinite precision and rounded according to the global floating +point environment. + +@code{typeof} applied on a @code{BigFloat} returns @code{bigfloat}. + +BigFloat can be compared with all the other numeric types and the +result follows the expected mathematical relations. + +However, since BigFloat and Number are different types they are never +equal when using the strict comparison operators (e.g. @code{0.0 === +0.0l} is false). + +@section BigFloat literals + +BigFloat literals are floating point numbers with a trailing @code{l} +suffix. BigFloat literals have an infinite precision. They are rounded +according to the global floating point environment when they are +evaluated.@footnote{Base 10 floating point literals cannot usually be +exactly represented as base 2 floating point number. In order to +ensure that the literal is represented accurately with the current +precision, it must be evaluated at runtime.} + +@section Builtin Object changes + +@subsection @code{BigFloat} function + +The @code{BigFloat} function cannot be invoked as a constructor. When +invoked as a function: the parameter is converted to a primitive +type. If the result is a numeric type, it is converted to BigFloat +without rounding. If the result is a string, it is converted to +BigFloat using the precision of the global floating point environment. + +@code{BigFloat} properties: + +@table @code + +@item LN2 +@item PI +Getter. Return the value of the corresponding mathematical constant +rounded to nearest, ties to even with the current global +precision. The constant values are cached for small precisions. + +@item MIN_VALUE +@item MAX_VALUE +@item EPSILON +Getter. Return the minimum, maximum and epsilon @code{BigFloat} values +(same definition as the corresponding @code{Number} constants). + +@item fpRound(a[, e]) +Round the floating point number @code{a} according to the floating +point environment @code{e} or the global environment if @code{e} is +undefined. + +@item parseFloat(a[, radix[, e]]) +Parse the string @code{a} as a floating point number in radix +@code{radix}. The radix is 0 (default) or from 2 to 36. The radix 0 +means radix 10 unless there is a hexadecimal or binary prefix. The +result is rounded according to the floating point environment @code{e} +or the global environment if @code{e} is undefined. + +@item isFinite(a) +Return true if @code{a} is a finite bigfloat. + +@item isNaN(a) +Return true if @code{a} is a NaN bigfloat. + +@item add(a, b[, e]) +@item sub(a, b[, e]) +@item mul(a, b[, e]) +@item div(a, b[, e]) +Perform the specified floating point operation and round the floating +point number @code{a} according to the floating point environment +@code{e} or the global environment if @code{e} is undefined. If +@code{e} is specified, the floating point status flags are updated. + +@item floor(x) +@item ceil(x) +@item round(x) +@item trunc(x) +Round to an integer. No additional rounding is performed. + +@item abs(x) +Return the absolute value of x. No additional rounding is performed. + +@item fmod(x, y[, e]) +@item remainder(x, y[, e]) +Floating point remainder. The quotient is truncated to zero (fmod) or +to the nearest integer with ties to even (remainder). @code{e} is an +optional floating point environment. + +@item sqrt(x[, e]) +Square root. Return a rounded floating point number. @code{e} is an +optional floating point environment. + +@item sin(x[, e]) +@item cos(x[, e]) +@item tan(x[, e]) +@item asin(x[, e]) +@item acos(x[, e]) +@item atan(x[, e]) +@item atan2(x, y[, e]) +@item exp(x[, e]) +@item log(x[, e]) +@item pow(x, y[, e]) +Transcendental operations. Return a rounded floating point +number. @code{e} is an optional floating point environment. + +@end table + +@subsection @code{BigFloat.prototype} + +The following properties are modified: + +@table @code +@item valueOf() +Return the bigfloat primitive value corresponding to @code{this}. + +@item toString(radix) + +For floating point numbers: + +@itemize +@item +If the radix is a power of two, the conversion is done with infinite +precision. +@item +Otherwise, the number is rounded to nearest with ties to even using +the global precision. It is then converted to string using the minimum +number of digits so that its conversion back to a floating point using +the global precision and round to nearest gives the same number. + +@end itemize + +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 and radix can be optionally specified. The radix must be +between 2 and 36. + +@end table + +@subsection @code{BigFloatEnv} constructor + +The @code{BigFloatEnv([p, [,rndMode]]} constructor cannot be invoked as a +function. The floating point environment contains: + +@itemize +@item the mantissa precision in bits + +@item the exponent size in bits assuming an IEEE 754 representation; + +@item the subnormal flag (if true, subnormal floating point numbers can +be generated by the floating point operations). + +@item the rounding mode + +@item the floating point status. The status flags can only be set by the floating point operations. They can be reset with @code{BigFloatEnv.prototype.clearStatus()} or with the various status flag setters. + +@end itemize + +@code{new BigFloatEnv([p, [,rndMode]]} creates a new floating point +environment. The status flags are reset. If no parameter is given the +precision, exponent bits and subnormal flags are copied from the +global floating point environment. Otherwise, the precision is set to +@code{p}, the number of exponent bits is set to @code{expBitsMax} and the +subnormal flags is set to @code{false}. If @code{rndMode} is +@code{undefined}, the rounding mode is set to @code{RNDN}. + +@code{BigFloatEnv} properties: + +@table @code + +@item prec +Getter. Return the precision in bits of the global floating point +environment. The initial value is @code{113}. + +@item expBits +Getter. Return the exponent size in bits of the global floating point +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} +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}. + +@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 113. + +@item expBitsMin +Read-only integer. Return the minimum allowed exponent size in +bits. Must be at least 3. + +@item expBitsMax +Read-only integer. Return the maximum allowed exponent size in +bits. Must be at least 15. + +@item RNDN +Read-only integer. Round to nearest, with ties to even rounding mode. + +@item RNDZ +Read-only integer. Round to zero rounding mode. + +@item RNDD +Read-only integer. Round to -Infinity rounding mode. + +@item RNDU +Read-only integer. Round to +Infinity rounding mode. + +@item RNDNA +Read-only integer. Round to nearest, with ties away from zero rounding mode. + +@item RNDA +Read-only integer. Round away from zero rounding mode. + +@item RNDF@footnote{Could be removed in case a deterministic behavior for floating point operations is required.} +Read-only integer. Faithful rounding mode. The result is +non-deterministically rounded to -Infinity or +Infinity. This rounding +mode usually gives a faster and deterministic running time for the +floating point operations. + +@end table + +@code{BigFloatEnv.prototype} properties: + +@table @code + +@item prec +Getter and setter (Integer). Return or set the precision in bits. + +@item expBits +Getter and setter (Integer). Return or set the exponent size in bits +assuming an IEEE 754 representation. + +@item rndMode +Getter and setter (Integer). Return or set the rounding mode. + +@item subnormal +Getter and setter (Boolean). subnormal flag. It is false when +@code{expBits = expBitsMax}. + +@item clearStatus() +Clear the status flags. + +@item invalidOperation +@item divideByZero +@item overflow +@item underflow +@item inexact +Getter and setter (Boolean). Status flags. + +@end table + +@chapter BigDecimal + +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 + +@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 + +A new @emph{math mode} is enabled with the @code{"use math"} +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. + +The following changes are made to the Javascript semantics: + +@itemize + +@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 modulo operator (@code{%}) returns the Euclidian remainder (always positive) instead of the truncated remainder. + +@item The integer division operator can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}. + +@item The integer power operator with a non zero negative exponent can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}. + +@end itemize + +@bye diff --git a/doc/quickjs.texi b/doc/quickjs.texi index 9eb6354b3..809824d72 100644 --- a/doc/quickjs.texi +++ b/doc/quickjs.texi @@ -1,1097 +1,1097 @@ -\input texinfo - -@iftex -@afourpaper -@headings double -@end iftex - -@titlepage -@afourpaper -@sp 7 -@center @titlefont{QuickJS Javascript Engine} -@sp 3 -@end titlepage - -@setfilename spec.info -@settitle QuickJS Javascript Engine - -@contents - -@chapter Introduction - -QuickJS is a small and embeddable Javascript engine. It supports the -ES2020 specification -@footnote{@url{https://tc39.es/ecma262/}} -including modules, asynchronous generators, proxies and BigInt. - -It supports mathematical extensions such as big decimal float float -numbers (BigDecimal), big binary floating point numbers (BigFloat), -and operator overloading. - -@section Main Features - -@itemize - -@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 ES2020 support including modules, asynchronous -generators and full Annex B support (legacy web compatibility). Many -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 ES2020 features. - -@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: BigDecimal, BigFloat, operator overloading, bigint mode, math mode. - -@item Command line interpreter with contextual colorization and completion implemented in Javascript. - -@item Small built-in standard library with C library wrappers. - -@end itemize - -@chapter Usage - -@section Installation - -A Makefile is provided to compile the engine on Linux or MacOS/X. A -preliminary Windows support is available thru cross compilation on a -Linux host with the MingGW tools. - -Edit the top of the @code{Makefile} if you wish to select specific -options then run @code{make}. - -You can type @code{make install} as root if you wish to install the binaries and support files to -@code{/usr/local} (this is not necessary to use QuickJS). - -@section Quick start - -@code{qjs} is the command line interpreter (Read-Eval-Print Loop). You can pass -Javascript files and/or expressions as arguments to execute them: - -@example -./qjs examples/hello.js -@end example - -@code{qjsc} is the command line compiler: - -@example -./qjsc -o hello examples/hello.js -./hello -@end example - -generates a @code{hello} executable with no external dependency. - -@section Command line options - -@subsection @code{qjs} interpreter - -@verbatim -usage: qjs [options] [file [args]] -@end verbatim - -Options are: -@table @code -@item -h -@item --help -List options. - -@item -e @code{EXPR} -@item --eval @code{EXPR} -Evaluate EXPR. - -@item -i -@item --interactive -Go to interactive mode (it is not the default when files are provided on the command line). - -@item -m -@item --module -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. - -@item -q -@item --quit -just instantiate the interpreter and quit. - -@end table - -@subsection @code{qjsc} compiler - -@verbatim -usage: qjsc [options] [files] -@end verbatim - -Options are: -@table @code -@item -c -Only output bytecode in a C file. The default is to output an executable file. -@item -e -Output @code{main()} and bytecode in a C file. The default is to output an -executable file. -@item -o output -Set the output filename (default = @file{out.c} or @file{a.out}). - -@item -N cname -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. - -@item -x -Byte swapped output (only used for cross compilation). - -@item -flto -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|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{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 -@file{qjscalc.js}. More documentation and a web version are available at -@url{http://numcalc.com}. - -@section Built-in tests - -Run @code{make test} to run the few built-in tests included in the -QuickJS archive. - -@section Test262 (ECMAScript Test Suite) - -A test262 runner is included in the QuickJS archive. The test262 tests -can be installed in the QuickJS source directory with: - -@example -git clone https://github.com/tc39/test262.git test262 -cd test262 -patch -p1 < ../tests/test262.patch -cd .. -@end example - -The patch adds the implementation specific @code{harness} functions -and optimizes the inefficient RegExp character classes and Unicode -property escapes tests (the tests themselves are not modified, only a -slow string initialization function is optimized). - -The tests can be run with -@example -make test2 -@end example - -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 -an existing error is corrected or modified. Use the @code{-u} option -to update the current list of errors (or @code{make test2-update}). - -The file @code{test262_report.txt} contains the logs of all the -tests. It is useful to have a clearer analysis of a particular -error. In case of crash, the last line corresponds to the failing -test. - -Use the syntax @code{./run-test262 -c test262.conf -f filename.js} to -run a single test. Use the syntax @code{./run-test262 -c test262.conf -N} to start testing at test number @code{N}. - -For more information, run @code{./run-test262} to see the command line -options of the test262 runner. - -@code{run-test262} accepts the @code{-N} option to be invoked from -@code{test262-harness}@footnote{@url{https://github.com/bterlson/test262-harness}} -thru @code{eshost}. Unless you want to compare QuickJS with other -engines under the same conditions, we do not recommend to run the -tests this way as it is much slower (typically half an hour instead of -about 100 seconds). - -@chapter Specifications - -@section Language support - -@subsection ES2020 support - -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 Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.} - -@end itemize - -@subsection ECMA402 - -ECMA402 (Internationalization API) is not supported. - -@subsection Extensions - -@itemize - -@item The directive @code{"use strip"} indicates that the debug information (including the source code of the functions) should not be retained to save memory. As @code{"use strict"}, the directive can be global to a script or local to a function. - -@item The first line of a script beginning with @code{#!} is ignored. - -@end itemize - -@subsection Mathematical extensions - -The mathematical extensions are fully backward compatible with -standard Javascript. See @code{jsbignum.pdf} for more information. - -@itemize - -@item @code{BigDecimal} support: arbitrary large floating point numbers in base 10. - -@item @code{BigFloat} support: arbitrary large floating point numbers in base 2. - -@item Operator overloading. - -@item The directive @code{"use bigint"} enables the bigint mode where integers are @code{BigInt} by default. - -@item The directive @code{"use math"} enables the math mode where the division and power operators on integers produce fractions. Floating point literals are @code{BigFloat} by default and integers are @code{BigInt} by default. - -@end itemize - -@section Modules - -ES6 modules are fully supported. The default name resolution is the -following: - -@itemize - -@item Module names with a leading @code{.} or @code{..} are relative -to the current module path. - -@item Module names without a leading @code{.} or @code{..} are system -modules, such as @code{std} or @code{os}. - -@item Module names ending with @code{.so} are native modules using the -QuickJS C API. - -@end itemize - -@section Standard library - -The standard library is included by default in the command line -interpreter. It contains the two modules @code{std} and @code{os} and -a few global objects. - -@subsection Global objects - -@table @code -@item scriptArgs -Provides the command line arguments. The first argument is the script name. -@item print(...args) -Print the arguments separated by spaces and a trailing newline. -@item console.log(...args) -Same as print(). - -@end table - -@subsection @code{std} module - -The @code{std} module provides wrappers to the libc @file{stdlib.h} -and @file{stdio.h} and a few other utilities. - -Available exports: - -@table @code - -@item exit(n) -Exit the process. - -@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 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. - -@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. - -@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. - -@item fdopen(fd, flags, errorObj = undefined) -Open a file from a file handle (wrapper to the libc -@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(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)}. - -@item sprintf(fmt, ...args) -Equivalent to the libc sprintf(). - -@item in -@item out -@item err -Wrappers to the libc file @code{stdin}, @code{stdout}, @code{stderr}. - -@item SEEK_SET -@item SEEK_CUR -@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 -useful in case of specific memory constraints or for testing. - -@item getenv(name) -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 -utility. @code{options} is an optional object containing the following -optional properties: - - @table @code - @item binary - Boolean (default = false). If true, the response is an ArrayBuffer - instead of a string. When a string is returned, the data is assumed - to be UTF-8 encoded. - - @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). @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. 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. - -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_*}). @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 -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{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 -1 if the end of file is reached. - -@item putByte(c) -Write one byte to the file. -@end table - -@subsection @code{os} module - -The @code{os} module provides Operating System specific functions: - -@itemize -@item low level file access -@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 -error code. - -Available exports: - -@table @code -@item open(filename, flags, mode = 0o666) -Open a file. Return a handle or < 0 if error. - -@item O_RDONLY -@item O_WRONLY -@item O_RDWR -@item O_APPEND -@item O_CREAT -@item O_EXCL -@item O_TRUNC -POSIX open flags. - -@item O_TEXT -(Windows specific). Open the file in text mode. The default is binary mode. - -@item close(fd) -Close the file handle @code{fd}. - -@item seek(fd, offset, 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 -ArrayBuffer @code{buffer} at byte position @code{offset}. -Return the number of read bytes or < 0 if error. - -@item write(fd, buffer, offset, length) -Write @code{length} bytes to the file handle @code{fd} from the -ArrayBuffer @code{buffer} at byte position @code{offset}. -Return the number of written bytes or < 0 if error. - -@item isatty(fd) -Return @code{true} is @code{fd} is a TTY (terminal) handle. - -@item ttyGetWinSize(fd) -Return the TTY size as @code{[width, height]} or @code{null} if not available. - -@item ttySetRaw(fd) -Set the TTY in raw mode. - -@item remove(filename) -Remove a file. Return 0 if OK or @code{-errno}. - -@item rename(oldname, newname) -Rename a file. Return 0 if OK or @code{-errno}. - -@item realpath(path) -Return @code{[str, err]} where @code{str} is the canonicalized absolute -pathname of @code{path} and @code{err} the error code. - -@item getcwd() -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 0 if OK or @code{-errno}. - -@item stat(path) -@item lstat(path) - -Return @code{[obj, err]} where @code{obj} is an object containing the -file status of @code{path}. @code{err} is the error code. The -following fields are defined in @code{obj}: dev, ino, mode, nlink, -uid, gid, rdev, size, blocks, atime, mtime, ctime. The times are -specified in milliseconds since 1970. @code{lstat()} is the same as -@code{stat()} excepts that it returns information about the link -itself. - -@item S_IFMT -@item S_IFIFO -@item S_IFCHR -@item S_IFDIR -@item S_IFBLK -@item S_IFREG -@item S_IFSOCK -@item S_IFLNK -@item S_ISGID -@item S_ISUID -Constants to interpret the @code{mode} property returned by -@code{stat()}. They have the same value as in the C system header -@file{sys/stat.h}. - -@item utimes(path, atime, mtime) -Change the access and modification times of the file @code{path}. The -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}. Return 0 if OK or @code{-errno}. - -@item readlink(path) -Return @code{[str, err]} where @code{str} is the link target and @code{err} -the error code. - -@item readdir(path) -Return @code{[array, err]} where @code{array} is an array of strings -containing the filenames of the directory @code{path}. @code{err} is -the error code. - -@item setReadHandler(fd, func) -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 -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 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. Signal handlers can only be defined in the main thread. - -@item SIGINT -@item SIGABRT -@item SIGFPE -@item SIGILL -@item SIGSEGV -@item SIGTERM -POSIX signal numbers. - -@item kill(pid, sig) -Send the signal @code{sig} to the process @code{pid}. - -@item exec(args[, options]) -Execute a process with the arguments @code{args}. @code{options} is an -object containing optional parameters: - - @table @code - @item block - Boolean (default = true). If true, wait until the process is - 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. - - @item usePath - Boolean (default = true). If true, the file is searched in the - @code{PATH} environment variable. - - @item file - String (default = @code{args[0]}). Set the file to be executed. - - @item cwd - String. If present, set the working directory of the new process. - - @item stdin - @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{ret} contains @code{-errno} in case of error. - -@item WNOHANG -Constant for the @code{options} argument of @code{waitpid}. - -@item dup(fd) -@code{dup} Unix system call. - -@item dup2(oldfd, newfd) -@code{dup2} Unix system call. - -@item pipe() -@code{pipe} Unix system call. Return two handles as @code{[read_fd, -write_fd]} or null in case of error. - -@item sleep(delay_ms) -Sleep during @code{delay_ms} milliseconds. - -@item setTimeout(func, delay) -Call the function @code{func} after @code{delay} ms. Return a handle -to the timer. - -@item clearTimeout(handle) -Cancel a timer. - -@item platform -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 - -The C API was designed to be simple and efficient. The C API is -defined in the header @code{quickjs.h}. - -@subsection Runtime and contexts - -@code{JSRuntime} represents a Javascript runtime corresponding to an -object heap. Several runtimes can exist at the same time but they -cannot exchange objects. Inside a given runtime, no multi-threading is -supported. - -@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, similar -to frames of the same origin sharing Javascript objects in a -web browser. - -@subsection JSValue - -@code{JSValue} represents a Javascript value which can be a primitive -type or an object. Reference counting is used, so it is important to -explicitly duplicate (@code{JS_DupValue()}, increment the reference -count) or free (@code{JS_FreeValue()}, decrement the reference count) -JSValues. - -@subsection C functions - -C functions can be created with -@code{JS_NewCFunction()}. @code{JS_SetPropertyFunctionList()} is a -shortcut to easily add functions, setters and getters properties to a -given object. - -Unlike other embedded Javascript engines, there is no implicit stack, -so C functions get their parameters as normal C parameters. As a -general rule, C functions take constant @code{JSValue}s as parameters -(so they don't need to free them) and return a newly allocated (=live) -@code{JSValue}. - -@subsection Exceptions - -Exceptions: most C functions can return a Javascript exception. It -must be explicitly tested and handled by the C code. The specific -@code{JSValue} @code{JS_EXCEPTION} indicates that an exception -occurred. The actual exception object is stored in the -@code{JSContext} and can be retrieved with @code{JS_GetException()}. - -@subsection Script evaluation - -Use @code{JS_Eval()} to evaluate a script or module source. - -If the script or module was compiled to bytecode with @code{qjsc}, it -can be evaluated by calling @code{js_std_eval_binary()}. The advantage -is that no compilation is needed so it is faster and smaller because -the compiler can be removed from the executable if no @code{eval} is -required. - -Note: the bytecode format is linked to a given QuickJS -version. Moreover, no security check is done before its -execution. Hence the bytecode should not be loaded from untrusted -sources. That's why there is no option to output the bytecode to a -binary file in @code{qjsc}. - -@subsection JS Classes - -C opaque data can be attached to a Javascript object. The type of the -C opaque data is determined with the class ID (@code{JSClassID}) of -the object. Hence the first step is to register a new class ID and JS -class (@code{JS_NewClassID()}, @code{JS_NewClass()}). Then you can -create objects of this class with @code{JS_NewObjectClass()} and get or -set the C opaque point with -@code{JS_GetOpaque()}/@code{JS_SetOpaque()}. - -When defining a new JS class, it is possible to declare a finalizer -which is called when the object is destroyed. The finalizer should be -used to release C resources. It is invalid to execute JS code from -it. A @code{gc_mark} method can be provided so that the cycle removal -algorithm can find the other objects referenced by this object. Other -methods are available to define exotic object behaviors. - -The Class ID are globally allocated (i.e. for all runtimes). The -JSClass are allocated per @code{JSRuntime}. @code{JS_SetClassProto()} -is used to define a prototype for a given class in a given -JSContext. @code{JS_NewObjectClass()} sets this prototype in the -created object. - -Examples are available in @file{quickjs-libc.c}. - -@subsection C Modules - -Native ES6 modules are supported and can be dynamically or statically -linked. Look at the @file{test_bjson} and @file{bjson.so} -examples. The standard library @file{quickjs-libc.c} is also a good example -of a native module. - -@subsection Memory handling - -Use @code{JS_SetMemoryLimit()} to set a global memory allocation limit -to a given JSRuntime. - -Custom memory allocation functions can be provided with -@code{JS_NewRuntime2()}. - -The maximum system stack size can be set with @code{JS_SetMaxStackSize()}. - -@subsection Execution timeout and interrupts - -Use @code{JS_SetInterruptHandler()} to set a callback which is -regularly called by the engine when it is executing code. This -callback can be used to implement an execution timeout. - -It is used by the command line interpreter to implement a -@code{Ctrl-C} handler. - -@chapter Internals - -@section Bytecode - -The compiler generates bytecode directly with no intermediate -representation such as a parse tree, hence it is very fast. Several -optimizations passes are done over the generated bytecode. - -A stack-based bytecode was chosen because it is simple and generates -compact code. - -For each function, the maximum stack size is computed at compile time so that -no runtime stack overflow tests are needed. - -A separate compressed line number table is maintained for the debug -information. - -Access to closure variables is optimized and is almost as fast as local -variables. - -Direct @code{eval} in strict mode is optimized. - -@section Executable generation - -@subsection @code{qjsc} compiler - -The @code{qjsc} compiler generates C sources from Javascript files. By -default the C sources are compiled with the system compiler -(@code{gcc} or @code{clang}). - -The generated C source contains the bytecode of the compiled functions -or modules. If a full complete executable is needed, it also -contains a @code{main()} function with the necessary C code to initialize the -Javascript engine and to load and execute the compiled functions and -modules. - -Javascript code can be mixed with C modules. - -In order to have smaller executables, specific Javascript features can -be disabled, in particular @code{eval} or the regular expressions. The -code removal relies on the Link Time Optimization of the system -compiler. - -@subsection Binary JSON - -@code{qjsc} works by compiling scripts or modules and then serializing -them to a binary format. A subset of this format (without functions or -modules) can be used as binary JSON. The example @file{test_bjson.js} -shows how to use it. - -Warning: the binary JSON format may change without notice, so it -should not be used to store persistent data. The @file{test_bjson.js} -example is only used to test the binary object format functions. - -@section Runtime - -@subsection Strings - -Strings are stored either as an 8 bit or a 16 bit array of -characters. Hence random access to characters is always fast. - -The C API provides functions to convert Javascript Strings to C UTF-8 encoded -strings. The most common case where the Javascript string contains -only ASCII characters involves no copying. - -@subsection Objects - -The object shapes (object prototype, property names and flags) are shared -between objects to save memory. - -Arrays with no holes (except at the end of the array) are optimized. - -TypedArray accesses are optimized. - -@subsection Atoms - -Object property names and some strings are stored as Atoms (unique -strings) to save memory and allow fast comparison. Atoms are -represented as a 32 bit integer. Half of the atom range is reserved for -immediate integer literals from @math{0} to @math{2^{31}-1}. - -@subsection Numbers - -Numbers are represented either as 32-bit signed integers or 64-bit IEEE-754 -floating point values. Most operations have fast paths for the 32-bit -integer case. - -@subsection Garbage collection - -Reference counting is used to free objects automatically and -deterministically. A separate cycle removal pass is done when the allocated -memory becomes too large. The cycle removal algorithm only uses the -reference counts and the object content, so no explicit garbage -collection roots need to be manipulated in the C code. - -@subsection JSValue - -It is a Javascript value which can be a primitive type (such as -Number, String, ...) or an Object. NaN boxing is used in the 32-bit version -to store 64-bit floating point numbers. The representation is -optimized so that 32-bit integers and reference counted values can be -efficiently tested. - -In 64-bit code, JSValue are 128-bit large and no NaN boxing is used. The -rationale is that in 64-bit code memory usage is less critical. - -In both cases (32 or 64 bits), JSValue exactly fits two CPU registers, -so it can be efficiently returned by C functions. - -@subsection Function call - -The engine is optimized so that function calls are fast. The system -stack holds the Javascript parameters and local variables. - -@section RegExp - -A specific regular expression engine was developed. It is both small -and efficient and supports all the ES2020 features including the -Unicode properties. As the Javascript compiler, it directly generates -bytecode without a parse tree. - -Backtracking with an explicit stack is used so that there is no -recursion on the system stack. Simple quantifiers are specifically -optimized to avoid recursions. - -Infinite recursions coming from quantifiers with empty terms are -avoided. - -The full regexp library weights about 15 KiB (x86 code), excluding the -Unicode library. - -@section Unicode - -A specific Unicode library was developed so that there is no -dependency on an external large Unicode library such as ICU. All the -Unicode tables are compressed while keeping a reasonable access -speed. - -The library supports case conversion, Unicode normalization, Unicode -script queries, Unicode general category queries and all Unicode -binary properties. - -The full Unicode library weights about 45 KiB (x86 code). - -@section BigInt, BigFloat, BigDecimal - -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. - -@chapter License - -QuickJS is released under the MIT license. - -Unless otherwise specified, the QuickJS sources are copyright Fabrice -Bellard and Charlie Gordon. - -@bye +\input texinfo + +@iftex +@afourpaper +@headings double +@end iftex + +@titlepage +@afourpaper +@sp 7 +@center @titlefont{QuickJS Javascript Engine} +@sp 3 +@end titlepage + +@setfilename spec.info +@settitle QuickJS Javascript Engine + +@contents + +@chapter Introduction + +QuickJS is a small and embeddable Javascript engine. It supports the +ES2020 specification +@footnote{@url{https://tc39.es/ecma262/}} +including modules, asynchronous generators, proxies and BigInt. + +It supports mathematical extensions such as big decimal float float +numbers (BigDecimal), big binary floating point numbers (BigFloat), +and operator overloading. + +@section Main Features + +@itemize + +@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 ES2020 support including modules, asynchronous +generators and full Annex B support (legacy web compatibility). Many +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 ES2020 features. + +@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: BigDecimal, BigFloat, operator overloading, bigint mode, math mode. + +@item Command line interpreter with contextual colorization and completion implemented in Javascript. + +@item Small built-in standard library with C library wrappers. + +@end itemize + +@chapter Usage + +@section Installation + +A Makefile is provided to compile the engine on Linux or MacOS/X. A +preliminary Windows support is available thru cross compilation on a +Linux host with the MingGW tools. + +Edit the top of the @code{Makefile} if you wish to select specific +options then run @code{make}. + +You can type @code{make install} as root if you wish to install the binaries and support files to +@code{/usr/local} (this is not necessary to use QuickJS). + +@section Quick start + +@code{qjs} is the command line interpreter (Read-Eval-Print Loop). You can pass +Javascript files and/or expressions as arguments to execute them: + +@example +./qjs examples/hello.js +@end example + +@code{qjsc} is the command line compiler: + +@example +./qjsc -o hello examples/hello.js +./hello +@end example + +generates a @code{hello} executable with no external dependency. + +@section Command line options + +@subsection @code{qjs} interpreter + +@verbatim +usage: qjs [options] [file [args]] +@end verbatim + +Options are: +@table @code +@item -h +@item --help +List options. + +@item -e @code{EXPR} +@item --eval @code{EXPR} +Evaluate EXPR. + +@item -i +@item --interactive +Go to interactive mode (it is not the default when files are provided on the command line). + +@item -m +@item --module +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. + +@item -q +@item --quit +just instantiate the interpreter and quit. + +@end table + +@subsection @code{qjsc} compiler + +@verbatim +usage: qjsc [options] [files] +@end verbatim + +Options are: +@table @code +@item -c +Only output bytecode in a C file. The default is to output an executable file. +@item -e +Output @code{main()} and bytecode in a C file. The default is to output an +executable file. +@item -o output +Set the output filename (default = @file{out.c} or @file{a.out}). + +@item -N cname +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. + +@item -x +Byte swapped output (only used for cross compilation). + +@item -flto +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|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{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 +@file{qjscalc.js}. More documentation and a web version are available at +@url{http://numcalc.com}. + +@section Built-in tests + +Run @code{make test} to run the few built-in tests included in the +QuickJS archive. + +@section Test262 (ECMAScript Test Suite) + +A test262 runner is included in the QuickJS archive. The test262 tests +can be installed in the QuickJS source directory with: + +@example +git clone https://github.com/tc39/test262.git test262 +cd test262 +patch -p1 < ../tests/test262.patch +cd .. +@end example + +The patch adds the implementation specific @code{harness} functions +and optimizes the inefficient RegExp character classes and Unicode +property escapes tests (the tests themselves are not modified, only a +slow string initialization function is optimized). + +The tests can be run with +@example +make test2 +@end example + +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 +an existing error is corrected or modified. Use the @code{-u} option +to update the current list of errors (or @code{make test2-update}). + +The file @code{test262_report.txt} contains the logs of all the +tests. It is useful to have a clearer analysis of a particular +error. In case of crash, the last line corresponds to the failing +test. + +Use the syntax @code{./run-test262 -c test262.conf -f filename.js} to +run a single test. Use the syntax @code{./run-test262 -c test262.conf +N} to start testing at test number @code{N}. + +For more information, run @code{./run-test262} to see the command line +options of the test262 runner. + +@code{run-test262} accepts the @code{-N} option to be invoked from +@code{test262-harness}@footnote{@url{https://github.com/bterlson/test262-harness}} +thru @code{eshost}. Unless you want to compare QuickJS with other +engines under the same conditions, we do not recommend to run the +tests this way as it is much slower (typically half an hour instead of +about 100 seconds). + +@chapter Specifications + +@section Language support + +@subsection ES2020 support + +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 Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.} + +@end itemize + +@subsection ECMA402 + +ECMA402 (Internationalization API) is not supported. + +@subsection Extensions + +@itemize + +@item The directive @code{"use strip"} indicates that the debug information (including the source code of the functions) should not be retained to save memory. As @code{"use strict"}, the directive can be global to a script or local to a function. + +@item The first line of a script beginning with @code{#!} is ignored. + +@end itemize + +@subsection Mathematical extensions + +The mathematical extensions are fully backward compatible with +standard Javascript. See @code{jsbignum.pdf} for more information. + +@itemize + +@item @code{BigDecimal} support: arbitrary large floating point numbers in base 10. + +@item @code{BigFloat} support: arbitrary large floating point numbers in base 2. + +@item Operator overloading. + +@item The directive @code{"use bigint"} enables the bigint mode where integers are @code{BigInt} by default. + +@item The directive @code{"use math"} enables the math mode where the division and power operators on integers produce fractions. Floating point literals are @code{BigFloat} by default and integers are @code{BigInt} by default. + +@end itemize + +@section Modules + +ES6 modules are fully supported. The default name resolution is the +following: + +@itemize + +@item Module names with a leading @code{.} or @code{..} are relative +to the current module path. + +@item Module names without a leading @code{.} or @code{..} are system +modules, such as @code{std} or @code{os}. + +@item Module names ending with @code{.so} are native modules using the +QuickJS C API. + +@end itemize + +@section Standard library + +The standard library is included by default in the command line +interpreter. It contains the two modules @code{std} and @code{os} and +a few global objects. + +@subsection Global objects + +@table @code +@item scriptArgs +Provides the command line arguments. The first argument is the script name. +@item print(...args) +Print the arguments separated by spaces and a trailing newline. +@item console.log(...args) +Same as print(). + +@end table + +@subsection @code{std} module + +The @code{std} module provides wrappers to the libc @file{stdlib.h} +and @file{stdio.h} and a few other utilities. + +Available exports: + +@table @code + +@item exit(n) +Exit the process. + +@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 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. + +@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. + +@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. + +@item fdopen(fd, flags, errorObj = undefined) +Open a file from a file handle (wrapper to the libc +@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(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)}. + +@item sprintf(fmt, ...args) +Equivalent to the libc sprintf(). + +@item in +@item out +@item err +Wrappers to the libc file @code{stdin}, @code{stdout}, @code{stderr}. + +@item SEEK_SET +@item SEEK_CUR +@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 +useful in case of specific memory constraints or for testing. + +@item getenv(name) +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 +utility. @code{options} is an optional object containing the following +optional properties: + + @table @code + @item binary + Boolean (default = false). If true, the response is an ArrayBuffer + instead of a string. When a string is returned, the data is assumed + to be UTF-8 encoded. + + @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). @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. 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. + +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_*}). @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 +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{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 -1 if the end of file is reached. + +@item putByte(c) +Write one byte to the file. +@end table + +@subsection @code{os} module + +The @code{os} module provides Operating System specific functions: + +@itemize +@item low level file access +@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 +error code. + +Available exports: + +@table @code +@item open(filename, flags, mode = 0o666) +Open a file. Return a handle or < 0 if error. + +@item O_RDONLY +@item O_WRONLY +@item O_RDWR +@item O_APPEND +@item O_CREAT +@item O_EXCL +@item O_TRUNC +POSIX open flags. + +@item O_TEXT +(Windows specific). Open the file in text mode. The default is binary mode. + +@item close(fd) +Close the file handle @code{fd}. + +@item seek(fd, offset, 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 +ArrayBuffer @code{buffer} at byte position @code{offset}. +Return the number of read bytes or < 0 if error. + +@item write(fd, buffer, offset, length) +Write @code{length} bytes to the file handle @code{fd} from the +ArrayBuffer @code{buffer} at byte position @code{offset}. +Return the number of written bytes or < 0 if error. + +@item isatty(fd) +Return @code{true} is @code{fd} is a TTY (terminal) handle. + +@item ttyGetWinSize(fd) +Return the TTY size as @code{[width, height]} or @code{null} if not available. + +@item ttySetRaw(fd) +Set the TTY in raw mode. + +@item remove(filename) +Remove a file. Return 0 if OK or @code{-errno}. + +@item rename(oldname, newname) +Rename a file. Return 0 if OK or @code{-errno}. + +@item realpath(path) +Return @code{[str, err]} where @code{str} is the canonicalized absolute +pathname of @code{path} and @code{err} the error code. + +@item getcwd() +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 0 if OK or @code{-errno}. + +@item stat(path) +@item lstat(path) + +Return @code{[obj, err]} where @code{obj} is an object containing the +file status of @code{path}. @code{err} is the error code. The +following fields are defined in @code{obj}: dev, ino, mode, nlink, +uid, gid, rdev, size, blocks, atime, mtime, ctime. The times are +specified in milliseconds since 1970. @code{lstat()} is the same as +@code{stat()} excepts that it returns information about the link +itself. + +@item S_IFMT +@item S_IFIFO +@item S_IFCHR +@item S_IFDIR +@item S_IFBLK +@item S_IFREG +@item S_IFSOCK +@item S_IFLNK +@item S_ISGID +@item S_ISUID +Constants to interpret the @code{mode} property returned by +@code{stat()}. They have the same value as in the C system header +@file{sys/stat.h}. + +@item utimes(path, atime, mtime) +Change the access and modification times of the file @code{path}. The +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}. Return 0 if OK or @code{-errno}. + +@item readlink(path) +Return @code{[str, err]} where @code{str} is the link target and @code{err} +the error code. + +@item readdir(path) +Return @code{[array, err]} where @code{array} is an array of strings +containing the filenames of the directory @code{path}. @code{err} is +the error code. + +@item setReadHandler(fd, func) +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 +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 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. Signal handlers can only be defined in the main thread. + +@item SIGINT +@item SIGABRT +@item SIGFPE +@item SIGILL +@item SIGSEGV +@item SIGTERM +POSIX signal numbers. + +@item kill(pid, sig) +Send the signal @code{sig} to the process @code{pid}. + +@item exec(args[, options]) +Execute a process with the arguments @code{args}. @code{options} is an +object containing optional parameters: + + @table @code + @item block + Boolean (default = true). If true, wait until the process is + 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. + + @item usePath + Boolean (default = true). If true, the file is searched in the + @code{PATH} environment variable. + + @item file + String (default = @code{args[0]}). Set the file to be executed. + + @item cwd + String. If present, set the working directory of the new process. + + @item stdin + @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{ret} contains @code{-errno} in case of error. + +@item WNOHANG +Constant for the @code{options} argument of @code{waitpid}. + +@item dup(fd) +@code{dup} Unix system call. + +@item dup2(oldfd, newfd) +@code{dup2} Unix system call. + +@item pipe() +@code{pipe} Unix system call. Return two handles as @code{[read_fd, +write_fd]} or null in case of error. + +@item sleep(delay_ms) +Sleep during @code{delay_ms} milliseconds. + +@item setTimeout(func, delay) +Call the function @code{func} after @code{delay} ms. Return a handle +to the timer. + +@item clearTimeout(handle) +Cancel a timer. + +@item platform +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 + +The C API was designed to be simple and efficient. The C API is +defined in the header @code{quickjs.h}. + +@subsection Runtime and contexts + +@code{JSRuntime} represents a Javascript runtime corresponding to an +object heap. Several runtimes can exist at the same time but they +cannot exchange objects. Inside a given runtime, no multi-threading is +supported. + +@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, similar +to frames of the same origin sharing Javascript objects in a +web browser. + +@subsection JSValue + +@code{JSValue} represents a Javascript value which can be a primitive +type or an object. Reference counting is used, so it is important to +explicitly duplicate (@code{JS_DupValue()}, increment the reference +count) or free (@code{JS_FreeValue()}, decrement the reference count) +JSValues. + +@subsection C functions + +C functions can be created with +@code{JS_NewCFunction()}. @code{JS_SetPropertyFunctionList()} is a +shortcut to easily add functions, setters and getters properties to a +given object. + +Unlike other embedded Javascript engines, there is no implicit stack, +so C functions get their parameters as normal C parameters. As a +general rule, C functions take constant @code{JSValue}s as parameters +(so they don't need to free them) and return a newly allocated (=live) +@code{JSValue}. + +@subsection Exceptions + +Exceptions: most C functions can return a Javascript exception. It +must be explicitly tested and handled by the C code. The specific +@code{JSValue} @code{JS_EXCEPTION} indicates that an exception +occurred. The actual exception object is stored in the +@code{JSContext} and can be retrieved with @code{JS_GetException()}. + +@subsection Script evaluation + +Use @code{JS_Eval()} to evaluate a script or module source. + +If the script or module was compiled to bytecode with @code{qjsc}, it +can be evaluated by calling @code{js_std_eval_binary()}. The advantage +is that no compilation is needed so it is faster and smaller because +the compiler can be removed from the executable if no @code{eval} is +required. + +Note: the bytecode format is linked to a given QuickJS +version. Moreover, no security check is done before its +execution. Hence the bytecode should not be loaded from untrusted +sources. That's why there is no option to output the bytecode to a +binary file in @code{qjsc}. + +@subsection JS Classes + +C opaque data can be attached to a Javascript object. The type of the +C opaque data is determined with the class ID (@code{JSClassID}) of +the object. Hence the first step is to register a new class ID and JS +class (@code{JS_NewClassID()}, @code{JS_NewClass()}). Then you can +create objects of this class with @code{JS_NewObjectClass()} and get or +set the C opaque point with +@code{JS_GetOpaque()}/@code{JS_SetOpaque()}. + +When defining a new JS class, it is possible to declare a finalizer +which is called when the object is destroyed. The finalizer should be +used to release C resources. It is invalid to execute JS code from +it. A @code{gc_mark} method can be provided so that the cycle removal +algorithm can find the other objects referenced by this object. Other +methods are available to define exotic object behaviors. + +The Class ID are globally allocated (i.e. for all runtimes). The +JSClass are allocated per @code{JSRuntime}. @code{JS_SetClassProto()} +is used to define a prototype for a given class in a given +JSContext. @code{JS_NewObjectClass()} sets this prototype in the +created object. + +Examples are available in @file{quickjs-libc.c}. + +@subsection C Modules + +Native ES6 modules are supported and can be dynamically or statically +linked. Look at the @file{test_bjson} and @file{bjson.so} +examples. The standard library @file{quickjs-libc.c} is also a good example +of a native module. + +@subsection Memory handling + +Use @code{JS_SetMemoryLimit()} to set a global memory allocation limit +to a given JSRuntime. + +Custom memory allocation functions can be provided with +@code{JS_NewRuntime2()}. + +The maximum system stack size can be set with @code{JS_SetMaxStackSize()}. + +@subsection Execution timeout and interrupts + +Use @code{JS_SetInterruptHandler()} to set a callback which is +regularly called by the engine when it is executing code. This +callback can be used to implement an execution timeout. + +It is used by the command line interpreter to implement a +@code{Ctrl-C} handler. + +@chapter Internals + +@section Bytecode + +The compiler generates bytecode directly with no intermediate +representation such as a parse tree, hence it is very fast. Several +optimizations passes are done over the generated bytecode. + +A stack-based bytecode was chosen because it is simple and generates +compact code. + +For each function, the maximum stack size is computed at compile time so that +no runtime stack overflow tests are needed. + +A separate compressed line number table is maintained for the debug +information. + +Access to closure variables is optimized and is almost as fast as local +variables. + +Direct @code{eval} in strict mode is optimized. + +@section Executable generation + +@subsection @code{qjsc} compiler + +The @code{qjsc} compiler generates C sources from Javascript files. By +default the C sources are compiled with the system compiler +(@code{gcc} or @code{clang}). + +The generated C source contains the bytecode of the compiled functions +or modules. If a full complete executable is needed, it also +contains a @code{main()} function with the necessary C code to initialize the +Javascript engine and to load and execute the compiled functions and +modules. + +Javascript code can be mixed with C modules. + +In order to have smaller executables, specific Javascript features can +be disabled, in particular @code{eval} or the regular expressions. The +code removal relies on the Link Time Optimization of the system +compiler. + +@subsection Binary JSON + +@code{qjsc} works by compiling scripts or modules and then serializing +them to a binary format. A subset of this format (without functions or +modules) can be used as binary JSON. The example @file{test_bjson.js} +shows how to use it. + +Warning: the binary JSON format may change without notice, so it +should not be used to store persistent data. The @file{test_bjson.js} +example is only used to test the binary object format functions. + +@section Runtime + +@subsection Strings + +Strings are stored either as an 8 bit or a 16 bit array of +characters. Hence random access to characters is always fast. + +The C API provides functions to convert Javascript Strings to C UTF-8 encoded +strings. The most common case where the Javascript string contains +only ASCII characters involves no copying. + +@subsection Objects + +The object shapes (object prototype, property names and flags) are shared +between objects to save memory. + +Arrays with no holes (except at the end of the array) are optimized. + +TypedArray accesses are optimized. + +@subsection Atoms + +Object property names and some strings are stored as Atoms (unique +strings) to save memory and allow fast comparison. Atoms are +represented as a 32 bit integer. Half of the atom range is reserved for +immediate integer literals from @math{0} to @math{2^{31}-1}. + +@subsection Numbers + +Numbers are represented either as 32-bit signed integers or 64-bit IEEE-754 +floating point values. Most operations have fast paths for the 32-bit +integer case. + +@subsection Garbage collection + +Reference counting is used to free objects automatically and +deterministically. A separate cycle removal pass is done when the allocated +memory becomes too large. The cycle removal algorithm only uses the +reference counts and the object content, so no explicit garbage +collection roots need to be manipulated in the C code. + +@subsection JSValue + +It is a Javascript value which can be a primitive type (such as +Number, String, ...) or an Object. NaN boxing is used in the 32-bit version +to store 64-bit floating point numbers. The representation is +optimized so that 32-bit integers and reference counted values can be +efficiently tested. + +In 64-bit code, JSValue are 128-bit large and no NaN boxing is used. The +rationale is that in 64-bit code memory usage is less critical. + +In both cases (32 or 64 bits), JSValue exactly fits two CPU registers, +so it can be efficiently returned by C functions. + +@subsection Function call + +The engine is optimized so that function calls are fast. The system +stack holds the Javascript parameters and local variables. + +@section RegExp + +A specific regular expression engine was developed. It is both small +and efficient and supports all the ES2020 features including the +Unicode properties. As the Javascript compiler, it directly generates +bytecode without a parse tree. + +Backtracking with an explicit stack is used so that there is no +recursion on the system stack. Simple quantifiers are specifically +optimized to avoid recursions. + +Infinite recursions coming from quantifiers with empty terms are +avoided. + +The full regexp library weights about 15 KiB (x86 code), excluding the +Unicode library. + +@section Unicode + +A specific Unicode library was developed so that there is no +dependency on an external large Unicode library such as ICU. All the +Unicode tables are compressed while keeping a reasonable access +speed. + +The library supports case conversion, Unicode normalization, Unicode +script queries, Unicode general category queries and all Unicode +binary properties. + +The full Unicode library weights about 45 KiB (x86 code). + +@section BigInt, BigFloat, BigDecimal + +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. + +@chapter License + +QuickJS is released under the MIT license. + +Unless otherwise specified, the QuickJS sources are copyright Fabrice +Bellard and Charlie Gordon. + +@bye diff --git a/examples/fib.c b/examples/fib.c index c77b70562..e4f00d5d9 100644 --- a/examples/fib.c +++ b/examples/fib.c @@ -1,72 +1,72 @@ -/* - * QuickJS: Example of C module - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "../quickjs.h" - -#define countof(x) (sizeof(x) / sizeof((x)[0])) - -static int fib(int n) -{ - if (n <= 0) - return 0; - else if (n == 1) - return 1; - else - return fib(n - 1) + fib(n - 2); -} - -static JSValue js_fib(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int n, res; - if (JS_ToInt32(ctx, &n, argv[0])) - return JS_EXCEPTION; - res = fib(n); - return JS_NewInt32(ctx, res); -} - -static const JSCFunctionListEntry js_fib_funcs[] = { - JS_CFUNC_DEF("fib", 1, js_fib ), -}; - -static int js_fib_init(JSContext *ctx, JSModuleDef *m) -{ - return JS_SetModuleExportList(ctx, m, js_fib_funcs, - countof(js_fib_funcs)); -} - -#ifdef JS_SHARED_LIBRARY -#define JS_INIT_MODULE js_init_module -#else -#define JS_INIT_MODULE js_init_module_fib -#endif - -JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_fib_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs)); - return m; -} +/* + * QuickJS: Example of C module + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "../quickjs.h" + +#define countof(x) (sizeof(x) / sizeof((x)[0])) + +static int fib(int n) +{ + if (n <= 0) + return 0; + else if (n == 1) + return 1; + else + return fib(n - 1) + fib(n - 2); +} + +static JSValue js_fib(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int n, res; + if (JS_ToInt32(ctx, &n, argv[0])) + return JS_EXCEPTION; + res = fib(n); + return JS_NewInt32(ctx, res); +} + +static const JSCFunctionListEntry js_fib_funcs[] = { + JS_CFUNC_DEF("fib", 1, js_fib ), +}; + +static int js_fib_init(JSContext *ctx, JSModuleDef *m) +{ + return JS_SetModuleExportList(ctx, m, js_fib_funcs, + countof(js_fib_funcs)); +} + +#ifdef JS_SHARED_LIBRARY +#define JS_INIT_MODULE js_init_module +#else +#define JS_INIT_MODULE js_init_module_fib +#endif + +JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_fib_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs)); + return m; +} diff --git a/examples/fib_module.js b/examples/fib_module.js index 6a8107163..6f80b4eb5 100644 --- a/examples/fib_module.js +++ b/examples/fib_module.js @@ -1,10 +1,10 @@ -/* fib module */ -export function fib(n) -{ - if (n <= 0) - return 0; - else if (n == 1) - return 1; - else - return fib(n - 1) + fib(n - 2); -} +/* fib module */ +export function fib(n) +{ + if (n <= 0) + return 0; + else if (n == 1) + return 1; + else + return fib(n - 1) + fib(n - 2); +} diff --git a/examples/hello.js b/examples/hello.js index accefceba..d5ba56dfd 100644 --- a/examples/hello.js +++ b/examples/hello.js @@ -1 +1 @@ -console.log("Hello World"); +console.log("Hello World"); diff --git a/examples/hello_module.js b/examples/hello_module.js index 463660f6a..cfe68037d 100644 --- a/examples/hello_module.js +++ b/examples/hello_module.js @@ -1,6 +1,6 @@ -/* example of JS module */ - -import { fib } from "./fib_module.js"; - -console.log("Hello World"); -console.log("fib(10)=", fib(10)); +/* example of JS module */ + +import { fib } from "./fib_module.js"; + +console.log("Hello World"); +console.log("fib(10)=", fib(10)); diff --git a/examples/pi_bigdecimal.js b/examples/pi_bigdecimal.js index 6a416b793..bdf5a365e 100644 --- a/examples/pi_bigdecimal.js +++ b/examples/pi_bigdecimal.js @@ -1,68 +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")); -})(); +/* + * 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 index 2bcda22fd..47fd9fd53 100644 --- a/examples/pi_bigfloat.js +++ b/examples/pi_bigfloat.js @@ -1,66 +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); -})(); +/* + * 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 index cbbb2c409..ebc6523c0 100644 --- a/examples/pi_bigint.js +++ b/examples/pi_bigint.js @@ -1,118 +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); +/* + * 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 index fbe2ce100..a7b93a17b 100644 --- a/examples/point.c +++ b/examples/point.c @@ -1,151 +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; -} +/* + * 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 index 70d26bd8c..ebe4b5ddc 100644 --- a/examples/test_fib.js +++ b/examples/test_fib.js @@ -1,6 +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)); +/* 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 index 0659bc350..a57c48acc 100644 --- a/examples/test_point.js +++ b/examples/test_point.js @@ -1,40 +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(); +/* 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/include/quickjs/cutils.h b/include/quickjs/cutils.h index 31f7cd84a..7108bb5db 100644 --- a/include/quickjs/cutils.h +++ b/include/quickjs/cutils.h @@ -1,297 +1,297 @@ -/* - * C utilities - * - * Copyright (c) 2017 Fabrice Bellard - * Copyright (c) 2018 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef CUTILS_H -#define CUTILS_H - -#include -#include - -/* set if CPU is big endian */ -#undef WORDS_BIGENDIAN - -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#define force_inline inline __attribute__((always_inline)) -#define no_inline __attribute__((noinline)) -#define __maybe_unused __attribute__((unused)) - -#define xglue(x, y) x ## y -#define glue(x, y) xglue(x, y) -#define stringify(s) tostring(s) -#define tostring(s) #s - -#ifndef offsetof -#define offsetof(type, field) ((size_t) &((type *)0)->field) -#endif -#ifndef countof -#define countof(x) (sizeof(x) / sizeof((x)[0])) -#endif - -typedef int BOOL; - -#ifndef FALSE -enum { - FALSE = 0, - TRUE = 1, -}; -#endif - -void pstrcpy(char *buf, int buf_size, const char *str); -char *pstrcat(char *buf, int buf_size, const char *s); -int strstart(const char *str, const char *val, const char **ptr); -int has_suffix(const char *str, const char *suffix); - -static inline int max_int(int a, int b) -{ - if (a > b) - return a; - else - return b; -} - -static inline int min_int(int a, int b) -{ - if (a < b) - return a; - else - return b; -} - -static inline uint32_t max_uint32(uint32_t a, uint32_t b) -{ - if (a > b) - return a; - else - return b; -} - -static inline uint32_t min_uint32(uint32_t a, uint32_t b) -{ - if (a < b) - return a; - else - return b; -} - -static inline int64_t max_int64(int64_t a, int64_t b) -{ - if (a > b) - return a; - else - return b; -} - -static inline int64_t min_int64(int64_t a, int64_t b) -{ - if (a < b) - return a; - else - return b; -} - -/* WARNING: undefined if a = 0 */ -static inline int clz32(unsigned int a) -{ - return __builtin_clz(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int clz64(uint64_t a) -{ - return __builtin_clzll(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int ctz32(unsigned int a) -{ - return __builtin_ctz(a); -} - -/* WARNING: undefined if a = 0 */ -static inline int ctz64(uint64_t a) -{ - return __builtin_ctzll(a); -} - -struct __attribute__((packed)) packed_u64 { - uint64_t v; -}; - -struct __attribute__((packed)) packed_u32 { - uint32_t v; -}; - -struct __attribute__((packed)) packed_u16 { - uint16_t v; -}; - -static inline uint64_t get_u64(const uint8_t *tab) -{ - return ((const struct packed_u64 *)tab)->v; -} - -static inline int64_t get_i64(const uint8_t *tab) -{ - return (int64_t)((const struct packed_u64 *)tab)->v; -} - -static inline void put_u64(uint8_t *tab, uint64_t val) -{ - ((struct packed_u64 *)tab)->v = val; -} - -static inline uint32_t get_u32(const uint8_t *tab) -{ - return ((const struct packed_u32 *)tab)->v; -} - -static inline int32_t get_i32(const uint8_t *tab) -{ - return (int32_t)((const struct packed_u32 *)tab)->v; -} - -static inline void put_u32(uint8_t *tab, uint32_t val) -{ - ((struct packed_u32 *)tab)->v = val; -} - -static inline uint32_t get_u16(const uint8_t *tab) -{ - return ((const struct packed_u16 *)tab)->v; -} - -static inline int32_t get_i16(const uint8_t *tab) -{ - return (int16_t)((const struct packed_u16 *)tab)->v; -} - -static inline void put_u16(uint8_t *tab, uint16_t val) -{ - ((struct packed_u16 *)tab)->v = val; -} - -static inline uint32_t get_u8(const uint8_t *tab) -{ - return *tab; -} - -static inline int32_t get_i8(const uint8_t *tab) -{ - return (int8_t)*tab; -} - -static inline void put_u8(uint8_t *tab, uint8_t val) -{ - *tab = val; -} - -static inline uint16_t bswap16(uint16_t x) -{ - return (x >> 8) | (x << 8); -} - -static inline uint32_t bswap32(uint32_t v) -{ - return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | - ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); -} - -static inline uint64_t bswap64(uint64_t v) -{ - return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | - ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | - ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | - ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | - ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | - ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | - ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | - ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); -} - -/* XXX: should take an extra argument to pass slack information to the caller */ -typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); - -typedef struct DynBuf { - uint8_t *buf; - size_t size; - size_t allocated_size; - BOOL error; /* true if a memory allocation error occurred */ - DynBufReallocFunc *realloc_func; - void *opaque; /* for realloc_func */ -} DynBuf; - -void dbuf_init(DynBuf *s); -void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); -int dbuf_realloc(DynBuf *s, size_t new_size); -int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len); -int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); -int dbuf_put_self(DynBuf *s, size_t offset, size_t len); -int dbuf_putc(DynBuf *s, uint8_t c); -int dbuf_putstr(DynBuf *s, const char *str); -static inline int dbuf_put_u16(DynBuf *s, uint16_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 2); -} -static inline int dbuf_put_u32(DynBuf *s, uint32_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 4); -} -static inline int dbuf_put_u64(DynBuf *s, uint64_t val) -{ - return dbuf_put(s, (uint8_t *)&val, 8); -} -int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, - const char *fmt, ...); -void dbuf_free(DynBuf *s); -static inline BOOL dbuf_error(DynBuf *s) { - return s->error; -} -static inline void dbuf_set_error(DynBuf *s) -{ - s->error = TRUE; -} - -#define UTF8_CHAR_LEN_MAX 6 - -int unicode_to_utf8(uint8_t *buf, unsigned int c); -int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); - -static inline int from_hex(int c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - else if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - else if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - else - return -1; -} - -void rqsort(void *base, size_t nmemb, size_t size, - int (*cmp)(const void *, const void *, void *), - void *arg); - -#endif /* CUTILS_H */ +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CUTILS_H +#define CUTILS_H + +#include +#include + +/* set if CPU is big endian */ +#undef WORDS_BIGENDIAN + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#define force_inline inline __attribute__((always_inline)) +#define no_inline __attribute__((noinline)) +#define __maybe_unused __attribute__((unused)) + +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#endif + +typedef int BOOL; + +#ifndef FALSE +enum { + FALSE = 0, + TRUE = 1, +}; +#endif + +void pstrcpy(char *buf, int buf_size, const char *str); +char *pstrcat(char *buf, int buf_size, const char *s); +int strstart(const char *str, const char *val, const char **ptr); +int has_suffix(const char *str, const char *suffix); + +static inline int max_int(int a, int b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int min_int(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +static inline uint32_t max_uint32(uint32_t a, uint32_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline uint32_t min_uint32(uint32_t a, uint32_t b) +{ + if (a < b) + return a; + else + return b; +} + +static inline int64_t max_int64(int64_t a, int64_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int64_t min_int64(int64_t a, int64_t b) +{ + if (a < b) + return a; + else + return b; +} + +/* WARNING: undefined if a = 0 */ +static inline int clz32(unsigned int a) +{ + return __builtin_clz(a); +} + +/* WARNING: undefined if a = 0 */ +static inline int clz64(uint64_t a) +{ + return __builtin_clzll(a); +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz32(unsigned int a) +{ + return __builtin_ctz(a); +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz64(uint64_t a) +{ + return __builtin_ctzll(a); +} + +struct __attribute__((packed)) packed_u64 { + uint64_t v; +}; + +struct __attribute__((packed)) packed_u32 { + uint32_t v; +}; + +struct __attribute__((packed)) packed_u16 { + uint16_t v; +}; + +static inline uint64_t get_u64(const uint8_t *tab) +{ + return ((const struct packed_u64 *)tab)->v; +} + +static inline int64_t get_i64(const uint8_t *tab) +{ + return (int64_t)((const struct packed_u64 *)tab)->v; +} + +static inline void put_u64(uint8_t *tab, uint64_t val) +{ + ((struct packed_u64 *)tab)->v = val; +} + +static inline uint32_t get_u32(const uint8_t *tab) +{ + return ((const struct packed_u32 *)tab)->v; +} + +static inline int32_t get_i32(const uint8_t *tab) +{ + return (int32_t)((const struct packed_u32 *)tab)->v; +} + +static inline void put_u32(uint8_t *tab, uint32_t val) +{ + ((struct packed_u32 *)tab)->v = val; +} + +static inline uint32_t get_u16(const uint8_t *tab) +{ + return ((const struct packed_u16 *)tab)->v; +} + +static inline int32_t get_i16(const uint8_t *tab) +{ + return (int16_t)((const struct packed_u16 *)tab)->v; +} + +static inline void put_u16(uint8_t *tab, uint16_t val) +{ + ((struct packed_u16 *)tab)->v = val; +} + +static inline uint32_t get_u8(const uint8_t *tab) +{ + return *tab; +} + +static inline int32_t get_i8(const uint8_t *tab) +{ + return (int8_t)*tab; +} + +static inline void put_u8(uint8_t *tab, uint8_t val) +{ + *tab = val; +} + +static inline uint16_t bswap16(uint16_t x) +{ + return (x >> 8) | (x << 8); +} + +static inline uint32_t bswap32(uint32_t v) +{ + return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | + ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); +} + +static inline uint64_t bswap64(uint64_t v) +{ + return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | + ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | + ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | + ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | + ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | + ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | + ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | + ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); +} + +/* XXX: should take an extra argument to pass slack information to the caller */ +typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); + +typedef struct DynBuf { + uint8_t *buf; + size_t size; + size_t allocated_size; + BOOL error; /* true if a memory allocation error occurred */ + DynBufReallocFunc *realloc_func; + void *opaque; /* for realloc_func */ +} DynBuf; + +void dbuf_init(DynBuf *s); +void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); +int dbuf_realloc(DynBuf *s, size_t new_size); +int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len); +int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); +int dbuf_put_self(DynBuf *s, size_t offset, size_t len); +int dbuf_putc(DynBuf *s, uint8_t c); +int dbuf_putstr(DynBuf *s, const char *str); +static inline int dbuf_put_u16(DynBuf *s, uint16_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 2); +} +static inline int dbuf_put_u32(DynBuf *s, uint32_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 4); +} +static inline int dbuf_put_u64(DynBuf *s, uint64_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 8); +} +int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, + const char *fmt, ...); +void dbuf_free(DynBuf *s); +static inline BOOL dbuf_error(DynBuf *s) { + return s->error; +} +static inline void dbuf_set_error(DynBuf *s) +{ + s->error = TRUE; +} + +#define UTF8_CHAR_LEN_MAX 6 + +int unicode_to_utf8(uint8_t *buf, unsigned int c); +int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); + +static inline int from_hex(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + return -1; +} + +void rqsort(void *base, size_t nmemb, size_t size, + int (*cmp)(const void *, const void *, void *), + void *arg); + +#endif /* CUTILS_H */ diff --git a/include/quickjs/libbf.h b/include/quickjs/libbf.h index 48e9d956a..5bfe1cc0b 100644 --- a/include/quickjs/libbf.h +++ b/include/quickjs/libbf.h @@ -1,535 +1,535 @@ -/* - * Tiny arbitrary precision floating point library - * - * Copyright (c) 2017-2021 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIBBF_H -#define LIBBF_H - -#include -#include - -#if INTPTR_MAX >= INT64_MAX -#define LIMB_LOG2_BITS 6 -#else -#define LIMB_LOG2_BITS 5 -#endif - -#define LIMB_BITS (1 << LIMB_LOG2_BITS) - -#if LIMB_BITS == 64 -typedef __int128 int128_t; -typedef unsigned __int128 uint128_t; -typedef int64_t slimb_t; -typedef uint64_t limb_t; -typedef uint128_t dlimb_t; -#define BF_RAW_EXP_MIN INT64_MIN -#define BF_RAW_EXP_MAX INT64_MAX - -#define LIMB_DIGITS 19 -#define BF_DEC_BASE UINT64_C(10000000000000000000) - -#else - -typedef int32_t slimb_t; -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; -#define BF_RAW_EXP_MIN INT32_MIN -#define BF_RAW_EXP_MAX INT32_MAX - -#define LIMB_DIGITS 9 -#define BF_DEC_BASE 1000000000U - -#endif - -/* in bits */ -/* minimum number of bits for the exponent */ -#define BF_EXP_BITS_MIN 3 -/* maximum number of bits for the exponent */ -#define BF_EXP_BITS_MAX (LIMB_BITS - 3) -/* extended range for exponent, used internally */ -#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) -/* minimum possible precision */ -#define BF_PREC_MIN 2 -/* minimum possible precision */ -#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) -/* some operations support infinite precision */ -#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ - -#if LIMB_BITS == 64 -#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) -#else -#define BF_CHKSUM_MOD 975620677U -#endif - -#define BF_EXP_ZERO BF_RAW_EXP_MIN -#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) -#define BF_EXP_NAN BF_RAW_EXP_MAX - -/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, - +/-infinity is represented with expn = BF_EXP_INF and len = 0, - NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) - */ -typedef struct { - struct bf_context_t *ctx; - int sign; - slimb_t expn; - limb_t len; - limb_t *tab; -} bf_t; - -typedef struct { - /* must be kept identical to bf_t */ - struct bf_context_t *ctx; - int sign; - slimb_t expn; - limb_t len; - limb_t *tab; -} bfdec_t; - -typedef enum { - BF_RNDN, /* round to nearest, ties to even */ - BF_RNDZ, /* round to zero */ - BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ - BF_RNDU, /* round to +inf */ - BF_RNDNA, /* round to nearest, ties away from zero */ - BF_RNDA, /* round away from zero */ - BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, - inexact flag is always set) */ -} bf_rnd_t; - -/* allow subnormal numbers. Only available if the number of exponent - bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ -#define BF_FLAG_SUBNORMAL (1 << 3) -/* 'prec' is the precision after the radix point instead of the whole - mantissa. Can only be used with bf_round() and - bfdec_[add|sub|mul|div|sqrt|round](). */ -#define BF_FLAG_RADPNT_PREC (1 << 4) - -#define BF_RND_MASK 0x7 -#define BF_EXP_BITS_SHIFT 5 -#define BF_EXP_BITS_MASK 0x3f - -/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ -#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) - -/* contains the rounding mode and number of exponents bits */ -typedef uint32_t bf_flags_t; - -typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); - -typedef struct { - bf_t val; - limb_t prec; -} BFConstCache; - -typedef struct bf_context_t { - void *realloc_opaque; - bf_realloc_func_t *realloc_func; - BFConstCache log2_cache; - BFConstCache pi_cache; - struct BFNTTState *ntt_state; -} bf_context_t; - -static inline int bf_get_exp_bits(bf_flags_t flags) -{ - int e; - e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; - if (e == BF_EXP_BITS_MASK) - return BF_EXP_BITS_MAX + 1; - else - return BF_EXP_BITS_MAX - e; -} - -static inline bf_flags_t bf_set_exp_bits(int n) -{ - return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; -} - -/* returned status */ -#define BF_ST_INVALID_OP (1 << 0) -#define BF_ST_DIVIDE_ZERO (1 << 1) -#define BF_ST_OVERFLOW (1 << 2) -#define BF_ST_UNDERFLOW (1 << 3) -#define BF_ST_INEXACT (1 << 4) -/* indicate that a memory allocation error occured. NaN is returned */ -#define BF_ST_MEM_ERROR (1 << 5) - -#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ - -static inline slimb_t bf_max(slimb_t a, slimb_t b) -{ - if (a > b) - return a; - else - return b; -} - -static inline slimb_t bf_min(slimb_t a, slimb_t b) -{ - if (a < b) - return a; - else - return b; -} - -void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, - void *realloc_opaque); -void bf_context_end(bf_context_t *s); -/* free memory allocated for the bf cache data */ -void bf_clear_cache(bf_context_t *s); - -static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) -{ - return s->realloc_func(s->realloc_opaque, ptr, size); -} - -/* 'size' must be != 0 */ -static inline void *bf_malloc(bf_context_t *s, size_t size) -{ - return bf_realloc(s, NULL, size); -} - -static inline void bf_free(bf_context_t *s, void *ptr) -{ - /* must test ptr otherwise equivalent to malloc(0) */ - if (ptr) - bf_realloc(s, ptr, 0); -} - -void bf_init(bf_context_t *s, bf_t *r); - -static inline void bf_delete(bf_t *r) -{ - bf_context_t *s = r->ctx; - /* we accept to delete a zeroed bf_t structure */ - if (s && r->tab) { - bf_realloc(s, r->tab, 0); - } -} - -static inline void bf_neg(bf_t *r) -{ - r->sign ^= 1; -} - -static inline int bf_is_finite(const bf_t *a) -{ - return (a->expn < BF_EXP_INF); -} - -static inline int bf_is_nan(const bf_t *a) -{ - return (a->expn == BF_EXP_NAN); -} - -static inline int bf_is_zero(const bf_t *a) -{ - return (a->expn == BF_EXP_ZERO); -} - -static inline void bf_memcpy(bf_t *r, const bf_t *a) -{ - *r = *a; -} - -int bf_set_ui(bf_t *r, uint64_t a); -int bf_set_si(bf_t *r, int64_t a); -void bf_set_nan(bf_t *r); -void bf_set_zero(bf_t *r, int is_neg); -void bf_set_inf(bf_t *r, int is_neg); -int bf_set(bf_t *r, const bf_t *a); -void bf_move(bf_t *r, bf_t *a); -int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); -int bf_set_float64(bf_t *a, double d); - -int bf_cmpu(const bf_t *a, const bf_t *b); -int bf_cmp_full(const bf_t *a, const bf_t *b); -int bf_cmp(const bf_t *a, const bf_t *b); -static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) == 0; -} - -static inline int bf_cmp_le(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) <= 0; -} - -static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) -{ - return bf_cmp(a, b) < 0; -} - -int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); -int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags); -int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); -int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); -#define BF_DIVREM_EUCLIDIAN BF_RNDF -int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode); -int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -/* round to integer with infinite precision */ -int bf_rint(bf_t *r, int rnd_mode); -int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); -int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); -int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -slimb_t bf_get_exp_min(const bf_t *a); -int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); -int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); -int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); - -/* additional flags for bf_atof */ -/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ -#define BF_ATOF_NO_HEX (1 << 16) -/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ -#define BF_ATOF_BIN_OCT (1 << 17) -/* Do not parse NaN or Inf */ -#define BF_ATOF_NO_NAN_INF (1 << 18) -/* return the exponent separately */ -#define BF_ATOF_EXPONENT (1 << 19) - -int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags); -/* this version accepts prec = BF_PREC_INF and returns the radix - exponent */ -int bf_atof2(bf_t *r, slimb_t *pexponent, - const char *str, const char **pnext, int radix, - limb_t prec, bf_flags_t flags); -int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, - slimb_t expn, limb_t prec, bf_flags_t flags); - - -/* Conversion of floating point number to string. Return a null - terminated string or NULL if memory error. *plen contains its - length if plen != NULL. The exponent letter is "e" for base 10, - "p" for bases 2, 8, 16 with a binary exponent and "@" for the other - bases. */ - -#define BF_FTOA_FORMAT_MASK (3 << 16) - -/* fixed format: prec significant digits rounded with (flags & - BF_RND_MASK). Exponential notation is used if too many zeros are - needed.*/ -#define BF_FTOA_FORMAT_FIXED (0 << 16) -/* fractional format: prec digits after the decimal point rounded with - (flags & BF_RND_MASK) */ -#define BF_FTOA_FORMAT_FRAC (1 << 16) -/* free format: - - For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum - number of digits to represent 'a'. The precision and the rounding - mode are ignored. - - For the non binary radices with bf_ftoa(): use as many digits as - necessary so that bf_atof() return the same number when using - precision 'prec', rounding to nearest and the subnormal - configuration of 'flags'. The result is meaningful only if 'a' is - already rounded to 'prec' bits. If the subnormal flag is set, the - exponent in 'flags' must also be set to the desired exponent range. -*/ -#define BF_FTOA_FORMAT_FREE (2 << 16) -/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits - (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for - binary radices with bf_ftoa() and for bfdec_ftoa(). */ -#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) - -/* force exponential notation for fixed or free format */ -#define BF_FTOA_FORCE_EXP (1 << 20) -/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for - base 2 if non zero value */ -#define BF_FTOA_ADD_PREFIX (1 << 21) -/* return "Infinity" instead of "Inf" and add a "+" for positive - exponents */ -#define BF_FTOA_JS_QUIRKS (1 << 22) - -char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, - bf_flags_t flags); - -/* modulo 2^n instead of saturation. NaN and infinity return 0 */ -#define BF_GET_INT_MOD (1 << 0) -int bf_get_int32(int *pres, const bf_t *a, int flags); -int bf_get_int64(int64_t *pres, const bf_t *a, int flags); -int bf_get_uint64(uint64_t *pres, const bf_t *a); - -/* the following functions are exported for testing only. */ -void mp_print_str(const char *str, const limb_t *tab, limb_t n); -void bf_print_str(const char *str, const bf_t *a); -int bf_resize(bf_t *r, limb_t len); -int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); -int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); -int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); -slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, - int is_ceil1); -int mp_mul(bf_context_t *s, limb_t *result, - const limb_t *op1, limb_t op1_size, - const limb_t *op2, limb_t op2_size); -limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, - limb_t n, limb_t carry); -limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); -int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n); -int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n); -limb_t bf_isqrt(limb_t a); - -/* transcendental functions */ -int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); -int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); -int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ -int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); -int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, - limb_t prec, bf_flags_t flags); -int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); -int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); - -/* decimal floating point */ - -static inline void bfdec_init(bf_context_t *s, bfdec_t *r) -{ - bf_init(s, (bf_t *)r); -} -static inline void bfdec_delete(bfdec_t *r) -{ - bf_delete((bf_t *)r); -} - -static inline void bfdec_neg(bfdec_t *r) -{ - r->sign ^= 1; -} - -static inline int bfdec_is_finite(const bfdec_t *a) -{ - return (a->expn < BF_EXP_INF); -} - -static inline int bfdec_is_nan(const bfdec_t *a) -{ - return (a->expn == BF_EXP_NAN); -} - -static inline int bfdec_is_zero(const bfdec_t *a) -{ - return (a->expn == BF_EXP_ZERO); -} - -static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) -{ - bf_memcpy((bf_t *)r, (const bf_t *)a); -} - -int bfdec_set_ui(bfdec_t *r, uint64_t a); -int bfdec_set_si(bfdec_t *r, int64_t a); - -static inline void bfdec_set_nan(bfdec_t *r) -{ - bf_set_nan((bf_t *)r); -} -static inline void bfdec_set_zero(bfdec_t *r, int is_neg) -{ - bf_set_zero((bf_t *)r, is_neg); -} -static inline void bfdec_set_inf(bfdec_t *r, int is_neg) -{ - bf_set_inf((bf_t *)r, is_neg); -} -static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) -{ - return bf_set((bf_t *)r, (bf_t *)a); -} -static inline void bfdec_move(bfdec_t *r, bfdec_t *a) -{ - bf_move((bf_t *)r, (bf_t *)a); -} -static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmpu((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmp_full((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) -{ - return bf_cmp((const bf_t *)a, (const bf_t *)b); -} -static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) == 0; -} -static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) <= 0; -} -static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) -{ - return bfdec_cmp(a, b) < 0; -} - -int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, - bf_flags_t flags); -int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags); -int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, - limb_t prec, bf_flags_t flags, int rnd_mode); -int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, - bf_flags_t flags, int rnd_mode); -int bfdec_rint(bfdec_t *r, int rnd_mode); -int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); -int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); -int bfdec_get_int32(int *pres, const bfdec_t *a); -int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); - -char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); -int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, - limb_t prec, bf_flags_t flags); - -/* the following functions are exported for testing only. */ -extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; -void bfdec_print_str(const char *str, const bfdec_t *a); -static inline int bfdec_resize(bfdec_t *r, limb_t len) -{ - return bf_resize((bf_t *)r, len); -} -int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); - -#endif /* LIBBF_H */ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBBF_H +#define LIBBF_H + +#include +#include + +#if INTPTR_MAX >= INT64_MAX +#define LIMB_LOG2_BITS 6 +#else +#define LIMB_LOG2_BITS 5 +#endif + +#define LIMB_BITS (1 << LIMB_LOG2_BITS) + +#if LIMB_BITS == 64 +typedef __int128 int128_t; +typedef unsigned __int128 uint128_t; +typedef int64_t slimb_t; +typedef uint64_t limb_t; +typedef uint128_t dlimb_t; +#define BF_RAW_EXP_MIN INT64_MIN +#define BF_RAW_EXP_MAX INT64_MAX + +#define LIMB_DIGITS 19 +#define BF_DEC_BASE UINT64_C(10000000000000000000) + +#else + +typedef int32_t slimb_t; +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; +#define BF_RAW_EXP_MIN INT32_MIN +#define BF_RAW_EXP_MAX INT32_MAX + +#define LIMB_DIGITS 9 +#define BF_DEC_BASE 1000000000U + +#endif + +/* in bits */ +/* minimum number of bits for the exponent */ +#define BF_EXP_BITS_MIN 3 +/* maximum number of bits for the exponent */ +#define BF_EXP_BITS_MAX (LIMB_BITS - 3) +/* extended range for exponent, used internally */ +#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) +/* minimum possible precision */ +#define BF_PREC_MIN 2 +/* minimum possible precision */ +#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) +/* some operations support infinite precision */ +#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ + +#if LIMB_BITS == 64 +#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) +#else +#define BF_CHKSUM_MOD 975620677U +#endif + +#define BF_EXP_ZERO BF_RAW_EXP_MIN +#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) +#define BF_EXP_NAN BF_RAW_EXP_MAX + +/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, + +/-infinity is represented with expn = BF_EXP_INF and len = 0, + NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) + */ +typedef struct { + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bf_t; + +typedef struct { + /* must be kept identical to bf_t */ + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bfdec_t; + +typedef enum { + BF_RNDN, /* round to nearest, ties to even */ + BF_RNDZ, /* round to zero */ + BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ + BF_RNDU, /* round to +inf */ + BF_RNDNA, /* round to nearest, ties away from zero */ + BF_RNDA, /* round away from zero */ + BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, + inexact flag is always set) */ +} bf_rnd_t; + +/* allow subnormal numbers. Only available if the number of exponent + bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ +#define BF_FLAG_SUBNORMAL (1 << 3) +/* 'prec' is the precision after the radix point instead of the whole + mantissa. Can only be used with bf_round() and + bfdec_[add|sub|mul|div|sqrt|round](). */ +#define BF_FLAG_RADPNT_PREC (1 << 4) + +#define BF_RND_MASK 0x7 +#define BF_EXP_BITS_SHIFT 5 +#define BF_EXP_BITS_MASK 0x3f + +/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ +#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) + +/* contains the rounding mode and number of exponents bits */ +typedef uint32_t bf_flags_t; + +typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); + +typedef struct { + bf_t val; + limb_t prec; +} BFConstCache; + +typedef struct bf_context_t { + void *realloc_opaque; + bf_realloc_func_t *realloc_func; + BFConstCache log2_cache; + BFConstCache pi_cache; + struct BFNTTState *ntt_state; +} bf_context_t; + +static inline int bf_get_exp_bits(bf_flags_t flags) +{ + int e; + e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; + if (e == BF_EXP_BITS_MASK) + return BF_EXP_BITS_MAX + 1; + else + return BF_EXP_BITS_MAX - e; +} + +static inline bf_flags_t bf_set_exp_bits(int n) +{ + return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; +} + +/* returned status */ +#define BF_ST_INVALID_OP (1 << 0) +#define BF_ST_DIVIDE_ZERO (1 << 1) +#define BF_ST_OVERFLOW (1 << 2) +#define BF_ST_UNDERFLOW (1 << 3) +#define BF_ST_INEXACT (1 << 4) +/* indicate that a memory allocation error occured. NaN is returned */ +#define BF_ST_MEM_ERROR (1 << 5) + +#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ + +static inline slimb_t bf_max(slimb_t a, slimb_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline slimb_t bf_min(slimb_t a, slimb_t b) +{ + if (a < b) + return a; + else + return b; +} + +void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, + void *realloc_opaque); +void bf_context_end(bf_context_t *s); +/* free memory allocated for the bf cache data */ +void bf_clear_cache(bf_context_t *s); + +static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) +{ + return s->realloc_func(s->realloc_opaque, ptr, size); +} + +/* 'size' must be != 0 */ +static inline void *bf_malloc(bf_context_t *s, size_t size) +{ + return bf_realloc(s, NULL, size); +} + +static inline void bf_free(bf_context_t *s, void *ptr) +{ + /* must test ptr otherwise equivalent to malloc(0) */ + if (ptr) + bf_realloc(s, ptr, 0); +} + +void bf_init(bf_context_t *s, bf_t *r); + +static inline void bf_delete(bf_t *r) +{ + bf_context_t *s = r->ctx; + /* we accept to delete a zeroed bf_t structure */ + if (s && r->tab) { + bf_realloc(s, r->tab, 0); + } +} + +static inline void bf_neg(bf_t *r) +{ + r->sign ^= 1; +} + +static inline int bf_is_finite(const bf_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bf_is_nan(const bf_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bf_is_zero(const bf_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bf_memcpy(bf_t *r, const bf_t *a) +{ + *r = *a; +} + +int bf_set_ui(bf_t *r, uint64_t a); +int bf_set_si(bf_t *r, int64_t a); +void bf_set_nan(bf_t *r); +void bf_set_zero(bf_t *r, int is_neg); +void bf_set_inf(bf_t *r, int is_neg); +int bf_set(bf_t *r, const bf_t *a); +void bf_move(bf_t *r, bf_t *a); +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); +int bf_set_float64(bf_t *a, double d); + +int bf_cmpu(const bf_t *a, const bf_t *b); +int bf_cmp_full(const bf_t *a, const bf_t *b); +int bf_cmp(const bf_t *a, const bf_t *b); +static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) == 0; +} + +static inline int bf_cmp_le(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) <= 0; +} + +static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) < 0; +} + +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags); +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +#define BF_DIVREM_EUCLIDIAN BF_RNDF +int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +/* round to integer with infinite precision */ +int bf_rint(bf_t *r, int rnd_mode); +int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +slimb_t bf_get_exp_min(const bf_t *a); +int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); +int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); +int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); + +/* additional flags for bf_atof */ +/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ +#define BF_ATOF_NO_HEX (1 << 16) +/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ +#define BF_ATOF_BIN_OCT (1 << 17) +/* Do not parse NaN or Inf */ +#define BF_ATOF_NO_NAN_INF (1 << 18) +/* return the exponent separately */ +#define BF_ATOF_EXPONENT (1 << 19) + +int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +/* this version accepts prec = BF_PREC_INF and returns the radix + exponent */ +int bf_atof2(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, + slimb_t expn, limb_t prec, bf_flags_t flags); + + +/* Conversion of floating point number to string. Return a null + terminated string or NULL if memory error. *plen contains its + length if plen != NULL. The exponent letter is "e" for base 10, + "p" for bases 2, 8, 16 with a binary exponent and "@" for the other + bases. */ + +#define BF_FTOA_FORMAT_MASK (3 << 16) + +/* fixed format: prec significant digits rounded with (flags & + BF_RND_MASK). Exponential notation is used if too many zeros are + needed.*/ +#define BF_FTOA_FORMAT_FIXED (0 << 16) +/* fractional format: prec digits after the decimal point rounded with + (flags & BF_RND_MASK) */ +#define BF_FTOA_FORMAT_FRAC (1 << 16) +/* free format: + + For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum + number of digits to represent 'a'. The precision and the rounding + mode are ignored. + + For the non binary radices with bf_ftoa(): use as many digits as + necessary so that bf_atof() return the same number when using + precision 'prec', rounding to nearest and the subnormal + configuration of 'flags'. The result is meaningful only if 'a' is + already rounded to 'prec' bits. If the subnormal flag is set, the + exponent in 'flags' must also be set to the desired exponent range. +*/ +#define BF_FTOA_FORMAT_FREE (2 << 16) +/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits + (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for + binary radices with bf_ftoa() and for bfdec_ftoa(). */ +#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) + +/* force exponential notation for fixed or free format */ +#define BF_FTOA_FORCE_EXP (1 << 20) +/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for + base 2 if non zero value */ +#define BF_FTOA_ADD_PREFIX (1 << 21) +/* return "Infinity" instead of "Inf" and add a "+" for positive + exponents */ +#define BF_FTOA_JS_QUIRKS (1 << 22) + +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags); + +/* modulo 2^n instead of saturation. NaN and infinity return 0 */ +#define BF_GET_INT_MOD (1 << 0) +int bf_get_int32(int *pres, const bf_t *a, int flags); +int bf_get_int64(int64_t *pres, const bf_t *a, int flags); +int bf_get_uint64(uint64_t *pres, const bf_t *a); + +/* the following functions are exported for testing only. */ +void mp_print_str(const char *str, const limb_t *tab, limb_t n); +void bf_print_str(const char *str, const bf_t *a); +int bf_resize(bf_t *r, limb_t len); +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); +int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); +int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); +slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, + int is_ceil1); +int mp_mul(bf_context_t *s, limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size); +limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, + limb_t n, limb_t carry); +limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); +int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n); +int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n); +limb_t bf_isqrt(limb_t a); + +/* transcendental functions */ +int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ +int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); +int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, + limb_t prec, bf_flags_t flags); +int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); + +/* decimal floating point */ + +static inline void bfdec_init(bf_context_t *s, bfdec_t *r) +{ + bf_init(s, (bf_t *)r); +} +static inline void bfdec_delete(bfdec_t *r) +{ + bf_delete((bf_t *)r); +} + +static inline void bfdec_neg(bfdec_t *r) +{ + r->sign ^= 1; +} + +static inline int bfdec_is_finite(const bfdec_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bfdec_is_nan(const bfdec_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bfdec_is_zero(const bfdec_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) +{ + bf_memcpy((bf_t *)r, (const bf_t *)a); +} + +int bfdec_set_ui(bfdec_t *r, uint64_t a); +int bfdec_set_si(bfdec_t *r, int64_t a); + +static inline void bfdec_set_nan(bfdec_t *r) +{ + bf_set_nan((bf_t *)r); +} +static inline void bfdec_set_zero(bfdec_t *r, int is_neg) +{ + bf_set_zero((bf_t *)r, is_neg); +} +static inline void bfdec_set_inf(bfdec_t *r, int is_neg) +{ + bf_set_inf((bf_t *)r, is_neg); +} +static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) +{ + return bf_set((bf_t *)r, (bf_t *)a); +} +static inline void bfdec_move(bfdec_t *r, bfdec_t *a) +{ + bf_move((bf_t *)r, (bf_t *)a); +} +static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmpu((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp_full((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) == 0; +} +static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) <= 0; +} +static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) < 0; +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bfdec_rint(bfdec_t *r, int rnd_mode); +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); +int bfdec_get_int32(int *pres, const bfdec_t *a); +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags); + +/* the following functions are exported for testing only. */ +extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; +void bfdec_print_str(const char *str, const bfdec_t *a); +static inline int bfdec_resize(bfdec_t *r, limb_t len) +{ + return bf_resize((bf_t *)r, len); +} +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); + +#endif /* LIBBF_H */ diff --git a/include/quickjs/libregexp-opcode.h b/include/quickjs/libregexp-opcode.h index f90c23b34..05beceb3a 100644 --- a/include/quickjs/libregexp-opcode.h +++ b/include/quickjs/libregexp-opcode.h @@ -1,58 +1,58 @@ -/* - * Regular Expression Engine - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifdef DEF - -DEF(invalid, 1) /* never used */ -DEF(char, 3) -DEF(char32, 5) -DEF(dot, 1) -DEF(any, 1) /* same as dot but match any character including line terminator */ -DEF(line_start, 1) -DEF(line_end, 1) -DEF(goto, 5) -DEF(split_goto_first, 5) -DEF(split_next_first, 5) -DEF(match, 1) -DEF(save_start, 2) /* save start position */ -DEF(save_end, 2) /* save end position, must come after saved_start */ -DEF(save_reset, 3) /* reset save positions */ -DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ -DEF(push_i32, 5) /* push integer on the stack */ -DEF(drop, 1) -DEF(word_boundary, 1) -DEF(not_word_boundary, 1) -DEF(back_reference, 2) -DEF(backward_back_reference, 2) /* must come after back_reference */ -DEF(range, 3) /* variable length */ -DEF(range32, 3) /* variable length */ -DEF(lookahead, 5) -DEF(negative_lookahead, 5) -DEF(push_char_pos, 1) /* push the character position on the stack */ -DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character - position */ -DEF(prev, 1) /* go to the previous char */ -DEF(simple_greedy_quant, 17) - -#endif /* DEF */ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +DEF(invalid, 1) /* never used */ +DEF(char, 3) +DEF(char32, 5) +DEF(dot, 1) +DEF(any, 1) /* same as dot but match any character including line terminator */ +DEF(line_start, 1) +DEF(line_end, 1) +DEF(goto, 5) +DEF(split_goto_first, 5) +DEF(split_next_first, 5) +DEF(match, 1) +DEF(save_start, 2) /* save start position */ +DEF(save_end, 2) /* save end position, must come after saved_start */ +DEF(save_reset, 3) /* reset save positions */ +DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ +DEF(push_i32, 5) /* push integer on the stack */ +DEF(drop, 1) +DEF(word_boundary, 1) +DEF(not_word_boundary, 1) +DEF(back_reference, 2) +DEF(backward_back_reference, 2) /* must come after back_reference */ +DEF(range, 3) /* variable length */ +DEF(range32, 3) /* variable length */ +DEF(lookahead, 5) +DEF(negative_lookahead, 5) +DEF(push_char_pos, 1) /* push the character position on the stack */ +DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character + position */ +DEF(prev, 1) /* go to the previous char */ +DEF(simple_greedy_quant, 17) + +#endif /* DEF */ diff --git a/include/quickjs/libregexp.h b/include/quickjs/libregexp.h index 9aedb7e93..d5313a092 100644 --- a/include/quickjs/libregexp.h +++ b/include/quickjs/libregexp.h @@ -1,92 +1,92 @@ -/* - * Regular Expression Engine - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIBREGEXP_H -#define LIBREGEXP_H - -#include - -#include "libunicode.h" - -#define LRE_BOOL int /* for documentation purposes */ - -#define LRE_FLAG_GLOBAL (1 << 0) -#define LRE_FLAG_IGNORECASE (1 << 1) -#define LRE_FLAG_MULTILINE (1 << 2) -#define LRE_FLAG_DOTALL (1 << 3) -#define LRE_FLAG_UTF16 (1 << 4) -#define LRE_FLAG_STICKY (1 << 5) - -#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ - -uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, - const char *buf, size_t buf_len, int re_flags, - void *opaque); -int lre_get_capture_count(const uint8_t *bc_buf); -int lre_get_flags(const uint8_t *bc_buf); -const char *lre_get_groupnames(const uint8_t *bc_buf); -int lre_exec(uint8_t **capture, - const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, - int cbuf_type, void *opaque); - -int lre_parse_escape(const uint8_t **pp, int allow_utf16); -LRE_BOOL lre_is_space(int c); - -/* must be provided by the user */ -LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); -void *lre_realloc(void *opaque, void *ptr, size_t size); - -/* JS identifier test */ -extern uint32_t const lre_id_start_table_ascii[4]; -extern uint32_t const lre_id_continue_table_ascii[4]; - -static inline int lre_js_is_ident_first(int c) -{ - if ((uint32_t)c < 128) { - return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; - } else { -#ifdef CONFIG_ALL_UNICODE - return lre_is_id_start(c); -#else - return !lre_is_space(c); -#endif - } -} - -static inline int lre_js_is_ident_next(int c) -{ - if ((uint32_t)c < 128) { - return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; - } else { - /* ZWNJ and ZWJ are accepted in identifiers */ -#ifdef CONFIG_ALL_UNICODE - return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; -#else - return !lre_is_space(c) || c == 0x200C || c == 0x200D; -#endif - } -} - -#undef LRE_BOOL - -#endif /* LIBREGEXP_H */ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBREGEXP_H +#define LIBREGEXP_H + +#include + +#include "libunicode.h" + +#define LRE_BOOL int /* for documentation purposes */ + +#define LRE_FLAG_GLOBAL (1 << 0) +#define LRE_FLAG_IGNORECASE (1 << 1) +#define LRE_FLAG_MULTILINE (1 << 2) +#define LRE_FLAG_DOTALL (1 << 3) +#define LRE_FLAG_UTF16 (1 << 4) +#define LRE_FLAG_STICKY (1 << 5) + +#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ + +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque); +int lre_get_capture_count(const uint8_t *bc_buf); +int lre_get_flags(const uint8_t *bc_buf); +const char *lre_get_groupnames(const uint8_t *bc_buf); +int lre_exec(uint8_t **capture, + const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, + int cbuf_type, void *opaque); + +int lre_parse_escape(const uint8_t **pp, int allow_utf16); +LRE_BOOL lre_is_space(int c); + +/* must be provided by the user */ +LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); +void *lre_realloc(void *opaque, void *ptr, size_t size); + +/* JS identifier test */ +extern uint32_t const lre_id_start_table_ascii[4]; +extern uint32_t const lre_id_continue_table_ascii[4]; + +static inline int lre_js_is_ident_first(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_start(c); +#else + return !lre_is_space(c); +#endif + } +} + +static inline int lre_js_is_ident_next(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; +#else + return !lre_is_space(c) || c == 0x200C || c == 0x200D; +#endif + } +} + +#undef LRE_BOOL + +#endif /* LIBREGEXP_H */ diff --git a/include/quickjs/libunicode-table.h b/include/quickjs/libunicode-table.h index 0ef211356..78a047848 100644 --- a/include/quickjs/libunicode-table.h +++ b/include/quickjs/libunicode-table.h @@ -1,4368 +1,4368 @@ -/* Compressed unicode tables */ -/* Automatically generated file - do not edit */ - -#include - -static const uint32_t case_conv_table1[361] = { - 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, - 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, - 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, - 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40, - 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100, - 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240, - 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130, - 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130, - 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240, - 0x00cd0100, 0x00ce0130, 0x00ce8130, 0x00cf0100, - 0x00cf8130, 0x00d00640, 0x00d30130, 0x00d38240, - 0x00d48130, 0x00d60240, 0x00d70130, 0x00d78240, - 0x00d88230, 0x00d98440, 0x00db8130, 0x00dc0240, - 0x00de0240, 0x00df8100, 0x00e20350, 0x00e38350, - 0x00e50350, 0x00e69040, 0x00ee8100, 0x00ef1240, - 0x00f801b4, 0x00f88350, 0x00fa0240, 0x00fb0130, - 0x00fb8130, 0x00fc2840, 0x01100130, 0x01111240, - 0x011d0131, 0x011d8240, 0x011e8130, 0x011f0131, - 0x011f8201, 0x01208240, 0x01218130, 0x01220130, - 0x01228130, 0x01230a40, 0x01280101, 0x01288101, - 0x01290101, 0x01298100, 0x012a0100, 0x012b0200, - 0x012c8100, 0x012d8100, 0x012e0101, 0x01300100, - 0x01308101, 0x01318100, 0x01328101, 0x01330101, - 0x01340100, 0x01348100, 0x01350101, 0x01358101, - 0x01360101, 0x01378100, 0x01388101, 0x01390100, - 0x013a8100, 0x013e8101, 0x01400100, 0x01410101, - 0x01418100, 0x01438101, 0x01440100, 0x01448100, - 0x01450200, 0x01460100, 0x01490100, 0x014e8101, - 0x014f0101, 0x01a28173, 0x01b80440, 0x01bb0240, - 0x01bd8300, 0x01bf8130, 0x01c30130, 0x01c40330, - 0x01c60130, 0x01c70230, 0x01c801d0, 0x01c89130, - 0x01d18930, 0x01d60100, 0x01d68300, 0x01d801d3, - 0x01d89100, 0x01e10173, 0x01e18900, 0x01e60100, - 0x01e68200, 0x01e78130, 0x01e80173, 0x01e88173, - 0x01ea8173, 0x01eb0173, 0x01eb8100, 0x01ec1840, - 0x01f80173, 0x01f88173, 0x01f90100, 0x01f98100, - 0x01fa01a0, 0x01fa8173, 0x01fb8240, 0x01fc8130, - 0x01fd0240, 0x01fe8330, 0x02001030, 0x02082030, - 0x02182000, 0x02281000, 0x02302240, 0x02453640, - 0x02600130, 0x02608e40, 0x02678100, 0x02686040, - 0x0298a630, 0x02b0a600, 0x02c381b5, 0x08502631, - 0x08638131, 0x08668131, 0x08682b00, 0x087e8300, - 0x09d05011, 0x09f80610, 0x09fc0620, 0x0e400174, - 0x0e408174, 0x0e410174, 0x0e418174, 0x0e420174, - 0x0e428174, 0x0e430174, 0x0e438180, 0x0e440180, - 0x0e482b30, 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, - 0x0ec70101, 0x0f007e40, 0x0f3f1840, 0x0f4b01b5, - 0x0f4b81b6, 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, - 0x0f4d8180, 0x0f4f0130, 0x0f506040, 0x0f800800, - 0x0f840830, 0x0f880600, 0x0f8c0630, 0x0f900800, - 0x0f940830, 0x0f980800, 0x0f9c0830, 0x0fa00600, - 0x0fa40630, 0x0fa801b0, 0x0fa88100, 0x0fa901d3, - 0x0fa98100, 0x0faa01d3, 0x0faa8100, 0x0fab01d3, - 0x0fab8100, 0x0fac8130, 0x0fad8130, 0x0fae8130, - 0x0faf8130, 0x0fb00800, 0x0fb40830, 0x0fb80200, - 0x0fb90400, 0x0fbb0200, 0x0fbc0201, 0x0fbd0201, - 0x0fbe0201, 0x0fc008b7, 0x0fc40867, 0x0fc808b8, - 0x0fcc0868, 0x0fd008b8, 0x0fd40868, 0x0fd80200, - 0x0fd901b9, 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1, - 0x0fdb81d7, 0x0fdc0230, 0x0fdd0230, 0x0fde0161, - 0x0fdf0173, 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, - 0x0fe301b2, 0x0fe381d8, 0x0fe40430, 0x0fe60162, - 0x0fe80200, 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, - 0x0feb81d0, 0x0fec0230, 0x0fed0230, 0x0ff00201, - 0x0ff101d3, 0x0ff181d3, 0x0ff201ba, 0x0ff28101, - 0x0ff301b0, 0x0ff381d3, 0x0ff40230, 0x0ff50230, - 0x0ff60131, 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, - 0x0ffb01b2, 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230, - 0x0ffe0162, 0x109301a0, 0x109501a0, 0x109581a0, - 0x10990131, 0x10a70101, 0x10b01031, 0x10b81001, - 0x10c18240, 0x125b1a31, 0x12681a01, 0x16002f31, - 0x16182f01, 0x16300240, 0x16310130, 0x16318130, - 0x16320130, 0x16328100, 0x16330100, 0x16338640, - 0x16368130, 0x16370130, 0x16378130, 0x16380130, - 0x16390240, 0x163a8240, 0x163f0230, 0x16406440, - 0x16758440, 0x16790240, 0x16802600, 0x16938100, - 0x16968100, 0x53202e40, 0x53401c40, 0x53910e40, - 0x53993e40, 0x53bc8440, 0x53be8130, 0x53bf0a40, - 0x53c58240, 0x53c68130, 0x53c80440, 0x53ca0101, - 0x53cb1440, 0x53d50130, 0x53d58130, 0x53d60130, - 0x53d68130, 0x53d70130, 0x53d80130, 0x53d88130, - 0x53d90130, 0x53d98131, 0x53da0c40, 0x53e10240, - 0x53e20131, 0x53e28130, 0x53e30130, 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[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, - 0x53, 0x00, 0x3a, 0x54, 0x55, 0x00, 0x57, 0x59, - 0x3f, 0x5d, 0x5c, 0x00, 0x46, 0x61, 0x63, 0x42, - 0x64, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, - 0x6c, 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, 0x20, - 0x35, 0x00, 0x27, 0x00, 0x21, 0x00, 0x24, 0x22, - 0x2a, 0x00, 0x13, 0x6b, 0x6d, 0x00, 0x26, 0x24, - 0x27, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x3e, 0x1e, - 0x3f, 0x1f, 0x39, 0x3d, 0x22, 0x21, 0x41, 0x1e, - 0x40, 0x25, 0x25, 0x26, 0x28, 0x20, 0x2a, 0x49, - 0x2c, 0x43, 0x2e, 0x4b, 0x30, 0x4c, 0x32, 0x44, - 0x42, 0x99, 0x00, 0x00, 0x95, 0x8f, 0x7d, 0x7e, - 0x83, 0x84, 0x12, 0x80, 0x82, 0x76, 0x77, 0x12, - 0x7b, 0xa3, 0x7c, 0x78, 0x79, 0x8a, 0x92, 0x98, - 0xa6, 0xa0, 0x85, 0x00, 0x9a, 0xa1, 0x93, 0x75, - 0x33, 0x95, 0x00, 0x8e, 0x00, 0x74, 0x99, 0x98, - 0x97, 0x96, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0xa1, 0xa0, 0x15, 0x2e, 0x2f, 0x30, 0xb4, 0xb5, - 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, - 0xc7, 0xca, 0xc9, 0xcc, 0xcb, 0xc4, 0xd5, 0x45, - 0xd6, 0x42, 0xd7, 0x46, 0xd8, 0xce, 0xd0, 0xd2, - 0xd4, 0xda, 0xd9, 0xee, 0xf6, 0xfe, 0x0e, 0x07, - 0x0f, 0x80, 0x9f, 0x00, 0x21, 0x80, 0xa3, 0xed, - 0x00, 0xc0, 0x40, 0xc6, 0x60, 0xe7, 0xdb, 0xe6, - 0x99, 0xc0, 0x00, 0x00, 0x06, 0x60, 0xdc, 0x29, - 0xfd, 0x15, 0x12, 0x06, 0x16, 0xf8, 0xdd, 0x06, - 0x15, 0x12, 0x84, 0x08, 0xc6, 0x16, 0xff, 0xdf, - 0x03, 0xc0, 0x40, 0x00, 0x46, 0x60, 0xde, 0xe0, - 0x6d, 0x37, 0x38, 0x39, 0x15, 0x14, 0x17, 0x16, - 0x00, 0x1a, 0x19, 0x1c, 0x1b, 0x00, 0x5f, 0xb7, - 0x65, 0x44, 0x47, 0x00, 0x4f, 0x62, 0x4e, 0x50, - 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xa3, 0xa4, - 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, - 0x00, 0x5a, 0x00, 0x48, 0x00, 0x5b, 0x56, 0x58, - 0x60, 0x5e, 0x70, 0x69, 0x6f, 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] = { - 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391, - 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307, - 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331, - 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80, - 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca, - 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546, - 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9, - 0x006b, 0x00e5, -}; - -static const uint8_t unicode_prop_Cased1_table[172] = { - 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, - 0x80, 0x8c, 0x80, 0x8d, 0x81, 0x8d, 0x02, 0x80, - 0xe1, 0x80, 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, - 0x11, 0x00, 0x01, 0x04, 0x08, 0x01, 0x08, 0x30, - 0x08, 0x01, 0x15, 0x20, 0x00, 0x39, 0x99, 0x31, - 0x9d, 0x84, 0x40, 0x94, 0x80, 0xd6, 0x82, 0xa6, - 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x57, 0x76, - 0xf8, 0x02, 0x80, 0x8f, 0x80, 0xb0, 0x40, 0xdb, - 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f, - 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28, - 0x10, 0x11, 0x02, 0x01, 0x18, 0x0b, 0x24, 0x4b, - 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79, - 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94, - 0x05, 0x80, 0x98, 0x80, 0xc7, 0x82, 0x43, 0x34, - 0xa2, 0x06, 0x80, 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, - 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, - 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, - 0x80, 0x9e, 0x80, 0x98, 0x07, 0x59, 0x63, 0x99, - 0x85, 0x99, 0x85, 0x99, -}; - -static const uint8_t unicode_prop_Cased1_index[18] = { - 0xb9, 0x02, 0xe0, 0xa0, 0x1e, 0x40, 0x9e, 0xa6, - 0x40, 0xba, 0xd4, 0x01, 0x89, 0xd7, 0x01, 0x8a, - 0xf1, 0x01, -}; - -static const uint8_t unicode_prop_Case_Ignorable_table[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, 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, 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] = { - 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[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, - 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80, - 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80, - 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac, - 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81, - 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8, - 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95, - 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a, - 0xb4, 0x94, 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[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[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, - 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89, - 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89, - 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02, - 0x04, 0xaa, 0x82, 0xf6, 0x8e, 0x80, 0xa0, 0xb5, - 0x10, 0x91, 0x06, 0x89, 0x09, 0x89, 0x90, 0x82, - 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, 0x80, 0x89, - 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, 0x00, 0x23, - 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10, 0x8a, 0x82, - 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93, 0x09, 0x89, - 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, 0x09, 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, 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, - 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe3, 0x93, 0x80, - 0x89, 0x8b, 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, - 0x8b, 0x80, 0x8e, 0x42, 0xbe, 0x82, 0x88, 0x88, - 0x43, 0x9f, 0x82, 0x9c, 0x82, 0x9c, 0x81, 0x9d, - 0x81, 0xbf, 0x9f, 0x88, 0x01, 0x89, 0xa0, 0x11, - 0x89, 0x40, 0x8e, 0x80, 0xf5, 0x8b, 0x83, 0x8b, - 0x89, 0x89, 0xff, 0x8a, 0xbb, 0x84, 0xb8, 0x89, - 0x80, 0x9c, 0x81, 0x8a, 0x85, 0x89, 0x95, 0x8d, - 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, 0x52, 0x95, 0x89, 0x6c, 0x05, 0x05, - 0x40, 0xef, -}; - -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, 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[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, - 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2, - 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00, - 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea, - 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0, - 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00, - 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05, - 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00, - 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00, - 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc, - 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e, - 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00, - 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0, - 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81, - 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2, - 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00, - 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0, - 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc, - 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, - 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc, - 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4, - 0xaa, 0x02, 0xdc, 0xb0, 0x46, 0x00, 0xdc, 0xcd, - 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, - 0xdc, 0xc2, 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, - 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, - 0x07, 0x8f, 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, - 0xc1, 0xb0, 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, - 0xaf, 0xc0, 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, - 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, - 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, - 0x4e, 0x00, 0x09, 0xb0, 0x4e, 0x00, 0x09, 0x86, - 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00, 0x07, - 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09, 0x8f, - 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0, 0x3c, - 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b, 0xb0, - 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03, 0x7a, - 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc, 0x80, - 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06, 0x41, - 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82, 0x81, - 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80, 0xc1, - 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00, 0x07, - 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc, 0xb2, - 0x9e, 0xc2, 0xb3, 0x83, 0x00, 0x09, 0x9e, 0x00, - 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0, 0xb0, - 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde, 0xc0, - 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc, 0xb0, - 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00, 0xdc, - 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc, 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, 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[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, 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[690] = { - 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, - 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, - 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, - 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117, - 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342, - 0x005240bf, 0x00530342, 0x00550942, 0x005a0842, - 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142, - 0x006bc142, 0x00710185, 0x0071c317, 0x00734844, - 0x00778344, 0x00798342, 0x007b02be, 0x007c4197, - 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142, - 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283, - 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097, - 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080, - 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad, - 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081, - 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be, - 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be, - 0x011d8144, 0x01304144, 0x01340244, 0x01358144, - 0x01368344, 0x01388344, 0x013a8644, 0x013e0144, - 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184, - 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084, - 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084, - 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084, - 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084, - 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122, - 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae, - 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081, - 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084, - 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e, - 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084, - 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084, - 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081, - 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120, - 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783, - 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942, - 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085, - 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122, - 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122, - 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e, - 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122, - 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be, - 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be, - 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be, - 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820, - 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080, - 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117, - 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080, - 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080, - 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080, - 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080, - 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080, - 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081, - 0x0805c097, 0x08090081, 0x08094097, 0x08098099, - 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085, - 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3, - 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215, - 0x0820051f, 0x08228583, 0x08254415, 0x082a0097, - 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119, - 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081, - 0x08458097, 0x08464295, 0x08480097, 0x08484099, - 0x08488097, 0x08490081, 0x08498080, 0x084a0081, - 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081, - 0x084ec099, 0x084f0283, 0x08514295, 0x08540119, - 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081, - 0x08584097, 0x08588099, 0x0858c097, 0x08590081, - 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097, - 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295, - 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081, - 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097, - 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215, - 0x08624099, 0x0866813e, 0x086b80be, 0x087341be, - 0x088100be, 0x088240be, 0x088300be, 0x088901be, - 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1, - 0x089040be, 0x089100be, 0x0891c1be, 0x089801be, - 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144, - 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244, - 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523, - 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b, - 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25, - 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3, - 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be, - 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081, - 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383, - 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081, - 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122, - 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122, - 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085, - 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122, - 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084, - 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d, - 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703, - 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9, - 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089, - 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203, - 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d, - 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3, - 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523, - 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097, - 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad, - 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3, - 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1, - 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1, - 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7, - 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3, - 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3, - 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5, - 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1, - 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5, - 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1, - 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1, - 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3, - 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133, - 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1, - 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1, - 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099, - 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127, - 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099, - 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205, - 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1, - 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097, - 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f, - 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b, - 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117, - 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099, - 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525, - 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103, - 0x29dc0081, 0x29fe0103, 0x2ad70203, 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[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, - 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c, - 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192, - 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7, - 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218, - 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b, - 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275, - 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9, - 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1, - 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317, - 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341, - 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363, - 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385, - 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc, - 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1, - 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c, - 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614, - 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e, - 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9, - 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705, - 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737, - 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a, - 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c, - 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010, - 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db, - 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801, - 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830, - 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e, - 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069, - 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049, - 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4, - 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076, - 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7, - 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9, - 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923, - 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956, - 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978, - 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993, - 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f, - 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020, - 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1, - 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39, - 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59, - 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec, - 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a, - 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc, - 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c, - 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e, - 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca, - 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04, - 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45, - 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76, - 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad, - 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5, - 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a, - 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f, - 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1, - 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2, - 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153, - 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181, - 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab, - 0xa76f, 0x11af, 0x11b3, 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[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, - 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41, - 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45, - 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49, - 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e, - 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f, - 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55, - 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59, - 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61, - 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61, - 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65, - 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69, - 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e, - 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f, - 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75, - 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79, - 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41, - 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43, - 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45, - 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47, - 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48, - 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49, - 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a, - 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c, - 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e, - 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e, - 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81, - 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82, - 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c, - 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a, - 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82, - 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c, - 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01, - 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01, - 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a, - 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49, - 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c, - 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00, - 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26, - 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b, - 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01, - 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a, - 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a, - 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81, - 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f, - 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f, - 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f, - 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6, - 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45, - 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84, - 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00, - 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72, - 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77, - 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20, - 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63, - 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95, - 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20, - 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03, - 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99, - 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81, - 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03, - 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07, - 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00, - 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07, - 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, - 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1, - 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba, - 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98, - 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04, - 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06, - 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80, - 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04, - 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00, - 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88, - 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04, - 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10, - 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86, - 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04, - 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e, - 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88, - 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04, - 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65, - 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00, - 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27, - 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23, - 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00, - 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54, - 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c, - 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00, - 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c, - 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe, - 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc, - 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c, - 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00, - 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c, - 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09, - 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92, - 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09, - 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf, - 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2, - 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08, - 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca, - 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f, - 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2, - 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42, - 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51, - 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b, - 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71, - 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41, - 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80, - 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80, - 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7, - 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7, - 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5, - 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35, - 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35, - 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35, - 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00, - 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47, - 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52, - 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61, - 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62, - 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b, - 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b, - 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54, - 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74, - 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76, - 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4, - 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72, - 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3, - 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52, - 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c, - 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65, - 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b, - 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f, - 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73, - 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82, - 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a, - 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a, - 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8, - 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42, - 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81, - 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00, - 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12, - 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad, - 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00, - 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48, - 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7, - 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00, - 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b, - 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84, - 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87, - 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1, - 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88, - 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00, - 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52, - 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1, - 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01, - 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54, - 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1, - 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00, - 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a, - 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80, - 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3, - 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82, - 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88, - 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02, - 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00, - 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2, - 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82, - 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01, - 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45, - 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83, - 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00, - 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49, - 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3, - 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00, - 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc, - 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80, - 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01, - 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf, - 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89, - 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00, - 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59, - 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f, - 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91, - 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f, - 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03, - 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03, - 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81, - 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f, - 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21, - 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2, - 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f, - 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29, - 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2, - 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f, - 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31, - 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2, - 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f, - 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39, - 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2, - 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f, - 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03, - 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03, - 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81, - 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00, - 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59, - 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2, - 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f, - 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61, - 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2, - 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f, - 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69, - 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2, - 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03, - 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5, - 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45, - 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45, - 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70, - 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5, - 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f, - 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91, - 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20, - 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f, - 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00, - 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5, - 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03, - 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf, - 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84, - 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca, - 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe, - 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2, - 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03, - 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5, - 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5, - 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85, - 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03, - 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9, - 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80, - 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94, - 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20, - 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35, - 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00, - 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f, - 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69, - 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b, - 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29, - 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f, - 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c, - 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61, - 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43, - 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00, - 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, - 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50, - 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45, - 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42, - 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f, - 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3, - 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44, - 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31, - 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0, - 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32, - 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35, - 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0, - 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37, - 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49, - 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49, - 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49, - 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69, - 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76, - 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, - 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c, - 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21, - 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0, - 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8, - 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22, - 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25, - 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22, - 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e, - 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43, - 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00, - 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00, - 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c, - 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8, - 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22, - 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86, - 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8, - 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22, - 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03, - 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, - 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00, - 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00, - 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00, - 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30, - 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41, - 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e, - 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0, - 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57, - 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e, - 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6, - 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f, - 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15, - 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80, - 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38, - 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5, - 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a, - 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b, - 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73, - 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b, - 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97, - 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5, - 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20, - 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb, - 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14, - 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36, - 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59, - 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89, - 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f, - 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92, - 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf, - 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3, - 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74, - 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8, - 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd, - 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33, - 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00, - 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f, - 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c, - 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2, - 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55, - 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70, - 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b, - 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49, - 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77, - 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9, - 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62, - 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3, - 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf, - 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8, - 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f, - 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5, - 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00, - 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e, - 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12, - 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45, - 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99, - 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99, - 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99, - 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99, - 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99, - 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99, - 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99, - 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, - 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99, - 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99, - 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99, - 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99, - 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8, - 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac, - 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3, - 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09, - 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01, - 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08, - 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13, - 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22, - 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45, - 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d, - 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, - 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75, - 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59, - 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00, - 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11, - 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11, - 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11, - 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11, - 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11, - 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, - 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11, - 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, - 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, - 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, - 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, - 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67, - 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91, - 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, - 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c, - 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54, - 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, - 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81, - 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65, - 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31, - 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00, - 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c, - 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00, - 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06, - 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c, - 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e, - 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63, - 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e, - 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e, - 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e, - 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, - 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65, - 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54, - 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, - 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90, - 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98, - 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e, - 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53, - 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76, - 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59, - 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, - 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00, - 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72, - 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30, - 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d, - 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, - 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d, - 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4, - 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b, - 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39, - 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30, - 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f, - 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11, - 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03, - 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58, - 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e, - 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30, - 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30, - 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad, - 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13, - 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00, - 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40, - 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40, - 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30, - 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b, - 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e, - 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02, - 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11, - 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c, - 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00, - 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f, - 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12, - 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a, - 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16, - 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38, - 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2, - 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30, - 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20, - 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28, - 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30, - 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00, - 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d, - 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22, - 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01, - 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30, - 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23, - 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14, - 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a, - 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30, - 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47, - 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c, - 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30, - 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c, - 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35, - 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2, - 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44, - 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28, - 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00, - 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec, - 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16, - 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30, - 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9, - 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68, - 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, - 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64, - 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55, - 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c, - 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb, - 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e, - 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc, - 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41, - 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00, - 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c, - 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03, - 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b, - 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a, - 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48, - 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13, - 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13, - 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc, - 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d, - 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f, - 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63, - 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00, - 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00, - 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22, - 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00, - 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d, - 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, - 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61, - 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2, - 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc, - 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56, - 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00, - 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70, - 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57, - 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00, - 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9, - 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63, - 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43, - 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, - 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d, - 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f, - 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c, - 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d, - 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72, - 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41, - 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00, - 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00, - 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65, - 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x26, - 0x01, 0x53, 0x01, 0x27, 0xa7, 0x37, 0xab, 0x6b, - 0x02, 0x52, 0xab, 0x48, 0x8c, 0xf4, 0x66, 0xca, - 0x8e, 0xc8, 0x8c, 0xd1, 0x6e, 0x32, 0x4e, 0xe5, - 0x53, 0x9c, 0x9f, 0x9c, 0x9f, 0x51, 0x59, 0xd1, - 0x91, 0x87, 0x55, 0x48, 0x59, 0xf6, 0x61, 0x69, - 0x76, 0x85, 0x7f, 0x3f, 0x86, 0xba, 0x87, 0xf8, - 0x88, 0x8f, 0x90, 0x02, 0x6a, 0x1b, 0x6d, 0xd9, - 0x70, 0xde, 0x73, 0x3d, 0x84, 0x6a, 0x91, 0xf1, - 0x99, 0x82, 0x4e, 0x75, 0x53, 0x04, 0x6b, 0x1b, - 0x72, 0x2d, 0x86, 0x1e, 0x9e, 0x50, 0x5d, 0xeb, - 0x6f, 0xcd, 0x85, 0x64, 0x89, 0xc9, 0x62, 0xd8, - 0x81, 0x1f, 0x88, 0xca, 0x5e, 0x17, 0x67, 0x6a, - 0x6d, 0xfc, 0x72, 0xce, 0x90, 0x86, 0x4f, 0xb7, - 0x51, 0xde, 0x52, 0xc4, 0x64, 0xd3, 0x6a, 0x10, - 0x72, 0xe7, 0x76, 0x01, 0x80, 0x06, 0x86, 0x5c, - 0x86, 0xef, 0x8d, 0x32, 0x97, 0x6f, 0x9b, 0xfa, - 0x9d, 0x8c, 0x78, 0x7f, 0x79, 0xa0, 0x7d, 0xc9, - 0x83, 0x04, 0x93, 0x7f, 0x9e, 0xd6, 0x8a, 0xdf, - 0x58, 0x04, 0x5f, 0x60, 0x7c, 0x7e, 0x80, 0x62, - 0x72, 0xca, 0x78, 0xc2, 0x8c, 0xf7, 0x96, 0xd8, - 0x58, 0x62, 0x5c, 0x13, 0x6a, 0xda, 0x6d, 0x0f, - 0x6f, 0x2f, 0x7d, 0x37, 0x7e, 0x4b, 0x96, 0xd2, - 0x52, 0x8b, 0x80, 0xdc, 0x51, 0xcc, 0x51, 0x1c, - 0x7a, 0xbe, 0x7d, 0xf1, 0x83, 0x75, 0x96, 0x80, - 0x8b, 0xcf, 0x62, 0x02, 0x6a, 0xfe, 0x8a, 0x39, - 0x4e, 0xe7, 0x5b, 0x12, 0x60, 0x87, 0x73, 0x70, - 0x75, 0x17, 0x53, 0xfb, 0x78, 0xbf, 0x4f, 0xa9, - 0x5f, 0x0d, 0x4e, 0xcc, 0x6c, 0x78, 0x65, 0x22, - 0x7d, 0xc3, 0x53, 0x5e, 0x58, 0x01, 0x77, 0x49, - 0x84, 0xaa, 0x8a, 0xba, 0x6b, 0xb0, 0x8f, 0x88, - 0x6c, 0xfe, 0x62, 0xe5, 0x82, 0xa0, 0x63, 0x65, - 0x75, 0xae, 0x4e, 0x69, 0x51, 0xc9, 0x51, 0x81, - 0x68, 0xe7, 0x7c, 0x6f, 0x82, 0xd2, 0x8a, 0xcf, - 0x91, 0xf5, 0x52, 0x42, 0x54, 0x73, 0x59, 0xec, - 0x5e, 0xc5, 0x65, 0xfe, 0x6f, 0x2a, 0x79, 0xad, - 0x95, 0x6a, 0x9a, 0x97, 0x9e, 0xce, 0x9e, 0x9b, - 0x52, 0xc6, 0x66, 0x77, 0x6b, 0x62, 0x8f, 0x74, - 0x5e, 0x90, 0x61, 0x00, 0x62, 0x9a, 0x64, 0x23, - 0x6f, 0x49, 0x71, 0x89, 0x74, 0xca, 0x79, 0xf4, - 0x7d, 0x6f, 0x80, 0x26, 0x8f, 0xee, 0x84, 0x23, - 0x90, 0x4a, 0x93, 0x17, 0x52, 0xa3, 0x52, 0xbd, - 0x54, 0xc8, 0x70, 0xc2, 0x88, 0xaa, 0x8a, 0xc9, - 0x5e, 0xf5, 0x5f, 0x7b, 0x63, 0xae, 0x6b, 0x3e, - 0x7c, 0x75, 0x73, 0xe4, 0x4e, 0xf9, 0x56, 0xe7, - 0x5b, 0xba, 0x5d, 0x1c, 0x60, 0xb2, 0x73, 0x69, - 0x74, 0x9a, 0x7f, 0x46, 0x80, 0x34, 0x92, 0xf6, - 0x96, 0x48, 0x97, 0x18, 0x98, 0x8b, 0x4f, 0xae, - 0x79, 0xb4, 0x91, 0xb8, 0x96, 0xe1, 0x60, 0x86, - 0x4e, 0xda, 0x50, 0xee, 0x5b, 0x3f, 0x5c, 0x99, - 0x65, 0x02, 0x6a, 0xce, 0x71, 0x42, 0x76, 0xfc, - 0x84, 0x7c, 0x90, 0x8d, 0x9f, 0x88, 0x66, 0x2e, - 0x96, 0x89, 0x52, 0x7b, 0x67, 0xf3, 0x67, 0x41, - 0x6d, 0x9c, 0x6e, 0x09, 0x74, 0x59, 0x75, 0x6b, - 0x78, 0x10, 0x7d, 0x5e, 0x98, 0x6d, 0x51, 0x2e, - 0x62, 0x78, 0x96, 0x2b, 0x50, 0x19, 0x5d, 0xea, - 0x6d, 0x2a, 0x8f, 0x8b, 0x5f, 0x44, 0x61, 0x17, - 0x68, 0x87, 0x73, 0x86, 0x96, 0x29, 0x52, 0x0f, - 0x54, 0x65, 0x5c, 0x13, 0x66, 0x4e, 0x67, 0xa8, - 0x68, 0xe5, 0x6c, 0x06, 0x74, 0xe2, 0x75, 0x79, - 0x7f, 0xcf, 0x88, 0xe1, 0x88, 0xcc, 0x91, 0xe2, - 0x96, 0x3f, 0x53, 0xba, 0x6e, 0x1d, 0x54, 0xd0, - 0x71, 0x98, 0x74, 0xfa, 0x85, 0xa3, 0x96, 0x57, - 0x9c, 0x9f, 0x9e, 0x97, 0x67, 0xcb, 0x6d, 0xe8, - 0x81, 0xcb, 0x7a, 0x20, 0x7b, 0x92, 0x7c, 0xc0, - 0x72, 0x99, 0x70, 0x58, 0x8b, 0xc0, 0x4e, 0x36, - 0x83, 0x3a, 0x52, 0x07, 0x52, 0xa6, 0x5e, 0xd3, - 0x62, 0xd6, 0x7c, 0x85, 0x5b, 0x1e, 0x6d, 0xb4, - 0x66, 0x3b, 0x8f, 0x4c, 0x88, 0x4d, 0x96, 0x8b, - 0x89, 0xd3, 0x5e, 0x40, 0x51, 0xc0, 0x55, 0x00, - 0x00, 0x00, 0x00, 0x5a, 0x58, 0x00, 0x00, 0x74, - 0x66, 0x00, 0x00, 0x00, 0x00, 0xde, 0x51, 0x2a, - 0x73, 0xca, 0x76, 0x3c, 0x79, 0x5e, 0x79, 0x65, - 0x79, 0x8f, 0x79, 0x56, 0x97, 0xbe, 0x7c, 0xbd, - 0x7f, 0x00, 0x00, 0x12, 0x86, 0x00, 0x00, 0xf8, - 0x8a, 0x00, 0x00, 0x00, 0x00, 0x38, 0x90, 0xfd, - 0x90, 0xef, 0x98, 0xfc, 0x98, 0x28, 0x99, 0xb4, - 0x9d, 0xde, 0x90, 0xb7, 0x96, 0xae, 0x4f, 0xe7, - 0x50, 0x4d, 0x51, 0xc9, 0x52, 0xe4, 0x52, 0x51, - 0x53, 0x9d, 0x55, 0x06, 0x56, 0x68, 0x56, 0x40, - 0x58, 0xa8, 0x58, 0x64, 0x5c, 0x6e, 0x5c, 0x94, - 0x60, 0x68, 0x61, 0x8e, 0x61, 0xf2, 0x61, 0x4f, - 0x65, 0xe2, 0x65, 0x91, 0x66, 0x85, 0x68, 0x77, - 0x6d, 0x1a, 0x6e, 0x22, 0x6f, 0x6e, 0x71, 0x2b, - 0x72, 0x22, 0x74, 0x91, 0x78, 0x3e, 0x79, 0x49, - 0x79, 0x48, 0x79, 0x50, 0x79, 0x56, 0x79, 0x5d, - 0x79, 0x8d, 0x79, 0x8e, 0x79, 0x40, 0x7a, 0x81, - 0x7a, 0xc0, 0x7b, 0xf4, 0x7d, 0x09, 0x7e, 0x41, - 0x7e, 0x72, 0x7f, 0x05, 0x80, 0xed, 0x81, 0x79, - 0x82, 0x79, 0x82, 0x57, 0x84, 0x10, 0x89, 0x96, - 0x89, 0x01, 0x8b, 0x39, 0x8b, 0xd3, 0x8c, 0x08, - 0x8d, 0xb6, 0x8f, 0x38, 0x90, 0xe3, 0x96, 0xff, - 0x97, 0x3b, 0x98, 0x75, 0x60, 0xee, 0x42, 0x18, - 0x82, 0x02, 0x26, 0x4e, 0xb5, 0x51, 0x68, 0x51, - 0x80, 0x4f, 0x45, 0x51, 0x80, 0x51, 0xc7, 0x52, - 0xfa, 0x52, 0x9d, 0x55, 0x55, 0x55, 0x99, 0x55, - 0xe2, 0x55, 0x5a, 0x58, 0xb3, 0x58, 0x44, 0x59, - 0x54, 0x59, 0x62, 0x5a, 0x28, 0x5b, 0xd2, 0x5e, - 0xd9, 0x5e, 0x69, 0x5f, 0xad, 0x5f, 0xd8, 0x60, - 0x4e, 0x61, 0x08, 0x61, 0x8e, 0x61, 0x60, 0x61, - 0xf2, 0x61, 0x34, 0x62, 0xc4, 0x63, 0x1c, 0x64, - 0x52, 0x64, 0x56, 0x65, 0x74, 0x66, 0x17, 0x67, - 0x1b, 0x67, 0x56, 0x67, 0x79, 0x6b, 0xba, 0x6b, - 0x41, 0x6d, 0xdb, 0x6e, 0xcb, 0x6e, 0x22, 0x6f, - 0x1e, 0x70, 0x6e, 0x71, 0xa7, 0x77, 0x35, 0x72, - 0xaf, 0x72, 0x2a, 0x73, 0x71, 0x74, 0x06, 0x75, - 0x3b, 0x75, 0x1d, 0x76, 0x1f, 0x76, 0xca, 0x76, - 0xdb, 0x76, 0xf4, 0x76, 0x4a, 0x77, 0x40, 0x77, - 0xcc, 0x78, 0xb1, 0x7a, 0xc0, 0x7b, 0x7b, 0x7c, - 0x5b, 0x7d, 0xf4, 0x7d, 0x3e, 0x7f, 0x05, 0x80, - 0x52, 0x83, 0xef, 0x83, 0x79, 0x87, 0x41, 0x89, - 0x86, 0x89, 0x96, 0x89, 0xbf, 0x8a, 0xf8, 0x8a, - 0xcb, 0x8a, 0x01, 0x8b, 0xfe, 0x8a, 0xed, 0x8a, - 0x39, 0x8b, 0x8a, 0x8b, 0x08, 0x8d, 0x38, 0x8f, - 0x72, 0x90, 0x99, 0x91, 0x76, 0x92, 0x7c, 0x96, - 0xe3, 0x96, 0x56, 0x97, 0xdb, 0x97, 0xff, 0x97, - 0x0b, 0x98, 0x3b, 0x98, 0x12, 0x9b, 0x9c, 0x9f, - 0x4a, 0x28, 0x44, 0x28, 0xd5, 0x33, 0x9d, 0x3b, - 0x18, 0x40, 0x39, 0x40, 0x49, 0x52, 0xd0, 0x5c, - 0xd3, 0x7e, 0x43, 0x9f, 0x8e, 0x9f, 0x2a, 0xa0, - 0x02, 0x66, 0x66, 0x66, 0x69, 0x66, 0x6c, 0x66, - 0x66, 0x69, 0x66, 0x66, 0x6c, 0x7f, 0x01, 0x74, - 0x73, 0x00, 0x74, 0x65, 0x05, 0x0f, 0x11, 0x0f, - 0x00, 0x0f, 0x06, 0x19, 0x11, 0x0f, 0x08, 0xd9, - 0x05, 0xb4, 0x05, 0x00, 0x00, 0x00, 0x00, 0xf2, - 0x05, 0xb7, 0x05, 0xd0, 0x05, 0x12, 0x00, 0x03, - 0x04, 0x0b, 0x0c, 0x0d, 0x18, 0x1a, 0xe9, 0x05, - 0xc1, 0x05, 0xe9, 0x05, 0xc2, 0x05, 0x49, 0xfb, - 0xc1, 0x05, 0x49, 0xfb, 0xc2, 0x05, 0xd0, 0x05, - 0xb7, 0x05, 0xd0, 0x05, 0xb8, 0x05, 0xd0, 0x05, - 0xbc, 0x05, 0xd8, 0x05, 0xbc, 0x05, 0xde, 0x05, - 0xbc, 0x05, 0xe0, 0x05, 0xbc, 0x05, 0xe3, 0x05, - 0xbc, 0x05, 0xb9, 0x05, 0x2d, 0x03, 0x2e, 0x03, - 0x2f, 0x03, 0x30, 0x03, 0x31, 0x03, 0x1c, 0x00, - 0x18, 0x06, 0x22, 0x06, 0x2b, 0x06, 0xd0, 0x05, - 0xdc, 0x05, 0x71, 0x06, 0x00, 0x00, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0f, 0x0f, - 0x0f, 0x0f, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, - 0x0e, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x33, 0x33, - 0x33, 0x33, 0x35, 0x35, 0x35, 0x35, 0x13, 0x13, - 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x15, 0x15, - 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x1c, 0x1c, - 0x1b, 0x1b, 0x1d, 0x1d, 0x17, 0x17, 0x27, 0x27, - 0x20, 0x20, 0x38, 0x38, 0x38, 0x38, 0x3e, 0x3e, - 0x3e, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x40, 0x40, - 0x40, 0x40, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x4d, 0x4d, - 0x4d, 0x4d, 0x61, 0x61, 0x62, 0x62, 0x49, 0x06, - 0x64, 0x64, 0x64, 0x64, 0x7e, 0x7e, 0x7d, 0x7d, - 0x7f, 0x7f, 0x2e, 0x82, 0x82, 0x7c, 0x7c, 0x80, - 0x80, 0x87, 0x87, 0x87, 0x87, 0x00, 0x00, 0x26, - 0x06, 0x00, 0x01, 0x00, 0x01, 0x00, 0xaf, 0x00, - 0xaf, 0x00, 0x22, 0x00, 0x22, 0x00, 0xa1, 0x00, - 0xa1, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa2, 0x00, - 0xa2, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0x23, 0x00, 0x23, 0x00, 0x23, 0xcc, 0x06, 0x00, - 0x00, 0x00, 0x00, 0x26, 0x06, 0x00, 0x06, 0x00, - 0x07, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x24, 0x02, - 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x1f, 0x02, - 0x23, 0x02, 0x24, 0x04, 0x06, 0x04, 0x07, 0x04, - 0x08, 0x04, 0x1f, 0x04, 0x23, 0x04, 0x24, 0x05, - 0x06, 0x05, 0x1f, 0x05, 0x23, 0x05, 0x24, 0x06, - 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07, 0x1f, 0x08, - 0x06, 0x08, 0x07, 0x08, 0x1f, 0x0d, 0x06, 0x0d, - 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f, - 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10, 0x08, 0x10, - 0x1f, 0x11, 0x07, 0x11, 0x1f, 0x12, 0x1f, 0x13, - 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b, - 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1b, - 0x23, 0x1b, 0x24, 0x1c, 0x07, 0x1c, 0x1f, 0x1c, - 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x06, 0x1d, - 0x07, 0x1d, 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1d, - 0x23, 0x1d, 0x24, 0x1e, 0x06, 0x1e, 0x07, 0x1e, - 0x08, 0x1e, 0x1f, 0x1e, 0x23, 0x1e, 0x24, 0x1f, - 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, 0x1f, - 0x23, 0x1f, 0x24, 0x20, 0x06, 0x20, 0x07, 0x20, - 0x08, 0x20, 0x1f, 0x20, 0x23, 0x20, 0x24, 0x21, - 0x06, 0x21, 0x1f, 0x21, 0x23, 0x21, 0x24, 0x24, - 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24, - 0x23, 0x24, 0x24, 0x0a, 0x4a, 0x0b, 0x4a, 0x23, - 0x4a, 0x20, 0x00, 0x4c, 0x06, 0x51, 0x06, 0x51, - 0x06, 0xff, 0x00, 0x1f, 0x26, 0x06, 0x00, 0x0b, - 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x23, - 0x00, 0x24, 0x02, 0x0b, 0x02, 0x0c, 0x02, 0x1f, - 0x02, 0x20, 0x02, 0x23, 0x02, 0x24, 0x04, 0x0b, - 0x04, 0x0c, 0x04, 0x1f, 0x26, 0x06, 0x04, 0x20, - 0x04, 0x23, 0x04, 0x24, 0x05, 0x0b, 0x05, 0x0c, - 0x05, 0x1f, 0x05, 0x20, 0x05, 0x23, 0x05, 0x24, - 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x23, 0x1c, 0x24, - 0x1d, 0x01, 0x1d, 0x1e, 0x1d, 0x1f, 0x1d, 0x23, - 0x1d, 0x24, 0x1e, 0x1f, 0x1e, 0x23, 0x1e, 0x24, - 0x1f, 0x01, 0x1f, 0x1f, 0x20, 0x0b, 0x20, 0x0c, - 0x20, 0x1f, 0x20, 0x20, 0x20, 0x23, 0x20, 0x24, - 0x23, 0x4a, 0x24, 0x0b, 0x24, 0x0c, 0x24, 0x1f, - 0x24, 0x20, 0x24, 0x23, 0x24, 0x24, 0x00, 0x06, - 0x00, 0x07, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x21, - 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x1f, - 0x02, 0x21, 0x04, 0x06, 0x04, 0x07, 0x04, 0x08, - 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x06, 0x07, - 0x06, 0x1f, 0x07, 0x06, 0x07, 0x1f, 0x08, 0x06, - 0x08, 0x1f, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, - 0x0d, 0x1f, 0x0f, 0x07, 0x0f, 0x08, 0x0f, 0x1f, - 0x10, 0x06, 0x10, 0x07, 0x10, 0x08, 0x10, 0x1f, - 0x11, 0x07, 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, - 0x14, 0x06, 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, - 0x1b, 0x08, 0x1b, 0x1f, 0x1c, 0x07, 0x1c, 0x1f, - 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e, - 0x1d, 0x1f, 0x1e, 0x06, 0x1e, 0x07, 0x1e, 0x08, - 0x1e, 0x1f, 0x1e, 0x21, 0x1f, 0x06, 0x1f, 0x07, - 0x1f, 0x08, 0x1f, 0x1f, 0x20, 0x06, 0x20, 0x07, - 0x20, 0x08, 0x20, 0x1f, 0x20, 0x21, 0x21, 0x06, - 0x21, 0x1f, 0x21, 0x4a, 0x24, 0x06, 0x24, 0x07, - 0x24, 0x08, 0x24, 0x1f, 0x24, 0x21, 0x00, 0x1f, - 0x00, 0x21, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x1f, - 0x04, 0x21, 0x05, 0x1f, 0x05, 0x21, 0x0d, 0x1f, - 0x0d, 0x21, 0x0e, 0x1f, 0x0e, 0x21, 0x1d, 0x1e, - 0x1d, 0x1f, 0x1e, 0x1f, 0x20, 0x1f, 0x20, 0x21, - 0x24, 0x1f, 0x24, 0x21, 0x40, 0x06, 0x4e, 0x06, - 0x51, 0x06, 0x27, 0x06, 0x10, 0x22, 0x10, 0x23, - 0x12, 0x22, 0x12, 0x23, 0x13, 0x22, 0x13, 0x23, - 0x0c, 0x22, 0x0c, 0x23, 0x0d, 0x22, 0x0d, 0x23, - 0x06, 0x22, 0x06, 0x23, 0x05, 0x22, 0x05, 0x23, - 0x07, 0x22, 0x07, 0x23, 0x0e, 0x22, 0x0e, 0x23, - 0x0f, 0x22, 0x0f, 0x23, 0x0d, 0x05, 0x0d, 0x06, - 0x0d, 0x07, 0x0d, 0x1e, 0x0d, 0x0a, 0x0c, 0x0a, - 0x0e, 0x0a, 0x0f, 0x0a, 0x10, 0x22, 0x10, 0x23, - 0x12, 0x22, 0x12, 0x23, 0x13, 0x22, 0x13, 0x23, - 0x0c, 0x22, 0x0c, 0x23, 0x0d, 0x22, 0x0d, 0x23, - 0x06, 0x22, 0x06, 0x23, 0x05, 0x22, 0x05, 0x23, - 0x07, 0x22, 0x07, 0x23, 0x0e, 0x22, 0x0e, 0x23, - 0x0f, 0x22, 0x0f, 0x23, 0x0d, 0x05, 0x0d, 0x06, - 0x0d, 0x07, 0x0d, 0x1e, 0x0d, 0x0a, 0x0c, 0x0a, - 0x0e, 0x0a, 0x0f, 0x0a, 0x0d, 0x05, 0x0d, 0x06, - 0x0d, 0x07, 0x0d, 0x1e, 0x0c, 0x20, 0x0d, 0x20, - 0x10, 0x1e, 0x0c, 0x05, 0x0c, 0x06, 0x0c, 0x07, - 0x0d, 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x10, 0x1e, - 0x11, 0x1e, 0x00, 0x24, 0x00, 0x24, 0x2a, 0x06, - 0x00, 0x02, 0x1b, 0x00, 0x03, 0x02, 0x00, 0x03, - 0x02, 0x00, 0x03, 0x1b, 0x00, 0x04, 0x1b, 0x00, - 0x1b, 0x02, 0x00, 0x1b, 0x03, 0x00, 0x1b, 0x04, - 0x02, 0x1b, 0x03, 0x02, 0x1b, 0x03, 0x03, 0x1b, - 0x20, 0x03, 0x1b, 0x1f, 0x09, 0x03, 0x02, 0x09, - 0x02, 0x03, 0x09, 0x02, 0x1f, 0x09, 0x1b, 0x03, - 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x02, 0x09, 0x1b, - 0x1b, 0x09, 0x1b, 0x1b, 0x0b, 0x03, 0x03, 0x0b, - 0x03, 0x03, 0x0b, 0x1b, 0x1b, 0x0a, 0x03, 0x1b, - 0x0a, 0x03, 0x1b, 0x0a, 0x02, 0x20, 0x0a, 0x1b, - 0x04, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x1b, 0x0a, - 0x1b, 0x1b, 0x0c, 0x03, 0x1f, 0x0c, 0x04, 0x1b, - 0x0c, 0x04, 0x1b, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, - 0x03, 0x0d, 0x1b, 0x1b, 0x0d, 0x1b, 0x20, 0x0f, - 0x02, 0x1b, 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1b, - 0x0f, 0x1b, 0x1f, 0x10, 0x1b, 0x1b, 0x10, 0x1b, - 0x20, 0x10, 0x1b, 0x1f, 0x17, 0x04, 0x1b, 0x17, - 0x04, 0x1b, 0x18, 0x1b, 0x03, 0x18, 0x1b, 0x1b, - 0x1a, 0x03, 0x1b, 0x1a, 0x03, 0x20, 0x1a, 0x03, - 0x1f, 0x1a, 0x02, 0x02, 0x1a, 0x02, 0x02, 0x1a, - 0x04, 0x1b, 0x1a, 0x04, 0x1b, 0x1a, 0x1b, 0x03, - 0x1a, 0x1b, 0x03, 0x1b, 0x03, 0x02, 0x1b, 0x03, - 0x1b, 0x1b, 0x03, 0x20, 0x1b, 0x02, 0x03, 0x1b, - 0x02, 0x1b, 0x1b, 0x04, 0x02, 0x1b, 0x04, 0x1b, - 0x28, 0x06, 0x1d, 0x04, 0x06, 0x1f, 0x1d, 0x04, - 0x1f, 0x1d, 0x1d, 0x1e, 0x05, 0x1d, 0x1e, 0x05, - 0x21, 0x1e, 0x04, 0x1d, 0x1e, 0x04, 0x1d, 0x1e, - 0x04, 0x21, 0x1e, 0x1d, 0x22, 0x1e, 0x1d, 0x21, - 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x00, 0x06, - 0x22, 0x02, 0x04, 0x22, 0x02, 0x04, 0x21, 0x02, - 0x06, 0x22, 0x02, 0x06, 0x21, 0x02, 0x1d, 0x22, - 0x02, 0x1d, 0x21, 0x04, 0x1d, 0x22, 0x04, 0x05, - 0x21, 0x04, 0x1d, 0x21, 0x0b, 0x06, 0x21, 0x0d, - 0x05, 0x22, 0x0c, 0x05, 0x22, 0x0e, 0x05, 0x22, - 0x1c, 0x04, 0x22, 0x1c, 0x1d, 0x22, 0x22, 0x05, - 0x22, 0x22, 0x04, 0x22, 0x22, 0x1d, 0x22, 0x1d, - 0x1d, 0x22, 0x1a, 0x1d, 0x22, 0x1e, 0x05, 0x22, - 0x1a, 0x1d, 0x05, 0x1c, 0x05, 0x1d, 0x11, 0x1d, - 0x22, 0x1b, 0x1d, 0x22, 0x1e, 0x04, 0x05, 0x1d, - 0x06, 0x22, 0x1c, 0x04, 0x1d, 0x1b, 0x1d, 0x1d, - 0x1c, 0x04, 0x1d, 0x1e, 0x04, 0x05, 0x04, 0x05, - 0x22, 0x05, 0x04, 0x22, 0x1d, 0x04, 0x22, 0x19, - 0x1d, 0x22, 0x00, 0x05, 0x22, 0x1b, 0x1d, 0x1d, - 0x11, 0x04, 0x1d, 0x0d, 0x1d, 0x1d, 0x0b, 0x06, - 0x22, 0x1e, 0x04, 0x22, 0x35, 0x06, 0x00, 0x0f, - 0x9d, 0x0d, 0x0f, 0x9d, 0x27, 0x06, 0x00, 0x1d, - 0x1d, 0x20, 0x00, 0x1c, 0x01, 0x0a, 0x1e, 0x06, - 0x1e, 0x08, 0x0e, 0x1d, 0x12, 0x1e, 0x0a, 0x0c, - 0x21, 0x1d, 0x12, 0x1d, 0x23, 0x20, 0x21, 0x0c, - 0x1d, 0x1e, 0x35, 0x06, 0x00, 0x0f, 0x14, 0x27, - 0x06, 0x0e, 0x1d, 0x22, 0xff, 0x00, 0x1d, 0x1d, - 0x20, 0xff, 0x12, 0x1d, 0x23, 0x20, 0xff, 0x21, - 0x0c, 0x1d, 0x1e, 0x27, 0x06, 0x05, 0x1d, 0xff, - 0x05, 0x1d, 0x00, 0x1d, 0x20, 0x27, 0x06, 0x0a, - 0xa5, 0x00, 0x1d, 0x2c, 0x00, 0x01, 0x30, 0x02, - 0x30, 0x3a, 0x00, 0x3b, 0x00, 0x21, 0x00, 0x3f, - 0x00, 0x16, 0x30, 0x17, 0x30, 0x26, 0x20, 0x13, - 0x20, 0x12, 0x01, 0x00, 0x5f, 0x5f, 0x28, 0x29, - 0x7b, 0x7d, 0x08, 0x30, 0x0c, 0x0d, 0x08, 0x09, - 0x02, 0x03, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, - 0x5b, 0x00, 0x5d, 0x00, 0x3e, 0x20, 0x3e, 0x20, - 0x3e, 0x20, 0x3e, 0x20, 0x5f, 0x00, 0x5f, 0x00, - 0x5f, 0x00, 0x2c, 0x00, 0x01, 0x30, 0x2e, 0x00, - 0x00, 0x00, 0x3b, 0x00, 0x3a, 0x00, 0x3f, 0x00, - 0x21, 0x00, 0x14, 0x20, 0x28, 0x00, 0x29, 0x00, - 0x7b, 0x00, 0x7d, 0x00, 0x14, 0x30, 0x15, 0x30, - 0x23, 0x26, 0x2a, 0x2b, 0x2d, 0x3c, 0x3e, 0x3d, - 0x00, 0x5c, 0x24, 0x25, 0x40, 0x40, 0x06, 0xff, - 0x0b, 0x00, 0x0b, 0xff, 0x0c, 0x20, 0x00, 0x4d, - 0x06, 0x40, 0x06, 0xff, 0x0e, 0x00, 0x0e, 0xff, - 0x0f, 0x00, 0x0f, 0xff, 0x10, 0x00, 0x10, 0xff, - 0x11, 0x00, 0x11, 0xff, 0x12, 0x00, 0x12, 0x21, - 0x06, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, - 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, - 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, - 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, - 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, - 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, - 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, - 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, - 0x19, 0x19, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, - 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, - 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, - 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, - 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, 0x22, 0x06, - 0x22, 0x00, 0x22, 0x00, 0x22, 0x01, 0x22, 0x01, - 0x22, 0x03, 0x22, 0x03, 0x22, 0x05, 0x22, 0x05, - 0x21, 0x00, 0x85, 0x29, 0x01, 0x30, 0x01, 0x0b, - 0x0c, 0x00, 0xfa, 0xf1, 0xa0, 0xa2, 0xa4, 0xa6, - 0xa8, 0xe2, 0xe4, 0xe6, 0xc2, 0xfb, 0xa1, 0xa3, - 0xa5, 0xa7, 0xa9, 0xaa, 0xac, 0xae, 0xb0, 0xb2, - 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 0xc0, 0xc3, - 0xc5, 0xc7, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, - 0xd1, 0xd4, 0xd7, 0xda, 0xdd, 0xde, 0xdf, 0xe0, - 0xe1, 0xe3, 0xe5, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, - 0xec, 0xee, 0xf2, 0x98, 0x99, 0x31, 0x31, 0x4f, - 0x31, 0x55, 0x31, 0x5b, 0x31, 0x61, 0x31, 0xa2, - 0x00, 0xa3, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xa6, - 0x00, 0xa5, 0x00, 0xa9, 0x20, 0x00, 0x00, 0x02, - 0x25, 0x90, 0x21, 0x91, 0x21, 0x92, 0x21, 0x93, - 0x21, 0xa0, 0x25, 0xcb, 0x25, 0x99, 0x10, 0xba, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, 0xba, - 0x10, 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, 0x05, - 0x31, 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, 0x11, - 0x55, 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, 0x57, - 0x13, 0x55, 0xb9, 0x14, 0xba, 0x14, 0xb9, 0x14, - 0xb0, 0x14, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x14, - 0xbd, 0x14, 0x55, 0x50, 0xb8, 0x15, 0xaf, 0x15, - 0xb9, 0x15, 0xaf, 0x15, 0x55, 0x35, 0x19, 0x30, - 0x19, 0x05, 0x57, 0xd1, 0x65, 0xd1, 0x58, 0xd1, - 0x65, 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, 0xd1, - 0x6f, 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, 0xd1, - 0x71, 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, 0x55, - 0x55, 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, 0xd1, - 0x65, 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, 0xd1, - 0x6e, 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, 0xd1, - 0x6f, 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, 0x61, - 0x00, 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, 0x41, - 0x00, 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, 0x00, - 0x00, 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, 0x00, - 0x4e, 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, - 0x64, 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, 0x41, - 0x00, 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, - 0x46, 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, 0x00, - 0x41, 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, 0x00, - 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, 0x53, - 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, - 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, - 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, - 0x00, 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, 0x91, - 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, - 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, - 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, - 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, - 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, - 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, - 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, - 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, - 0x00, 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, 0x30, - 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, - 0x00, 0x27, 0x06, 0x00, 0x01, 0x05, 0x08, 0x2a, - 0x06, 0x1e, 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, - 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, - 0x0a, 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, - 0x44, 0x90, 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, - 0x00, 0x00, 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, - 0x11, 0x12, 0x13, 0x00, 0x06, 0x0e, 0x02, 0x0f, - 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, - 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, - 0x2d, 0x06, 0x00, 0x00, 0x4a, 0x06, 0x00, 0x00, - 0x44, 0x06, 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, - 0x39, 0x06, 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, - 0x00, 0x00, 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x2e, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, - 0x3a, 0x06, 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, - 0x6f, 0x06, 0x00, 0x00, 0x28, 0x06, 0x2c, 0x06, - 0x00, 0x00, 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, - 0x00, 0x00, 0x45, 0x06, 0x46, 0x06, 0x33, 0x06, - 0x39, 0x06, 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, - 0x00, 0x00, 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, - 0x2e, 0x06, 0x00, 0x00, 0x36, 0x06, 0x38, 0x06, - 0x3a, 0x06, 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, - 0x27, 0x06, 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, - 0x0b, 0x06, 0x10, 0x23, 0x2a, 0x06, 0x1a, 0x1b, - 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, - 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, - 0x06, 0x2c, 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, - 0x06, 0x32, 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, - 0x06, 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, - 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, - 0x06, 0x0c, 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, - 0x2c, 0x00, 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, - 0x14, 0x30, 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, - 0x43, 0x44, 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, - 0x4d, 0x56, 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, - 0x56, 0x57, 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, - 0x52, 0x44, 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, - 0x68, 0x4b, 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, - 0x30, 0x8c, 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, - 0x59, 0xa4, 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, - 0x65, 0x4d, 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, - 0x65, 0x1d, 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, - 0x8c, 0xf0, 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, - 0x62, 0x55, 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, - 0x90, 0xe6, 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, - 0x63, 0x70, 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, - 0x7a, 0x08, 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, - 0x67, 0x33, 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, - 0x91, 0x14, 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, - 0x4e, 0x8c, 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, - 0x62, 0xd7, 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, - 0x5f, 0xef, 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, - 0x00, 0x09, 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, - 0xbb, 0x4f, 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, - 0xe7, 0x50, 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, - 0x4d, 0x51, 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, - 0x1c, 0x05, 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, - 0x4b, 0x05, 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, - 0xac, 0x51, 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, - 0x03, 0x52, 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, - 0x72, 0x52, 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, - 0x20, 0x80, 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, - 0x52, 0x00, 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, - 0x82, 0x8a, 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, - 0x2c, 0x0a, 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, - 0x63, 0x0b, 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, - 0x9e, 0x54, 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, - 0xa2, 0x54, 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, - 0x63, 0x55, 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, - 0xab, 0x55, 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, - 0x06, 0x56, 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, - 0x07, 0x52, 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, - 0x0d, 0x58, 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, - 0xac, 0x58, 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, - 0x06, 0x59, 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, - 0xa8, 0x16, 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, - 0x27, 0x5a, 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, - 0xfc, 0x36, 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, - 0xc8, 0x19, 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, - 0xf3, 0x5b, 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, - 0x53, 0x5f, 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, - 0x6e, 0x5c, 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, - 0x43, 0x5d, 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, - 0x7c, 0x5d, 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, - 0xfd, 0x5d, 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, - 0x62, 0x38, 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, - 0xb3, 0x5e, 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, - 0xfe, 0x5e, 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, - 0x22, 0x5f, 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, - 0xda, 0x61, 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, - 0x9a, 0x5f, 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, - 0x81, 0x60, 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, - 0xd4, 0x26, 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, - 0x00, 0x00, 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, - 0x00, 0x08, 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, - 0x02, 0x48, 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, - 0x46, 0x6a, 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, - 0xd3, 0x5d, 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, - 0x2b, 0x3d, 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, - 0x63, 0xe4, 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, - 0x63, 0xa9, 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, - 0x64, 0x9d, 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, - 0x65, 0x6c, 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, - 0x66, 0x49, 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, - 0x3b, 0xe4, 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, - 0x67, 0x9c, 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, - 0x67, 0x1b, 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, - 0x67, 0xc3, 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, - 0x67, 0x52, 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, - 0x68, 0x1f, 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, - 0x69, 0xa3, 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, - 0x36, 0xdb, 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, - 0x38, 0x54, 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, - 0x6b, 0xba, 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, - 0x1d, 0xfa, 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, - 0x6c, 0xcd, 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, - 0x6d, 0x77, 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, - 0x6d, 0x85, 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, - 0x6e, 0x6e, 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, - 0x6e, 0xd1, 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, - 0x3f, 0x8e, 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, - 0x70, 0x1b, 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, - 0x70, 0x77, 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, - 0x71, 0x63, 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, - 0x72, 0x35, 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, - 0x72, 0x95, 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, - 0x00, 0x00, 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, - 0x20, 0x00, 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, - 0x20, 0x14, 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, - 0x3e, 0xa5, 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, - 0x74, 0x5c, 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, - 0x74, 0x1b, 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, - 0x75, 0x92, 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, - 0x76, 0xa1, 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, - 0x3f, 0x08, 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, - 0x50, 0x19, 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, - 0x77, 0x1f, 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, - 0x77, 0x46, 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, - 0x78, 0x8c, 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, - 0x56, 0x56, 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, - 0x79, 0xeb, 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, - 0x7a, 0x4f, 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, - 0x5a, 0xee, 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, - 0x7b, 0xc9, 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, - 0x7c, 0xa0, 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, - 0x7d, 0x86, 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, - 0x7d, 0x02, 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, - 0x62, 0x47, 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, - 0x7f, 0x3e, 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, - 0x80, 0xda, 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, - 0x65, 0x70, 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, - 0x80, 0x03, 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, - 0x5a, 0xa7, 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, - 0x33, 0x01, 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, - 0x44, 0x91, 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, - 0x52, 0xb1, 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, - 0x82, 0x3c, 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, - 0x83, 0xad, 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, - 0x83, 0x57, 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, - 0x83, 0xdc, 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, - 0x00, 0x00, 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, - 0x20, 0x80, 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, - 0x02, 0x80, 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, - 0x6c, 0x2b, 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, - 0x85, 0xca, 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, - 0x45, 0x61, 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, - 0x45, 0x50, 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, - 0x86, 0xa9, 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, - 0x86, 0x79, 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, - 0x87, 0xd7, 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, - 0x45, 0x60, 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, - 0x88, 0xde, 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, - 0x34, 0xae, 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, - 0x46, 0xa0, 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, - 0x8c, 0xa8, 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, - 0x8d, 0x77, 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, - 0x8d, 0xbc, 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, - 0x8e, 0x38, 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, - 0x90, 0xf1, 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, - 0x91, 0x38, 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, - 0x92, 0xf9, 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, - 0x95, 0x95, 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, - 0x49, 0xc3, 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, - 0x91, 0x1a, 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, - 0x97, 0x0a, 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, - 0x98, 0x0b, 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, - 0x98, 0x33, 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, - 0x99, 0xfe, 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, - 0x9b, 0x40, 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, - 0x4c, 0x67, 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, - 0xa1, 0x0e, 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, - 0x4d, 0xf9, 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, - 0x9f, 0x16, 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, - 0x88, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x28, 0x00, 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, - 0x80, 0x80, 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, - 0x00, 0x20, 0x2a, 0x00, 0x80, -}; - -static const uint16_t unicode_comp_table[945] = { - 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, - 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, - 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, - 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292, - 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304, - 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306, - 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e, - 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8, - 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380, - 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac, - 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444, - 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940, - 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce, - 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296, - 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844, - 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e, - 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998, - 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322, - 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac, - 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326, - 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc, - 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce, - 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354, - 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3, - 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981, - 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7, - 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291, - 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303, - 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03, - 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d, - 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5, - 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343, - 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347, - 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5, - 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7, - 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3, - 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543, - 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1, - 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991, - 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d, - 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b, - 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3, - 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997, - 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343, - 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f, - 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf, - 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357, - 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448, - 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a, - 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06, - 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447, - 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289, - 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d, - 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb, - 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f, - 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306, - 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d, - 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8, - 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4, - 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882, - 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b, - 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541, - 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8, - 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8, - 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06, - 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850, - 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0, - 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940, - 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05, - 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0, - 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81, - 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184, - 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182, - 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0, - 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242, - 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0, - 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141, - 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245, - 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080, - 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341, - 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480, - 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800, - 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901, - 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80, - 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008, - 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9, - 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457, - 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0, - 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5, - 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583, - 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, - 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683, - 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01, - 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc, - 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b, - 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3, - 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df, - 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783, - 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844, - 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e, - 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851, - 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a, - 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180, - 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001, - 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700, - 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982, - 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81, - 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41, - 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01, - 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448, - 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480, - 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541, - 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702, - 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712, - 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0, - 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900, - 0x5901, 0x5902, 0x5903, 0x5940, 0x8e80, 0x8e82, 0x8ec0, 0x8f00, - 0x8f01, 0x8f40, 0x8f41, 0x8f81, 0x8f80, 0x8f83, 0x8fc0, 0x8fc1, - 0x9000, -}; - -typedef enum { - UNICODE_GC_Cn, - UNICODE_GC_Lu, - UNICODE_GC_Ll, - UNICODE_GC_Lt, - UNICODE_GC_Lm, - UNICODE_GC_Lo, - UNICODE_GC_Mn, - UNICODE_GC_Mc, - UNICODE_GC_Me, - UNICODE_GC_Nd, - UNICODE_GC_Nl, - UNICODE_GC_No, - UNICODE_GC_Sm, - UNICODE_GC_Sc, - UNICODE_GC_Sk, - UNICODE_GC_So, - UNICODE_GC_Pc, - UNICODE_GC_Pd, - UNICODE_GC_Ps, - UNICODE_GC_Pe, - UNICODE_GC_Pi, - UNICODE_GC_Pf, - UNICODE_GC_Po, - UNICODE_GC_Zs, - UNICODE_GC_Zl, - UNICODE_GC_Zp, - UNICODE_GC_Cc, - UNICODE_GC_Cf, - UNICODE_GC_Cs, - UNICODE_GC_Co, - UNICODE_GC_LC, - UNICODE_GC_L, - UNICODE_GC_M, - UNICODE_GC_N, - UNICODE_GC_S, - UNICODE_GC_P, - UNICODE_GC_Z, - UNICODE_GC_C, - UNICODE_GC_COUNT, -} UnicodeGCEnum; - -static const char unicode_gc_name_table[] = - "Cn,Unassigned" "\0" - "Lu,Uppercase_Letter" "\0" - "Ll,Lowercase_Letter" "\0" - "Lt,Titlecase_Letter" "\0" - "Lm,Modifier_Letter" "\0" - "Lo,Other_Letter" "\0" - "Mn,Nonspacing_Mark" "\0" - "Mc,Spacing_Mark" "\0" - "Me,Enclosing_Mark" "\0" - "Nd,Decimal_Number,digit" "\0" - "Nl,Letter_Number" "\0" - "No,Other_Number" "\0" - "Sm,Math_Symbol" "\0" - "Sc,Currency_Symbol" "\0" - "Sk,Modifier_Symbol" "\0" - "So,Other_Symbol" "\0" - "Pc,Connector_Punctuation" "\0" - "Pd,Dash_Punctuation" "\0" - "Ps,Open_Punctuation" "\0" - "Pe,Close_Punctuation" "\0" - "Pi,Initial_Punctuation" "\0" - "Pf,Final_Punctuation" "\0" - "Po,Other_Punctuation" "\0" - "Zs,Space_Separator" "\0" - "Zl,Line_Separator" "\0" - "Zp,Paragraph_Separator" "\0" - "Cc,Control,cntrl" "\0" - "Cf,Format" "\0" - "Cs,Surrogate" "\0" - "Co,Private_Use" "\0" - "LC,Cased_Letter" "\0" - "L,Letter" "\0" - "M,Mark,Combining_Mark" "\0" - "N,Number" "\0" - "S,Symbol" "\0" - "P,Punctuation,punct" "\0" - "Z,Separator" "\0" - "C,Other" "\0" -; - -static const uint8_t unicode_gc_table[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, - 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, - 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e, - 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f, - 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05, - 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2, - 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff, - 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02, - 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02, - 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f, - 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02, - 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01, - 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02, - 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21, - 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22, - 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05, - 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee, - 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09, - 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04, - 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41, - 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00, - 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42, - 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02, - 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86, - 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58, - 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21, - 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25, - 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06, - 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0, - 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6, - 0x03, 0x16, 0x1b, 0x00, 0x36, 0xe5, 0x18, 0x04, - 0xe5, 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, - 0x06, 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, - 0xa6, 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, - 0x45, 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, - 0x06, 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, - 0xe6, 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, - 0x19, 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, - 0x06, 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, - 0x04, 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, - 0xe5, 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, - 0xe0, 0x2d, 0xe5, 0x0d, 0x00, 0xe5, 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, - 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, 0x20, 0xe5, - 0x0e, 0x00, 0xc5, 0x00, 0x05, 0x40, 0x65, 0x20, - 0x06, 0x05, 0x47, 0x66, 0x20, 0x27, 0x20, 0x27, - 0x06, 0x05, 0xe0, 0x00, 0x07, 0x60, 0x25, 0x00, - 0x45, 0x26, 0x20, 0xe9, 0x02, 0x25, 0x2d, 0xab, - 0x0f, 0x0d, 0x05, 0x16, 0x06, 0x20, 0x26, 0x07, - 0x00, 0xa5, 0x60, 0x25, 0x20, 0xe5, 0x0e, 0x00, - 0xc5, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x20, - 0x06, 0x00, 0x47, 0x26, 0x60, 0x26, 0x20, 0x46, - 0x40, 0x06, 0xc0, 0x65, 0x00, 0x05, 0xc0, 0xe9, - 0x02, 0x26, 0x45, 0x06, 0x16, 0xe0, 0x02, 0x26, - 0x07, 0x00, 0xe5, 0x01, 0x00, 0x45, 0x00, 0xe5, - 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x85, 0x20, - 0x06, 0x05, 0x47, 0x86, 0x00, 0x26, 0x07, 0x00, - 0x27, 0x06, 0x20, 0x05, 0xe0, 0x07, 0x25, 0x26, - 0x20, 0xe9, 0x02, 0x16, 0x0d, 0xc0, 0x05, 0xa6, - 0x00, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, - 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, - 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x07, 0x66, - 0x20, 0x27, 0x20, 0x27, 0x06, 0xc0, 0x26, 0x07, - 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02, - 0x0f, 0x05, 0xab, 0xe0, 0x02, 0x06, 0x05, 0x00, - 0xa5, 0x40, 0x45, 0x00, 0x65, 0x40, 0x25, 0x00, - 0x05, 0x00, 0x25, 0x40, 0x25, 0x40, 0x45, 0x40, - 0xe5, 0x04, 0x60, 0x27, 0x06, 0x27, 0x40, 0x47, - 0x00, 0x47, 0x06, 0x20, 0x05, 0xa0, 0x07, 0xe0, - 0x06, 0xe9, 0x02, 0x4b, 0xaf, 0x0d, 0x0f, 0x80, - 0x06, 0x47, 0x06, 0xe5, 0x00, 0x00, 0x45, 0x00, - 0xe5, 0x0f, 0x00, 0xe5, 0x08, 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, - 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, 0x07, 0x46, - 0x25, 0xe9, 0x02, 0xe5, 0x24, 0x06, 0x07, 0x26, - 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, 0x00, 0x76, - 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, 0x27, 0x26, - 0x40, 0x96, 0xe9, 0x02, 0x40, 0x45, 0xe9, 0x02, - 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, 0xc0, 0xe1, - 0x23, 0x20, 0x41, 0xf6, 0x00, 0xe0, 0x00, 0x46, - 0x16, 0xe6, 0x05, 0x07, 0xc6, 0x65, 0x06, 0xa5, - 0x06, 0x25, 0x07, 0x26, 0x05, 0x80, 0xe2, 0x24, - 0xe4, 0x37, 0xe2, 0x05, 0x04, 0xe2, 0x1a, 0xe4, - 0x1d, 0xe6, 0x32, 0x00, 0x86, 0xff, 0x80, 0x0e, - 0xe2, 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, 0x00, - 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, 0x00, - 0xe2, 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, 0x20, - 0xe2, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, 0x20, - 0xe2, 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, 0x00, - 0xe2, 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, 0x61, - 0x03, 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, 0x61, - 0x03, 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, 0x4e, - 0xe2, 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, 0x22, - 0x61, 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, 0xb1, - 0x36, 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, 0x14, - 0xf6, 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, 0x01, - 0x14, 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, 0x13, - 0xf6, 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, 0x17, - 0x9b, 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, 0xab, - 0x4c, 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, 0x12, - 0x13, 0x00, 0xe4, 0x05, 0x40, 0xed, 0x18, 0xe0, - 0x08, 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, 0x04, - 0xe0, 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, 0x02, - 0x41, 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, 0x0c, - 0x81, 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, 0x0f, - 0x61, 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, 0x2f, - 0x22, 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, 0x2f, - 0x02, 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, 0x6a, - 0x0b, 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, 0x0c, - 0x2f, 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, 0x17, - 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, 0xec, - 0x80, 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, 0x13, - 0xef, 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, 0x49, - 0x0c, 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, 0xac, - 0xef, 0x3d, 0xe0, 0x11, 0xef, 0x03, 0xe0, 0x0d, - 0xeb, 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, 0x80, - 0x2f, 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, 0xec, - 0x00, 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, 0xef, - 0x24, 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, 0x13, - 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, - 0xec, 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, - 0x13, 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, 0x12, - 0x13, 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, 0xec, - 0x80, 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, 0xac, - 0xef, 0x1f, 0x20, 0xef, 0x18, 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, - 0x46, 0x3f, 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, - 0x00, 0x02, 0x80, 0x02, 0x20, 0xe5, 0x30, 0xc0, - 0x04, 0x16, 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, - 0x01, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, - 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, - 0x00, 0xe6, 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, - 0x56, 0x14, 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, - 0x11, 0x36, 0x11, 0x16, 0x14, 0x15, 0x36, 0x14, - 0x15, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, - 0x13, 0x96, 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, - 0x16, 0x12, 0xf6, 0x05, 0x2f, 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, 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, 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 { - UNICODE_SCRIPT_Unknown, - UNICODE_SCRIPT_Adlam, - UNICODE_SCRIPT_Ahom, - UNICODE_SCRIPT_Anatolian_Hieroglyphs, - UNICODE_SCRIPT_Arabic, - UNICODE_SCRIPT_Armenian, - UNICODE_SCRIPT_Avestan, - UNICODE_SCRIPT_Balinese, - UNICODE_SCRIPT_Bamum, - UNICODE_SCRIPT_Bassa_Vah, - UNICODE_SCRIPT_Batak, - UNICODE_SCRIPT_Bengali, - UNICODE_SCRIPT_Bhaiksuki, - UNICODE_SCRIPT_Bopomofo, - UNICODE_SCRIPT_Brahmi, - UNICODE_SCRIPT_Braille, - UNICODE_SCRIPT_Buginese, - UNICODE_SCRIPT_Buhid, - UNICODE_SCRIPT_Canadian_Aboriginal, - UNICODE_SCRIPT_Carian, - UNICODE_SCRIPT_Caucasian_Albanian, - UNICODE_SCRIPT_Chakma, - UNICODE_SCRIPT_Cham, - UNICODE_SCRIPT_Cherokee, - UNICODE_SCRIPT_Chorasmian, - UNICODE_SCRIPT_Common, - UNICODE_SCRIPT_Coptic, - UNICODE_SCRIPT_Cuneiform, - UNICODE_SCRIPT_Cypriot, - UNICODE_SCRIPT_Cyrillic, - UNICODE_SCRIPT_Deseret, - UNICODE_SCRIPT_Devanagari, - UNICODE_SCRIPT_Dives_Akuru, - UNICODE_SCRIPT_Dogra, - UNICODE_SCRIPT_Duployan, - UNICODE_SCRIPT_Egyptian_Hieroglyphs, - UNICODE_SCRIPT_Elbasan, - UNICODE_SCRIPT_Elymaic, - UNICODE_SCRIPT_Ethiopic, - UNICODE_SCRIPT_Georgian, - UNICODE_SCRIPT_Glagolitic, - UNICODE_SCRIPT_Gothic, - UNICODE_SCRIPT_Grantha, - UNICODE_SCRIPT_Greek, - UNICODE_SCRIPT_Gujarati, - UNICODE_SCRIPT_Gunjala_Gondi, - UNICODE_SCRIPT_Gurmukhi, - UNICODE_SCRIPT_Han, - UNICODE_SCRIPT_Hangul, - UNICODE_SCRIPT_Hanifi_Rohingya, - UNICODE_SCRIPT_Hanunoo, - UNICODE_SCRIPT_Hatran, - UNICODE_SCRIPT_Hebrew, - UNICODE_SCRIPT_Hiragana, - UNICODE_SCRIPT_Imperial_Aramaic, - UNICODE_SCRIPT_Inherited, - UNICODE_SCRIPT_Inscriptional_Pahlavi, - UNICODE_SCRIPT_Inscriptional_Parthian, - UNICODE_SCRIPT_Javanese, - UNICODE_SCRIPT_Kaithi, - UNICODE_SCRIPT_Kannada, - UNICODE_SCRIPT_Katakana, - UNICODE_SCRIPT_Kayah_Li, - UNICODE_SCRIPT_Kharoshthi, - UNICODE_SCRIPT_Khmer, - UNICODE_SCRIPT_Khojki, - UNICODE_SCRIPT_Khitan_Small_Script, - UNICODE_SCRIPT_Khudawadi, - UNICODE_SCRIPT_Lao, - UNICODE_SCRIPT_Latin, - UNICODE_SCRIPT_Lepcha, - UNICODE_SCRIPT_Limbu, - UNICODE_SCRIPT_Linear_A, - UNICODE_SCRIPT_Linear_B, - UNICODE_SCRIPT_Lisu, - UNICODE_SCRIPT_Lycian, - UNICODE_SCRIPT_Lydian, - UNICODE_SCRIPT_Makasar, - UNICODE_SCRIPT_Mahajani, - UNICODE_SCRIPT_Malayalam, - UNICODE_SCRIPT_Mandaic, - UNICODE_SCRIPT_Manichaean, - UNICODE_SCRIPT_Marchen, - UNICODE_SCRIPT_Masaram_Gondi, - UNICODE_SCRIPT_Medefaidrin, - UNICODE_SCRIPT_Meetei_Mayek, - UNICODE_SCRIPT_Mende_Kikakui, - UNICODE_SCRIPT_Meroitic_Cursive, - UNICODE_SCRIPT_Meroitic_Hieroglyphs, - UNICODE_SCRIPT_Miao, - UNICODE_SCRIPT_Modi, - UNICODE_SCRIPT_Mongolian, - UNICODE_SCRIPT_Mro, - UNICODE_SCRIPT_Multani, - UNICODE_SCRIPT_Myanmar, - UNICODE_SCRIPT_Nabataean, - UNICODE_SCRIPT_Nandinagari, - UNICODE_SCRIPT_New_Tai_Lue, - UNICODE_SCRIPT_Newa, - UNICODE_SCRIPT_Nko, - UNICODE_SCRIPT_Nushu, - UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, - UNICODE_SCRIPT_Ogham, - UNICODE_SCRIPT_Ol_Chiki, - UNICODE_SCRIPT_Old_Hungarian, - UNICODE_SCRIPT_Old_Italic, - UNICODE_SCRIPT_Old_North_Arabian, - UNICODE_SCRIPT_Old_Permic, - UNICODE_SCRIPT_Old_Persian, - UNICODE_SCRIPT_Old_Sogdian, - UNICODE_SCRIPT_Old_South_Arabian, - UNICODE_SCRIPT_Old_Turkic, - UNICODE_SCRIPT_Oriya, - UNICODE_SCRIPT_Osage, - UNICODE_SCRIPT_Osmanya, - UNICODE_SCRIPT_Pahawh_Hmong, - UNICODE_SCRIPT_Palmyrene, - UNICODE_SCRIPT_Pau_Cin_Hau, - UNICODE_SCRIPT_Phags_Pa, - UNICODE_SCRIPT_Phoenician, - UNICODE_SCRIPT_Psalter_Pahlavi, - UNICODE_SCRIPT_Rejang, - UNICODE_SCRIPT_Runic, - UNICODE_SCRIPT_Samaritan, - UNICODE_SCRIPT_Saurashtra, - UNICODE_SCRIPT_Sharada, - UNICODE_SCRIPT_Shavian, - UNICODE_SCRIPT_Siddham, - UNICODE_SCRIPT_SignWriting, - UNICODE_SCRIPT_Sinhala, - UNICODE_SCRIPT_Sogdian, - UNICODE_SCRIPT_Sora_Sompeng, - UNICODE_SCRIPT_Soyombo, - UNICODE_SCRIPT_Sundanese, - UNICODE_SCRIPT_Syloti_Nagri, - UNICODE_SCRIPT_Syriac, - UNICODE_SCRIPT_Tagalog, - UNICODE_SCRIPT_Tagbanwa, - UNICODE_SCRIPT_Tai_Le, - UNICODE_SCRIPT_Tai_Tham, - UNICODE_SCRIPT_Tai_Viet, - UNICODE_SCRIPT_Takri, - UNICODE_SCRIPT_Tamil, - UNICODE_SCRIPT_Tangut, - UNICODE_SCRIPT_Telugu, - UNICODE_SCRIPT_Thaana, - UNICODE_SCRIPT_Thai, - UNICODE_SCRIPT_Tibetan, - UNICODE_SCRIPT_Tifinagh, - UNICODE_SCRIPT_Tirhuta, - UNICODE_SCRIPT_Ugaritic, - UNICODE_SCRIPT_Vai, - UNICODE_SCRIPT_Wancho, - UNICODE_SCRIPT_Warang_Citi, - UNICODE_SCRIPT_Yezidi, - UNICODE_SCRIPT_Yi, - UNICODE_SCRIPT_Zanabazar_Square, - UNICODE_SCRIPT_COUNT, -} UnicodeScriptEnum; - -static const char unicode_script_name_table[] = - "Adlam,Adlm" "\0" - "Ahom,Ahom" "\0" - "Anatolian_Hieroglyphs,Hluw" "\0" - "Arabic,Arab" "\0" - "Armenian,Armn" "\0" - "Avestan,Avst" "\0" - "Balinese,Bali" "\0" - "Bamum,Bamu" "\0" - "Bassa_Vah,Bass" "\0" - "Batak,Batk" "\0" - "Bengali,Beng" "\0" - "Bhaiksuki,Bhks" "\0" - "Bopomofo,Bopo" "\0" - "Brahmi,Brah" "\0" - "Braille,Brai" "\0" - "Buginese,Bugi" "\0" - "Buhid,Buhd" "\0" - "Canadian_Aboriginal,Cans" "\0" - "Carian,Cari" "\0" - "Caucasian_Albanian,Aghb" "\0" - "Chakma,Cakm" "\0" - "Cham,Cham" "\0" - "Cherokee,Cher" "\0" - "Chorasmian,Chrs" "\0" - "Common,Zyyy" "\0" - "Coptic,Copt,Qaac" "\0" - "Cuneiform,Xsux" "\0" - "Cypriot,Cprt" "\0" - "Cyrillic,Cyrl" "\0" - "Deseret,Dsrt" "\0" - "Devanagari,Deva" "\0" - "Dives_Akuru,Diak" "\0" - "Dogra,Dogr" "\0" - "Duployan,Dupl" "\0" - "Egyptian_Hieroglyphs,Egyp" "\0" - "Elbasan,Elba" "\0" - "Elymaic,Elym" "\0" - "Ethiopic,Ethi" "\0" - "Georgian,Geor" "\0" - "Glagolitic,Glag" "\0" - "Gothic,Goth" "\0" - "Grantha,Gran" "\0" - "Greek,Grek" "\0" - "Gujarati,Gujr" "\0" - "Gunjala_Gondi,Gong" "\0" - "Gurmukhi,Guru" "\0" - "Han,Hani" "\0" - "Hangul,Hang" "\0" - "Hanifi_Rohingya,Rohg" "\0" - "Hanunoo,Hano" "\0" - "Hatran,Hatr" "\0" - "Hebrew,Hebr" "\0" - "Hiragana,Hira" "\0" - "Imperial_Aramaic,Armi" "\0" - "Inherited,Zinh,Qaai" "\0" - "Inscriptional_Pahlavi,Phli" "\0" - "Inscriptional_Parthian,Prti" "\0" - "Javanese,Java" "\0" - "Kaithi,Kthi" "\0" - "Kannada,Knda" "\0" - "Katakana,Kana" "\0" - "Kayah_Li,Kali" "\0" - "Kharoshthi,Khar" "\0" - "Khmer,Khmr" "\0" - "Khojki,Khoj" "\0" - "Khitan_Small_Script,Kits" "\0" - "Khudawadi,Sind" "\0" - "Lao,Laoo" "\0" - "Latin,Latn" "\0" - "Lepcha,Lepc" "\0" - "Limbu,Limb" "\0" - "Linear_A,Lina" "\0" - "Linear_B,Linb" "\0" - "Lisu,Lisu" "\0" - "Lycian,Lyci" "\0" - "Lydian,Lydi" "\0" - "Makasar,Maka" "\0" - "Mahajani,Mahj" "\0" - "Malayalam,Mlym" "\0" - "Mandaic,Mand" "\0" - "Manichaean,Mani" "\0" - "Marchen,Marc" "\0" - "Masaram_Gondi,Gonm" "\0" - "Medefaidrin,Medf" "\0" - "Meetei_Mayek,Mtei" "\0" - "Mende_Kikakui,Mend" "\0" - "Meroitic_Cursive,Merc" "\0" - "Meroitic_Hieroglyphs,Mero" "\0" - "Miao,Plrd" "\0" - "Modi,Modi" "\0" - "Mongolian,Mong" "\0" - "Mro,Mroo" "\0" - "Multani,Mult" "\0" - "Myanmar,Mymr" "\0" - "Nabataean,Nbat" "\0" - "Nandinagari,Nand" "\0" - "New_Tai_Lue,Talu" "\0" - "Newa,Newa" "\0" - "Nko,Nkoo" "\0" - "Nushu,Nshu" "\0" - "Nyiakeng_Puachue_Hmong,Hmnp" "\0" - "Ogham,Ogam" "\0" - "Ol_Chiki,Olck" "\0" - "Old_Hungarian,Hung" "\0" - "Old_Italic,Ital" "\0" - "Old_North_Arabian,Narb" "\0" - "Old_Permic,Perm" "\0" - "Old_Persian,Xpeo" "\0" - "Old_Sogdian,Sogo" "\0" - "Old_South_Arabian,Sarb" "\0" - "Old_Turkic,Orkh" "\0" - "Oriya,Orya" "\0" - "Osage,Osge" "\0" - "Osmanya,Osma" "\0" - "Pahawh_Hmong,Hmng" "\0" - "Palmyrene,Palm" "\0" - "Pau_Cin_Hau,Pauc" "\0" - "Phags_Pa,Phag" "\0" - "Phoenician,Phnx" "\0" - "Psalter_Pahlavi,Phlp" "\0" - "Rejang,Rjng" "\0" - "Runic,Runr" "\0" - "Samaritan,Samr" "\0" - "Saurashtra,Saur" "\0" - "Sharada,Shrd" "\0" - "Shavian,Shaw" "\0" - "Siddham,Sidd" "\0" - "SignWriting,Sgnw" "\0" - "Sinhala,Sinh" "\0" - "Sogdian,Sogd" "\0" - "Sora_Sompeng,Sora" "\0" - "Soyombo,Soyo" "\0" - "Sundanese,Sund" "\0" - "Syloti_Nagri,Sylo" "\0" - "Syriac,Syrc" "\0" - "Tagalog,Tglg" "\0" - "Tagbanwa,Tagb" "\0" - "Tai_Le,Tale" "\0" - "Tai_Tham,Lana" "\0" - "Tai_Viet,Tavt" "\0" - "Takri,Takr" "\0" - "Tamil,Taml" "\0" - "Tangut,Tang" "\0" - "Telugu,Telu" "\0" - "Thaana,Thaa" "\0" - "Thai,Thai" "\0" - "Tibetan,Tibt" "\0" - "Tifinagh,Tfng" "\0" - "Tirhuta,Tirh" "\0" - "Ugaritic,Ugar" "\0" - "Vai,Vaii" "\0" - "Wancho,Wcho" "\0" - "Warang_Citi,Wara" "\0" - "Yezidi,Yezi" "\0" - "Yi,Yiii" "\0" - "Zanabazar_Square,Zanb" "\0" -; - -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[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] = { - 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52, - 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80, - 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40, - 0xa8, 0x80, 0xd6, 0x80, -}; - -static const uint8_t unicode_prop_Other_Math_table[200] = { - 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09, - 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80, - 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c, - 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00, - 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24, - 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b, - 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41, - 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80, - 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24, - 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97, - 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81, - 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95, - 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00, - 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4, - 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, - 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, - 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, - 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, - 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, - 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x81, - 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08, - 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, - 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, - 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, -}; - -static const uint8_t unicode_prop_Other_Alphabetic_table[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, - 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, 0x88, 0x02, - 0x03, 0x40, 0xa6, 0x8b, 0x16, 0x85, 0x93, 0xb5, - 0x09, 0x8e, 0x01, 0x22, 0x89, 0x81, 0x9c, 0x82, - 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81, - 0x9c, 0x82, 0xb9, 0x23, 0x09, 0x0b, 0x80, 0x9d, - 0x0a, 0x80, 0x8a, 0x82, 0xb9, 0x38, 0x10, 0x81, - 0x94, 0x81, 0x95, 0x13, 0x82, 0xb9, 0x31, 0x09, - 0x81, 0x88, 0x81, 0x89, 0x81, 0x9d, 0x80, 0xba, - 0x22, 0x10, 0x82, 0x89, 0x80, 0xa7, 0x83, 0xb9, - 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82, - 0xb9, 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9b, - 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, 0x80, 0x89, - 0x81, 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, - 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x00, - 0x89, 0x83, 0x46, 0x73, 0x81, 0x9d, 0x81, 0x9d, - 0x81, 0x9d, 0x81, 0xc1, 0x92, 0x40, 0xbb, 0x81, - 0xa1, 0x80, 0xf5, 0x8b, 0x83, 0x88, 0x40, 0xdd, - 0x84, 0xb8, 0x89, 0x81, 0x93, 0xc9, 0x81, 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] = { - 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, - 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x59, - 0xb0, 0xbe, 0x8c, 0x80, 0xa1, 0xa4, 0x42, 0xb0, - 0x80, 0x8c, 0x80, 0x8f, 0x8c, 0x40, 0xd2, 0x8f, - 0x43, 0x4f, 0x99, 0x47, 0x91, 0x81, 0x60, 0x7a, - 0x1d, 0x81, 0x40, 0xd1, 0x80, 0x40, 0x86, 0x81, - 0x43, 0x61, 0x83, -}; - -static const uint8_t unicode_prop_Other_Uppercase_table[15] = { - 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61, - 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, -}; - -static const uint8_t unicode_prop_Other_Grapheme_Extend_table[65] = { - 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, - 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe9, - 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, 0x80, 0xf6, - 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5, - 0x80, 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81, - 0x53, 0x9d, 0x80, 0x97, 0x80, 0x41, 0x57, 0x80, - 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x43, 0x7f, 0x80, - 0x60, 0xb8, 0x33, 0x07, 0x84, 0x6c, 0x2e, 0xac, - 0xdf, -}; - -static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { - 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52, - 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60, - 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06, - 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f, -}; - -static const uint8_t unicode_prop_Other_ID_Start_table[11] = { - 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80, - 0x4f, 0x6b, 0x81, -}; - -static const uint8_t unicode_prop_Other_ID_Continue_table[12] = { - 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, - 0x88, 0x46, 0x67, 0x80, -}; - -static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[17] = { - 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, - 0x41, 0xd1, 0x80, 0x61, 0x07, 0xd9, 0x80, 0x8e, - 0x80, -}; - -static const uint8_t unicode_prop_XID_Start1_table[31] = { - 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80, - 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85, - 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81, -}; - -static const uint8_t unicode_prop_XID_Continue1_table[23] = { - 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60, - 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -}; - -static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { - 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e, - 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87, - 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, -}; - -static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = { - 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44, - 0x3c, 0x80, 0x59, 0x11, 0x80, 0x40, 0xe4, 0x3f, - 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, 0x11, 0x80, - 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b, - 0x84, -}; - -static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[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, - 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, - 0x81, 0x89, 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, - 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80, - 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, - 0xfb, 0x08, 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, - 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, - 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, - 0x03, 0x03, 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, - 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, - 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, - 0x46, 0x52, 0x81, 0xd4, 0x83, 0x45, 0x1c, 0x10, - 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, - 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x3f, 0x87, - 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80, - 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08, - 0x8f, 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, - 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, - 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, - 0x01, 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, - 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, - 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, - 0x34, 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, - 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, - 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, - 0xd5, 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, - 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, - 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, - 0x60, 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, - 0x86, 0x81, 0x43, 0x61, 0x83, 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] = { - 0xaf, 0x89, 0x35, 0x99, 0x85, -}; - -static const uint8_t unicode_prop_Bidi_Control_table[10] = { - 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84, - 0xb6, 0x83, -}; - -static const uint8_t unicode_prop_Dash_table[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, 0x4f, 0x9e, 0x80, -}; - -static const uint8_t unicode_prop_Deprecated_table[23] = { - 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02, - 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85, - 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, -}; - -static const uint8_t unicode_prop_Diacritic_table[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, - 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, - 0x40, 0x85, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a, - 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, - 0x81, 0x40, 0xc8, 0x9b, 0xbc, 0x80, 0x8f, 0x02, - 0x83, 0x9b, 0x80, 0xc9, 0x80, 0x8f, 0x80, 0xed, - 0x80, 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xae, - 0x82, 0xbb, 0x80, 0x8f, 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[89] = { - 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, - 0x80, 0x41, 0xb8, 0x80, 0x43, 0x59, 0x80, 0x42, - 0xef, 0x80, 0xfe, 0x80, 0x49, 0x42, 0x80, 0xb7, - 0x80, 0x42, 0x62, 0x80, 0x41, 0x8d, 0x80, 0xc3, - 0x80, 0x53, 0x88, 0x80, 0xaa, 0x84, 0xe6, 0x81, - 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x45, 0xf5, - 0x80, 0x43, 0xc1, 0x80, 0x95, 0x80, 0x40, 0x88, - 0x80, 0xeb, 0x80, 0x94, 0x81, 0x60, 0x54, 0x7a, - 0x80, 0x53, 0xeb, 0x80, 0x42, 0x67, 0x82, 0x44, - 0xce, 0x80, 0x60, 0x50, 0xa8, 0x81, 0x44, 0x9b, - 0x08, 0x80, 0x60, 0x71, 0x57, 0x81, 0x48, 0x05, - 0x82, -}; - -static const uint8_t unicode_prop_Hex_Digit_table[12] = { - 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8, - 0x89, 0x35, 0x99, 0x85, -}; - -static const uint8_t unicode_prop_IDS_Binary_Operator_table[5] = { - 0x60, 0x2f, 0xef, 0x09, 0x87, -}; - -static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { - 0x60, 0x2f, 0xf1, 0x81, -}; - -static const uint8_t unicode_prop_Ideographic_table[66] = { - 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, - 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] = { - 0x60, 0x20, 0x0b, 0x81, -}; - -static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { - 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11, - 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, -}; - -static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = { - 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, - 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, -}; - -static const uint8_t unicode_prop_Pattern_Syntax_table[58] = { - 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99, - 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03, - 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17, - 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41, - 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d, - 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13, - 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41, - 0x04, 0x81, -}; - -static const uint8_t unicode_prop_Pattern_White_Space_table[11] = { - 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87, - 0x81, 0x97, 0x81, -}; - -static const uint8_t unicode_prop_Quotation_Mark_table[31] = { - 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80, - 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80, - 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20, - 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81, -}; - -static const uint8_t unicode_prop_Radical_table[9] = { - 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40, - 0xd5, -}; - -static const uint8_t unicode_prop_Regional_Indicator_table[4] = { - 0x61, 0xf1, 0xe5, 0x99, -}; - -static const uint8_t unicode_prop_Sentence_Terminal_table[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, - 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15, - 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81, - 0x40, 0xcb, 0x04, 0x80, 0x41, 0x39, 0x81, 0x41, - 0x61, 0x83, 0x40, 0xad, 0x09, 0x81, 0x40, 0xda, - 0x81, 0xc0, 0x81, 0x43, 0xbb, 0x81, 0x88, 0x82, - 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x41, 0xc4, 0x80, - 0x60, 0x74, 0xfb, 0x80, 0x41, 0x0d, 0x81, 0x40, - 0xe2, 0x02, 0x80, 0x41, 0x7d, 0x81, 0xd5, 0x81, - 0xde, 0x80, 0x40, 0x97, 0x81, 0x40, 0x92, 0x82, - 0x40, 0x8f, 0x81, 0x40, 0xf8, 0x80, 0x60, 0x52, - 0x65, 0x02, 0x81, 0x40, 0xa8, 0x80, 0x8b, 0x80, - 0x8f, 0x80, 0xc0, 0x80, 0x4a, 0xf3, 0x81, 0x44, - 0xfc, 0x84, 0x40, 0xec, 0x81, 0xf4, 0x83, 0xfe, - 0x82, 0x40, 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, - 0x08, 0x81, 0xeb, 0x80, 0x41, 0xa0, 0x81, 0x41, - 0x74, 0x0c, 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, - 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] = { - 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, - 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f, - 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, - 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40, - 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81, - 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1, - 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, - 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, - 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, -}; - -static const uint8_t unicode_prop_Terminal_Punctuation_table[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, - 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, - 0x8e, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, 0xf3, - 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, 0x81, - 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, 0x82, - 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, 0x19, - 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, 0x40, - 0xad, 0x08, 0x82, 0x40, 0xda, 0x84, 0xbd, 0x81, - 0x43, 0xbb, 0x81, 0x88, 0x82, 0x4d, 0xe3, 0x80, - 0x8c, 0x03, 0x80, 0x89, 0x00, 0x81, 0x41, 0xb0, - 0x81, 0x60, 0x74, 0xfa, 0x81, 0x41, 0x0c, 0x82, - 0x40, 0xe2, 0x84, 0x41, 0x7d, 0x81, 0xd5, 0x81, - 0xde, 0x80, 0x40, 0x96, 0x82, 0x40, 0x92, 0x82, - 0xfe, 0x80, 0x8f, 0x81, 0x40, 0xf8, 0x80, 0x60, - 0x52, 0x63, 0x10, 0x83, 0x40, 0xa8, 0x80, 0x89, - 0x00, 0x80, 0x8a, 0x0a, 0x80, 0xc0, 0x01, 0x80, - 0x44, 0x39, 0x80, 0xaf, 0x80, 0x44, 0x85, 0x80, - 0x40, 0xc6, 0x80, 0x41, 0x35, 0x81, 0x40, 0x97, - 0x85, 0xc3, 0x85, 0xd8, 0x83, 0x43, 0xb7, 0x84, - 0x40, 0xec, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, - 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x84, 0xeb, - 0x80, 0x41, 0xa0, 0x82, 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[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, 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] = { - 0x58, 0x0a, 0x82, 0x60, 0xe5, 0xf1, 0x8f, 0x6d, - 0x02, 0xef, 0x40, 0xef, -}; - -static const uint8_t unicode_prop_White_Space_table[22] = { - 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80, - 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c, - 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80, -}; - -static const uint8_t unicode_prop_Bidi_Mirrored_table[171] = { - 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, - 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e, - 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81, - 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0, - 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18, - 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23, - 0x88, 0x08, 0x00, 0x39, 0x9e, 0x0b, 0x20, 0x88, - 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81, - 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, - 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, - 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35, - 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89, - 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08, - 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18, - 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00, - 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80, - 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89, - 0x41, 0xdd, 0x89, 0x0f, 0x60, 0xce, 0x3c, 0x2c, - 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00, 0x80, 0x9b, - 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08, 0x81, 0x60, - 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8, 0x80, 0xb8, - 0x80, 0xb8, 0x80, -}; - -static const uint8_t unicode_prop_Emoji_table[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, - 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a, - 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, - 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01, - 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f, - 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08, - 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a, - 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, - 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03, - 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, - 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, - 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88, - 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, - 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, - 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, - 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, - 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, - 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88, - 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, - 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, - 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, - 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, - 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, - 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 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] = { - 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40, - 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3, - 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83, - 0x6c, 0x06, 0x6b, 0xdf, -}; - -static const uint8_t unicode_prop_Emoji_Modifier_table[4] = { - 0x61, 0xf3, 0xfa, 0x84, -}; - -static const uint8_t unicode_prop_Emoji_Modifier_Base_table[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, 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[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, - 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, - 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00, - 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, - 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, - 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd, - 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93, - 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0, - 0x8b, 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa, - 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80, - 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91, - 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf, - 0xc5, 0x28, 0x12, 0x0a, 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[156] = { - 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, - 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, - 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, - 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, - 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, - 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, - 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, - 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, - 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80, - 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82, - 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80, - 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, - 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, - 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8, - 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, - 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, - 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b, - 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, - 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, - 0x40, 0xff, 0x43, 0xfd, -}; - -static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { - 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb, - 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4, - 0x83, 0x47, 0xfb, 0x84, 0x99, 0x84, 0xb0, 0x8f, - 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40, - 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60, - 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e, - 0x84, 0x4f, 0xff, -}; - -typedef enum { - UNICODE_PROP_Hyphen, - UNICODE_PROP_Other_Math, - UNICODE_PROP_Other_Alphabetic, - UNICODE_PROP_Other_Lowercase, - UNICODE_PROP_Other_Uppercase, - UNICODE_PROP_Other_Grapheme_Extend, - UNICODE_PROP_Other_Default_Ignorable_Code_Point, - UNICODE_PROP_Other_ID_Start, - UNICODE_PROP_Other_ID_Continue, - UNICODE_PROP_Prepended_Concatenation_Mark, - UNICODE_PROP_ID_Continue1, - UNICODE_PROP_XID_Start1, - UNICODE_PROP_XID_Continue1, - UNICODE_PROP_Changes_When_Titlecased1, - UNICODE_PROP_Changes_When_Casefolded1, - UNICODE_PROP_Changes_When_NFKC_Casefolded1, - UNICODE_PROP_ASCII_Hex_Digit, - UNICODE_PROP_Bidi_Control, - UNICODE_PROP_Dash, - UNICODE_PROP_Deprecated, - UNICODE_PROP_Diacritic, - UNICODE_PROP_Extender, - UNICODE_PROP_Hex_Digit, - UNICODE_PROP_IDS_Binary_Operator, - UNICODE_PROP_IDS_Trinary_Operator, - UNICODE_PROP_Ideographic, - UNICODE_PROP_Join_Control, - UNICODE_PROP_Logical_Order_Exception, - UNICODE_PROP_Noncharacter_Code_Point, - UNICODE_PROP_Pattern_Syntax, - UNICODE_PROP_Pattern_White_Space, - UNICODE_PROP_Quotation_Mark, - UNICODE_PROP_Radical, - UNICODE_PROP_Regional_Indicator, - UNICODE_PROP_Sentence_Terminal, - UNICODE_PROP_Soft_Dotted, - UNICODE_PROP_Terminal_Punctuation, - UNICODE_PROP_Unified_Ideograph, - UNICODE_PROP_Variation_Selector, - UNICODE_PROP_White_Space, - UNICODE_PROP_Bidi_Mirrored, - UNICODE_PROP_Emoji, - UNICODE_PROP_Emoji_Component, - UNICODE_PROP_Emoji_Modifier, - UNICODE_PROP_Emoji_Modifier_Base, - UNICODE_PROP_Emoji_Presentation, - UNICODE_PROP_Extended_Pictographic, - UNICODE_PROP_Default_Ignorable_Code_Point, - UNICODE_PROP_ID_Start, - UNICODE_PROP_Case_Ignorable, - UNICODE_PROP_ASCII, - UNICODE_PROP_Alphabetic, - UNICODE_PROP_Any, - UNICODE_PROP_Assigned, - UNICODE_PROP_Cased, - UNICODE_PROP_Changes_When_Casefolded, - UNICODE_PROP_Changes_When_Casemapped, - UNICODE_PROP_Changes_When_Lowercased, - UNICODE_PROP_Changes_When_NFKC_Casefolded, - UNICODE_PROP_Changes_When_Titlecased, - UNICODE_PROP_Changes_When_Uppercased, - UNICODE_PROP_Grapheme_Base, - UNICODE_PROP_Grapheme_Extend, - UNICODE_PROP_ID_Continue, - UNICODE_PROP_Lowercase, - UNICODE_PROP_Math, - UNICODE_PROP_Uppercase, - UNICODE_PROP_XID_Continue, - UNICODE_PROP_XID_Start, - UNICODE_PROP_Cased1, - UNICODE_PROP_COUNT, -} UnicodePropertyEnum; - -static const char unicode_prop_name_table[] = - "ASCII_Hex_Digit,AHex" "\0" - "Bidi_Control,Bidi_C" "\0" - "Dash" "\0" - "Deprecated,Dep" "\0" - "Diacritic,Dia" "\0" - "Extender,Ext" "\0" - "Hex_Digit,Hex" "\0" - "IDS_Binary_Operator,IDSB" "\0" - "IDS_Trinary_Operator,IDST" "\0" - "Ideographic,Ideo" "\0" - "Join_Control,Join_C" "\0" - "Logical_Order_Exception,LOE" "\0" - "Noncharacter_Code_Point,NChar" "\0" - "Pattern_Syntax,Pat_Syn" "\0" - "Pattern_White_Space,Pat_WS" "\0" - "Quotation_Mark,QMark" "\0" - "Radical" "\0" - "Regional_Indicator,RI" "\0" - "Sentence_Terminal,STerm" "\0" - "Soft_Dotted,SD" "\0" - "Terminal_Punctuation,Term" "\0" - "Unified_Ideograph,UIdeo" "\0" - "Variation_Selector,VS" "\0" - "White_Space,space" "\0" - "Bidi_Mirrored,Bidi_M" "\0" - "Emoji" "\0" - "Emoji_Component,EComp" "\0" - "Emoji_Modifier,EMod" "\0" - "Emoji_Modifier_Base,EBase" "\0" - "Emoji_Presentation,EPres" "\0" - "Extended_Pictographic,ExtPict" "\0" - "Default_Ignorable_Code_Point,DI" "\0" - "ID_Start,IDS" "\0" - "Case_Ignorable,CI" "\0" - "ASCII" "\0" - "Alphabetic,Alpha" "\0" - "Any" "\0" - "Assigned" "\0" - "Cased" "\0" - "Changes_When_Casefolded,CWCF" "\0" - "Changes_When_Casemapped,CWCM" "\0" - "Changes_When_Lowercased,CWL" "\0" - "Changes_When_NFKC_Casefolded,CWKCF" "\0" - "Changes_When_Titlecased,CWT" "\0" - "Changes_When_Uppercased,CWU" "\0" - "Grapheme_Base,Gr_Base" "\0" - "Grapheme_Extend,Gr_Ext" "\0" - "ID_Continue,IDC" "\0" - "Lowercase,Lower" "\0" - "Math" "\0" - "Uppercase,Upper" "\0" - "XID_Continue,XIDC" "\0" - "XID_Start,XIDS" "\0" -; - -static const uint8_t * const unicode_prop_table[] = { - unicode_prop_Hyphen_table, - unicode_prop_Other_Math_table, - unicode_prop_Other_Alphabetic_table, - unicode_prop_Other_Lowercase_table, - unicode_prop_Other_Uppercase_table, - unicode_prop_Other_Grapheme_Extend_table, - unicode_prop_Other_Default_Ignorable_Code_Point_table, - unicode_prop_Other_ID_Start_table, - unicode_prop_Other_ID_Continue_table, - unicode_prop_Prepended_Concatenation_Mark_table, - unicode_prop_ID_Continue1_table, - unicode_prop_XID_Start1_table, - unicode_prop_XID_Continue1_table, - unicode_prop_Changes_When_Titlecased1_table, - unicode_prop_Changes_When_Casefolded1_table, - unicode_prop_Changes_When_NFKC_Casefolded1_table, - unicode_prop_ASCII_Hex_Digit_table, - unicode_prop_Bidi_Control_table, - unicode_prop_Dash_table, - unicode_prop_Deprecated_table, - unicode_prop_Diacritic_table, - unicode_prop_Extender_table, - unicode_prop_Hex_Digit_table, - unicode_prop_IDS_Binary_Operator_table, - unicode_prop_IDS_Trinary_Operator_table, - unicode_prop_Ideographic_table, - unicode_prop_Join_Control_table, - unicode_prop_Logical_Order_Exception_table, - unicode_prop_Noncharacter_Code_Point_table, - unicode_prop_Pattern_Syntax_table, - unicode_prop_Pattern_White_Space_table, - unicode_prop_Quotation_Mark_table, - unicode_prop_Radical_table, - unicode_prop_Regional_Indicator_table, - unicode_prop_Sentence_Terminal_table, - unicode_prop_Soft_Dotted_table, - unicode_prop_Terminal_Punctuation_table, - unicode_prop_Unified_Ideograph_table, - unicode_prop_Variation_Selector_table, - unicode_prop_White_Space_table, - unicode_prop_Bidi_Mirrored_table, - unicode_prop_Emoji_table, - unicode_prop_Emoji_Component_table, - unicode_prop_Emoji_Modifier_table, - unicode_prop_Emoji_Modifier_Base_table, - unicode_prop_Emoji_Presentation_table, - unicode_prop_Extended_Pictographic_table, - unicode_prop_Default_Ignorable_Code_Point_table, - unicode_prop_ID_Start_table, - unicode_prop_Case_Ignorable_table, -}; - -static const uint16_t unicode_prop_len_table[] = { - countof(unicode_prop_Hyphen_table), - countof(unicode_prop_Other_Math_table), - countof(unicode_prop_Other_Alphabetic_table), - countof(unicode_prop_Other_Lowercase_table), - countof(unicode_prop_Other_Uppercase_table), - countof(unicode_prop_Other_Grapheme_Extend_table), - countof(unicode_prop_Other_Default_Ignorable_Code_Point_table), - countof(unicode_prop_Other_ID_Start_table), - countof(unicode_prop_Other_ID_Continue_table), - countof(unicode_prop_Prepended_Concatenation_Mark_table), - countof(unicode_prop_ID_Continue1_table), - countof(unicode_prop_XID_Start1_table), - countof(unicode_prop_XID_Continue1_table), - countof(unicode_prop_Changes_When_Titlecased1_table), - countof(unicode_prop_Changes_When_Casefolded1_table), - countof(unicode_prop_Changes_When_NFKC_Casefolded1_table), - countof(unicode_prop_ASCII_Hex_Digit_table), - countof(unicode_prop_Bidi_Control_table), - countof(unicode_prop_Dash_table), - countof(unicode_prop_Deprecated_table), - countof(unicode_prop_Diacritic_table), - countof(unicode_prop_Extender_table), - countof(unicode_prop_Hex_Digit_table), - countof(unicode_prop_IDS_Binary_Operator_table), - countof(unicode_prop_IDS_Trinary_Operator_table), - countof(unicode_prop_Ideographic_table), - countof(unicode_prop_Join_Control_table), - countof(unicode_prop_Logical_Order_Exception_table), - countof(unicode_prop_Noncharacter_Code_Point_table), - countof(unicode_prop_Pattern_Syntax_table), - countof(unicode_prop_Pattern_White_Space_table), - countof(unicode_prop_Quotation_Mark_table), - countof(unicode_prop_Radical_table), - countof(unicode_prop_Regional_Indicator_table), - countof(unicode_prop_Sentence_Terminal_table), - countof(unicode_prop_Soft_Dotted_table), - countof(unicode_prop_Terminal_Punctuation_table), - countof(unicode_prop_Unified_Ideograph_table), - countof(unicode_prop_Variation_Selector_table), - countof(unicode_prop_White_Space_table), - countof(unicode_prop_Bidi_Mirrored_table), - countof(unicode_prop_Emoji_table), - countof(unicode_prop_Emoji_Component_table), - countof(unicode_prop_Emoji_Modifier_table), - countof(unicode_prop_Emoji_Modifier_Base_table), - countof(unicode_prop_Emoji_Presentation_table), - countof(unicode_prop_Extended_Pictographic_table), - countof(unicode_prop_Default_Ignorable_Code_Point_table), - countof(unicode_prop_ID_Start_table), - countof(unicode_prop_Case_Ignorable_table), -}; - -#endif /* CONFIG_ALL_UNICODE */ +/* Compressed unicode tables */ +/* Automatically generated file - do not edit */ + +#include + +static const uint32_t case_conv_table1[361] = { + 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, + 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, + 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, + 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40, + 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100, + 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240, + 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130, + 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130, + 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240, + 0x00cd0100, 0x00ce0130, 0x00ce8130, 0x00cf0100, + 0x00cf8130, 0x00d00640, 0x00d30130, 0x00d38240, + 0x00d48130, 0x00d60240, 0x00d70130, 0x00d78240, + 0x00d88230, 0x00d98440, 0x00db8130, 0x00dc0240, + 0x00de0240, 0x00df8100, 0x00e20350, 0x00e38350, + 0x00e50350, 0x00e69040, 0x00ee8100, 0x00ef1240, + 0x00f801b4, 0x00f88350, 0x00fa0240, 0x00fb0130, + 0x00fb8130, 0x00fc2840, 0x01100130, 0x01111240, + 0x011d0131, 0x011d8240, 0x011e8130, 0x011f0131, + 0x011f8201, 0x01208240, 0x01218130, 0x01220130, + 0x01228130, 0x01230a40, 0x01280101, 0x01288101, + 0x01290101, 0x01298100, 0x012a0100, 0x012b0200, + 0x012c8100, 0x012d8100, 0x012e0101, 0x01300100, + 0x01308101, 0x01318100, 0x01328101, 0x01330101, + 0x01340100, 0x01348100, 0x01350101, 0x01358101, + 0x01360101, 0x01378100, 0x01388101, 0x01390100, + 0x013a8100, 0x013e8101, 0x01400100, 0x01410101, + 0x01418100, 0x01438101, 0x01440100, 0x01448100, + 0x01450200, 0x01460100, 0x01490100, 0x014e8101, + 0x014f0101, 0x01a28173, 0x01b80440, 0x01bb0240, + 0x01bd8300, 0x01bf8130, 0x01c30130, 0x01c40330, + 0x01c60130, 0x01c70230, 0x01c801d0, 0x01c89130, + 0x01d18930, 0x01d60100, 0x01d68300, 0x01d801d3, + 0x01d89100, 0x01e10173, 0x01e18900, 0x01e60100, + 0x01e68200, 0x01e78130, 0x01e80173, 0x01e88173, + 0x01ea8173, 0x01eb0173, 0x01eb8100, 0x01ec1840, + 0x01f80173, 0x01f88173, 0x01f90100, 0x01f98100, + 0x01fa01a0, 0x01fa8173, 0x01fb8240, 0x01fc8130, + 0x01fd0240, 0x01fe8330, 0x02001030, 0x02082030, + 0x02182000, 0x02281000, 0x02302240, 0x02453640, + 0x02600130, 0x02608e40, 0x02678100, 0x02686040, + 0x0298a630, 0x02b0a600, 0x02c381b5, 0x08502631, + 0x08638131, 0x08668131, 0x08682b00, 0x087e8300, + 0x09d05011, 0x09f80610, 0x09fc0620, 0x0e400174, + 0x0e408174, 0x0e410174, 0x0e418174, 0x0e420174, + 0x0e428174, 0x0e430174, 0x0e438180, 0x0e440180, + 0x0e482b30, 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, + 0x0ec70101, 0x0f007e40, 0x0f3f1840, 0x0f4b01b5, + 0x0f4b81b6, 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, + 0x0f4d8180, 0x0f4f0130, 0x0f506040, 0x0f800800, + 0x0f840830, 0x0f880600, 0x0f8c0630, 0x0f900800, + 0x0f940830, 0x0f980800, 0x0f9c0830, 0x0fa00600, + 0x0fa40630, 0x0fa801b0, 0x0fa88100, 0x0fa901d3, + 0x0fa98100, 0x0faa01d3, 0x0faa8100, 0x0fab01d3, + 0x0fab8100, 0x0fac8130, 0x0fad8130, 0x0fae8130, + 0x0faf8130, 0x0fb00800, 0x0fb40830, 0x0fb80200, + 0x0fb90400, 0x0fbb0200, 0x0fbc0201, 0x0fbd0201, + 0x0fbe0201, 0x0fc008b7, 0x0fc40867, 0x0fc808b8, + 0x0fcc0868, 0x0fd008b8, 0x0fd40868, 0x0fd80200, + 0x0fd901b9, 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1, + 0x0fdb81d7, 0x0fdc0230, 0x0fdd0230, 0x0fde0161, + 0x0fdf0173, 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, + 0x0fe301b2, 0x0fe381d8, 0x0fe40430, 0x0fe60162, + 0x0fe80200, 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, + 0x0feb81d0, 0x0fec0230, 0x0fed0230, 0x0ff00201, + 0x0ff101d3, 0x0ff181d3, 0x0ff201ba, 0x0ff28101, + 0x0ff301b0, 0x0ff381d3, 0x0ff40230, 0x0ff50230, + 0x0ff60131, 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, + 0x0ffb01b2, 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230, + 0x0ffe0162, 0x109301a0, 0x109501a0, 0x109581a0, + 0x10990131, 0x10a70101, 0x10b01031, 0x10b81001, + 0x10c18240, 0x125b1a31, 0x12681a01, 0x16002f31, + 0x16182f01, 0x16300240, 0x16310130, 0x16318130, + 0x16320130, 0x16328100, 0x16330100, 0x16338640, + 0x16368130, 0x16370130, 0x16378130, 0x16380130, + 0x16390240, 0x163a8240, 0x163f0230, 0x16406440, + 0x16758440, 0x16790240, 0x16802600, 0x16938100, + 0x16968100, 0x53202e40, 0x53401c40, 0x53910e40, + 0x53993e40, 0x53bc8440, 0x53be8130, 0x53bf0a40, + 0x53c58240, 0x53c68130, 0x53c80440, 0x53ca0101, + 0x53cb1440, 0x53d50130, 0x53d58130, 0x53d60130, + 0x53d68130, 0x53d70130, 0x53d80130, 0x53d88130, + 0x53d90130, 0x53d98131, 0x53da0c40, 0x53e10240, + 0x53e20131, 0x53e28130, 0x53e30130, 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[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, + 0x53, 0x00, 0x3a, 0x54, 0x55, 0x00, 0x57, 0x59, + 0x3f, 0x5d, 0x5c, 0x00, 0x46, 0x61, 0x63, 0x42, + 0x64, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, + 0x6c, 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, 0x20, + 0x35, 0x00, 0x27, 0x00, 0x21, 0x00, 0x24, 0x22, + 0x2a, 0x00, 0x13, 0x6b, 0x6d, 0x00, 0x26, 0x24, + 0x27, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x3e, 0x1e, + 0x3f, 0x1f, 0x39, 0x3d, 0x22, 0x21, 0x41, 0x1e, + 0x40, 0x25, 0x25, 0x26, 0x28, 0x20, 0x2a, 0x49, + 0x2c, 0x43, 0x2e, 0x4b, 0x30, 0x4c, 0x32, 0x44, + 0x42, 0x99, 0x00, 0x00, 0x95, 0x8f, 0x7d, 0x7e, + 0x83, 0x84, 0x12, 0x80, 0x82, 0x76, 0x77, 0x12, + 0x7b, 0xa3, 0x7c, 0x78, 0x79, 0x8a, 0x92, 0x98, + 0xa6, 0xa0, 0x85, 0x00, 0x9a, 0xa1, 0x93, 0x75, + 0x33, 0x95, 0x00, 0x8e, 0x00, 0x74, 0x99, 0x98, + 0x97, 0x96, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0xa1, 0xa0, 0x15, 0x2e, 0x2f, 0x30, 0xb4, 0xb5, + 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, + 0xc7, 0xca, 0xc9, 0xcc, 0xcb, 0xc4, 0xd5, 0x45, + 0xd6, 0x42, 0xd7, 0x46, 0xd8, 0xce, 0xd0, 0xd2, + 0xd4, 0xda, 0xd9, 0xee, 0xf6, 0xfe, 0x0e, 0x07, + 0x0f, 0x80, 0x9f, 0x00, 0x21, 0x80, 0xa3, 0xed, + 0x00, 0xc0, 0x40, 0xc6, 0x60, 0xe7, 0xdb, 0xe6, + 0x99, 0xc0, 0x00, 0x00, 0x06, 0x60, 0xdc, 0x29, + 0xfd, 0x15, 0x12, 0x06, 0x16, 0xf8, 0xdd, 0x06, + 0x15, 0x12, 0x84, 0x08, 0xc6, 0x16, 0xff, 0xdf, + 0x03, 0xc0, 0x40, 0x00, 0x46, 0x60, 0xde, 0xe0, + 0x6d, 0x37, 0x38, 0x39, 0x15, 0x14, 0x17, 0x16, + 0x00, 0x1a, 0x19, 0x1c, 0x1b, 0x00, 0x5f, 0xb7, + 0x65, 0x44, 0x47, 0x00, 0x4f, 0x62, 0x4e, 0x50, + 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xa3, 0xa4, + 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, + 0x00, 0x5a, 0x00, 0x48, 0x00, 0x5b, 0x56, 0x58, + 0x60, 0x5e, 0x70, 0x69, 0x6f, 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] = { + 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391, + 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307, + 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331, + 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80, + 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca, + 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546, + 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9, + 0x006b, 0x00e5, +}; + +static const uint8_t unicode_prop_Cased1_table[172] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, + 0x80, 0x8c, 0x80, 0x8d, 0x81, 0x8d, 0x02, 0x80, + 0xe1, 0x80, 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, + 0x11, 0x00, 0x01, 0x04, 0x08, 0x01, 0x08, 0x30, + 0x08, 0x01, 0x15, 0x20, 0x00, 0x39, 0x99, 0x31, + 0x9d, 0x84, 0x40, 0x94, 0x80, 0xd6, 0x82, 0xa6, + 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x57, 0x76, + 0xf8, 0x02, 0x80, 0x8f, 0x80, 0xb0, 0x40, 0xdb, + 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f, + 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28, + 0x10, 0x11, 0x02, 0x01, 0x18, 0x0b, 0x24, 0x4b, + 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79, + 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94, + 0x05, 0x80, 0x98, 0x80, 0xc7, 0x82, 0x43, 0x34, + 0xa2, 0x06, 0x80, 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, + 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, + 0x80, 0x9e, 0x80, 0x98, 0x07, 0x59, 0x63, 0x99, + 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Cased1_index[18] = { + 0xb9, 0x02, 0xe0, 0xa0, 0x1e, 0x40, 0x9e, 0xa6, + 0x40, 0xba, 0xd4, 0x01, 0x89, 0xd7, 0x01, 0x8a, + 0xf1, 0x01, +}; + +static const uint8_t unicode_prop_Case_Ignorable_table[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, 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, 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] = { + 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[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, + 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80, + 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80, + 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac, + 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81, + 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8, + 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95, + 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a, + 0xb4, 0x94, 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[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[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, + 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89, + 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89, + 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02, + 0x04, 0xaa, 0x82, 0xf6, 0x8e, 0x80, 0xa0, 0xb5, + 0x10, 0x91, 0x06, 0x89, 0x09, 0x89, 0x90, 0x82, + 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, 0x80, 0x89, + 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, 0x00, 0x23, + 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10, 0x8a, 0x82, + 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93, 0x09, 0x89, + 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, 0x09, 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, 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, + 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe3, 0x93, 0x80, + 0x89, 0x8b, 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, + 0x8b, 0x80, 0x8e, 0x42, 0xbe, 0x82, 0x88, 0x88, + 0x43, 0x9f, 0x82, 0x9c, 0x82, 0x9c, 0x81, 0x9d, + 0x81, 0xbf, 0x9f, 0x88, 0x01, 0x89, 0xa0, 0x11, + 0x89, 0x40, 0x8e, 0x80, 0xf5, 0x8b, 0x83, 0x8b, + 0x89, 0x89, 0xff, 0x8a, 0xbb, 0x84, 0xb8, 0x89, + 0x80, 0x9c, 0x81, 0x8a, 0x85, 0x89, 0x95, 0x8d, + 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, 0x52, 0x95, 0x89, 0x6c, 0x05, 0x05, + 0x40, 0xef, +}; + +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, 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[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, + 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2, + 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00, + 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea, + 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0, + 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00, + 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00, + 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00, + 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc, + 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e, + 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0, + 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81, + 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2, + 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00, + 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0, + 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc, + 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc, + 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4, + 0xaa, 0x02, 0xdc, 0xb0, 0x46, 0x00, 0xdc, 0xcd, + 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, + 0xdc, 0xc2, 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, + 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, + 0x07, 0x8f, 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, + 0xc1, 0xb0, 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, + 0xaf, 0xc0, 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, + 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, + 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, + 0x4e, 0x00, 0x09, 0xb0, 0x4e, 0x00, 0x09, 0x86, + 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00, 0x07, + 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09, 0x8f, + 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0, 0x3c, + 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b, 0xb0, + 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03, 0x7a, + 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc, 0x80, + 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06, 0x41, + 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82, 0x81, + 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80, 0xc1, + 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00, 0x07, + 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc, 0xb2, + 0x9e, 0xc2, 0xb3, 0x83, 0x00, 0x09, 0x9e, 0x00, + 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0, 0xb0, + 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde, 0xc0, + 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc, 0xb0, + 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00, 0xdc, + 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc, 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, 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[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, 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[690] = { + 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, + 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, + 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, + 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117, + 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342, + 0x005240bf, 0x00530342, 0x00550942, 0x005a0842, + 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142, + 0x006bc142, 0x00710185, 0x0071c317, 0x00734844, + 0x00778344, 0x00798342, 0x007b02be, 0x007c4197, + 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142, + 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283, + 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097, + 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080, + 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad, + 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081, + 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be, + 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be, + 0x011d8144, 0x01304144, 0x01340244, 0x01358144, + 0x01368344, 0x01388344, 0x013a8644, 0x013e0144, + 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184, + 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084, + 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084, + 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084, + 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084, + 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122, + 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae, + 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081, + 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084, + 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e, + 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084, + 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084, + 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081, + 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120, + 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783, + 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942, + 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085, + 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122, + 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122, + 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e, + 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122, + 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be, + 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be, + 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be, + 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820, + 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080, + 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117, + 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080, + 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080, + 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080, + 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080, + 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080, + 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081, + 0x0805c097, 0x08090081, 0x08094097, 0x08098099, + 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085, + 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3, + 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215, + 0x0820051f, 0x08228583, 0x08254415, 0x082a0097, + 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119, + 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081, + 0x08458097, 0x08464295, 0x08480097, 0x08484099, + 0x08488097, 0x08490081, 0x08498080, 0x084a0081, + 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081, + 0x084ec099, 0x084f0283, 0x08514295, 0x08540119, + 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081, + 0x08584097, 0x08588099, 0x0858c097, 0x08590081, + 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097, + 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295, + 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081, + 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097, + 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215, + 0x08624099, 0x0866813e, 0x086b80be, 0x087341be, + 0x088100be, 0x088240be, 0x088300be, 0x088901be, + 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1, + 0x089040be, 0x089100be, 0x0891c1be, 0x089801be, + 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144, + 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244, + 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523, + 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b, + 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25, + 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3, + 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be, + 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081, + 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383, + 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081, + 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122, + 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122, + 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085, + 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122, + 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084, + 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d, + 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703, + 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9, + 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089, + 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203, + 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d, + 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3, + 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523, + 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097, + 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad, + 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3, + 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1, + 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1, + 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7, + 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3, + 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3, + 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5, + 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1, + 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5, + 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1, + 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1, + 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3, + 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133, + 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1, + 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1, + 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099, + 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127, + 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099, + 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205, + 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1, + 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097, + 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f, + 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b, + 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117, + 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099, + 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525, + 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103, + 0x29dc0081, 0x29fe0103, 0x2ad70203, 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[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, + 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c, + 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192, + 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7, + 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218, + 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b, + 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275, + 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9, + 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1, + 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317, + 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341, + 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363, + 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385, + 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc, + 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1, + 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c, + 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614, + 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e, + 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9, + 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705, + 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737, + 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a, + 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c, + 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010, + 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db, + 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801, + 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830, + 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e, + 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069, + 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049, + 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4, + 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076, + 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7, + 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9, + 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923, + 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956, + 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978, + 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993, + 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f, + 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020, + 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1, + 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39, + 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59, + 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec, + 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a, + 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc, + 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c, + 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e, + 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca, + 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04, + 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45, + 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76, + 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad, + 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5, + 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a, + 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f, + 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1, + 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2, + 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153, + 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181, + 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab, + 0xa76f, 0x11af, 0x11b3, 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[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, + 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41, + 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45, + 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49, + 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e, + 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f, + 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55, + 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61, + 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61, + 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65, + 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69, + 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e, + 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f, + 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79, + 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41, + 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43, + 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45, + 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47, + 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48, + 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49, + 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a, + 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c, + 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e, + 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e, + 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81, + 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82, + 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c, + 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a, + 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82, + 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c, + 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01, + 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01, + 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a, + 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49, + 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c, + 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00, + 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26, + 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b, + 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01, + 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a, + 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a, + 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81, + 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f, + 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f, + 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f, + 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6, + 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45, + 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84, + 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00, + 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72, + 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77, + 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20, + 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63, + 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95, + 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20, + 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03, + 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99, + 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81, + 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03, + 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07, + 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07, + 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, + 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1, + 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba, + 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98, + 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04, + 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06, + 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80, + 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04, + 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00, + 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88, + 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04, + 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10, + 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86, + 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04, + 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e, + 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88, + 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04, + 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65, + 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00, + 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27, + 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23, + 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00, + 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54, + 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c, + 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00, + 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c, + 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe, + 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc, + 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c, + 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00, + 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c, + 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09, + 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92, + 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09, + 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf, + 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2, + 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08, + 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca, + 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f, + 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2, + 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42, + 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51, + 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b, + 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71, + 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41, + 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80, + 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80, + 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7, + 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7, + 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5, + 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35, + 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35, + 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35, + 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00, + 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47, + 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52, + 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61, + 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62, + 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b, + 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b, + 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54, + 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74, + 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76, + 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4, + 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72, + 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3, + 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52, + 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c, + 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65, + 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b, + 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f, + 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73, + 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82, + 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a, + 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a, + 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8, + 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42, + 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81, + 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00, + 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12, + 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad, + 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00, + 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48, + 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7, + 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00, + 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b, + 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84, + 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87, + 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1, + 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88, + 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00, + 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52, + 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1, + 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01, + 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54, + 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1, + 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00, + 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a, + 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80, + 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3, + 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82, + 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88, + 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02, + 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00, + 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2, + 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82, + 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01, + 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45, + 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83, + 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00, + 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49, + 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3, + 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00, + 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc, + 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80, + 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01, + 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf, + 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89, + 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00, + 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59, + 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f, + 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91, + 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f, + 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03, + 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03, + 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81, + 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f, + 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21, + 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2, + 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f, + 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29, + 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2, + 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f, + 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31, + 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2, + 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f, + 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39, + 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2, + 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f, + 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03, + 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03, + 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81, + 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00, + 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59, + 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2, + 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f, + 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61, + 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2, + 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f, + 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69, + 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2, + 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03, + 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5, + 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45, + 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45, + 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70, + 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5, + 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f, + 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91, + 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20, + 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f, + 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00, + 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5, + 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03, + 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf, + 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84, + 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca, + 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe, + 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2, + 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03, + 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5, + 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5, + 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85, + 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03, + 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9, + 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80, + 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94, + 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20, + 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00, + 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f, + 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69, + 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b, + 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29, + 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f, + 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c, + 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61, + 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43, + 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00, + 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, + 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50, + 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45, + 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42, + 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f, + 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3, + 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44, + 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31, + 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0, + 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32, + 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35, + 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0, + 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37, + 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49, + 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49, + 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76, + 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, + 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c, + 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21, + 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0, + 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8, + 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22, + 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25, + 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22, + 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e, + 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43, + 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00, + 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00, + 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c, + 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8, + 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22, + 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86, + 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8, + 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22, + 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03, + 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00, + 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30, + 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e, + 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0, + 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57, + 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e, + 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6, + 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f, + 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15, + 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80, + 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38, + 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5, + 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a, + 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b, + 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73, + 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b, + 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97, + 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5, + 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20, + 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb, + 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14, + 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36, + 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59, + 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89, + 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f, + 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92, + 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf, + 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3, + 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74, + 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8, + 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd, + 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33, + 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00, + 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f, + 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c, + 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2, + 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55, + 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70, + 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b, + 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49, + 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77, + 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9, + 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62, + 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3, + 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf, + 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8, + 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f, + 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5, + 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00, + 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e, + 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12, + 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45, + 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99, + 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99, + 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99, + 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99, + 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99, + 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99, + 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99, + 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99, + 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99, + 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99, + 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8, + 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac, + 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09, + 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01, + 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08, + 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13, + 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22, + 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45, + 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d, + 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, + 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75, + 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59, + 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00, + 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11, + 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11, + 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11, + 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11, + 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11, + 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, + 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, + 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, + 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, + 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67, + 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91, + 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, + 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c, + 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54, + 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, + 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81, + 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65, + 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31, + 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00, + 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00, + 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06, + 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c, + 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e, + 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63, + 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e, + 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e, + 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e, + 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, + 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65, + 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54, + 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, + 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90, + 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98, + 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e, + 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53, + 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76, + 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59, + 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, + 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00, + 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72, + 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d, + 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, + 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d, + 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4, + 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b, + 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39, + 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30, + 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f, + 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11, + 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03, + 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58, + 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e, + 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30, + 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30, + 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad, + 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13, + 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00, + 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40, + 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40, + 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30, + 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b, + 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e, + 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02, + 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11, + 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c, + 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00, + 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f, + 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12, + 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a, + 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16, + 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38, + 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2, + 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30, + 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20, + 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28, + 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30, + 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00, + 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d, + 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22, + 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01, + 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30, + 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23, + 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14, + 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a, + 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30, + 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47, + 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c, + 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30, + 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c, + 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35, + 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2, + 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44, + 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28, + 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00, + 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec, + 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16, + 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30, + 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9, + 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68, + 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, + 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64, + 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55, + 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c, + 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb, + 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e, + 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc, + 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41, + 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00, + 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c, + 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03, + 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b, + 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a, + 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48, + 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13, + 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13, + 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc, + 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d, + 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f, + 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63, + 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00, + 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00, + 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22, + 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00, + 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d, + 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, + 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61, + 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2, + 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc, + 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56, + 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00, + 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70, + 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57, + 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00, + 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9, + 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63, + 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43, + 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, + 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d, + 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f, + 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c, + 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d, + 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72, + 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41, + 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00, + 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00, + 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65, + 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x26, + 0x01, 0x53, 0x01, 0x27, 0xa7, 0x37, 0xab, 0x6b, + 0x02, 0x52, 0xab, 0x48, 0x8c, 0xf4, 0x66, 0xca, + 0x8e, 0xc8, 0x8c, 0xd1, 0x6e, 0x32, 0x4e, 0xe5, + 0x53, 0x9c, 0x9f, 0x9c, 0x9f, 0x51, 0x59, 0xd1, + 0x91, 0x87, 0x55, 0x48, 0x59, 0xf6, 0x61, 0x69, + 0x76, 0x85, 0x7f, 0x3f, 0x86, 0xba, 0x87, 0xf8, + 0x88, 0x8f, 0x90, 0x02, 0x6a, 0x1b, 0x6d, 0xd9, + 0x70, 0xde, 0x73, 0x3d, 0x84, 0x6a, 0x91, 0xf1, + 0x99, 0x82, 0x4e, 0x75, 0x53, 0x04, 0x6b, 0x1b, + 0x72, 0x2d, 0x86, 0x1e, 0x9e, 0x50, 0x5d, 0xeb, + 0x6f, 0xcd, 0x85, 0x64, 0x89, 0xc9, 0x62, 0xd8, + 0x81, 0x1f, 0x88, 0xca, 0x5e, 0x17, 0x67, 0x6a, + 0x6d, 0xfc, 0x72, 0xce, 0x90, 0x86, 0x4f, 0xb7, + 0x51, 0xde, 0x52, 0xc4, 0x64, 0xd3, 0x6a, 0x10, + 0x72, 0xe7, 0x76, 0x01, 0x80, 0x06, 0x86, 0x5c, + 0x86, 0xef, 0x8d, 0x32, 0x97, 0x6f, 0x9b, 0xfa, + 0x9d, 0x8c, 0x78, 0x7f, 0x79, 0xa0, 0x7d, 0xc9, + 0x83, 0x04, 0x93, 0x7f, 0x9e, 0xd6, 0x8a, 0xdf, + 0x58, 0x04, 0x5f, 0x60, 0x7c, 0x7e, 0x80, 0x62, + 0x72, 0xca, 0x78, 0xc2, 0x8c, 0xf7, 0x96, 0xd8, + 0x58, 0x62, 0x5c, 0x13, 0x6a, 0xda, 0x6d, 0x0f, + 0x6f, 0x2f, 0x7d, 0x37, 0x7e, 0x4b, 0x96, 0xd2, + 0x52, 0x8b, 0x80, 0xdc, 0x51, 0xcc, 0x51, 0x1c, + 0x7a, 0xbe, 0x7d, 0xf1, 0x83, 0x75, 0x96, 0x80, + 0x8b, 0xcf, 0x62, 0x02, 0x6a, 0xfe, 0x8a, 0x39, + 0x4e, 0xe7, 0x5b, 0x12, 0x60, 0x87, 0x73, 0x70, + 0x75, 0x17, 0x53, 0xfb, 0x78, 0xbf, 0x4f, 0xa9, + 0x5f, 0x0d, 0x4e, 0xcc, 0x6c, 0x78, 0x65, 0x22, + 0x7d, 0xc3, 0x53, 0x5e, 0x58, 0x01, 0x77, 0x49, + 0x84, 0xaa, 0x8a, 0xba, 0x6b, 0xb0, 0x8f, 0x88, + 0x6c, 0xfe, 0x62, 0xe5, 0x82, 0xa0, 0x63, 0x65, + 0x75, 0xae, 0x4e, 0x69, 0x51, 0xc9, 0x51, 0x81, + 0x68, 0xe7, 0x7c, 0x6f, 0x82, 0xd2, 0x8a, 0xcf, + 0x91, 0xf5, 0x52, 0x42, 0x54, 0x73, 0x59, 0xec, + 0x5e, 0xc5, 0x65, 0xfe, 0x6f, 0x2a, 0x79, 0xad, + 0x95, 0x6a, 0x9a, 0x97, 0x9e, 0xce, 0x9e, 0x9b, + 0x52, 0xc6, 0x66, 0x77, 0x6b, 0x62, 0x8f, 0x74, + 0x5e, 0x90, 0x61, 0x00, 0x62, 0x9a, 0x64, 0x23, + 0x6f, 0x49, 0x71, 0x89, 0x74, 0xca, 0x79, 0xf4, + 0x7d, 0x6f, 0x80, 0x26, 0x8f, 0xee, 0x84, 0x23, + 0x90, 0x4a, 0x93, 0x17, 0x52, 0xa3, 0x52, 0xbd, + 0x54, 0xc8, 0x70, 0xc2, 0x88, 0xaa, 0x8a, 0xc9, + 0x5e, 0xf5, 0x5f, 0x7b, 0x63, 0xae, 0x6b, 0x3e, + 0x7c, 0x75, 0x73, 0xe4, 0x4e, 0xf9, 0x56, 0xe7, + 0x5b, 0xba, 0x5d, 0x1c, 0x60, 0xb2, 0x73, 0x69, + 0x74, 0x9a, 0x7f, 0x46, 0x80, 0x34, 0x92, 0xf6, + 0x96, 0x48, 0x97, 0x18, 0x98, 0x8b, 0x4f, 0xae, + 0x79, 0xb4, 0x91, 0xb8, 0x96, 0xe1, 0x60, 0x86, + 0x4e, 0xda, 0x50, 0xee, 0x5b, 0x3f, 0x5c, 0x99, + 0x65, 0x02, 0x6a, 0xce, 0x71, 0x42, 0x76, 0xfc, + 0x84, 0x7c, 0x90, 0x8d, 0x9f, 0x88, 0x66, 0x2e, + 0x96, 0x89, 0x52, 0x7b, 0x67, 0xf3, 0x67, 0x41, + 0x6d, 0x9c, 0x6e, 0x09, 0x74, 0x59, 0x75, 0x6b, + 0x78, 0x10, 0x7d, 0x5e, 0x98, 0x6d, 0x51, 0x2e, + 0x62, 0x78, 0x96, 0x2b, 0x50, 0x19, 0x5d, 0xea, + 0x6d, 0x2a, 0x8f, 0x8b, 0x5f, 0x44, 0x61, 0x17, + 0x68, 0x87, 0x73, 0x86, 0x96, 0x29, 0x52, 0x0f, + 0x54, 0x65, 0x5c, 0x13, 0x66, 0x4e, 0x67, 0xa8, + 0x68, 0xe5, 0x6c, 0x06, 0x74, 0xe2, 0x75, 0x79, + 0x7f, 0xcf, 0x88, 0xe1, 0x88, 0xcc, 0x91, 0xe2, + 0x96, 0x3f, 0x53, 0xba, 0x6e, 0x1d, 0x54, 0xd0, + 0x71, 0x98, 0x74, 0xfa, 0x85, 0xa3, 0x96, 0x57, + 0x9c, 0x9f, 0x9e, 0x97, 0x67, 0xcb, 0x6d, 0xe8, + 0x81, 0xcb, 0x7a, 0x20, 0x7b, 0x92, 0x7c, 0xc0, + 0x72, 0x99, 0x70, 0x58, 0x8b, 0xc0, 0x4e, 0x36, + 0x83, 0x3a, 0x52, 0x07, 0x52, 0xa6, 0x5e, 0xd3, + 0x62, 0xd6, 0x7c, 0x85, 0x5b, 0x1e, 0x6d, 0xb4, + 0x66, 0x3b, 0x8f, 0x4c, 0x88, 0x4d, 0x96, 0x8b, + 0x89, 0xd3, 0x5e, 0x40, 0x51, 0xc0, 0x55, 0x00, + 0x00, 0x00, 0x00, 0x5a, 0x58, 0x00, 0x00, 0x74, + 0x66, 0x00, 0x00, 0x00, 0x00, 0xde, 0x51, 0x2a, + 0x73, 0xca, 0x76, 0x3c, 0x79, 0x5e, 0x79, 0x65, + 0x79, 0x8f, 0x79, 0x56, 0x97, 0xbe, 0x7c, 0xbd, + 0x7f, 0x00, 0x00, 0x12, 0x86, 0x00, 0x00, 0xf8, + 0x8a, 0x00, 0x00, 0x00, 0x00, 0x38, 0x90, 0xfd, + 0x90, 0xef, 0x98, 0xfc, 0x98, 0x28, 0x99, 0xb4, + 0x9d, 0xde, 0x90, 0xb7, 0x96, 0xae, 0x4f, 0xe7, + 0x50, 0x4d, 0x51, 0xc9, 0x52, 0xe4, 0x52, 0x51, + 0x53, 0x9d, 0x55, 0x06, 0x56, 0x68, 0x56, 0x40, + 0x58, 0xa8, 0x58, 0x64, 0x5c, 0x6e, 0x5c, 0x94, + 0x60, 0x68, 0x61, 0x8e, 0x61, 0xf2, 0x61, 0x4f, + 0x65, 0xe2, 0x65, 0x91, 0x66, 0x85, 0x68, 0x77, + 0x6d, 0x1a, 0x6e, 0x22, 0x6f, 0x6e, 0x71, 0x2b, + 0x72, 0x22, 0x74, 0x91, 0x78, 0x3e, 0x79, 0x49, + 0x79, 0x48, 0x79, 0x50, 0x79, 0x56, 0x79, 0x5d, + 0x79, 0x8d, 0x79, 0x8e, 0x79, 0x40, 0x7a, 0x81, + 0x7a, 0xc0, 0x7b, 0xf4, 0x7d, 0x09, 0x7e, 0x41, + 0x7e, 0x72, 0x7f, 0x05, 0x80, 0xed, 0x81, 0x79, + 0x82, 0x79, 0x82, 0x57, 0x84, 0x10, 0x89, 0x96, + 0x89, 0x01, 0x8b, 0x39, 0x8b, 0xd3, 0x8c, 0x08, + 0x8d, 0xb6, 0x8f, 0x38, 0x90, 0xe3, 0x96, 0xff, + 0x97, 0x3b, 0x98, 0x75, 0x60, 0xee, 0x42, 0x18, + 0x82, 0x02, 0x26, 0x4e, 0xb5, 0x51, 0x68, 0x51, + 0x80, 0x4f, 0x45, 0x51, 0x80, 0x51, 0xc7, 0x52, + 0xfa, 0x52, 0x9d, 0x55, 0x55, 0x55, 0x99, 0x55, + 0xe2, 0x55, 0x5a, 0x58, 0xb3, 0x58, 0x44, 0x59, + 0x54, 0x59, 0x62, 0x5a, 0x28, 0x5b, 0xd2, 0x5e, + 0xd9, 0x5e, 0x69, 0x5f, 0xad, 0x5f, 0xd8, 0x60, + 0x4e, 0x61, 0x08, 0x61, 0x8e, 0x61, 0x60, 0x61, + 0xf2, 0x61, 0x34, 0x62, 0xc4, 0x63, 0x1c, 0x64, + 0x52, 0x64, 0x56, 0x65, 0x74, 0x66, 0x17, 0x67, + 0x1b, 0x67, 0x56, 0x67, 0x79, 0x6b, 0xba, 0x6b, + 0x41, 0x6d, 0xdb, 0x6e, 0xcb, 0x6e, 0x22, 0x6f, + 0x1e, 0x70, 0x6e, 0x71, 0xa7, 0x77, 0x35, 0x72, + 0xaf, 0x72, 0x2a, 0x73, 0x71, 0x74, 0x06, 0x75, + 0x3b, 0x75, 0x1d, 0x76, 0x1f, 0x76, 0xca, 0x76, + 0xdb, 0x76, 0xf4, 0x76, 0x4a, 0x77, 0x40, 0x77, + 0xcc, 0x78, 0xb1, 0x7a, 0xc0, 0x7b, 0x7b, 0x7c, + 0x5b, 0x7d, 0xf4, 0x7d, 0x3e, 0x7f, 0x05, 0x80, + 0x52, 0x83, 0xef, 0x83, 0x79, 0x87, 0x41, 0x89, + 0x86, 0x89, 0x96, 0x89, 0xbf, 0x8a, 0xf8, 0x8a, + 0xcb, 0x8a, 0x01, 0x8b, 0xfe, 0x8a, 0xed, 0x8a, + 0x39, 0x8b, 0x8a, 0x8b, 0x08, 0x8d, 0x38, 0x8f, + 0x72, 0x90, 0x99, 0x91, 0x76, 0x92, 0x7c, 0x96, + 0xe3, 0x96, 0x56, 0x97, 0xdb, 0x97, 0xff, 0x97, + 0x0b, 0x98, 0x3b, 0x98, 0x12, 0x9b, 0x9c, 0x9f, + 0x4a, 0x28, 0x44, 0x28, 0xd5, 0x33, 0x9d, 0x3b, + 0x18, 0x40, 0x39, 0x40, 0x49, 0x52, 0xd0, 0x5c, + 0xd3, 0x7e, 0x43, 0x9f, 0x8e, 0x9f, 0x2a, 0xa0, + 0x02, 0x66, 0x66, 0x66, 0x69, 0x66, 0x6c, 0x66, + 0x66, 0x69, 0x66, 0x66, 0x6c, 0x7f, 0x01, 0x74, + 0x73, 0x00, 0x74, 0x65, 0x05, 0x0f, 0x11, 0x0f, + 0x00, 0x0f, 0x06, 0x19, 0x11, 0x0f, 0x08, 0xd9, + 0x05, 0xb4, 0x05, 0x00, 0x00, 0x00, 0x00, 0xf2, + 0x05, 0xb7, 0x05, 0xd0, 0x05, 0x12, 0x00, 0x03, + 0x04, 0x0b, 0x0c, 0x0d, 0x18, 0x1a, 0xe9, 0x05, + 0xc1, 0x05, 0xe9, 0x05, 0xc2, 0x05, 0x49, 0xfb, + 0xc1, 0x05, 0x49, 0xfb, 0xc2, 0x05, 0xd0, 0x05, + 0xb7, 0x05, 0xd0, 0x05, 0xb8, 0x05, 0xd0, 0x05, + 0xbc, 0x05, 0xd8, 0x05, 0xbc, 0x05, 0xde, 0x05, + 0xbc, 0x05, 0xe0, 0x05, 0xbc, 0x05, 0xe3, 0x05, + 0xbc, 0x05, 0xb9, 0x05, 0x2d, 0x03, 0x2e, 0x03, + 0x2f, 0x03, 0x30, 0x03, 0x31, 0x03, 0x1c, 0x00, + 0x18, 0x06, 0x22, 0x06, 0x2b, 0x06, 0xd0, 0x05, + 0xdc, 0x05, 0x71, 0x06, 0x00, 0x00, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, 0x0d, 0x0f, 0x0f, + 0x0f, 0x0f, 0x09, 0x09, 0x09, 0x09, 0x0e, 0x0e, + 0x0e, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x33, 0x33, + 0x33, 0x33, 0x35, 0x35, 0x35, 0x35, 0x13, 0x13, + 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x15, 0x15, + 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x1c, 0x1c, + 0x1b, 0x1b, 0x1d, 0x1d, 0x17, 0x17, 0x27, 0x27, + 0x20, 0x20, 0x38, 0x38, 0x38, 0x38, 0x3e, 0x3e, + 0x3e, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x40, 0x40, + 0x40, 0x40, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x4d, 0x4d, + 0x4d, 0x4d, 0x61, 0x61, 0x62, 0x62, 0x49, 0x06, + 0x64, 0x64, 0x64, 0x64, 0x7e, 0x7e, 0x7d, 0x7d, + 0x7f, 0x7f, 0x2e, 0x82, 0x82, 0x7c, 0x7c, 0x80, + 0x80, 0x87, 0x87, 0x87, 0x87, 0x00, 0x00, 0x26, + 0x06, 0x00, 0x01, 0x00, 0x01, 0x00, 0xaf, 0x00, + 0xaf, 0x00, 0x22, 0x00, 0x22, 0x00, 0xa1, 0x00, + 0xa1, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa2, 0x00, + 0xa2, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0x23, 0x00, 0x23, 0x00, 0x23, 0xcc, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x26, 0x06, 0x00, 0x06, 0x00, + 0x07, 0x00, 0x1f, 0x00, 0x23, 0x00, 0x24, 0x02, + 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x1f, 0x02, + 0x23, 0x02, 0x24, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x1f, 0x04, 0x23, 0x04, 0x24, 0x05, + 0x06, 0x05, 0x1f, 0x05, 0x23, 0x05, 0x24, 0x06, + 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07, 0x1f, 0x08, + 0x06, 0x08, 0x07, 0x08, 0x1f, 0x0d, 0x06, 0x0d, + 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f, + 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10, 0x08, 0x10, + 0x1f, 0x11, 0x07, 0x11, 0x1f, 0x12, 0x1f, 0x13, + 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b, + 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1b, + 0x23, 0x1b, 0x24, 0x1c, 0x07, 0x1c, 0x1f, 0x1c, + 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x06, 0x1d, + 0x07, 0x1d, 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1d, + 0x23, 0x1d, 0x24, 0x1e, 0x06, 0x1e, 0x07, 0x1e, + 0x08, 0x1e, 0x1f, 0x1e, 0x23, 0x1e, 0x24, 0x1f, + 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, 0x1f, + 0x23, 0x1f, 0x24, 0x20, 0x06, 0x20, 0x07, 0x20, + 0x08, 0x20, 0x1f, 0x20, 0x23, 0x20, 0x24, 0x21, + 0x06, 0x21, 0x1f, 0x21, 0x23, 0x21, 0x24, 0x24, + 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24, + 0x23, 0x24, 0x24, 0x0a, 0x4a, 0x0b, 0x4a, 0x23, + 0x4a, 0x20, 0x00, 0x4c, 0x06, 0x51, 0x06, 0x51, + 0x06, 0xff, 0x00, 0x1f, 0x26, 0x06, 0x00, 0x0b, + 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x23, + 0x00, 0x24, 0x02, 0x0b, 0x02, 0x0c, 0x02, 0x1f, + 0x02, 0x20, 0x02, 0x23, 0x02, 0x24, 0x04, 0x0b, + 0x04, 0x0c, 0x04, 0x1f, 0x26, 0x06, 0x04, 0x20, + 0x04, 0x23, 0x04, 0x24, 0x05, 0x0b, 0x05, 0x0c, + 0x05, 0x1f, 0x05, 0x20, 0x05, 0x23, 0x05, 0x24, + 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x23, 0x1c, 0x24, + 0x1d, 0x01, 0x1d, 0x1e, 0x1d, 0x1f, 0x1d, 0x23, + 0x1d, 0x24, 0x1e, 0x1f, 0x1e, 0x23, 0x1e, 0x24, + 0x1f, 0x01, 0x1f, 0x1f, 0x20, 0x0b, 0x20, 0x0c, + 0x20, 0x1f, 0x20, 0x20, 0x20, 0x23, 0x20, 0x24, + 0x23, 0x4a, 0x24, 0x0b, 0x24, 0x0c, 0x24, 0x1f, + 0x24, 0x20, 0x24, 0x23, 0x24, 0x24, 0x00, 0x06, + 0x00, 0x07, 0x00, 0x08, 0x00, 0x1f, 0x00, 0x21, + 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x1f, + 0x02, 0x21, 0x04, 0x06, 0x04, 0x07, 0x04, 0x08, + 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x06, 0x07, + 0x06, 0x1f, 0x07, 0x06, 0x07, 0x1f, 0x08, 0x06, + 0x08, 0x1f, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, + 0x0d, 0x1f, 0x0f, 0x07, 0x0f, 0x08, 0x0f, 0x1f, + 0x10, 0x06, 0x10, 0x07, 0x10, 0x08, 0x10, 0x1f, + 0x11, 0x07, 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, + 0x14, 0x06, 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, + 0x1b, 0x08, 0x1b, 0x1f, 0x1c, 0x07, 0x1c, 0x1f, + 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e, + 0x1d, 0x1f, 0x1e, 0x06, 0x1e, 0x07, 0x1e, 0x08, + 0x1e, 0x1f, 0x1e, 0x21, 0x1f, 0x06, 0x1f, 0x07, + 0x1f, 0x08, 0x1f, 0x1f, 0x20, 0x06, 0x20, 0x07, + 0x20, 0x08, 0x20, 0x1f, 0x20, 0x21, 0x21, 0x06, + 0x21, 0x1f, 0x21, 0x4a, 0x24, 0x06, 0x24, 0x07, + 0x24, 0x08, 0x24, 0x1f, 0x24, 0x21, 0x00, 0x1f, + 0x00, 0x21, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x1f, + 0x04, 0x21, 0x05, 0x1f, 0x05, 0x21, 0x0d, 0x1f, + 0x0d, 0x21, 0x0e, 0x1f, 0x0e, 0x21, 0x1d, 0x1e, + 0x1d, 0x1f, 0x1e, 0x1f, 0x20, 0x1f, 0x20, 0x21, + 0x24, 0x1f, 0x24, 0x21, 0x40, 0x06, 0x4e, 0x06, + 0x51, 0x06, 0x27, 0x06, 0x10, 0x22, 0x10, 0x23, + 0x12, 0x22, 0x12, 0x23, 0x13, 0x22, 0x13, 0x23, + 0x0c, 0x22, 0x0c, 0x23, 0x0d, 0x22, 0x0d, 0x23, + 0x06, 0x22, 0x06, 0x23, 0x05, 0x22, 0x05, 0x23, + 0x07, 0x22, 0x07, 0x23, 0x0e, 0x22, 0x0e, 0x23, + 0x0f, 0x22, 0x0f, 0x23, 0x0d, 0x05, 0x0d, 0x06, + 0x0d, 0x07, 0x0d, 0x1e, 0x0d, 0x0a, 0x0c, 0x0a, + 0x0e, 0x0a, 0x0f, 0x0a, 0x10, 0x22, 0x10, 0x23, + 0x12, 0x22, 0x12, 0x23, 0x13, 0x22, 0x13, 0x23, + 0x0c, 0x22, 0x0c, 0x23, 0x0d, 0x22, 0x0d, 0x23, + 0x06, 0x22, 0x06, 0x23, 0x05, 0x22, 0x05, 0x23, + 0x07, 0x22, 0x07, 0x23, 0x0e, 0x22, 0x0e, 0x23, + 0x0f, 0x22, 0x0f, 0x23, 0x0d, 0x05, 0x0d, 0x06, + 0x0d, 0x07, 0x0d, 0x1e, 0x0d, 0x0a, 0x0c, 0x0a, + 0x0e, 0x0a, 0x0f, 0x0a, 0x0d, 0x05, 0x0d, 0x06, + 0x0d, 0x07, 0x0d, 0x1e, 0x0c, 0x20, 0x0d, 0x20, + 0x10, 0x1e, 0x0c, 0x05, 0x0c, 0x06, 0x0c, 0x07, + 0x0d, 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x10, 0x1e, + 0x11, 0x1e, 0x00, 0x24, 0x00, 0x24, 0x2a, 0x06, + 0x00, 0x02, 0x1b, 0x00, 0x03, 0x02, 0x00, 0x03, + 0x02, 0x00, 0x03, 0x1b, 0x00, 0x04, 0x1b, 0x00, + 0x1b, 0x02, 0x00, 0x1b, 0x03, 0x00, 0x1b, 0x04, + 0x02, 0x1b, 0x03, 0x02, 0x1b, 0x03, 0x03, 0x1b, + 0x20, 0x03, 0x1b, 0x1f, 0x09, 0x03, 0x02, 0x09, + 0x02, 0x03, 0x09, 0x02, 0x1f, 0x09, 0x1b, 0x03, + 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x02, 0x09, 0x1b, + 0x1b, 0x09, 0x1b, 0x1b, 0x0b, 0x03, 0x03, 0x0b, + 0x03, 0x03, 0x0b, 0x1b, 0x1b, 0x0a, 0x03, 0x1b, + 0x0a, 0x03, 0x1b, 0x0a, 0x02, 0x20, 0x0a, 0x1b, + 0x04, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x1b, 0x0a, + 0x1b, 0x1b, 0x0c, 0x03, 0x1f, 0x0c, 0x04, 0x1b, + 0x0c, 0x04, 0x1b, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, + 0x03, 0x0d, 0x1b, 0x1b, 0x0d, 0x1b, 0x20, 0x0f, + 0x02, 0x1b, 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1b, + 0x0f, 0x1b, 0x1f, 0x10, 0x1b, 0x1b, 0x10, 0x1b, + 0x20, 0x10, 0x1b, 0x1f, 0x17, 0x04, 0x1b, 0x17, + 0x04, 0x1b, 0x18, 0x1b, 0x03, 0x18, 0x1b, 0x1b, + 0x1a, 0x03, 0x1b, 0x1a, 0x03, 0x20, 0x1a, 0x03, + 0x1f, 0x1a, 0x02, 0x02, 0x1a, 0x02, 0x02, 0x1a, + 0x04, 0x1b, 0x1a, 0x04, 0x1b, 0x1a, 0x1b, 0x03, + 0x1a, 0x1b, 0x03, 0x1b, 0x03, 0x02, 0x1b, 0x03, + 0x1b, 0x1b, 0x03, 0x20, 0x1b, 0x02, 0x03, 0x1b, + 0x02, 0x1b, 0x1b, 0x04, 0x02, 0x1b, 0x04, 0x1b, + 0x28, 0x06, 0x1d, 0x04, 0x06, 0x1f, 0x1d, 0x04, + 0x1f, 0x1d, 0x1d, 0x1e, 0x05, 0x1d, 0x1e, 0x05, + 0x21, 0x1e, 0x04, 0x1d, 0x1e, 0x04, 0x1d, 0x1e, + 0x04, 0x21, 0x1e, 0x1d, 0x22, 0x1e, 0x1d, 0x21, + 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x00, 0x06, + 0x22, 0x02, 0x04, 0x22, 0x02, 0x04, 0x21, 0x02, + 0x06, 0x22, 0x02, 0x06, 0x21, 0x02, 0x1d, 0x22, + 0x02, 0x1d, 0x21, 0x04, 0x1d, 0x22, 0x04, 0x05, + 0x21, 0x04, 0x1d, 0x21, 0x0b, 0x06, 0x21, 0x0d, + 0x05, 0x22, 0x0c, 0x05, 0x22, 0x0e, 0x05, 0x22, + 0x1c, 0x04, 0x22, 0x1c, 0x1d, 0x22, 0x22, 0x05, + 0x22, 0x22, 0x04, 0x22, 0x22, 0x1d, 0x22, 0x1d, + 0x1d, 0x22, 0x1a, 0x1d, 0x22, 0x1e, 0x05, 0x22, + 0x1a, 0x1d, 0x05, 0x1c, 0x05, 0x1d, 0x11, 0x1d, + 0x22, 0x1b, 0x1d, 0x22, 0x1e, 0x04, 0x05, 0x1d, + 0x06, 0x22, 0x1c, 0x04, 0x1d, 0x1b, 0x1d, 0x1d, + 0x1c, 0x04, 0x1d, 0x1e, 0x04, 0x05, 0x04, 0x05, + 0x22, 0x05, 0x04, 0x22, 0x1d, 0x04, 0x22, 0x19, + 0x1d, 0x22, 0x00, 0x05, 0x22, 0x1b, 0x1d, 0x1d, + 0x11, 0x04, 0x1d, 0x0d, 0x1d, 0x1d, 0x0b, 0x06, + 0x22, 0x1e, 0x04, 0x22, 0x35, 0x06, 0x00, 0x0f, + 0x9d, 0x0d, 0x0f, 0x9d, 0x27, 0x06, 0x00, 0x1d, + 0x1d, 0x20, 0x00, 0x1c, 0x01, 0x0a, 0x1e, 0x06, + 0x1e, 0x08, 0x0e, 0x1d, 0x12, 0x1e, 0x0a, 0x0c, + 0x21, 0x1d, 0x12, 0x1d, 0x23, 0x20, 0x21, 0x0c, + 0x1d, 0x1e, 0x35, 0x06, 0x00, 0x0f, 0x14, 0x27, + 0x06, 0x0e, 0x1d, 0x22, 0xff, 0x00, 0x1d, 0x1d, + 0x20, 0xff, 0x12, 0x1d, 0x23, 0x20, 0xff, 0x21, + 0x0c, 0x1d, 0x1e, 0x27, 0x06, 0x05, 0x1d, 0xff, + 0x05, 0x1d, 0x00, 0x1d, 0x20, 0x27, 0x06, 0x0a, + 0xa5, 0x00, 0x1d, 0x2c, 0x00, 0x01, 0x30, 0x02, + 0x30, 0x3a, 0x00, 0x3b, 0x00, 0x21, 0x00, 0x3f, + 0x00, 0x16, 0x30, 0x17, 0x30, 0x26, 0x20, 0x13, + 0x20, 0x12, 0x01, 0x00, 0x5f, 0x5f, 0x28, 0x29, + 0x7b, 0x7d, 0x08, 0x30, 0x0c, 0x0d, 0x08, 0x09, + 0x02, 0x03, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07, + 0x5b, 0x00, 0x5d, 0x00, 0x3e, 0x20, 0x3e, 0x20, + 0x3e, 0x20, 0x3e, 0x20, 0x5f, 0x00, 0x5f, 0x00, + 0x5f, 0x00, 0x2c, 0x00, 0x01, 0x30, 0x2e, 0x00, + 0x00, 0x00, 0x3b, 0x00, 0x3a, 0x00, 0x3f, 0x00, + 0x21, 0x00, 0x14, 0x20, 0x28, 0x00, 0x29, 0x00, + 0x7b, 0x00, 0x7d, 0x00, 0x14, 0x30, 0x15, 0x30, + 0x23, 0x26, 0x2a, 0x2b, 0x2d, 0x3c, 0x3e, 0x3d, + 0x00, 0x5c, 0x24, 0x25, 0x40, 0x40, 0x06, 0xff, + 0x0b, 0x00, 0x0b, 0xff, 0x0c, 0x20, 0x00, 0x4d, + 0x06, 0x40, 0x06, 0xff, 0x0e, 0x00, 0x0e, 0xff, + 0x0f, 0x00, 0x0f, 0xff, 0x10, 0x00, 0x10, 0xff, + 0x11, 0x00, 0x11, 0xff, 0x12, 0x00, 0x12, 0x21, + 0x06, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, + 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, + 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, + 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, + 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, + 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, + 0x19, 0x19, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, + 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, + 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, + 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, + 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, 0x22, 0x06, + 0x22, 0x00, 0x22, 0x00, 0x22, 0x01, 0x22, 0x01, + 0x22, 0x03, 0x22, 0x03, 0x22, 0x05, 0x22, 0x05, + 0x21, 0x00, 0x85, 0x29, 0x01, 0x30, 0x01, 0x0b, + 0x0c, 0x00, 0xfa, 0xf1, 0xa0, 0xa2, 0xa4, 0xa6, + 0xa8, 0xe2, 0xe4, 0xe6, 0xc2, 0xfb, 0xa1, 0xa3, + 0xa5, 0xa7, 0xa9, 0xaa, 0xac, 0xae, 0xb0, 0xb2, + 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 0xc0, 0xc3, + 0xc5, 0xc7, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, + 0xd1, 0xd4, 0xd7, 0xda, 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe3, 0xe5, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xee, 0xf2, 0x98, 0x99, 0x31, 0x31, 0x4f, + 0x31, 0x55, 0x31, 0x5b, 0x31, 0x61, 0x31, 0xa2, + 0x00, 0xa3, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xa6, + 0x00, 0xa5, 0x00, 0xa9, 0x20, 0x00, 0x00, 0x02, + 0x25, 0x90, 0x21, 0x91, 0x21, 0x92, 0x21, 0x93, + 0x21, 0xa0, 0x25, 0xcb, 0x25, 0x99, 0x10, 0xba, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, 0xba, + 0x10, 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, 0x05, + 0x31, 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, 0x11, + 0x55, 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, 0x57, + 0x13, 0x55, 0xb9, 0x14, 0xba, 0x14, 0xb9, 0x14, + 0xb0, 0x14, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x14, + 0xbd, 0x14, 0x55, 0x50, 0xb8, 0x15, 0xaf, 0x15, + 0xb9, 0x15, 0xaf, 0x15, 0x55, 0x35, 0x19, 0x30, + 0x19, 0x05, 0x57, 0xd1, 0x65, 0xd1, 0x58, 0xd1, + 0x65, 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, 0xd1, + 0x6f, 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, 0xd1, + 0x71, 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, 0x55, + 0x55, 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, 0xd1, + 0x65, 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, 0xd1, + 0x6e, 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, 0xd1, + 0x6f, 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, 0x61, + 0x00, 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, 0x00, + 0x00, 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, 0x00, + 0x4e, 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, + 0x64, 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, + 0x46, 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, 0x00, + 0x41, 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, 0x00, + 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, 0x53, + 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, 0x91, + 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, + 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, + 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, + 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, + 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, + 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, + 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, + 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, + 0x00, 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, 0x30, + 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, + 0x00, 0x27, 0x06, 0x00, 0x01, 0x05, 0x08, 0x2a, + 0x06, 0x1e, 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, + 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, + 0x0a, 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, + 0x44, 0x90, 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, + 0x00, 0x00, 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, + 0x11, 0x12, 0x13, 0x00, 0x06, 0x0e, 0x02, 0x0f, + 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, + 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, + 0x2d, 0x06, 0x00, 0x00, 0x4a, 0x06, 0x00, 0x00, + 0x44, 0x06, 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, + 0x39, 0x06, 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, + 0x00, 0x00, 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, + 0x3a, 0x06, 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, + 0x6f, 0x06, 0x00, 0x00, 0x28, 0x06, 0x2c, 0x06, + 0x00, 0x00, 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, + 0x00, 0x00, 0x45, 0x06, 0x46, 0x06, 0x33, 0x06, + 0x39, 0x06, 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, + 0x00, 0x00, 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, + 0x2e, 0x06, 0x00, 0x00, 0x36, 0x06, 0x38, 0x06, + 0x3a, 0x06, 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, + 0x27, 0x06, 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, + 0x0b, 0x06, 0x10, 0x23, 0x2a, 0x06, 0x1a, 0x1b, + 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, + 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, + 0x06, 0x2c, 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, + 0x06, 0x32, 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, + 0x06, 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, + 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, + 0x06, 0x0c, 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, + 0x2c, 0x00, 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, + 0x14, 0x30, 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, + 0x43, 0x44, 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, + 0x4d, 0x56, 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, + 0x56, 0x57, 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, + 0x52, 0x44, 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, + 0x68, 0x4b, 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, + 0x30, 0x8c, 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, + 0x59, 0xa4, 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, + 0x65, 0x4d, 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, + 0x65, 0x1d, 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, + 0x8c, 0xf0, 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, + 0x62, 0x55, 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, + 0x90, 0xe6, 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, + 0x63, 0x70, 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, + 0x7a, 0x08, 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, + 0x67, 0x33, 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, + 0x91, 0x14, 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, + 0x4e, 0x8c, 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, + 0x62, 0xd7, 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, + 0x5f, 0xef, 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, + 0x00, 0x09, 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, + 0xbb, 0x4f, 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, + 0xe7, 0x50, 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, + 0x4d, 0x51, 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, + 0x1c, 0x05, 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, + 0x4b, 0x05, 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, + 0xac, 0x51, 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, + 0x03, 0x52, 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, + 0x72, 0x52, 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, + 0x20, 0x80, 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, + 0x52, 0x00, 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, + 0x82, 0x8a, 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, + 0x2c, 0x0a, 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, + 0x63, 0x0b, 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, + 0x9e, 0x54, 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, + 0xa2, 0x54, 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, + 0x63, 0x55, 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, + 0xab, 0x55, 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, + 0x06, 0x56, 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, + 0x07, 0x52, 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, + 0x0d, 0x58, 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, + 0xac, 0x58, 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, + 0x06, 0x59, 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, + 0xa8, 0x16, 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, + 0x27, 0x5a, 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, + 0xfc, 0x36, 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, + 0xc8, 0x19, 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, + 0xf3, 0x5b, 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, + 0x53, 0x5f, 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, + 0x6e, 0x5c, 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, + 0x43, 0x5d, 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, + 0x7c, 0x5d, 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, + 0xfd, 0x5d, 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, + 0x62, 0x38, 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, + 0xb3, 0x5e, 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, + 0xfe, 0x5e, 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, + 0x22, 0x5f, 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, + 0xda, 0x61, 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, + 0x9a, 0x5f, 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, + 0x81, 0x60, 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, + 0xd4, 0x26, 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, + 0x00, 0x00, 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, + 0x00, 0x08, 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, + 0x02, 0x48, 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, + 0x46, 0x6a, 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, + 0xd3, 0x5d, 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, + 0x2b, 0x3d, 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, + 0x63, 0xe4, 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, + 0x63, 0xa9, 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, + 0x64, 0x9d, 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, + 0x65, 0x6c, 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, + 0x66, 0x49, 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, + 0x3b, 0xe4, 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, + 0x67, 0x9c, 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, + 0x67, 0x1b, 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, + 0x67, 0xc3, 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, + 0x67, 0x52, 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, + 0x68, 0x1f, 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, + 0x69, 0xa3, 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, + 0x36, 0xdb, 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, + 0x38, 0x54, 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, + 0x6b, 0xba, 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, + 0x1d, 0xfa, 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, + 0x6c, 0xcd, 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, + 0x6d, 0x77, 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, + 0x6d, 0x85, 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, + 0x6e, 0x6e, 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, + 0x6e, 0xd1, 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, + 0x3f, 0x8e, 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, + 0x70, 0x1b, 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, + 0x70, 0x77, 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, + 0x71, 0x63, 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, + 0x72, 0x35, 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, + 0x72, 0x95, 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, + 0x00, 0x00, 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, + 0x20, 0x00, 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, + 0x20, 0x14, 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, + 0x3e, 0xa5, 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, + 0x74, 0x5c, 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, + 0x74, 0x1b, 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, + 0x75, 0x92, 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, + 0x76, 0xa1, 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, + 0x3f, 0x08, 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, + 0x50, 0x19, 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, + 0x77, 0x1f, 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, + 0x77, 0x46, 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, + 0x78, 0x8c, 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, + 0x56, 0x56, 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, + 0x79, 0xeb, 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, + 0x7a, 0x4f, 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, + 0x5a, 0xee, 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, + 0x7b, 0xc9, 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, + 0x7c, 0xa0, 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, + 0x7d, 0x86, 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, + 0x7d, 0x02, 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, + 0x62, 0x47, 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, + 0x7f, 0x3e, 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, + 0x80, 0xda, 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, + 0x65, 0x70, 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, + 0x80, 0x03, 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, + 0x5a, 0xa7, 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, + 0x33, 0x01, 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, + 0x44, 0x91, 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, + 0x52, 0xb1, 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, + 0x82, 0x3c, 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, + 0x83, 0xad, 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, + 0x83, 0x57, 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, + 0x83, 0xdc, 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, + 0x00, 0x00, 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, + 0x20, 0x80, 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, + 0x02, 0x80, 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, + 0x6c, 0x2b, 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, + 0x85, 0xca, 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, + 0x45, 0x61, 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, + 0x45, 0x50, 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, + 0x86, 0xa9, 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, + 0x86, 0x79, 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, + 0x87, 0xd7, 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, + 0x45, 0x60, 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, + 0x88, 0xde, 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, + 0x34, 0xae, 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, + 0x46, 0xa0, 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, + 0x8c, 0xa8, 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, + 0x8d, 0x77, 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, + 0x8d, 0xbc, 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, + 0x8e, 0x38, 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, + 0x90, 0xf1, 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, + 0x91, 0x38, 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, + 0x92, 0xf9, 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, + 0x95, 0x95, 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, + 0x49, 0xc3, 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, + 0x91, 0x1a, 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, + 0x97, 0x0a, 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, + 0x98, 0x0b, 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, + 0x98, 0x33, 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, + 0x99, 0xfe, 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, + 0x9b, 0x40, 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, + 0x4c, 0x67, 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, + 0xa1, 0x0e, 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, + 0x4d, 0xf9, 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, + 0x9f, 0x16, 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, + 0x88, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x28, 0x00, 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, + 0x80, 0x80, 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, + 0x00, 0x20, 0x2a, 0x00, 0x80, +}; + +static const uint16_t unicode_comp_table[945] = { + 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, + 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, + 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, + 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292, + 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304, + 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306, + 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e, + 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8, + 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380, + 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac, + 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444, + 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940, + 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce, + 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296, + 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844, + 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e, + 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998, + 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322, + 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac, + 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326, + 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc, + 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce, + 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354, + 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3, + 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981, + 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7, + 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291, + 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303, + 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03, + 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d, + 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5, + 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343, + 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347, + 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5, + 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7, + 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3, + 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543, + 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1, + 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991, + 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d, + 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b, + 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3, + 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997, + 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343, + 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f, + 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf, + 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357, + 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448, + 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a, + 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06, + 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447, + 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289, + 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d, + 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb, + 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f, + 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306, + 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d, + 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8, + 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4, + 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882, + 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b, + 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541, + 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8, + 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8, + 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06, + 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850, + 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0, + 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940, + 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05, + 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0, + 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81, + 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184, + 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182, + 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0, + 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242, + 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0, + 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141, + 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245, + 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080, + 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341, + 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480, + 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800, + 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901, + 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80, + 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008, + 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9, + 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457, + 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0, + 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5, + 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583, + 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, + 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683, + 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01, + 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc, + 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b, + 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3, + 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df, + 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783, + 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844, + 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e, + 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851, + 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a, + 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180, + 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001, + 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700, + 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982, + 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81, + 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41, + 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01, + 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448, + 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480, + 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541, + 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702, + 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712, + 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0, + 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900, + 0x5901, 0x5902, 0x5903, 0x5940, 0x8e80, 0x8e82, 0x8ec0, 0x8f00, + 0x8f01, 0x8f40, 0x8f41, 0x8f81, 0x8f80, 0x8f83, 0x8fc0, 0x8fc1, + 0x9000, +}; + +typedef enum { + UNICODE_GC_Cn, + UNICODE_GC_Lu, + UNICODE_GC_Ll, + UNICODE_GC_Lt, + UNICODE_GC_Lm, + UNICODE_GC_Lo, + UNICODE_GC_Mn, + UNICODE_GC_Mc, + UNICODE_GC_Me, + UNICODE_GC_Nd, + UNICODE_GC_Nl, + UNICODE_GC_No, + UNICODE_GC_Sm, + UNICODE_GC_Sc, + UNICODE_GC_Sk, + UNICODE_GC_So, + UNICODE_GC_Pc, + UNICODE_GC_Pd, + UNICODE_GC_Ps, + UNICODE_GC_Pe, + UNICODE_GC_Pi, + UNICODE_GC_Pf, + UNICODE_GC_Po, + UNICODE_GC_Zs, + UNICODE_GC_Zl, + UNICODE_GC_Zp, + UNICODE_GC_Cc, + UNICODE_GC_Cf, + UNICODE_GC_Cs, + UNICODE_GC_Co, + UNICODE_GC_LC, + UNICODE_GC_L, + UNICODE_GC_M, + UNICODE_GC_N, + UNICODE_GC_S, + UNICODE_GC_P, + UNICODE_GC_Z, + UNICODE_GC_C, + UNICODE_GC_COUNT, +} UnicodeGCEnum; + +static const char unicode_gc_name_table[] = + "Cn,Unassigned" "\0" + "Lu,Uppercase_Letter" "\0" + "Ll,Lowercase_Letter" "\0" + "Lt,Titlecase_Letter" "\0" + "Lm,Modifier_Letter" "\0" + "Lo,Other_Letter" "\0" + "Mn,Nonspacing_Mark" "\0" + "Mc,Spacing_Mark" "\0" + "Me,Enclosing_Mark" "\0" + "Nd,Decimal_Number,digit" "\0" + "Nl,Letter_Number" "\0" + "No,Other_Number" "\0" + "Sm,Math_Symbol" "\0" + "Sc,Currency_Symbol" "\0" + "Sk,Modifier_Symbol" "\0" + "So,Other_Symbol" "\0" + "Pc,Connector_Punctuation" "\0" + "Pd,Dash_Punctuation" "\0" + "Ps,Open_Punctuation" "\0" + "Pe,Close_Punctuation" "\0" + "Pi,Initial_Punctuation" "\0" + "Pf,Final_Punctuation" "\0" + "Po,Other_Punctuation" "\0" + "Zs,Space_Separator" "\0" + "Zl,Line_Separator" "\0" + "Zp,Paragraph_Separator" "\0" + "Cc,Control,cntrl" "\0" + "Cf,Format" "\0" + "Cs,Surrogate" "\0" + "Co,Private_Use" "\0" + "LC,Cased_Letter" "\0" + "L,Letter" "\0" + "M,Mark,Combining_Mark" "\0" + "N,Number" "\0" + "S,Symbol" "\0" + "P,Punctuation,punct" "\0" + "Z,Separator" "\0" + "C,Other" "\0" +; + +static const uint8_t unicode_gc_table[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, + 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, + 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e, + 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f, + 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05, + 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2, + 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff, + 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02, + 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02, + 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f, + 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02, + 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01, + 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02, + 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21, + 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22, + 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05, + 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee, + 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09, + 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04, + 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41, + 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00, + 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42, + 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02, + 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86, + 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58, + 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21, + 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25, + 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06, + 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0, + 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6, + 0x03, 0x16, 0x1b, 0x00, 0x36, 0xe5, 0x18, 0x04, + 0xe5, 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, + 0x06, 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, + 0xa6, 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, + 0x45, 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, + 0x06, 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, + 0xe6, 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, + 0x19, 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, + 0x06, 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, + 0x04, 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, + 0xe5, 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, + 0xe0, 0x2d, 0xe5, 0x0d, 0x00, 0xe5, 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, + 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, 0x20, 0xe5, + 0x0e, 0x00, 0xc5, 0x00, 0x05, 0x40, 0x65, 0x20, + 0x06, 0x05, 0x47, 0x66, 0x20, 0x27, 0x20, 0x27, + 0x06, 0x05, 0xe0, 0x00, 0x07, 0x60, 0x25, 0x00, + 0x45, 0x26, 0x20, 0xe9, 0x02, 0x25, 0x2d, 0xab, + 0x0f, 0x0d, 0x05, 0x16, 0x06, 0x20, 0x26, 0x07, + 0x00, 0xa5, 0x60, 0x25, 0x20, 0xe5, 0x0e, 0x00, + 0xc5, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x20, + 0x06, 0x00, 0x47, 0x26, 0x60, 0x26, 0x20, 0x46, + 0x40, 0x06, 0xc0, 0x65, 0x00, 0x05, 0xc0, 0xe9, + 0x02, 0x26, 0x45, 0x06, 0x16, 0xe0, 0x02, 0x26, + 0x07, 0x00, 0xe5, 0x01, 0x00, 0x45, 0x00, 0xe5, + 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x85, 0x20, + 0x06, 0x05, 0x47, 0x86, 0x00, 0x26, 0x07, 0x00, + 0x27, 0x06, 0x20, 0x05, 0xe0, 0x07, 0x25, 0x26, + 0x20, 0xe9, 0x02, 0x16, 0x0d, 0xc0, 0x05, 0xa6, + 0x00, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, + 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, + 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x07, 0x66, + 0x20, 0x27, 0x20, 0x27, 0x06, 0xc0, 0x26, 0x07, + 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02, + 0x0f, 0x05, 0xab, 0xe0, 0x02, 0x06, 0x05, 0x00, + 0xa5, 0x40, 0x45, 0x00, 0x65, 0x40, 0x25, 0x00, + 0x05, 0x00, 0x25, 0x40, 0x25, 0x40, 0x45, 0x40, + 0xe5, 0x04, 0x60, 0x27, 0x06, 0x27, 0x40, 0x47, + 0x00, 0x47, 0x06, 0x20, 0x05, 0xa0, 0x07, 0xe0, + 0x06, 0xe9, 0x02, 0x4b, 0xaf, 0x0d, 0x0f, 0x80, + 0x06, 0x47, 0x06, 0xe5, 0x00, 0x00, 0x45, 0x00, + 0xe5, 0x0f, 0x00, 0xe5, 0x08, 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, + 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, 0x07, 0x46, + 0x25, 0xe9, 0x02, 0xe5, 0x24, 0x06, 0x07, 0x26, + 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, 0x00, 0x76, + 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, 0x27, 0x26, + 0x40, 0x96, 0xe9, 0x02, 0x40, 0x45, 0xe9, 0x02, + 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, 0xc0, 0xe1, + 0x23, 0x20, 0x41, 0xf6, 0x00, 0xe0, 0x00, 0x46, + 0x16, 0xe6, 0x05, 0x07, 0xc6, 0x65, 0x06, 0xa5, + 0x06, 0x25, 0x07, 0x26, 0x05, 0x80, 0xe2, 0x24, + 0xe4, 0x37, 0xe2, 0x05, 0x04, 0xe2, 0x1a, 0xe4, + 0x1d, 0xe6, 0x32, 0x00, 0x86, 0xff, 0x80, 0x0e, + 0xe2, 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, 0x00, + 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, 0x00, + 0xe2, 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, 0x20, + 0xe2, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, 0x20, + 0xe2, 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, 0x00, + 0xe2, 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, 0x61, + 0x03, 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, 0x61, + 0x03, 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, 0x4e, + 0xe2, 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, 0x22, + 0x61, 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, 0xb1, + 0x36, 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, 0x14, + 0xf6, 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, 0x01, + 0x14, 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, 0x13, + 0xf6, 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, 0x17, + 0x9b, 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, 0xab, + 0x4c, 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, 0x12, + 0x13, 0x00, 0xe4, 0x05, 0x40, 0xed, 0x18, 0xe0, + 0x08, 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, 0x04, + 0xe0, 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, 0x02, + 0x41, 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, 0x0c, + 0x81, 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, 0x0f, + 0x61, 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, 0x2f, + 0x22, 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, 0x2f, + 0x02, 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, 0x6a, + 0x0b, 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, 0x0c, + 0x2f, 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, 0x17, + 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, 0xec, + 0x80, 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, 0x13, + 0xef, 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, 0x49, + 0x0c, 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, 0xac, + 0xef, 0x3d, 0xe0, 0x11, 0xef, 0x03, 0xe0, 0x0d, + 0xeb, 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, 0x80, + 0x2f, 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, 0xec, + 0x00, 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, 0xef, + 0x24, 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0xec, 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, 0x12, + 0x13, 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, 0xec, + 0x80, 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, 0xac, + 0xef, 0x1f, 0x20, 0xef, 0x18, 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, + 0x46, 0x3f, 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, + 0x00, 0x02, 0x80, 0x02, 0x20, 0xe5, 0x30, 0xc0, + 0x04, 0x16, 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, + 0x01, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, + 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, + 0x00, 0xe6, 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, + 0x56, 0x14, 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, + 0x11, 0x36, 0x11, 0x16, 0x14, 0x15, 0x36, 0x14, + 0x15, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x96, 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, + 0x16, 0x12, 0xf6, 0x05, 0x2f, 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, 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, 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 { + UNICODE_SCRIPT_Unknown, + UNICODE_SCRIPT_Adlam, + UNICODE_SCRIPT_Ahom, + UNICODE_SCRIPT_Anatolian_Hieroglyphs, + UNICODE_SCRIPT_Arabic, + UNICODE_SCRIPT_Armenian, + UNICODE_SCRIPT_Avestan, + UNICODE_SCRIPT_Balinese, + UNICODE_SCRIPT_Bamum, + UNICODE_SCRIPT_Bassa_Vah, + UNICODE_SCRIPT_Batak, + UNICODE_SCRIPT_Bengali, + UNICODE_SCRIPT_Bhaiksuki, + UNICODE_SCRIPT_Bopomofo, + UNICODE_SCRIPT_Brahmi, + UNICODE_SCRIPT_Braille, + UNICODE_SCRIPT_Buginese, + UNICODE_SCRIPT_Buhid, + UNICODE_SCRIPT_Canadian_Aboriginal, + UNICODE_SCRIPT_Carian, + UNICODE_SCRIPT_Caucasian_Albanian, + UNICODE_SCRIPT_Chakma, + UNICODE_SCRIPT_Cham, + UNICODE_SCRIPT_Cherokee, + UNICODE_SCRIPT_Chorasmian, + UNICODE_SCRIPT_Common, + UNICODE_SCRIPT_Coptic, + UNICODE_SCRIPT_Cuneiform, + UNICODE_SCRIPT_Cypriot, + UNICODE_SCRIPT_Cyrillic, + UNICODE_SCRIPT_Deseret, + UNICODE_SCRIPT_Devanagari, + UNICODE_SCRIPT_Dives_Akuru, + UNICODE_SCRIPT_Dogra, + UNICODE_SCRIPT_Duployan, + UNICODE_SCRIPT_Egyptian_Hieroglyphs, + UNICODE_SCRIPT_Elbasan, + UNICODE_SCRIPT_Elymaic, + UNICODE_SCRIPT_Ethiopic, + UNICODE_SCRIPT_Georgian, + UNICODE_SCRIPT_Glagolitic, + UNICODE_SCRIPT_Gothic, + UNICODE_SCRIPT_Grantha, + UNICODE_SCRIPT_Greek, + UNICODE_SCRIPT_Gujarati, + UNICODE_SCRIPT_Gunjala_Gondi, + UNICODE_SCRIPT_Gurmukhi, + UNICODE_SCRIPT_Han, + UNICODE_SCRIPT_Hangul, + UNICODE_SCRIPT_Hanifi_Rohingya, + UNICODE_SCRIPT_Hanunoo, + UNICODE_SCRIPT_Hatran, + UNICODE_SCRIPT_Hebrew, + UNICODE_SCRIPT_Hiragana, + UNICODE_SCRIPT_Imperial_Aramaic, + UNICODE_SCRIPT_Inherited, + UNICODE_SCRIPT_Inscriptional_Pahlavi, + UNICODE_SCRIPT_Inscriptional_Parthian, + UNICODE_SCRIPT_Javanese, + UNICODE_SCRIPT_Kaithi, + UNICODE_SCRIPT_Kannada, + UNICODE_SCRIPT_Katakana, + UNICODE_SCRIPT_Kayah_Li, + UNICODE_SCRIPT_Kharoshthi, + UNICODE_SCRIPT_Khmer, + UNICODE_SCRIPT_Khojki, + UNICODE_SCRIPT_Khitan_Small_Script, + UNICODE_SCRIPT_Khudawadi, + UNICODE_SCRIPT_Lao, + UNICODE_SCRIPT_Latin, + UNICODE_SCRIPT_Lepcha, + UNICODE_SCRIPT_Limbu, + UNICODE_SCRIPT_Linear_A, + UNICODE_SCRIPT_Linear_B, + UNICODE_SCRIPT_Lisu, + UNICODE_SCRIPT_Lycian, + UNICODE_SCRIPT_Lydian, + UNICODE_SCRIPT_Makasar, + UNICODE_SCRIPT_Mahajani, + UNICODE_SCRIPT_Malayalam, + UNICODE_SCRIPT_Mandaic, + UNICODE_SCRIPT_Manichaean, + UNICODE_SCRIPT_Marchen, + UNICODE_SCRIPT_Masaram_Gondi, + UNICODE_SCRIPT_Medefaidrin, + UNICODE_SCRIPT_Meetei_Mayek, + UNICODE_SCRIPT_Mende_Kikakui, + UNICODE_SCRIPT_Meroitic_Cursive, + UNICODE_SCRIPT_Meroitic_Hieroglyphs, + UNICODE_SCRIPT_Miao, + UNICODE_SCRIPT_Modi, + UNICODE_SCRIPT_Mongolian, + UNICODE_SCRIPT_Mro, + UNICODE_SCRIPT_Multani, + UNICODE_SCRIPT_Myanmar, + UNICODE_SCRIPT_Nabataean, + UNICODE_SCRIPT_Nandinagari, + UNICODE_SCRIPT_New_Tai_Lue, + UNICODE_SCRIPT_Newa, + UNICODE_SCRIPT_Nko, + UNICODE_SCRIPT_Nushu, + UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, + UNICODE_SCRIPT_Ogham, + UNICODE_SCRIPT_Ol_Chiki, + UNICODE_SCRIPT_Old_Hungarian, + UNICODE_SCRIPT_Old_Italic, + UNICODE_SCRIPT_Old_North_Arabian, + UNICODE_SCRIPT_Old_Permic, + UNICODE_SCRIPT_Old_Persian, + UNICODE_SCRIPT_Old_Sogdian, + UNICODE_SCRIPT_Old_South_Arabian, + UNICODE_SCRIPT_Old_Turkic, + UNICODE_SCRIPT_Oriya, + UNICODE_SCRIPT_Osage, + UNICODE_SCRIPT_Osmanya, + UNICODE_SCRIPT_Pahawh_Hmong, + UNICODE_SCRIPT_Palmyrene, + UNICODE_SCRIPT_Pau_Cin_Hau, + UNICODE_SCRIPT_Phags_Pa, + UNICODE_SCRIPT_Phoenician, + UNICODE_SCRIPT_Psalter_Pahlavi, + UNICODE_SCRIPT_Rejang, + UNICODE_SCRIPT_Runic, + UNICODE_SCRIPT_Samaritan, + UNICODE_SCRIPT_Saurashtra, + UNICODE_SCRIPT_Sharada, + UNICODE_SCRIPT_Shavian, + UNICODE_SCRIPT_Siddham, + UNICODE_SCRIPT_SignWriting, + UNICODE_SCRIPT_Sinhala, + UNICODE_SCRIPT_Sogdian, + UNICODE_SCRIPT_Sora_Sompeng, + UNICODE_SCRIPT_Soyombo, + UNICODE_SCRIPT_Sundanese, + UNICODE_SCRIPT_Syloti_Nagri, + UNICODE_SCRIPT_Syriac, + UNICODE_SCRIPT_Tagalog, + UNICODE_SCRIPT_Tagbanwa, + UNICODE_SCRIPT_Tai_Le, + UNICODE_SCRIPT_Tai_Tham, + UNICODE_SCRIPT_Tai_Viet, + UNICODE_SCRIPT_Takri, + UNICODE_SCRIPT_Tamil, + UNICODE_SCRIPT_Tangut, + UNICODE_SCRIPT_Telugu, + UNICODE_SCRIPT_Thaana, + UNICODE_SCRIPT_Thai, + UNICODE_SCRIPT_Tibetan, + UNICODE_SCRIPT_Tifinagh, + UNICODE_SCRIPT_Tirhuta, + UNICODE_SCRIPT_Ugaritic, + UNICODE_SCRIPT_Vai, + UNICODE_SCRIPT_Wancho, + UNICODE_SCRIPT_Warang_Citi, + UNICODE_SCRIPT_Yezidi, + UNICODE_SCRIPT_Yi, + UNICODE_SCRIPT_Zanabazar_Square, + UNICODE_SCRIPT_COUNT, +} UnicodeScriptEnum; + +static const char unicode_script_name_table[] = + "Adlam,Adlm" "\0" + "Ahom,Ahom" "\0" + "Anatolian_Hieroglyphs,Hluw" "\0" + "Arabic,Arab" "\0" + "Armenian,Armn" "\0" + "Avestan,Avst" "\0" + "Balinese,Bali" "\0" + "Bamum,Bamu" "\0" + "Bassa_Vah,Bass" "\0" + "Batak,Batk" "\0" + "Bengali,Beng" "\0" + "Bhaiksuki,Bhks" "\0" + "Bopomofo,Bopo" "\0" + "Brahmi,Brah" "\0" + "Braille,Brai" "\0" + "Buginese,Bugi" "\0" + "Buhid,Buhd" "\0" + "Canadian_Aboriginal,Cans" "\0" + "Carian,Cari" "\0" + "Caucasian_Albanian,Aghb" "\0" + "Chakma,Cakm" "\0" + "Cham,Cham" "\0" + "Cherokee,Cher" "\0" + "Chorasmian,Chrs" "\0" + "Common,Zyyy" "\0" + "Coptic,Copt,Qaac" "\0" + "Cuneiform,Xsux" "\0" + "Cypriot,Cprt" "\0" + "Cyrillic,Cyrl" "\0" + "Deseret,Dsrt" "\0" + "Devanagari,Deva" "\0" + "Dives_Akuru,Diak" "\0" + "Dogra,Dogr" "\0" + "Duployan,Dupl" "\0" + "Egyptian_Hieroglyphs,Egyp" "\0" + "Elbasan,Elba" "\0" + "Elymaic,Elym" "\0" + "Ethiopic,Ethi" "\0" + "Georgian,Geor" "\0" + "Glagolitic,Glag" "\0" + "Gothic,Goth" "\0" + "Grantha,Gran" "\0" + "Greek,Grek" "\0" + "Gujarati,Gujr" "\0" + "Gunjala_Gondi,Gong" "\0" + "Gurmukhi,Guru" "\0" + "Han,Hani" "\0" + "Hangul,Hang" "\0" + "Hanifi_Rohingya,Rohg" "\0" + "Hanunoo,Hano" "\0" + "Hatran,Hatr" "\0" + "Hebrew,Hebr" "\0" + "Hiragana,Hira" "\0" + "Imperial_Aramaic,Armi" "\0" + "Inherited,Zinh,Qaai" "\0" + "Inscriptional_Pahlavi,Phli" "\0" + "Inscriptional_Parthian,Prti" "\0" + "Javanese,Java" "\0" + "Kaithi,Kthi" "\0" + "Kannada,Knda" "\0" + "Katakana,Kana" "\0" + "Kayah_Li,Kali" "\0" + "Kharoshthi,Khar" "\0" + "Khmer,Khmr" "\0" + "Khojki,Khoj" "\0" + "Khitan_Small_Script,Kits" "\0" + "Khudawadi,Sind" "\0" + "Lao,Laoo" "\0" + "Latin,Latn" "\0" + "Lepcha,Lepc" "\0" + "Limbu,Limb" "\0" + "Linear_A,Lina" "\0" + "Linear_B,Linb" "\0" + "Lisu,Lisu" "\0" + "Lycian,Lyci" "\0" + "Lydian,Lydi" "\0" + "Makasar,Maka" "\0" + "Mahajani,Mahj" "\0" + "Malayalam,Mlym" "\0" + "Mandaic,Mand" "\0" + "Manichaean,Mani" "\0" + "Marchen,Marc" "\0" + "Masaram_Gondi,Gonm" "\0" + "Medefaidrin,Medf" "\0" + "Meetei_Mayek,Mtei" "\0" + "Mende_Kikakui,Mend" "\0" + "Meroitic_Cursive,Merc" "\0" + "Meroitic_Hieroglyphs,Mero" "\0" + "Miao,Plrd" "\0" + "Modi,Modi" "\0" + "Mongolian,Mong" "\0" + "Mro,Mroo" "\0" + "Multani,Mult" "\0" + "Myanmar,Mymr" "\0" + "Nabataean,Nbat" "\0" + "Nandinagari,Nand" "\0" + "New_Tai_Lue,Talu" "\0" + "Newa,Newa" "\0" + "Nko,Nkoo" "\0" + "Nushu,Nshu" "\0" + "Nyiakeng_Puachue_Hmong,Hmnp" "\0" + "Ogham,Ogam" "\0" + "Ol_Chiki,Olck" "\0" + "Old_Hungarian,Hung" "\0" + "Old_Italic,Ital" "\0" + "Old_North_Arabian,Narb" "\0" + "Old_Permic,Perm" "\0" + "Old_Persian,Xpeo" "\0" + "Old_Sogdian,Sogo" "\0" + "Old_South_Arabian,Sarb" "\0" + "Old_Turkic,Orkh" "\0" + "Oriya,Orya" "\0" + "Osage,Osge" "\0" + "Osmanya,Osma" "\0" + "Pahawh_Hmong,Hmng" "\0" + "Palmyrene,Palm" "\0" + "Pau_Cin_Hau,Pauc" "\0" + "Phags_Pa,Phag" "\0" + "Phoenician,Phnx" "\0" + "Psalter_Pahlavi,Phlp" "\0" + "Rejang,Rjng" "\0" + "Runic,Runr" "\0" + "Samaritan,Samr" "\0" + "Saurashtra,Saur" "\0" + "Sharada,Shrd" "\0" + "Shavian,Shaw" "\0" + "Siddham,Sidd" "\0" + "SignWriting,Sgnw" "\0" + "Sinhala,Sinh" "\0" + "Sogdian,Sogd" "\0" + "Sora_Sompeng,Sora" "\0" + "Soyombo,Soyo" "\0" + "Sundanese,Sund" "\0" + "Syloti_Nagri,Sylo" "\0" + "Syriac,Syrc" "\0" + "Tagalog,Tglg" "\0" + "Tagbanwa,Tagb" "\0" + "Tai_Le,Tale" "\0" + "Tai_Tham,Lana" "\0" + "Tai_Viet,Tavt" "\0" + "Takri,Takr" "\0" + "Tamil,Taml" "\0" + "Tangut,Tang" "\0" + "Telugu,Telu" "\0" + "Thaana,Thaa" "\0" + "Thai,Thai" "\0" + "Tibetan,Tibt" "\0" + "Tifinagh,Tfng" "\0" + "Tirhuta,Tirh" "\0" + "Ugaritic,Ugar" "\0" + "Vai,Vaii" "\0" + "Wancho,Wcho" "\0" + "Warang_Citi,Wara" "\0" + "Yezidi,Yezi" "\0" + "Yi,Yiii" "\0" + "Zanabazar_Square,Zanb" "\0" +; + +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[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] = { + 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52, + 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80, + 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40, + 0xa8, 0x80, 0xd6, 0x80, +}; + +static const uint8_t unicode_prop_Other_Math_table[200] = { + 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09, + 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80, + 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c, + 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00, + 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24, + 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b, + 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41, + 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80, + 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24, + 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97, + 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81, + 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95, + 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00, + 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4, + 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, + 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, + 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, + 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x81, + 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08, + 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, + 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, + 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, +}; + +static const uint8_t unicode_prop_Other_Alphabetic_table[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, + 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, 0x88, 0x02, + 0x03, 0x40, 0xa6, 0x8b, 0x16, 0x85, 0x93, 0xb5, + 0x09, 0x8e, 0x01, 0x22, 0x89, 0x81, 0x9c, 0x82, + 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81, + 0x9c, 0x82, 0xb9, 0x23, 0x09, 0x0b, 0x80, 0x9d, + 0x0a, 0x80, 0x8a, 0x82, 0xb9, 0x38, 0x10, 0x81, + 0x94, 0x81, 0x95, 0x13, 0x82, 0xb9, 0x31, 0x09, + 0x81, 0x88, 0x81, 0x89, 0x81, 0x9d, 0x80, 0xba, + 0x22, 0x10, 0x82, 0x89, 0x80, 0xa7, 0x83, 0xb9, + 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82, + 0xb9, 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9b, + 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, 0x80, 0x89, + 0x81, 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, + 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x00, + 0x89, 0x83, 0x46, 0x73, 0x81, 0x9d, 0x81, 0x9d, + 0x81, 0x9d, 0x81, 0xc1, 0x92, 0x40, 0xbb, 0x81, + 0xa1, 0x80, 0xf5, 0x8b, 0x83, 0x88, 0x40, 0xdd, + 0x84, 0xb8, 0x89, 0x81, 0x93, 0xc9, 0x81, 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] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, + 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x59, + 0xb0, 0xbe, 0x8c, 0x80, 0xa1, 0xa4, 0x42, 0xb0, + 0x80, 0x8c, 0x80, 0x8f, 0x8c, 0x40, 0xd2, 0x8f, + 0x43, 0x4f, 0x99, 0x47, 0x91, 0x81, 0x60, 0x7a, + 0x1d, 0x81, 0x40, 0xd1, 0x80, 0x40, 0x86, 0x81, + 0x43, 0x61, 0x83, +}; + +static const uint8_t unicode_prop_Other_Uppercase_table[15] = { + 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61, + 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Grapheme_Extend_table[65] = { + 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, + 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe9, + 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, 0x80, 0xf6, + 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5, + 0x80, 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81, + 0x53, 0x9d, 0x80, 0x97, 0x80, 0x41, 0x57, 0x80, + 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x43, 0x7f, 0x80, + 0x60, 0xb8, 0x33, 0x07, 0x84, 0x6c, 0x2e, 0xac, + 0xdf, +}; + +static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { + 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52, + 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60, + 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06, + 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f, +}; + +static const uint8_t unicode_prop_Other_ID_Start_table[11] = { + 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80, + 0x4f, 0x6b, 0x81, +}; + +static const uint8_t unicode_prop_Other_ID_Continue_table[12] = { + 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, + 0x88, 0x46, 0x67, 0x80, +}; + +static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[17] = { + 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, + 0x41, 0xd1, 0x80, 0x61, 0x07, 0xd9, 0x80, 0x8e, + 0x80, +}; + +static const uint8_t unicode_prop_XID_Start1_table[31] = { + 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80, + 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85, + 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81, +}; + +static const uint8_t unicode_prop_XID_Continue1_table[23] = { + 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60, + 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { + 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e, + 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87, + 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = { + 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44, + 0x3c, 0x80, 0x59, 0x11, 0x80, 0x40, 0xe4, 0x3f, + 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, 0x11, 0x80, + 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b, + 0x84, +}; + +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[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, + 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, + 0x81, 0x89, 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, + 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80, + 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, + 0xfb, 0x08, 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, + 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, + 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, + 0x03, 0x03, 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, + 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, + 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, + 0x46, 0x52, 0x81, 0xd4, 0x83, 0x45, 0x1c, 0x10, + 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, + 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x3f, 0x87, + 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80, + 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08, + 0x8f, 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, + 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, + 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, + 0x01, 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, + 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, + 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, + 0x34, 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, + 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, + 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, + 0xd5, 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, + 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, + 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, + 0x60, 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, + 0x86, 0x81, 0x43, 0x61, 0x83, 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] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_Bidi_Control_table[10] = { + 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84, + 0xb6, 0x83, +}; + +static const uint8_t unicode_prop_Dash_table[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, 0x4f, 0x9e, 0x80, +}; + +static const uint8_t unicode_prop_Deprecated_table[23] = { + 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02, + 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85, + 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, +}; + +static const uint8_t unicode_prop_Diacritic_table[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, + 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, + 0x40, 0x85, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a, + 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, + 0x81, 0x40, 0xc8, 0x9b, 0xbc, 0x80, 0x8f, 0x02, + 0x83, 0x9b, 0x80, 0xc9, 0x80, 0x8f, 0x80, 0xed, + 0x80, 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xae, + 0x82, 0xbb, 0x80, 0x8f, 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[89] = { + 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, + 0x80, 0x41, 0xb8, 0x80, 0x43, 0x59, 0x80, 0x42, + 0xef, 0x80, 0xfe, 0x80, 0x49, 0x42, 0x80, 0xb7, + 0x80, 0x42, 0x62, 0x80, 0x41, 0x8d, 0x80, 0xc3, + 0x80, 0x53, 0x88, 0x80, 0xaa, 0x84, 0xe6, 0x81, + 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x45, 0xf5, + 0x80, 0x43, 0xc1, 0x80, 0x95, 0x80, 0x40, 0x88, + 0x80, 0xeb, 0x80, 0x94, 0x81, 0x60, 0x54, 0x7a, + 0x80, 0x53, 0xeb, 0x80, 0x42, 0x67, 0x82, 0x44, + 0xce, 0x80, 0x60, 0x50, 0xa8, 0x81, 0x44, 0x9b, + 0x08, 0x80, 0x60, 0x71, 0x57, 0x81, 0x48, 0x05, + 0x82, +}; + +static const uint8_t unicode_prop_Hex_Digit_table[12] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8, + 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_IDS_Binary_Operator_table[5] = { + 0x60, 0x2f, 0xef, 0x09, 0x87, +}; + +static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { + 0x60, 0x2f, 0xf1, 0x81, +}; + +static const uint8_t unicode_prop_Ideographic_table[66] = { + 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, + 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] = { + 0x60, 0x20, 0x0b, 0x81, +}; + +static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { + 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11, + 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, +}; + +static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = { + 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_Syntax_table[58] = { + 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99, + 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03, + 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17, + 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41, + 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d, + 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13, + 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41, + 0x04, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_White_Space_table[11] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87, + 0x81, 0x97, 0x81, +}; + +static const uint8_t unicode_prop_Quotation_Mark_table[31] = { + 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80, + 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80, + 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20, + 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81, +}; + +static const uint8_t unicode_prop_Radical_table[9] = { + 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40, + 0xd5, +}; + +static const uint8_t unicode_prop_Regional_Indicator_table[4] = { + 0x61, 0xf1, 0xe5, 0x99, +}; + +static const uint8_t unicode_prop_Sentence_Terminal_table[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, + 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15, + 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81, + 0x40, 0xcb, 0x04, 0x80, 0x41, 0x39, 0x81, 0x41, + 0x61, 0x83, 0x40, 0xad, 0x09, 0x81, 0x40, 0xda, + 0x81, 0xc0, 0x81, 0x43, 0xbb, 0x81, 0x88, 0x82, + 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x41, 0xc4, 0x80, + 0x60, 0x74, 0xfb, 0x80, 0x41, 0x0d, 0x81, 0x40, + 0xe2, 0x02, 0x80, 0x41, 0x7d, 0x81, 0xd5, 0x81, + 0xde, 0x80, 0x40, 0x97, 0x81, 0x40, 0x92, 0x82, + 0x40, 0x8f, 0x81, 0x40, 0xf8, 0x80, 0x60, 0x52, + 0x65, 0x02, 0x81, 0x40, 0xa8, 0x80, 0x8b, 0x80, + 0x8f, 0x80, 0xc0, 0x80, 0x4a, 0xf3, 0x81, 0x44, + 0xfc, 0x84, 0x40, 0xec, 0x81, 0xf4, 0x83, 0xfe, + 0x82, 0x40, 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, + 0x08, 0x81, 0xeb, 0x80, 0x41, 0xa0, 0x81, 0x41, + 0x74, 0x0c, 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, + 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] = { + 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, + 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f, + 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, + 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40, + 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81, + 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, +}; + +static const uint8_t unicode_prop_Terminal_Punctuation_table[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, + 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, + 0x8e, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, 0xf3, + 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, 0x81, + 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, 0x82, + 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, 0x19, + 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, 0x40, + 0xad, 0x08, 0x82, 0x40, 0xda, 0x84, 0xbd, 0x81, + 0x43, 0xbb, 0x81, 0x88, 0x82, 0x4d, 0xe3, 0x80, + 0x8c, 0x03, 0x80, 0x89, 0x00, 0x81, 0x41, 0xb0, + 0x81, 0x60, 0x74, 0xfa, 0x81, 0x41, 0x0c, 0x82, + 0x40, 0xe2, 0x84, 0x41, 0x7d, 0x81, 0xd5, 0x81, + 0xde, 0x80, 0x40, 0x96, 0x82, 0x40, 0x92, 0x82, + 0xfe, 0x80, 0x8f, 0x81, 0x40, 0xf8, 0x80, 0x60, + 0x52, 0x63, 0x10, 0x83, 0x40, 0xa8, 0x80, 0x89, + 0x00, 0x80, 0x8a, 0x0a, 0x80, 0xc0, 0x01, 0x80, + 0x44, 0x39, 0x80, 0xaf, 0x80, 0x44, 0x85, 0x80, + 0x40, 0xc6, 0x80, 0x41, 0x35, 0x81, 0x40, 0x97, + 0x85, 0xc3, 0x85, 0xd8, 0x83, 0x43, 0xb7, 0x84, + 0x40, 0xec, 0x86, 0xef, 0x83, 0xfe, 0x82, 0x40, + 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x84, 0xeb, + 0x80, 0x41, 0xa0, 0x82, 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[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, 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] = { + 0x58, 0x0a, 0x82, 0x60, 0xe5, 0xf1, 0x8f, 0x6d, + 0x02, 0xef, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_White_Space_table[22] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80, + 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c, + 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80, +}; + +static const uint8_t unicode_prop_Bidi_Mirrored_table[171] = { + 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, + 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e, + 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81, + 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0, + 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18, + 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23, + 0x88, 0x08, 0x00, 0x39, 0x9e, 0x0b, 0x20, 0x88, + 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81, + 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, + 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, + 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35, + 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89, + 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08, + 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18, + 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00, + 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80, + 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89, + 0x41, 0xdd, 0x89, 0x0f, 0x60, 0xce, 0x3c, 0x2c, + 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00, 0x80, 0x9b, + 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08, 0x81, 0x60, + 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8, 0x80, 0xb8, + 0x80, 0xb8, 0x80, +}; + +static const uint8_t unicode_prop_Emoji_table[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, + 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a, + 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, + 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01, + 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f, + 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08, + 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a, + 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, + 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03, + 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, + 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, + 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88, + 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, + 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, + 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, + 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, + 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, + 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88, + 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, + 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, + 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, + 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, + 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, + 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 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] = { + 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40, + 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3, + 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83, + 0x6c, 0x06, 0x6b, 0xdf, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_table[4] = { + 0x61, 0xf3, 0xfa, 0x84, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_Base_table[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, 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[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, + 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, + 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00, + 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, + 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, + 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd, + 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93, + 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0, + 0x8b, 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa, + 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80, + 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91, + 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf, + 0xc5, 0x28, 0x12, 0x0a, 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[156] = { + 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, + 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, + 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, + 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, + 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, + 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, + 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, + 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, + 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80, + 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82, + 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80, + 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, + 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, + 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8, + 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, + 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, + 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b, + 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, + 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, + 0x40, 0xff, 0x43, 0xfd, +}; + +static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { + 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb, + 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4, + 0x83, 0x47, 0xfb, 0x84, 0x99, 0x84, 0xb0, 0x8f, + 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40, + 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60, + 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e, + 0x84, 0x4f, 0xff, +}; + +typedef enum { + UNICODE_PROP_Hyphen, + UNICODE_PROP_Other_Math, + UNICODE_PROP_Other_Alphabetic, + UNICODE_PROP_Other_Lowercase, + UNICODE_PROP_Other_Uppercase, + UNICODE_PROP_Other_Grapheme_Extend, + UNICODE_PROP_Other_Default_Ignorable_Code_Point, + UNICODE_PROP_Other_ID_Start, + UNICODE_PROP_Other_ID_Continue, + UNICODE_PROP_Prepended_Concatenation_Mark, + UNICODE_PROP_ID_Continue1, + UNICODE_PROP_XID_Start1, + UNICODE_PROP_XID_Continue1, + UNICODE_PROP_Changes_When_Titlecased1, + UNICODE_PROP_Changes_When_Casefolded1, + UNICODE_PROP_Changes_When_NFKC_Casefolded1, + UNICODE_PROP_ASCII_Hex_Digit, + UNICODE_PROP_Bidi_Control, + UNICODE_PROP_Dash, + UNICODE_PROP_Deprecated, + UNICODE_PROP_Diacritic, + UNICODE_PROP_Extender, + UNICODE_PROP_Hex_Digit, + UNICODE_PROP_IDS_Binary_Operator, + UNICODE_PROP_IDS_Trinary_Operator, + UNICODE_PROP_Ideographic, + UNICODE_PROP_Join_Control, + UNICODE_PROP_Logical_Order_Exception, + UNICODE_PROP_Noncharacter_Code_Point, + UNICODE_PROP_Pattern_Syntax, + UNICODE_PROP_Pattern_White_Space, + UNICODE_PROP_Quotation_Mark, + UNICODE_PROP_Radical, + UNICODE_PROP_Regional_Indicator, + UNICODE_PROP_Sentence_Terminal, + UNICODE_PROP_Soft_Dotted, + UNICODE_PROP_Terminal_Punctuation, + UNICODE_PROP_Unified_Ideograph, + UNICODE_PROP_Variation_Selector, + UNICODE_PROP_White_Space, + UNICODE_PROP_Bidi_Mirrored, + UNICODE_PROP_Emoji, + UNICODE_PROP_Emoji_Component, + UNICODE_PROP_Emoji_Modifier, + UNICODE_PROP_Emoji_Modifier_Base, + UNICODE_PROP_Emoji_Presentation, + UNICODE_PROP_Extended_Pictographic, + UNICODE_PROP_Default_Ignorable_Code_Point, + UNICODE_PROP_ID_Start, + UNICODE_PROP_Case_Ignorable, + UNICODE_PROP_ASCII, + UNICODE_PROP_Alphabetic, + UNICODE_PROP_Any, + UNICODE_PROP_Assigned, + UNICODE_PROP_Cased, + UNICODE_PROP_Changes_When_Casefolded, + UNICODE_PROP_Changes_When_Casemapped, + UNICODE_PROP_Changes_When_Lowercased, + UNICODE_PROP_Changes_When_NFKC_Casefolded, + UNICODE_PROP_Changes_When_Titlecased, + UNICODE_PROP_Changes_When_Uppercased, + UNICODE_PROP_Grapheme_Base, + UNICODE_PROP_Grapheme_Extend, + UNICODE_PROP_ID_Continue, + UNICODE_PROP_Lowercase, + UNICODE_PROP_Math, + UNICODE_PROP_Uppercase, + UNICODE_PROP_XID_Continue, + UNICODE_PROP_XID_Start, + UNICODE_PROP_Cased1, + UNICODE_PROP_COUNT, +} UnicodePropertyEnum; + +static const char unicode_prop_name_table[] = + "ASCII_Hex_Digit,AHex" "\0" + "Bidi_Control,Bidi_C" "\0" + "Dash" "\0" + "Deprecated,Dep" "\0" + "Diacritic,Dia" "\0" + "Extender,Ext" "\0" + "Hex_Digit,Hex" "\0" + "IDS_Binary_Operator,IDSB" "\0" + "IDS_Trinary_Operator,IDST" "\0" + "Ideographic,Ideo" "\0" + "Join_Control,Join_C" "\0" + "Logical_Order_Exception,LOE" "\0" + "Noncharacter_Code_Point,NChar" "\0" + "Pattern_Syntax,Pat_Syn" "\0" + "Pattern_White_Space,Pat_WS" "\0" + "Quotation_Mark,QMark" "\0" + "Radical" "\0" + "Regional_Indicator,RI" "\0" + "Sentence_Terminal,STerm" "\0" + "Soft_Dotted,SD" "\0" + "Terminal_Punctuation,Term" "\0" + "Unified_Ideograph,UIdeo" "\0" + "Variation_Selector,VS" "\0" + "White_Space,space" "\0" + "Bidi_Mirrored,Bidi_M" "\0" + "Emoji" "\0" + "Emoji_Component,EComp" "\0" + "Emoji_Modifier,EMod" "\0" + "Emoji_Modifier_Base,EBase" "\0" + "Emoji_Presentation,EPres" "\0" + "Extended_Pictographic,ExtPict" "\0" + "Default_Ignorable_Code_Point,DI" "\0" + "ID_Start,IDS" "\0" + "Case_Ignorable,CI" "\0" + "ASCII" "\0" + "Alphabetic,Alpha" "\0" + "Any" "\0" + "Assigned" "\0" + "Cased" "\0" + "Changes_When_Casefolded,CWCF" "\0" + "Changes_When_Casemapped,CWCM" "\0" + "Changes_When_Lowercased,CWL" "\0" + "Changes_When_NFKC_Casefolded,CWKCF" "\0" + "Changes_When_Titlecased,CWT" "\0" + "Changes_When_Uppercased,CWU" "\0" + "Grapheme_Base,Gr_Base" "\0" + "Grapheme_Extend,Gr_Ext" "\0" + "ID_Continue,IDC" "\0" + "Lowercase,Lower" "\0" + "Math" "\0" + "Uppercase,Upper" "\0" + "XID_Continue,XIDC" "\0" + "XID_Start,XIDS" "\0" +; + +static const uint8_t * const unicode_prop_table[] = { + unicode_prop_Hyphen_table, + unicode_prop_Other_Math_table, + unicode_prop_Other_Alphabetic_table, + unicode_prop_Other_Lowercase_table, + unicode_prop_Other_Uppercase_table, + unicode_prop_Other_Grapheme_Extend_table, + unicode_prop_Other_Default_Ignorable_Code_Point_table, + unicode_prop_Other_ID_Start_table, + unicode_prop_Other_ID_Continue_table, + unicode_prop_Prepended_Concatenation_Mark_table, + unicode_prop_ID_Continue1_table, + unicode_prop_XID_Start1_table, + unicode_prop_XID_Continue1_table, + unicode_prop_Changes_When_Titlecased1_table, + unicode_prop_Changes_When_Casefolded1_table, + unicode_prop_Changes_When_NFKC_Casefolded1_table, + unicode_prop_ASCII_Hex_Digit_table, + unicode_prop_Bidi_Control_table, + unicode_prop_Dash_table, + unicode_prop_Deprecated_table, + unicode_prop_Diacritic_table, + unicode_prop_Extender_table, + unicode_prop_Hex_Digit_table, + unicode_prop_IDS_Binary_Operator_table, + unicode_prop_IDS_Trinary_Operator_table, + unicode_prop_Ideographic_table, + unicode_prop_Join_Control_table, + unicode_prop_Logical_Order_Exception_table, + unicode_prop_Noncharacter_Code_Point_table, + unicode_prop_Pattern_Syntax_table, + unicode_prop_Pattern_White_Space_table, + unicode_prop_Quotation_Mark_table, + unicode_prop_Radical_table, + unicode_prop_Regional_Indicator_table, + unicode_prop_Sentence_Terminal_table, + unicode_prop_Soft_Dotted_table, + unicode_prop_Terminal_Punctuation_table, + unicode_prop_Unified_Ideograph_table, + unicode_prop_Variation_Selector_table, + unicode_prop_White_Space_table, + unicode_prop_Bidi_Mirrored_table, + unicode_prop_Emoji_table, + unicode_prop_Emoji_Component_table, + unicode_prop_Emoji_Modifier_table, + unicode_prop_Emoji_Modifier_Base_table, + unicode_prop_Emoji_Presentation_table, + unicode_prop_Extended_Pictographic_table, + unicode_prop_Default_Ignorable_Code_Point_table, + unicode_prop_ID_Start_table, + unicode_prop_Case_Ignorable_table, +}; + +static const uint16_t unicode_prop_len_table[] = { + countof(unicode_prop_Hyphen_table), + countof(unicode_prop_Other_Math_table), + countof(unicode_prop_Other_Alphabetic_table), + countof(unicode_prop_Other_Lowercase_table), + countof(unicode_prop_Other_Uppercase_table), + countof(unicode_prop_Other_Grapheme_Extend_table), + countof(unicode_prop_Other_Default_Ignorable_Code_Point_table), + countof(unicode_prop_Other_ID_Start_table), + countof(unicode_prop_Other_ID_Continue_table), + countof(unicode_prop_Prepended_Concatenation_Mark_table), + countof(unicode_prop_ID_Continue1_table), + countof(unicode_prop_XID_Start1_table), + countof(unicode_prop_XID_Continue1_table), + countof(unicode_prop_Changes_When_Titlecased1_table), + countof(unicode_prop_Changes_When_Casefolded1_table), + countof(unicode_prop_Changes_When_NFKC_Casefolded1_table), + countof(unicode_prop_ASCII_Hex_Digit_table), + countof(unicode_prop_Bidi_Control_table), + countof(unicode_prop_Dash_table), + countof(unicode_prop_Deprecated_table), + countof(unicode_prop_Diacritic_table), + countof(unicode_prop_Extender_table), + countof(unicode_prop_Hex_Digit_table), + countof(unicode_prop_IDS_Binary_Operator_table), + countof(unicode_prop_IDS_Trinary_Operator_table), + countof(unicode_prop_Ideographic_table), + countof(unicode_prop_Join_Control_table), + countof(unicode_prop_Logical_Order_Exception_table), + countof(unicode_prop_Noncharacter_Code_Point_table), + countof(unicode_prop_Pattern_Syntax_table), + countof(unicode_prop_Pattern_White_Space_table), + countof(unicode_prop_Quotation_Mark_table), + countof(unicode_prop_Radical_table), + countof(unicode_prop_Regional_Indicator_table), + countof(unicode_prop_Sentence_Terminal_table), + countof(unicode_prop_Soft_Dotted_table), + countof(unicode_prop_Terminal_Punctuation_table), + countof(unicode_prop_Unified_Ideograph_table), + countof(unicode_prop_Variation_Selector_table), + countof(unicode_prop_White_Space_table), + countof(unicode_prop_Bidi_Mirrored_table), + countof(unicode_prop_Emoji_table), + countof(unicode_prop_Emoji_Component_table), + countof(unicode_prop_Emoji_Modifier_table), + countof(unicode_prop_Emoji_Modifier_Base_table), + countof(unicode_prop_Emoji_Presentation_table), + countof(unicode_prop_Extended_Pictographic_table), + countof(unicode_prop_Default_Ignorable_Code_Point_table), + countof(unicode_prop_ID_Start_table), + countof(unicode_prop_Case_Ignorable_table), +}; + +#endif /* CONFIG_ALL_UNICODE */ diff --git a/include/quickjs/libunicode.h b/include/quickjs/libunicode.h index cfa600a50..6510297be 100644 --- a/include/quickjs/libunicode.h +++ b/include/quickjs/libunicode.h @@ -1,124 +1,124 @@ -/* - * Unicode utilities - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIBUNICODE_H -#define LIBUNICODE_H - -#include - -#define LRE_BOOL int /* for documentation purposes */ - -/* define it to include all the unicode tables (40KB larger) */ -#define CONFIG_ALL_UNICODE - -#define LRE_CC_RES_LEN_MAX 3 - -typedef enum { - UNICODE_NFC, - UNICODE_NFD, - UNICODE_NFKC, - UNICODE_NFKD, -} UnicodeNormalizationEnum; - -int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); -LRE_BOOL lre_is_cased(uint32_t c); -LRE_BOOL lre_is_case_ignorable(uint32_t c); - -/* char ranges */ - -typedef struct { - int len; /* in points, always even */ - int size; - uint32_t *points; /* points sorted by increasing value */ - void *mem_opaque; - void *(*realloc_func)(void *opaque, void *ptr, size_t size); -} CharRange; - -typedef enum { - CR_OP_UNION, - CR_OP_INTER, - CR_OP_XOR, -} CharRangeOpEnum; - -void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); -void cr_free(CharRange *cr); -int cr_realloc(CharRange *cr, int size); -int cr_copy(CharRange *cr, const CharRange *cr1); - -static inline int cr_add_point(CharRange *cr, uint32_t v) -{ - if (cr->len >= cr->size) { - if (cr_realloc(cr, cr->len + 1)) - return -1; - } - cr->points[cr->len++] = v; - return 0; -} - -static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) -{ - if ((cr->len + 2) > cr->size) { - if (cr_realloc(cr, cr->len + 2)) - return -1; - } - cr->points[cr->len++] = c1; - cr->points[cr->len++] = c2; - return 0; -} - -int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); - -static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) -{ - uint32_t b_pt[2]; - b_pt[0] = c1; - b_pt[1] = c2 + 1; - return cr_union1(cr, b_pt, 2); -} - -int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, - const uint32_t *b_pt, int b_len, int op); - -int cr_invert(CharRange *cr); - -#ifdef CONFIG_ALL_UNICODE - -LRE_BOOL lre_is_id_start(uint32_t c); -LRE_BOOL lre_is_id_continue(uint32_t c); - -int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, - UnicodeNormalizationEnum n_type, - void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); - -/* Unicode character range functions */ - -int unicode_script(CharRange *cr, - const char *script_name, LRE_BOOL is_ext); -int unicode_general_category(CharRange *cr, const char *gc_name); -int unicode_prop(CharRange *cr, const char *prop_name); - -#endif /* CONFIG_ALL_UNICODE */ - -#undef LRE_BOOL - -#endif /* LIBUNICODE_H */ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBUNICODE_H +#define LIBUNICODE_H + +#include + +#define LRE_BOOL int /* for documentation purposes */ + +/* define it to include all the unicode tables (40KB larger) */ +#define CONFIG_ALL_UNICODE + +#define LRE_CC_RES_LEN_MAX 3 + +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; + +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +LRE_BOOL lre_is_cased(uint32_t c); +LRE_BOOL lre_is_case_ignorable(uint32_t c); + +/* char ranges */ + +typedef struct { + int len; /* in points, always even */ + int size; + uint32_t *points; /* points sorted by increasing value */ + void *mem_opaque; + void *(*realloc_func)(void *opaque, void *ptr, size_t size); +} CharRange; + +typedef enum { + CR_OP_UNION, + CR_OP_INTER, + CR_OP_XOR, +} CharRangeOpEnum; + +void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); +void cr_free(CharRange *cr); +int cr_realloc(CharRange *cr, int size); +int cr_copy(CharRange *cr, const CharRange *cr1); + +static inline int cr_add_point(CharRange *cr, uint32_t v) +{ + if (cr->len >= cr->size) { + if (cr_realloc(cr, cr->len + 1)) + return -1; + } + cr->points[cr->len++] = v; + return 0; +} + +static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + if ((cr->len + 2) > cr->size) { + if (cr_realloc(cr, cr->len + 2)) + return -1; + } + cr->points[cr->len++] = c1; + cr->points[cr->len++] = c2; + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); + +static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + uint32_t b_pt[2]; + b_pt[0] = c1; + b_pt[1] = c2 + 1; + return cr_union1(cr, b_pt, 2); +} + +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op); + +int cr_invert(CharRange *cr); + +#ifdef CONFIG_ALL_UNICODE + +LRE_BOOL lre_is_id_start(uint32_t c); +LRE_BOOL lre_is_id_continue(uint32_t c); + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); + +/* Unicode character range functions */ + +int unicode_script(CharRange *cr, + const char *script_name, LRE_BOOL is_ext); +int unicode_general_category(CharRange *cr, const char *gc_name); +int unicode_prop(CharRange *cr, const char *prop_name); + +#endif /* CONFIG_ALL_UNICODE */ + +#undef LRE_BOOL + +#endif /* LIBUNICODE_H */ diff --git a/include/quickjs/list.h b/include/quickjs/list.h index 46ee90e8c..7fcbf675e 100644 --- a/include/quickjs/list.h +++ b/include/quickjs/list.h @@ -1,102 +1,102 @@ -/* - * Linux klist like system - * - * Copyright (c) 2016-2017 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef LIST_H -#define LIST_H - -#ifndef NULL -#include -#endif - -#include - -struct list_head { - struct list_head *prev; - struct list_head *next; -}; - -#define LIST_HEAD_INIT(el) { &(el), &(el) } - -/* return the pointer of type 'type *' containing 'el' as field 'member' */ -#define list_entry(el, type, member) \ - ((type *)((uint8_t *)(el) - offsetof(type, member))) - -static inline void init_list_head(struct list_head *head) -{ - head->prev = head; - head->next = head; -} - -/* insert 'el' between 'prev' and 'next' */ -static inline void __list_add(struct list_head *el, - struct list_head *prev, struct list_head *next) -{ - prev->next = el; - el->prev = prev; - el->next = next; - next->prev = el; -} - -/* add 'el' at the head of the list 'head' (= after element head) */ -static inline void list_add(struct list_head *el, struct list_head *head) -{ - __list_add(el, head, head->next); -} - -/* add 'el' at the end of the list 'head' (= before element head) */ -static inline void list_add_tail(struct list_head *el, struct list_head *head) -{ - __list_add(el, head->prev, head); -} - -static inline void list_del(struct list_head *el) -{ - struct list_head *prev, *next; - prev = el->prev; - next = el->next; - prev->next = next; - next->prev = prev; - el->prev = NULL; /* fail safe */ - el->next = NULL; /* fail safe */ -} - -static inline int list_empty(struct list_head *el) -{ - return el->next == el; -} - -#define list_for_each(el, head) \ - for(el = (head)->next; el != (head); el = el->next) - -#define list_for_each_safe(el, el1, head) \ - for(el = (head)->next, el1 = el->next; el != (head); \ - el = el1, el1 = el->next) - -#define list_for_each_prev(el, head) \ - for(el = (head)->prev; el != (head); el = el->prev) - -#define list_for_each_prev_safe(el, el1, head) \ - for(el = (head)->prev, el1 = el->prev; el != (head); \ - el = el1, el1 = el->prev) - -#endif /* LIST_H */ +/* + * Linux klist like system + * + * Copyright (c) 2016-2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIST_H +#define LIST_H + +#ifndef NULL +#include +#endif + +#include + +struct list_head { + struct list_head *prev; + struct list_head *next; +}; + +#define LIST_HEAD_INIT(el) { &(el), &(el) } + +/* return the pointer of type 'type *' containing 'el' as field 'member' */ +#define list_entry(el, type, member) \ + ((type *)((uint8_t *)(el) - offsetof(type, member))) + +static inline void init_list_head(struct list_head *head) +{ + head->prev = head; + head->next = head; +} + +/* insert 'el' between 'prev' and 'next' */ +static inline void __list_add(struct list_head *el, + struct list_head *prev, struct list_head *next) +{ + prev->next = el; + el->prev = prev; + el->next = next; + next->prev = el; +} + +/* add 'el' at the head of the list 'head' (= after element head) */ +static inline void list_add(struct list_head *el, struct list_head *head) +{ + __list_add(el, head, head->next); +} + +/* add 'el' at the end of the list 'head' (= before element head) */ +static inline void list_add_tail(struct list_head *el, struct list_head *head) +{ + __list_add(el, head->prev, head); +} + +static inline void list_del(struct list_head *el) +{ + struct list_head *prev, *next; + prev = el->prev; + next = el->next; + prev->next = next; + next->prev = prev; + el->prev = NULL; /* fail safe */ + el->next = NULL; /* fail safe */ +} + +static inline int list_empty(struct list_head *el) +{ + return el->next == el; +} + +#define list_for_each(el, head) \ + for(el = (head)->next; el != (head); el = el->next) + +#define list_for_each_safe(el, el1, head) \ + for(el = (head)->next, el1 = el->next; el != (head); \ + el = el1, el1 = el->next) + +#define list_for_each_prev(el, head) \ + for(el = (head)->prev; el != (head); el = el->prev) + +#define list_for_each_prev_safe(el, el1, head) \ + for(el = (head)->prev, el1 = el->prev; el != (head); \ + el = el1, el1 = el->prev) + +#endif /* LIST_H */ diff --git a/include/quickjs/quickjs-atom.h b/include/quickjs/quickjs-atom.h index 4c2279452..e10b29735 100644 --- a/include/quickjs/quickjs-atom.h +++ b/include/quickjs/quickjs-atom.h @@ -1,273 +1,273 @@ -/* - * QuickJS atom definitions - * - * Copyright (c) 2017-2018 Fabrice Bellard - * Copyright (c) 2017-2018 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifdef DEF - -/* Note: first atoms are considered as keywords in the parser */ -DEF(null, "null") /* must be first */ -DEF(false, "false") -DEF(true, "true") -DEF(if, "if") -DEF(else, "else") -DEF(return, "return") -DEF(var, "var") -DEF(this, "this") -DEF(delete, "delete") -DEF(void, "void") -DEF(typeof, "typeof") -DEF(new, "new") -DEF(in, "in") -DEF(instanceof, "instanceof") -DEF(do, "do") -DEF(while, "while") -DEF(for, "for") -DEF(break, "break") -DEF(continue, "continue") -DEF(switch, "switch") -DEF(case, "case") -DEF(default, "default") -DEF(throw, "throw") -DEF(try, "try") -DEF(catch, "catch") -DEF(finally, "finally") -DEF(function, "function") -DEF(debugger, "debugger") -DEF(with, "with") -/* FutureReservedWord */ -DEF(class, "class") -DEF(const, "const") -DEF(enum, "enum") -DEF(export, "export") -DEF(extends, "extends") -DEF(import, "import") -DEF(super, "super") -/* FutureReservedWords when parsing strict mode code */ -DEF(implements, "implements") -DEF(interface, "interface") -DEF(let, "let") -DEF(package, "package") -DEF(private, "private") -DEF(protected, "protected") -DEF(public, "public") -DEF(static, "static") -DEF(yield, "yield") -DEF(await, "await") - -/* empty string */ -DEF(empty_string, "") -/* identifiers */ -DEF(length, "length") -DEF(fileName, "fileName") -DEF(lineNumber, "lineNumber") -DEF(message, "message") -DEF(errors, "errors") -DEF(stack, "stack") -DEF(name, "name") -DEF(toString, "toString") -DEF(toLocaleString, "toLocaleString") -DEF(valueOf, "valueOf") -DEF(eval, "eval") -DEF(prototype, "prototype") -DEF(constructor, "constructor") -DEF(configurable, "configurable") -DEF(writable, "writable") -DEF(enumerable, "enumerable") -DEF(value, "value") -DEF(get, "get") -DEF(set, "set") -DEF(of, "of") -DEF(__proto__, "__proto__") -DEF(undefined, "undefined") -DEF(number, "number") -DEF(boolean, "boolean") -DEF(string, "string") -DEF(object, "object") -DEF(symbol, "symbol") -DEF(integer, "integer") -DEF(unknown, "unknown") -DEF(arguments, "arguments") -DEF(callee, "callee") -DEF(caller, "caller") -DEF(_eval_, "") -DEF(_ret_, "") -DEF(_var_, "") -DEF(_arg_var_, "") -DEF(_with_, "") -DEF(lastIndex, "lastIndex") -DEF(target, "target") -DEF(index, "index") -DEF(input, "input") -DEF(defineProperties, "defineProperties") -DEF(apply, "apply") -DEF(join, "join") -DEF(concat, "concat") -DEF(split, "split") -DEF(construct, "construct") -DEF(getPrototypeOf, "getPrototypeOf") -DEF(setPrototypeOf, "setPrototypeOf") -DEF(isExtensible, "isExtensible") -DEF(preventExtensions, "preventExtensions") -DEF(has, "has") -DEF(deleteProperty, "deleteProperty") -DEF(defineProperty, "defineProperty") -DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") -DEF(ownKeys, "ownKeys") -DEF(add, "add") -DEF(done, "done") -DEF(next, "next") -DEF(values, "values") -DEF(source, "source") -DEF(flags, "flags") -DEF(global, "global") -DEF(unicode, "unicode") -DEF(raw, "raw") -DEF(new_target, "new.target") -DEF(this_active_func, "this.active_func") -DEF(home_object, "") -DEF(computed_field, "") -DEF(static_computed_field, "") /* must come after computed_fields */ -DEF(class_fields_init, "") -DEF(brand, "") -DEF(hash_constructor, "#constructor") -DEF(as, "as") -DEF(from, "from") -DEF(meta, "meta") -DEF(_default_, "*default*") -DEF(_star_, "*") -DEF(Module, "Module") -DEF(then, "then") -DEF(resolve, "resolve") -DEF(reject, "reject") -DEF(promise, "promise") -DEF(proxy, "proxy") -DEF(revoke, "revoke") -DEF(async, "async") -DEF(exec, "exec") -DEF(groups, "groups") -DEF(status, "status") -DEF(reason, "reason") -DEF(globalThis, "globalThis") -#ifdef CONFIG_BIGNUM -DEF(bigint, "bigint") -DEF(bigfloat, "bigfloat") -DEF(bigdecimal, "bigdecimal") -DEF(roundingMode, "roundingMode") -DEF(maximumSignificantDigits, "maximumSignificantDigits") -DEF(maximumFractionDigits, "maximumFractionDigits") -#endif -#ifdef CONFIG_ATOMICS -DEF(not_equal, "not-equal") -DEF(timed_out, "timed-out") -DEF(ok, "ok") -#endif -DEF(toJSON, "toJSON") -/* class names */ -DEF(Object, "Object") -DEF(Array, "Array") -DEF(Error, "Error") -DEF(Number, "Number") -DEF(String, "String") -DEF(Boolean, "Boolean") -DEF(Symbol, "Symbol") -DEF(Arguments, "Arguments") -DEF(Math, "Math") -DEF(JSON, "JSON") -DEF(Date, "Date") -DEF(Function, "Function") -DEF(GeneratorFunction, "GeneratorFunction") -DEF(ForInIterator, "ForInIterator") -DEF(RegExp, "RegExp") -DEF(ArrayBuffer, "ArrayBuffer") -DEF(SharedArrayBuffer, "SharedArrayBuffer") -/* must keep same order as class IDs for typed arrays */ -DEF(Uint8ClampedArray, "Uint8ClampedArray") -DEF(Int8Array, "Int8Array") -DEF(Uint8Array, "Uint8Array") -DEF(Int16Array, "Int16Array") -DEF(Uint16Array, "Uint16Array") -DEF(Int32Array, "Int32Array") -DEF(Uint32Array, "Uint32Array") -#ifdef CONFIG_BIGNUM -DEF(BigInt64Array, "BigInt64Array") -DEF(BigUint64Array, "BigUint64Array") -#endif -DEF(Float32Array, "Float32Array") -DEF(Float64Array, "Float64Array") -DEF(DataView, "DataView") -#ifdef CONFIG_BIGNUM -DEF(BigInt, "BigInt") -DEF(BigFloat, "BigFloat") -DEF(BigFloatEnv, "BigFloatEnv") -DEF(BigDecimal, "BigDecimal") -DEF(OperatorSet, "OperatorSet") -DEF(Operators, "Operators") -#endif -DEF(Map, "Map") -DEF(Set, "Set") /* Map + 1 */ -DEF(WeakMap, "WeakMap") /* Map + 2 */ -DEF(WeakSet, "WeakSet") /* Map + 3 */ -DEF(Map_Iterator, "Map Iterator") -DEF(Set_Iterator, "Set Iterator") -DEF(Array_Iterator, "Array Iterator") -DEF(String_Iterator, "String Iterator") -DEF(RegExp_String_Iterator, "RegExp String Iterator") -DEF(Generator, "Generator") -DEF(Proxy, "Proxy") -DEF(Promise, "Promise") -DEF(PromiseResolveFunction, "PromiseResolveFunction") -DEF(PromiseRejectFunction, "PromiseRejectFunction") -DEF(AsyncFunction, "AsyncFunction") -DEF(AsyncFunctionResolve, "AsyncFunctionResolve") -DEF(AsyncFunctionReject, "AsyncFunctionReject") -DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") -DEF(AsyncGenerator, "AsyncGenerator") -DEF(EvalError, "EvalError") -DEF(RangeError, "RangeError") -DEF(ReferenceError, "ReferenceError") -DEF(SyntaxError, "SyntaxError") -DEF(TypeError, "TypeError") -DEF(URIError, "URIError") -DEF(InternalError, "InternalError") -/* private symbols */ -DEF(Private_brand, "") -/* symbols */ -DEF(Symbol_toPrimitive, "Symbol.toPrimitive") -DEF(Symbol_iterator, "Symbol.iterator") -DEF(Symbol_match, "Symbol.match") -DEF(Symbol_matchAll, "Symbol.matchAll") -DEF(Symbol_replace, "Symbol.replace") -DEF(Symbol_search, "Symbol.search") -DEF(Symbol_split, "Symbol.split") -DEF(Symbol_toStringTag, "Symbol.toStringTag") -DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") -DEF(Symbol_hasInstance, "Symbol.hasInstance") -DEF(Symbol_species, "Symbol.species") -DEF(Symbol_unscopables, "Symbol.unscopables") -DEF(Symbol_asyncIterator, "Symbol.asyncIterator") -#ifdef CONFIG_BIGNUM -DEF(Symbol_operatorSet, "Symbol.operatorSet") -#endif - -#endif /* DEF */ +/* + * QuickJS atom definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +/* Note: first atoms are considered as keywords in the parser */ +DEF(null, "null") /* must be first */ +DEF(false, "false") +DEF(true, "true") +DEF(if, "if") +DEF(else, "else") +DEF(return, "return") +DEF(var, "var") +DEF(this, "this") +DEF(delete, "delete") +DEF(void, "void") +DEF(typeof, "typeof") +DEF(new, "new") +DEF(in, "in") +DEF(instanceof, "instanceof") +DEF(do, "do") +DEF(while, "while") +DEF(for, "for") +DEF(break, "break") +DEF(continue, "continue") +DEF(switch, "switch") +DEF(case, "case") +DEF(default, "default") +DEF(throw, "throw") +DEF(try, "try") +DEF(catch, "catch") +DEF(finally, "finally") +DEF(function, "function") +DEF(debugger, "debugger") +DEF(with, "with") +/* FutureReservedWord */ +DEF(class, "class") +DEF(const, "const") +DEF(enum, "enum") +DEF(export, "export") +DEF(extends, "extends") +DEF(import, "import") +DEF(super, "super") +/* FutureReservedWords when parsing strict mode code */ +DEF(implements, "implements") +DEF(interface, "interface") +DEF(let, "let") +DEF(package, "package") +DEF(private, "private") +DEF(protected, "protected") +DEF(public, "public") +DEF(static, "static") +DEF(yield, "yield") +DEF(await, "await") + +/* empty string */ +DEF(empty_string, "") +/* identifiers */ +DEF(length, "length") +DEF(fileName, "fileName") +DEF(lineNumber, "lineNumber") +DEF(message, "message") +DEF(errors, "errors") +DEF(stack, "stack") +DEF(name, "name") +DEF(toString, "toString") +DEF(toLocaleString, "toLocaleString") +DEF(valueOf, "valueOf") +DEF(eval, "eval") +DEF(prototype, "prototype") +DEF(constructor, "constructor") +DEF(configurable, "configurable") +DEF(writable, "writable") +DEF(enumerable, "enumerable") +DEF(value, "value") +DEF(get, "get") +DEF(set, "set") +DEF(of, "of") +DEF(__proto__, "__proto__") +DEF(undefined, "undefined") +DEF(number, "number") +DEF(boolean, "boolean") +DEF(string, "string") +DEF(object, "object") +DEF(symbol, "symbol") +DEF(integer, "integer") +DEF(unknown, "unknown") +DEF(arguments, "arguments") +DEF(callee, "callee") +DEF(caller, "caller") +DEF(_eval_, "") +DEF(_ret_, "") +DEF(_var_, "") +DEF(_arg_var_, "") +DEF(_with_, "") +DEF(lastIndex, "lastIndex") +DEF(target, "target") +DEF(index, "index") +DEF(input, "input") +DEF(defineProperties, "defineProperties") +DEF(apply, "apply") +DEF(join, "join") +DEF(concat, "concat") +DEF(split, "split") +DEF(construct, "construct") +DEF(getPrototypeOf, "getPrototypeOf") +DEF(setPrototypeOf, "setPrototypeOf") +DEF(isExtensible, "isExtensible") +DEF(preventExtensions, "preventExtensions") +DEF(has, "has") +DEF(deleteProperty, "deleteProperty") +DEF(defineProperty, "defineProperty") +DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") +DEF(ownKeys, "ownKeys") +DEF(add, "add") +DEF(done, "done") +DEF(next, "next") +DEF(values, "values") +DEF(source, "source") +DEF(flags, "flags") +DEF(global, "global") +DEF(unicode, "unicode") +DEF(raw, "raw") +DEF(new_target, "new.target") +DEF(this_active_func, "this.active_func") +DEF(home_object, "") +DEF(computed_field, "") +DEF(static_computed_field, "") /* must come after computed_fields */ +DEF(class_fields_init, "") +DEF(brand, "") +DEF(hash_constructor, "#constructor") +DEF(as, "as") +DEF(from, "from") +DEF(meta, "meta") +DEF(_default_, "*default*") +DEF(_star_, "*") +DEF(Module, "Module") +DEF(then, "then") +DEF(resolve, "resolve") +DEF(reject, "reject") +DEF(promise, "promise") +DEF(proxy, "proxy") +DEF(revoke, "revoke") +DEF(async, "async") +DEF(exec, "exec") +DEF(groups, "groups") +DEF(status, "status") +DEF(reason, "reason") +DEF(globalThis, "globalThis") +#ifdef CONFIG_BIGNUM +DEF(bigint, "bigint") +DEF(bigfloat, "bigfloat") +DEF(bigdecimal, "bigdecimal") +DEF(roundingMode, "roundingMode") +DEF(maximumSignificantDigits, "maximumSignificantDigits") +DEF(maximumFractionDigits, "maximumFractionDigits") +#endif +#ifdef CONFIG_ATOMICS +DEF(not_equal, "not-equal") +DEF(timed_out, "timed-out") +DEF(ok, "ok") +#endif +DEF(toJSON, "toJSON") +/* class names */ +DEF(Object, "Object") +DEF(Array, "Array") +DEF(Error, "Error") +DEF(Number, "Number") +DEF(String, "String") +DEF(Boolean, "Boolean") +DEF(Symbol, "Symbol") +DEF(Arguments, "Arguments") +DEF(Math, "Math") +DEF(JSON, "JSON") +DEF(Date, "Date") +DEF(Function, "Function") +DEF(GeneratorFunction, "GeneratorFunction") +DEF(ForInIterator, "ForInIterator") +DEF(RegExp, "RegExp") +DEF(ArrayBuffer, "ArrayBuffer") +DEF(SharedArrayBuffer, "SharedArrayBuffer") +/* must keep same order as class IDs for typed arrays */ +DEF(Uint8ClampedArray, "Uint8ClampedArray") +DEF(Int8Array, "Int8Array") +DEF(Uint8Array, "Uint8Array") +DEF(Int16Array, "Int16Array") +DEF(Uint16Array, "Uint16Array") +DEF(Int32Array, "Int32Array") +DEF(Uint32Array, "Uint32Array") +#ifdef CONFIG_BIGNUM +DEF(BigInt64Array, "BigInt64Array") +DEF(BigUint64Array, "BigUint64Array") +#endif +DEF(Float32Array, "Float32Array") +DEF(Float64Array, "Float64Array") +DEF(DataView, "DataView") +#ifdef CONFIG_BIGNUM +DEF(BigInt, "BigInt") +DEF(BigFloat, "BigFloat") +DEF(BigFloatEnv, "BigFloatEnv") +DEF(BigDecimal, "BigDecimal") +DEF(OperatorSet, "OperatorSet") +DEF(Operators, "Operators") +#endif +DEF(Map, "Map") +DEF(Set, "Set") /* Map + 1 */ +DEF(WeakMap, "WeakMap") /* Map + 2 */ +DEF(WeakSet, "WeakSet") /* Map + 3 */ +DEF(Map_Iterator, "Map Iterator") +DEF(Set_Iterator, "Set Iterator") +DEF(Array_Iterator, "Array Iterator") +DEF(String_Iterator, "String Iterator") +DEF(RegExp_String_Iterator, "RegExp String Iterator") +DEF(Generator, "Generator") +DEF(Proxy, "Proxy") +DEF(Promise, "Promise") +DEF(PromiseResolveFunction, "PromiseResolveFunction") +DEF(PromiseRejectFunction, "PromiseRejectFunction") +DEF(AsyncFunction, "AsyncFunction") +DEF(AsyncFunctionResolve, "AsyncFunctionResolve") +DEF(AsyncFunctionReject, "AsyncFunctionReject") +DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") +DEF(AsyncGenerator, "AsyncGenerator") +DEF(EvalError, "EvalError") +DEF(RangeError, "RangeError") +DEF(ReferenceError, "ReferenceError") +DEF(SyntaxError, "SyntaxError") +DEF(TypeError, "TypeError") +DEF(URIError, "URIError") +DEF(InternalError, "InternalError") +/* private symbols */ +DEF(Private_brand, "") +/* symbols */ +DEF(Symbol_toPrimitive, "Symbol.toPrimitive") +DEF(Symbol_iterator, "Symbol.iterator") +DEF(Symbol_match, "Symbol.match") +DEF(Symbol_matchAll, "Symbol.matchAll") +DEF(Symbol_replace, "Symbol.replace") +DEF(Symbol_search, "Symbol.search") +DEF(Symbol_split, "Symbol.split") +DEF(Symbol_toStringTag, "Symbol.toStringTag") +DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") +DEF(Symbol_hasInstance, "Symbol.hasInstance") +DEF(Symbol_species, "Symbol.species") +DEF(Symbol_unscopables, "Symbol.unscopables") +DEF(Symbol_asyncIterator, "Symbol.asyncIterator") +#ifdef CONFIG_BIGNUM +DEF(Symbol_operatorSet, "Symbol.operatorSet") +#endif + +#endif /* DEF */ diff --git a/include/quickjs/quickjs-opcode.h b/include/quickjs/quickjs-opcode.h index c731a14a9..65d859857 100644 --- a/include/quickjs/quickjs-opcode.h +++ b/include/quickjs/quickjs-opcode.h @@ -1,365 +1,365 @@ -/* - * QuickJS opcode definitions - * - * Copyright (c) 2017-2018 Fabrice Bellard - * Copyright (c) 2017-2018 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifdef FMT -FMT(none) -FMT(none_int) -FMT(none_loc) -FMT(none_arg) -FMT(none_var_ref) -FMT(u8) -FMT(i8) -FMT(loc8) -FMT(const8) -FMT(label8) -FMT(u16) -FMT(i16) -FMT(label16) -FMT(npop) -FMT(npopx) -FMT(npop_u16) -FMT(loc) -FMT(arg) -FMT(var_ref) -FMT(u32) -FMT(i32) -FMT(const) -FMT(label) -FMT(atom) -FMT(atom_u8) -FMT(atom_u16) -FMT(atom_label_u8) -FMT(atom_label_u16) -FMT(label_u16) -#undef FMT -#endif /* FMT */ - -#ifdef DEF - -#ifndef def -#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) -#endif - -DEF(invalid, 1, 0, 0, none) /* never emitted */ - -/* push values */ -DEF( push_i32, 5, 0, 1, i32) -DEF( push_const, 5, 0, 1, const) -DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ -DEF(push_atom_value, 5, 0, 1, atom) -DEF( private_symbol, 5, 0, 1, atom) -DEF( undefined, 1, 0, 1, none) -DEF( null, 1, 0, 1, none) -DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ -DEF( push_false, 1, 0, 1, none) -DEF( push_true, 1, 0, 1, none) -DEF( object, 1, 0, 1, none) -DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ -DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ - -DEF( drop, 1, 1, 0, none) /* a -> */ -DEF( nip, 1, 2, 1, none) /* a b -> b */ -DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ -DEF( dup, 1, 1, 2, none) /* a -> a a */ -DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ -DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ -DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ -DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ -DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ -DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ -DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ -DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ -DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ -DEF( swap, 1, 2, 2, none) /* a b -> b a */ -DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ -DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ -DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ -DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ -DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ - -DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ -DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ -DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ -DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ -DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ -DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ -DEF( apply, 3, 3, 1, u16) -DEF( return, 1, 1, 0, none) -DEF( return_undef, 1, 0, 0, none) -DEF(check_ctor_return, 1, 1, 2, none) -DEF( check_ctor, 1, 0, 0, none) -DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ -DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ -DEF( return_async, 1, 1, 0, none) -DEF( throw, 1, 1, 0, none) -DEF( throw_error, 6, 0, 0, atom_u8) -DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ -DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ -DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a - bytecode string */ -DEF( get_super, 1, 1, 1, none) -DEF( import, 1, 1, 1, none) /* dynamic module import */ - -DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ -DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ -DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ -DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ -DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ -DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ - -DEF( get_ref_value, 1, 2, 3, none) -DEF( put_ref_value, 1, 3, 0, none) - -DEF( define_var, 6, 0, 0, atom_u8) -DEF(check_define_var, 6, 0, 0, atom_u8) -DEF( define_func, 6, 1, 0, atom_u8) -DEF( get_field, 5, 1, 1, atom) -DEF( get_field2, 5, 1, 2, atom) -DEF( put_field, 5, 2, 0, atom) -DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ -DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ -DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ -DEF( get_array_el, 1, 2, 1, none) -DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ -DEF( put_array_el, 1, 3, 0, none) -DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ -DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ -DEF( define_field, 5, 2, 1, atom) -DEF( set_name, 5, 1, 1, atom) -DEF(set_name_computed, 1, 2, 2, none) -DEF( set_proto, 1, 2, 1, none) -DEF(set_home_object, 1, 2, 2, none) -DEF(define_array_el, 1, 3, 2, none) -DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ -DEF(copy_data_properties, 2, 3, 3, u8) -DEF( define_method, 6, 2, 1, atom_u8) -DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ -DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ -DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ - -DEF( get_loc, 3, 0, 1, loc) -DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ -DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ -DEF( get_arg, 3, 0, 1, arg) -DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ -DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ -DEF( get_var_ref, 3, 0, 1, var_ref) -DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ -DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ -DEF(set_loc_uninitialized, 3, 0, 0, loc) -DEF( get_loc_check, 3, 0, 1, loc) -DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ -DEF( put_loc_check_init, 3, 1, 0, loc) -DEF(get_var_ref_check, 3, 0, 1, var_ref) -DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ -DEF(put_var_ref_check_init, 3, 1, 0, var_ref) -DEF( close_loc, 3, 0, 0, loc) -DEF( if_false, 5, 1, 0, label) -DEF( if_true, 5, 1, 0, label) /* must come after if_false */ -DEF( goto, 5, 0, 0, label) /* must come after if_true */ -DEF( catch, 5, 0, 1, label) -DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ -DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ - -DEF( to_object, 1, 1, 1, none) -//DEF( to_string, 1, 1, 1, none) -DEF( to_propkey, 1, 1, 1, none) -DEF( to_propkey2, 1, 2, 2, none) - -DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ -DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ -DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ -DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ -DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ -DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) - -DEF( make_loc_ref, 7, 0, 2, atom_u16) -DEF( make_arg_ref, 7, 0, 2, atom_u16) -DEF(make_var_ref_ref, 7, 0, 2, atom_u16) -DEF( make_var_ref, 5, 0, 2, atom) - -DEF( for_in_start, 1, 1, 1, none) -DEF( for_of_start, 1, 1, 3, none) -DEF(for_await_of_start, 1, 1, 3, none) -DEF( for_in_next, 1, 1, 3, none) -DEF( for_of_next, 2, 3, 5, u8) -DEF(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( iterator_next, 1, 4, 4, none) -DEF( iterator_call, 2, 4, 5, u8) -DEF( initial_yield, 1, 0, 0, none) -DEF( yield, 1, 1, 2, none) -DEF( yield_star, 1, 1, 2, none) -DEF(async_yield_star, 1, 1, 2, none) -DEF( await, 1, 1, 1, none) - -/* arithmetic/logic operations */ -DEF( neg, 1, 1, 1, none) -DEF( plus, 1, 1, 1, none) -DEF( dec, 1, 1, 1, none) -DEF( inc, 1, 1, 1, none) -DEF( post_dec, 1, 1, 2, none) -DEF( post_inc, 1, 1, 2, none) -DEF( dec_loc, 2, 0, 0, loc8) -DEF( inc_loc, 2, 0, 0, loc8) -DEF( add_loc, 2, 1, 0, loc8) -DEF( not, 1, 1, 1, none) -DEF( lnot, 1, 1, 1, none) -DEF( typeof, 1, 1, 1, none) -DEF( delete, 1, 2, 1, none) -DEF( delete_var, 5, 0, 1, atom) - -DEF( mul, 1, 2, 1, none) -DEF( div, 1, 2, 1, none) -DEF( mod, 1, 2, 1, none) -DEF( add, 1, 2, 1, none) -DEF( sub, 1, 2, 1, none) -DEF( pow, 1, 2, 1, none) -DEF( shl, 1, 2, 1, none) -DEF( sar, 1, 2, 1, none) -DEF( shr, 1, 2, 1, none) -DEF( lt, 1, 2, 1, none) -DEF( lte, 1, 2, 1, none) -DEF( gt, 1, 2, 1, none) -DEF( gte, 1, 2, 1, none) -DEF( instanceof, 1, 2, 1, none) -DEF( in, 1, 2, 1, none) -DEF( eq, 1, 2, 1, none) -DEF( neq, 1, 2, 1, none) -DEF( strict_eq, 1, 2, 1, none) -DEF( strict_neq, 1, 2, 1, none) -DEF( and, 1, 2, 1, none) -DEF( xor, 1, 2, 1, none) -DEF( or, 1, 2, 1, none) -DEF(is_undefined_or_null, 1, 1, 1, none) -#ifdef CONFIG_BIGNUM -DEF( mul_pow10, 1, 2, 1, none) -DEF( math_mod, 1, 2, 1, none) -#endif -/* must be the last non short and non temporary opcode */ -DEF( nop, 1, 0, 0, none) - -/* temporary opcodes: never emitted in the final bytecode */ - -def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ -def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ - -def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ - -def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ -def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ -def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ -def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ -def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ -def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ -def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ -def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ -def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ -def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ - -def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ - -def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */ - -#if SHORT_OPCODES -DEF( push_minus1, 1, 0, 1, none_int) -DEF( push_0, 1, 0, 1, none_int) -DEF( push_1, 1, 0, 1, none_int) -DEF( push_2, 1, 0, 1, none_int) -DEF( push_3, 1, 0, 1, none_int) -DEF( push_4, 1, 0, 1, none_int) -DEF( push_5, 1, 0, 1, none_int) -DEF( push_6, 1, 0, 1, none_int) -DEF( push_7, 1, 0, 1, none_int) -DEF( push_i8, 2, 0, 1, i8) -DEF( push_i16, 3, 0, 1, i16) -DEF( push_const8, 2, 0, 1, const8) -DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ -DEF(push_empty_string, 1, 0, 1, none) - -DEF( get_loc8, 2, 0, 1, loc8) -DEF( put_loc8, 2, 1, 0, loc8) -DEF( set_loc8, 2, 1, 1, loc8) - -DEF( get_loc0, 1, 0, 1, none_loc) -DEF( get_loc1, 1, 0, 1, none_loc) -DEF( get_loc2, 1, 0, 1, none_loc) -DEF( get_loc3, 1, 0, 1, none_loc) -DEF( put_loc0, 1, 1, 0, none_loc) -DEF( put_loc1, 1, 1, 0, none_loc) -DEF( put_loc2, 1, 1, 0, none_loc) -DEF( put_loc3, 1, 1, 0, none_loc) -DEF( set_loc0, 1, 1, 1, none_loc) -DEF( set_loc1, 1, 1, 1, none_loc) -DEF( set_loc2, 1, 1, 1, none_loc) -DEF( set_loc3, 1, 1, 1, none_loc) -DEF( get_arg0, 1, 0, 1, none_arg) -DEF( get_arg1, 1, 0, 1, none_arg) -DEF( get_arg2, 1, 0, 1, none_arg) -DEF( get_arg3, 1, 0, 1, none_arg) -DEF( put_arg0, 1, 1, 0, none_arg) -DEF( put_arg1, 1, 1, 0, none_arg) -DEF( put_arg2, 1, 1, 0, none_arg) -DEF( put_arg3, 1, 1, 0, none_arg) -DEF( set_arg0, 1, 1, 1, none_arg) -DEF( set_arg1, 1, 1, 1, none_arg) -DEF( set_arg2, 1, 1, 1, none_arg) -DEF( set_arg3, 1, 1, 1, none_arg) -DEF( get_var_ref0, 1, 0, 1, none_var_ref) -DEF( get_var_ref1, 1, 0, 1, none_var_ref) -DEF( get_var_ref2, 1, 0, 1, none_var_ref) -DEF( get_var_ref3, 1, 0, 1, none_var_ref) -DEF( put_var_ref0, 1, 1, 0, none_var_ref) -DEF( put_var_ref1, 1, 1, 0, none_var_ref) -DEF( put_var_ref2, 1, 1, 0, none_var_ref) -DEF( put_var_ref3, 1, 1, 0, none_var_ref) -DEF( set_var_ref0, 1, 1, 1, none_var_ref) -DEF( set_var_ref1, 1, 1, 1, none_var_ref) -DEF( set_var_ref2, 1, 1, 1, none_var_ref) -DEF( set_var_ref3, 1, 1, 1, none_var_ref) - -DEF( get_length, 1, 1, 1, none) - -DEF( if_false8, 2, 1, 0, label8) -DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ -DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ -DEF( goto16, 3, 0, 0, label16) - -DEF( call0, 1, 1, 1, npopx) -DEF( call1, 1, 1, 1, npopx) -DEF( call2, 1, 1, 1, npopx) -DEF( call3, 1, 1, 1, npopx) - -DEF( is_undefined, 1, 1, 1, none) -DEF( is_null, 1, 1, 1, none) -DEF(typeof_is_undefined, 1, 1, 1, none) -DEF( typeof_is_function, 1, 1, 1, none) -#endif - -#undef DEF -#undef def -#endif /* DEF */ +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 1, 1, none) /* dynamic module import */ + +DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ +DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(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( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +#ifdef CONFIG_BIGNUM +DEF( mul_pow10, 1, 2, 1, none) +DEF( math_mod, 1, 2, 1, none) +#endif +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ + +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */ + +#if SHORT_OPCODES +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) +#endif + +#undef DEF +#undef def +#endif /* DEF */ diff --git a/include/quickjs/quickjs.h b/include/quickjs/quickjs.h index 0a5a0c302..5c7d9c49a 100644 --- a/include/quickjs/quickjs.h +++ b/include/quickjs/quickjs.h @@ -1,951 +1,951 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef QUICKJS_H -#define QUICKJS_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define js_likely(x) __builtin_expect(!!(x), 1) -#define js_unlikely(x) __builtin_expect(!!(x), 0) -#define js_force_inline inline __attribute__((always_inline)) -#define __js_printf_like(f, a) __attribute__((format(printf, f, a))) -#else -#define js_likely(x) (x) -#define js_unlikely(x) (x) -#define js_force_inline inline -#define __js_printf_like(a, b) -#endif - -#define JS_BOOL int - -typedef struct JSRuntime JSRuntime; -typedef struct JSContext JSContext; -typedef struct JSObject JSObject; -typedef struct JSClass JSClass; -typedef uint32_t JSClassID; -typedef uint32_t JSAtom; - -#if INTPTR_MAX >= INT64_MAX -#define JS_PTR64 -#define JS_PTR64_DEF(a) a -#else -#define JS_PTR64_DEF(a) -#endif - -#ifndef JS_PTR64 -#define JS_NAN_BOXING -#endif - -enum { - /* all tags with a reference count are negative */ - JS_TAG_FIRST = -11, /* first negative tag */ - JS_TAG_BIG_DECIMAL = -11, - JS_TAG_BIG_INT = -10, - JS_TAG_BIG_FLOAT = -9, - JS_TAG_SYMBOL = -8, - JS_TAG_STRING = -7, - JS_TAG_MODULE = -3, /* used internally */ - JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ - JS_TAG_OBJECT = -1, - - JS_TAG_INT = 0, - JS_TAG_BOOL = 1, - JS_TAG_NULL = 2, - JS_TAG_UNDEFINED = 3, - JS_TAG_UNINITIALIZED = 4, - JS_TAG_CATCH_OFFSET = 5, - JS_TAG_EXCEPTION = 6, - JS_TAG_FLOAT64 = 7, - /* any larger tag is FLOAT64 if JS_NAN_BOXING */ -}; - -typedef struct JSRefCountHeader { - int ref_count; -} JSRefCountHeader; - -#define JS_FLOAT64_NAN NAN - -#ifdef CONFIG_CHECK_JSVALUE -/* JSValue consistency : it is not possible to run the code in this - mode, but it is useful to detect simple reference counting - errors. It would be interesting to modify a static C analyzer to - handle specific annotations (clang has such annotations but only - for objective C) */ -typedef struct __JSValue* JSValue; -typedef const struct __JSValue* JSValueConst; - -#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v)&0xf) -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) -#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) -#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v) -#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v) -#define JS_VALUE_GET_PTR(v) (void*)((intptr_t)(v) & ~0xf) - -#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag)) -#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag)) - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) - -#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1) - -static inline JSValue __JS_NewFloat64(JSContext* ctx, double d) { - return JS_MKVAL(JS_TAG_FLOAT64, (int)d); -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) { - return 0; -} - -#elif defined(JS_NAN_BOXING) - -typedef uint64_t JSValue; - -#define JSValueConst JSValue - -#define JS_VALUE_GET_TAG(v) (int)((v) >> 32) -#define JS_VALUE_GET_INT(v) (int)(v) -#define JS_VALUE_GET_BOOL(v) (int)(v) -#define JS_VALUE_GET_PTR(v) (void*)(intptr_t)(v) - -#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) -#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) - -#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */ - -static inline double JS_VALUE_GET_FLOAT64(JSValue v) { - union { - JSValue v; - double d; - } u; - u.v = v; - u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32; - return u.d; -} - -#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) - -static inline JSValue __JS_NewFloat64(JSContext* ctx, double d) { - union { - double d; - uint64_t u64; - } u; - JSValue v; - u.d = d; - /* normalize NaN */ - if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000)) - v = JS_NAN; - else - v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32); - return v; -} - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag)-JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) - -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -static inline int JS_VALUE_GET_NORM_TAG(JSValue v) { - uint32_t tag; - tag = JS_VALUE_GET_TAG(v); - if (JS_TAG_IS_FLOAT64(tag)) - return JS_TAG_FLOAT64; - else - return tag; -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) { - uint32_t tag; - tag = JS_VALUE_GET_TAG(v); - return tag == (JS_NAN >> 32); -} - -#else /* !JS_NAN_BOXING */ - -typedef union JSValueUnion { - int32_t int32; - double float64; - void* ptr; -} JSValueUnion; - -typedef struct JSValue { - JSValueUnion u; - int64_t tag; -} JSValue; - -#define JSValueConst JSValue - -#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag) -/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ -#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) -#define JS_VALUE_GET_INT(v) ((v).u.int32) -#define JS_VALUE_GET_BOOL(v) ((v).u.int32) -#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) -#define JS_VALUE_GET_PTR(v) ((v).u.ptr) - -#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } -#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } - -#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) - -#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } - -static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) { - JSValue v; - v.tag = JS_TAG_FLOAT64; - v.u.float64 = d; - return v; -} - -static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) { - union { - double d; - uint64_t u64; - } u; - if (v.tag != JS_TAG_FLOAT64) - return 0; - u.d = v.u.float64; - return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; -} - -#endif /* !JS_NAN_BOXING */ - -#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) -#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) - -#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) -#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) -#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) - -/* special values */ -#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0) -#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0) -#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0) -#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1) -#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0) -#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0) - -/* flags for object properties */ -#define JS_PROP_CONFIGURABLE (1 << 0) -#define JS_PROP_WRITABLE (1 << 1) -#define JS_PROP_ENUMERABLE (1 << 2) -#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE) -#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ -#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ -#define JS_PROP_NORMAL (0 << 4) -#define JS_PROP_GETSET (1 << 4) -#define JS_PROP_VARREF (2 << 4) /* used internally */ -#define JS_PROP_AUTOINIT (3 << 4) /* used internally */ - -/* flags for JS_DefineProperty */ -#define JS_PROP_HAS_SHIFT 8 -#define JS_PROP_HAS_CONFIGURABLE (1 << 8) -#define JS_PROP_HAS_WRITABLE (1 << 9) -#define JS_PROP_HAS_ENUMERABLE (1 << 10) -#define JS_PROP_HAS_GET (1 << 11) -#define JS_PROP_HAS_SET (1 << 12) -#define JS_PROP_HAS_VALUE (1 << 13) - -/* throw an exception if false would be returned - (JS_DefineProperty/JS_SetProperty) */ -#define JS_PROP_THROW (1 << 14) -/* throw an exception if false would be returned in strict mode - (JS_SetProperty) */ -#define JS_PROP_THROW_STRICT (1 << 15) - -#define JS_PROP_NO_ADD (1 << 16) /* internal use */ -#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ - -#define JS_DEFAULT_STACK_SIZE (256 * 1024) - -/* JS_Eval() flags */ -#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ -#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */ -#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ -#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ -#define JS_EVAL_TYPE_MASK (3 << 0) - -#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ -#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */ -/* compile but do not run. The result is an object with a - JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed - with JS_EvalFunction(). */ -#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) -/* don't include the stack frames before this eval in the Error() backtraces */ -#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) - -typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); -typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); -typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data); - -typedef struct JSMallocState { - size_t malloc_count; - size_t malloc_size; - size_t malloc_limit; - void* opaque; /* user opaque */ -} JSMallocState; - -typedef struct JSMallocFunctions { - void* (*js_malloc)(JSMallocState* s, size_t size); - void (*js_free)(JSMallocState* s, void* ptr); - void* (*js_realloc)(JSMallocState* s, void* ptr, size_t size); - size_t (*js_malloc_usable_size)(const void* ptr); -} JSMallocFunctions; - -typedef struct JSGCObjectHeader JSGCObjectHeader; - -JSRuntime *JS_NewRuntime(void); -/* info lifetime must exceed that of rt */ -void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); -void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); -void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); -/* use 0 to disable maximum stack size check */ -void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); -/* should be called when changing thread to update the stack top value - used to check stack overflow. */ -void JS_UpdateStackTop(JSRuntime *rt); -JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); -void JS_FreeRuntime(JSRuntime *rt); -void *JS_GetRuntimeOpaque(JSRuntime *rt); -void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); -typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp); -void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); -void JS_RunGC(JSRuntime *rt); -JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj); - -JSContext *JS_NewContext(JSRuntime *rt); -void JS_FreeContext(JSContext *s); -JSContext *JS_DupContext(JSContext *ctx); -void *JS_GetContextOpaque(JSContext *ctx); -void JS_SetContextOpaque(JSContext *ctx, void *opaque); -JSRuntime *JS_GetRuntime(JSContext *ctx); -void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj); -JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id); - -/* the following functions are used to select the intrinsic object to - save memory */ -JSContext *JS_NewContextRaw(JSRuntime *rt); -void JS_AddIntrinsicBaseObjects(JSContext *ctx); -void JS_AddIntrinsicDate(JSContext *ctx); -void JS_AddIntrinsicEval(JSContext *ctx); -void JS_AddIntrinsicStringNormalize(JSContext *ctx); -void JS_AddIntrinsicRegExpCompiler(JSContext *ctx); -void JS_AddIntrinsicRegExp(JSContext *ctx); -void JS_AddIntrinsicJSON(JSContext *ctx); -void JS_AddIntrinsicProxy(JSContext *ctx); -void JS_AddIntrinsicMapSet(JSContext *ctx); -void JS_AddIntrinsicTypedArrays(JSContext *ctx); -void JS_AddIntrinsicPromise(JSContext *ctx); -void JS_AddIntrinsicBigInt(JSContext *ctx); -void JS_AddIntrinsicBigFloat(JSContext *ctx); -void JS_AddIntrinsicBigDecimal(JSContext *ctx); -/* enable operator overloading */ -void JS_AddIntrinsicOperators(JSContext *ctx); -/* enable "use math" */ -void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); - -JSValue js_string_codePointRange(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - -void *js_malloc_rt(JSRuntime *rt, size_t size); -void js_free_rt(JSRuntime *rt, void *ptr); -void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); -size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); -void *js_mallocz_rt(JSRuntime *rt, size_t size); - -void *js_malloc(JSContext *ctx, size_t size); -void js_free(JSContext *ctx, void *ptr); -void *js_realloc(JSContext *ctx, void *ptr, size_t size); -size_t js_malloc_usable_size(JSContext *ctx, const void *ptr); -void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack); -void *js_mallocz(JSContext *ctx, size_t size); -char *js_strdup(JSContext *ctx, const char *str); -char *js_strndup(JSContext *ctx, const char *s, size_t n); - -typedef struct JSMemoryUsage { - int64_t malloc_size, malloc_limit, memory_used_size; - int64_t malloc_count; - int64_t memory_used_count; - int64_t atom_count, atom_size; - int64_t str_count, str_size; - int64_t obj_count, obj_size; - int64_t prop_count, prop_size; - int64_t shape_count, shape_size; - int64_t js_func_count, js_func_size, js_func_code_size; - int64_t js_func_pc2line_count, js_func_pc2line_size; - int64_t c_func_count, array_count; - int64_t fast_array_count, fast_array_elements; - int64_t binary_object_count, binary_object_size; -} JSMemoryUsage; - -void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); -void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); - -/* atom support */ -#define JS_ATOM_NULL 0 - -JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); -JSAtom JS_NewAtom(JSContext *ctx, const char *str); -JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); -JSAtom JS_DupAtom(JSContext *ctx, JSAtom v); -void JS_FreeAtom(JSContext *ctx, JSAtom v); -void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); -JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); -JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); -const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); -JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); - -/* object class support */ - -typedef struct JSPropertyEnum { - JS_BOOL is_enumerable; - JSAtom atom; -} JSPropertyEnum; - -typedef struct JSPropertyDescriptor { - int flags; - JSValue value; - JSValue getter; - JSValue setter; -} JSPropertyDescriptor; - -typedef struct JSClassExoticMethods { - /* Return -1 if exception (can only happen in case of Proxy object), - FALSE if the property does not exists, TRUE if it exists. If 1 is - returned, the property descriptor 'desc' is filled if != NULL. */ - int (*get_own_property)(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop); - /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, - -1 if exception. The 'is_enumerable' field is ignored. - */ - int (*get_own_property_names)(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSValueConst obj); - /* return < 0 if exception, or TRUE/FALSE */ - int (*delete_property)(JSContext* ctx, JSValueConst obj, JSAtom prop); - /* return < 0 if exception or TRUE/FALSE */ - int (*define_own_property)(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); - /* The following methods can be emulated with the previous ones, - so they are usually not needed */ - /* return < 0 if exception or TRUE/FALSE */ - int (*has_property)(JSContext* ctx, JSValueConst obj, JSAtom atom); - JSValue (*get_property)(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst receiver); - /* return < 0 if exception or TRUE/FALSE */ - int (*set_property)(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags); -} JSClassExoticMethods; - -typedef void JSClassFinalizer(JSRuntime* rt, JSValue val); -typedef void JSClassGCMark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) -typedef JSValue JSClassCall(JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags); - -typedef struct JSClassDef { - const char* class_name; - JSClassFinalizer* finalizer; - JSClassGCMark* gc_mark; - /* if call != NULL, the object is a function. If (flags & - JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a - constructor. In this case, 'this_val' is new.target. A - constructor call only happens if the object constructor bit is - set (see JS_SetConstructorBit()). */ - JSClassCall* call; - /* XXX: suppress this indirection ? It is here only to save memory - because only a few classes need these methods */ - JSClassExoticMethods* exotic; -} JSClassDef; - -JSClassID JS_NewClassID(JSClassID *pclass_id); -int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); -int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); - -/* value handling */ - -static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) -{ - return JS_MKVAL(JS_TAG_BOOL, (val != 0)); -} - -static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) -{ - return JS_MKVAL(JS_TAG_INT, val); -} - -static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) -{ - return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); -} - -static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) { - JSValue v; - if (val == (int32_t)val) { - v = JS_NewInt32(ctx, val); - } else { - v = __JS_NewFloat64(ctx, val); - } - return v; -} - -static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) { - JSValue v; - if (val <= 0x7fffffff) { - v = JS_NewInt32(ctx, val); - } else { - v = __JS_NewFloat64(ctx, val); - } - return v; -} - -JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); -JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); - -static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) { - JSValue v; - int32_t val; - union { - double d; - uint64_t u; - } u, t; - u.d = d; - val = (int32_t)d; - t.d = val; - /* -0 cannot be represented as integer, so we compare the bit - representation */ - if (u.u == t.u) { - v = JS_MKVAL(JS_TAG_INT, val); - } else { - v = __JS_NewFloat64(ctx, d); - } - return v; -} - -static inline JS_BOOL JS_IsNumber(JSValueConst v) { - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); -} - -static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) { - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_INT; -} - -static inline JS_BOOL JS_IsBigFloat(JSValueConst v) { - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_FLOAT; -} - -static inline JS_BOOL JS_IsBigDecimal(JSValueConst v) { - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_DECIMAL; -} - -static inline JS_BOOL JS_IsBool(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; -} - -static inline JS_BOOL JS_IsNull(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; -} - -static inline JS_BOOL JS_IsUndefined(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; -} - -static inline JS_BOOL JS_IsException(JSValueConst v) -{ - return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION); -} - -static inline JS_BOOL JS_IsUninitialized(JSValueConst v) -{ - return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED); -} - -static inline JS_BOOL JS_IsString(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; -} - -static inline JS_BOOL JS_IsSymbol(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; -} - -static inline JS_BOOL JS_IsObject(JSValueConst v) -{ - return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; -} - -JSValue JS_Throw(JSContext *ctx, JSValue obj); -JSValue JS_GetException(JSContext *ctx); -JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); -void JS_ResetUncatchableError(JSContext *ctx); -JSValue JS_NewError(JSContext *ctx); -JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); -JSValue JS_ThrowOutOfMemory(JSContext *ctx); - -void __JS_FreeValue(JSContext *ctx, JSValue v); -static inline void JS_FreeValue(JSContext *ctx, JSValue v) { - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader* p = (JSRefCountHeader*)JS_VALUE_GET_PTR(v); - if (--p->ref_count <= 0) { - __JS_FreeValue(ctx, v); - } - } -} -void __JS_FreeValueRT(JSRuntime *rt, JSValue v); -static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v) { - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader* p = (JSRefCountHeader*)JS_VALUE_GET_PTR(v); - if (--p->ref_count <= 0) { - __JS_FreeValueRT(rt, v); - } - } -} - -static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) { - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader* p = (JSRefCountHeader*)JS_VALUE_GET_PTR(v); - p->ref_count++; - } - return (JSValue)v; -} - -static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) { - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader* p = (JSRefCountHeader*)JS_VALUE_GET_PTR(v); - p->ref_count++; - } - return (JSValue)v; -} - -int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ -int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); -static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) -{ - return JS_ToInt32(ctx, (int32_t*)pres, val); -} -int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val); -int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val); -int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val); -/* return an exception if 'val' is a Number */ -int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); -/* same as JS_ToInt64() but allow BigInt */ -int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); - -JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); -JSValue JS_NewString(JSContext *ctx, const char *str); -JSValue JS_NewAtomString(JSContext *ctx, const char *str); -JSValue JS_ToString(JSContext *ctx, JSValueConst val); -JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); -const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8); -static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1) -{ - return JS_ToCStringLen2(ctx, plen, val1, 0); -} -static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) -{ - return JS_ToCStringLen2(ctx, NULL, val1, 0); -} -void JS_FreeCString(JSContext *ctx, const char *ptr); - -JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id); -JSValue JS_NewObjectClass(JSContext *ctx, int class_id); -JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); -JSValue JS_NewObject(JSContext *ctx); - -JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val); -JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val); -JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val); - -JSValue JS_NewArray(JSContext *ctx); -int JS_IsArray(JSContext *ctx, JSValueConst val); - -JSValue JS_GetPropertyInternal(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValueConst receiver, JS_BOOL throw_ref_error); -static js_force_inline JSValue JS_GetProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop) { - return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0); -} -JSValue JS_GetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop); -JSValue JS_GetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx); - -int JS_SetPropertyInternal(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags); -static inline int JS_SetProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val) { - return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); -} -int JS_SetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx, JSValue val); -int JS_SetPropertyInt64(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val); -int JS_SetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop, JSValue val); -int JS_HasProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop); -int JS_IsExtensible(JSContext *ctx, JSValueConst obj); -int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); -int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags); -int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); -JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); - -#define JS_GPN_STRING_MASK (1 << 0) -#define JS_GPN_SYMBOL_MASK (1 << 1) -#define JS_GPN_PRIVATE_MASK (1 << 2) -/* only include the enumerable properties */ -#define JS_GPN_ENUM_ONLY (1 << 4) -/* set theJSPropertyEnum.is_enumerable field */ -#define JS_GPN_SET_ENUM (1 << 5) - -int JS_GetOwnPropertyNames(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSValueConst obj, int flags); -int JS_GetOwnProperty(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop); - -JSValue JS_Call(JSContext* ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst* argv); -JSValue JS_Invoke(JSContext* ctx, JSValueConst this_val, JSAtom atom, int argc, JSValueConst* argv); -JSValue JS_CallConstructor(JSContext* ctx, JSValueConst func_obj, int argc, JSValueConst* argv); -JSValue JS_CallConstructor2(JSContext* ctx, JSValueConst func_obj, JSValueConst new_target, int argc, JSValueConst* argv); -JS_BOOL JS_DetectModule(const char* input, size_t input_len); -/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ -JSValue JS_Eval(JSContext* ctx, const char* input, size_t input_len, const char* filename, int eval_flags); -/* same as JS_Eval() but with an explicit 'this_obj' parameter */ -JSValue JS_EvalThis(JSContext* ctx, JSValueConst this_obj, const char* input, size_t input_len, const char* filename, int eval_flags); -JSValue JS_GetGlobalObject(JSContext* ctx); -int JS_IsInstanceOf(JSContext* ctx, JSValueConst val, JSValueConst obj); -int JS_DefineProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); -int JS_DefinePropertyValue(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags); -int JS_DefinePropertyValueUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx, JSValue val, int flags); -int JS_DefinePropertyValueStr(JSContext* ctx, JSValueConst this_obj, const char* prop, JSValue val, int flags); -int JS_DefinePropertyGetSet(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue getter, JSValue setter, int flags); -void JS_SetOpaque(JSValue obj, void* opaque); -void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); -void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); - -/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ -JSValue JS_ParseJSON(JSContext* ctx, const char* buf, size_t buf_len, const char* filename); -#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */ -JSValue JS_ParseJSON2(JSContext* ctx, const char* buf, size_t buf_len, const char* filename, int flags); -JSValue JS_JSONStringify(JSContext* ctx, JSValueConst obj, JSValueConst replacer, JSValueConst space0); - -typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); -JSValue JS_NewArrayBuffer(JSContext* ctx, uint8_t* buf, size_t len, JSFreeArrayBufferDataFunc* free_func, void* opaque, JS_BOOL is_shared); -JSValue JS_NewArrayBufferCopy(JSContext* ctx, const uint8_t* buf, size_t len); -void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); -uint8_t* JS_GetArrayBuffer(JSContext* ctx, size_t* psize, JSValueConst obj); -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; - -/* return the module specifier (allocated with js_malloc()) or NULL if - exception */ -typedef char* JSModuleNormalizeFunc(JSContext* ctx, const char* module_base_name, const char* module_name, void* opaque); -typedef JSModuleDef* JSModuleLoaderFunc(JSContext* ctx, const char* module_name, void* opaque); - -/* module_normalize = NULL is allowed and invokes the default module - filename normalizer */ -void JS_SetModuleLoaderFunc(JSRuntime* rt, JSModuleNormalizeFunc* module_normalize, JSModuleLoaderFunc* module_loader, void* opaque); -/* return the import.meta object of a module */ -JSValue JS_GetImportMeta(JSContext* ctx, JSModuleDef* m); -JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); - -/* JS Job support */ - -typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv); -int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv); - -JS_BOOL JS_IsJobPending(JSRuntime *rt); -int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); - -/* Object Writer/Reader (currently only used to handle precompiled code) */ -#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ -#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ -#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, - JS_CFUNC_generic_magic, - JS_CFUNC_constructor, - JS_CFUNC_constructor_magic, - JS_CFUNC_constructor_or_func, - JS_CFUNC_constructor_or_func_magic, - JS_CFUNC_f_f, - JS_CFUNC_f_f_f, - JS_CFUNC_getter, - JS_CFUNC_setter, - JS_CFUNC_getter_magic, - JS_CFUNC_setter_magic, - JS_CFUNC_iterator_next, -} JSCFunctionEnum; - -typedef union JSCFunctionType { - JSCFunction* generic; - JSValue (*generic_magic)(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); - JSCFunction* constructor; - JSValue (*constructor_magic)(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv, int magic); - JSCFunction* constructor_or_func; - double (*f_f)(double); - double (*f_f_f)(double, double); - JSValue (*getter)(JSContext* ctx, JSValueConst this_val); - JSValue (*setter)(JSContext* ctx, JSValueConst this_val, JSValueConst val); - JSValue (*getter_magic)(JSContext* ctx, JSValueConst this_val, int magic); - JSValue (*setter_magic)(JSContext* ctx, JSValueConst this_val, JSValueConst val, int magic); - JSValue (*iterator_next)(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int* pdone, int magic); -} JSCFunctionType; - -JSValue JS_NewCFunction2(JSContext* ctx, JSCFunction* func, const char* name, int length, JSCFunctionEnum cproto, int magic); -JSValue JS_NewCFunctionData(JSContext* ctx, JSCFunctionData* func, int length, int magic, int data_len, JSValueConst* data); - -static inline JSValue JS_NewCFunction(JSContext* ctx, JSCFunction* func, const char* name, int length) { - return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); -} - -static inline JSValue JS_NewCFunctionMagic(JSContext* ctx, JSCFunctionMagic* func, const char* name, int length, JSCFunctionEnum cproto, int magic) { - return JS_NewCFunction2(ctx, (JSCFunction*)func, name, length, cproto, magic); -} -void JS_SetConstructor(JSContext* ctx, JSValueConst func_obj, JSValueConst proto); - -/* C property definition */ - -typedef struct JSCFunctionListEntry { - const char* name; - uint8_t prop_flags; - uint8_t def_type; - int16_t magic; - union { - struct { - uint8_t length; /* XXX: should move outside union */ - uint8_t cproto; /* XXX: should move outside union */ - JSCFunctionType cfunc; - } func; - struct { - JSCFunctionType get; - JSCFunctionType set; - } getset; - struct { - const char* name; - int base; - } alias; - struct { - const struct JSCFunctionListEntry* tab; - int len; - } prop_list; - const char* str; - int32_t i32; - int64_t i64; - double f64; - } u; -} JSCFunctionListEntry; - -#define JS_DEF_CFUNC 0 -#define JS_DEF_CGETSET 1 -#define JS_DEF_CGETSET_MAGIC 2 -#define JS_DEF_PROP_STRING 3 -#define JS_DEF_PROP_INT32 4 -#define JS_DEF_PROP_INT64 5 -#define JS_DEF_PROP_DOUBLE 6 -#define JS_DEF_PROP_UNDEFINED 7 -#define JS_DEF_OBJECT 8 -#define JS_DEF_ALIAS 9 - -/* Note: c++ does not like nested designators */ -#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } -#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } } -#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } } -#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } } -#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } -#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } } -#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } } -#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } } -#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } } -#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } } -#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } } -#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } } -#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } } -#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } } - -void JS_SetPropertyFunctionList(JSContext* ctx, JSValueConst obj, const JSCFunctionListEntry* tab, int len); - -/* C module definition */ - -typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m); - -JSModuleDef* JS_NewCModule(JSContext* ctx, const char* name_str, JSModuleInitFunc* func); -/* can only be called before the module is instantiated */ -int JS_AddModuleExport(JSContext* ctx, JSModuleDef* m, const char* name_str); -int JS_AddModuleExportList(JSContext* ctx, JSModuleDef* m, const JSCFunctionListEntry* tab, int len); -/* can only be called after the module is instantiated */ -int JS_SetModuleExport(JSContext* ctx, JSModuleDef* m, const char* export_name, JSValue val); -int JS_SetModuleExportList(JSContext* ctx, JSModuleDef* m, const JSCFunctionListEntry* tab, int len); - -#undef js_unlikely -#undef js_force_inline - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* QUICKJS_H */ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_H +#define QUICKJS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define js_likely(x) __builtin_expect(!!(x), 1) +#define js_unlikely(x) __builtin_expect(!!(x), 0) +#define js_force_inline inline __attribute__((always_inline)) +#define __js_printf_like(f, a) __attribute__((format(printf, f, a))) +#else +#define js_likely(x) (x) +#define js_unlikely(x) (x) +#define js_force_inline inline +#define __js_printf_like(a, b) +#endif + +#define JS_BOOL int + +typedef struct JSRuntime JSRuntime; +typedef struct JSContext JSContext; +typedef struct JSObject JSObject; +typedef struct JSClass JSClass; +typedef uint32_t JSClassID; +typedef uint32_t JSAtom; + +#if INTPTR_MAX >= INT64_MAX +#define JS_PTR64 +#define JS_PTR64_DEF(a) a +#else +#define JS_PTR64_DEF(a) +#endif + +#ifndef JS_PTR64 +#define JS_NAN_BOXING +#endif + +enum { + /* all tags with a reference count are negative */ + JS_TAG_FIRST = -11, /* first negative tag */ + JS_TAG_BIG_DECIMAL = -11, + JS_TAG_BIG_INT = -10, + JS_TAG_BIG_FLOAT = -9, + JS_TAG_SYMBOL = -8, + JS_TAG_STRING = -7, + JS_TAG_MODULE = -3, /* used internally */ + JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ + JS_TAG_OBJECT = -1, + + JS_TAG_INT = 0, + JS_TAG_BOOL = 1, + JS_TAG_NULL = 2, + JS_TAG_UNDEFINED = 3, + JS_TAG_UNINITIALIZED = 4, + JS_TAG_CATCH_OFFSET = 5, + JS_TAG_EXCEPTION = 6, + JS_TAG_FLOAT64 = 7, + /* any larger tag is FLOAT64 if JS_NAN_BOXING */ +}; + +typedef struct JSRefCountHeader { + int ref_count; +} JSRefCountHeader; + +#define JS_FLOAT64_NAN NAN + +#ifdef CONFIG_CHECK_JSVALUE +/* JSValue consistency : it is not possible to run the code in this + mode, but it is useful to detect simple reference counting + errors. It would be interesting to modify a static C analyzer to + handle specific annotations (clang has such annotations but only + for objective C) */ +typedef struct __JSValue* JSValue; +typedef const struct __JSValue* JSValueConst; + +#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v)&0xf) +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) +#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) +#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v) +#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v) +#define JS_VALUE_GET_PTR(v) (void*)((intptr_t)(v) & ~0xf) + +#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag)) +#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag)) + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) + +#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1) + +static inline JSValue __JS_NewFloat64(JSContext* ctx, double d) { + return JS_MKVAL(JS_TAG_FLOAT64, (int)d); +} + +static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) { + return 0; +} + +#elif defined(JS_NAN_BOXING) + +typedef uint64_t JSValue; + +#define JSValueConst JSValue + +#define JS_VALUE_GET_TAG(v) (int)((v) >> 32) +#define JS_VALUE_GET_INT(v) (int)(v) +#define JS_VALUE_GET_BOOL(v) (int)(v) +#define JS_VALUE_GET_PTR(v) (void*)(intptr_t)(v) + +#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) +#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr)) + +#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */ + +static inline double JS_VALUE_GET_FLOAT64(JSValue v) { + union { + JSValue v; + double d; + } u; + u.v = v; + u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32; + return u.d; +} + +#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) + +static inline JSValue __JS_NewFloat64(JSContext* ctx, double d) { + union { + double d; + uint64_t u64; + } u; + JSValue v; + u.d = d; + /* normalize NaN */ + if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000)) + v = JS_NAN; + else + v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32); + return v; +} + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag)-JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST)) + +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +static inline int JS_VALUE_GET_NORM_TAG(JSValue v) { + uint32_t tag; + tag = JS_VALUE_GET_TAG(v); + if (JS_TAG_IS_FLOAT64(tag)) + return JS_TAG_FLOAT64; + else + return tag; +} + +static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) { + uint32_t tag; + tag = JS_VALUE_GET_TAG(v); + return tag == (JS_NAN >> 32); +} + +#else /* !JS_NAN_BOXING */ + +typedef union JSValueUnion { + int32_t int32; + double float64; + void* ptr; +} JSValueUnion; + +typedef struct JSValue { + JSValueUnion u; + int64_t tag; +} JSValue; + +#define JSValueConst JSValue + +#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag) +/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */ +#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v) +#define JS_VALUE_GET_INT(v) ((v).u.int32) +#define JS_VALUE_GET_BOOL(v) ((v).u.int32) +#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) +#define JS_VALUE_GET_PTR(v) ((v).u.ptr) + +#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } +#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } + +#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) + +#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } + +static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) { + JSValue v; + v.tag = JS_TAG_FLOAT64; + v.u.float64 = d; + return v; +} + +static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) { + union { + double d; + uint64_t u64; + } u; + if (v.tag != JS_TAG_FLOAT64) + return 0; + u.d = v.u.float64; + return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; +} + +#endif /* !JS_NAN_BOXING */ + +#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) +#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) + +#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) + +/* special values */ +#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0) +#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0) +#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0) +#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1) +#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0) +#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0) + +/* flags for object properties */ +#define JS_PROP_CONFIGURABLE (1 << 0) +#define JS_PROP_WRITABLE (1 << 1) +#define JS_PROP_ENUMERABLE (1 << 2) +#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE) +#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */ +#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */ +#define JS_PROP_NORMAL (0 << 4) +#define JS_PROP_GETSET (1 << 4) +#define JS_PROP_VARREF (2 << 4) /* used internally */ +#define JS_PROP_AUTOINIT (3 << 4) /* used internally */ + +/* flags for JS_DefineProperty */ +#define JS_PROP_HAS_SHIFT 8 +#define JS_PROP_HAS_CONFIGURABLE (1 << 8) +#define JS_PROP_HAS_WRITABLE (1 << 9) +#define JS_PROP_HAS_ENUMERABLE (1 << 10) +#define JS_PROP_HAS_GET (1 << 11) +#define JS_PROP_HAS_SET (1 << 12) +#define JS_PROP_HAS_VALUE (1 << 13) + +/* throw an exception if false would be returned + (JS_DefineProperty/JS_SetProperty) */ +#define JS_PROP_THROW (1 << 14) +/* throw an exception if false would be returned in strict mode + (JS_SetProperty) */ +#define JS_PROP_THROW_STRICT (1 << 15) + +#define JS_PROP_NO_ADD (1 << 16) /* internal use */ +#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ + +#define JS_DEFAULT_STACK_SIZE (256 * 1024) + +/* JS_Eval() flags */ +#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ +#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */ +#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */ +#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */ +#define JS_EVAL_TYPE_MASK (3 << 0) + +#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ +#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */ +/* compile but do not run. The result is an object with a + JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed + with JS_EvalFunction(). */ +#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) +/* don't include the stack frames before this eval in the Error() backtraces */ +#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) + +typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); +typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); +typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data); + +typedef struct JSMallocState { + size_t malloc_count; + size_t malloc_size; + size_t malloc_limit; + void* opaque; /* user opaque */ +} JSMallocState; + +typedef struct JSMallocFunctions { + void* (*js_malloc)(JSMallocState* s, size_t size); + void (*js_free)(JSMallocState* s, void* ptr); + void* (*js_realloc)(JSMallocState* s, void* ptr, size_t size); + size_t (*js_malloc_usable_size)(const void* ptr); +} JSMallocFunctions; + +typedef struct JSGCObjectHeader JSGCObjectHeader; + +JSRuntime *JS_NewRuntime(void); +/* info lifetime must exceed that of rt */ +void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); +void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); +void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); +/* use 0 to disable maximum stack size check */ +void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); +/* should be called when changing thread to update the stack top value + used to check stack overflow. */ +void JS_UpdateStackTop(JSRuntime *rt); +JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); +void JS_FreeRuntime(JSRuntime *rt); +void *JS_GetRuntimeOpaque(JSRuntime *rt); +void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque); +typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp); +void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); +void JS_RunGC(JSRuntime *rt); +JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj); + +JSContext *JS_NewContext(JSRuntime *rt); +void JS_FreeContext(JSContext *s); +JSContext *JS_DupContext(JSContext *ctx); +void *JS_GetContextOpaque(JSContext *ctx); +void JS_SetContextOpaque(JSContext *ctx, void *opaque); +JSRuntime *JS_GetRuntime(JSContext *ctx); +void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj); +JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id); + +/* the following functions are used to select the intrinsic object to + save memory */ +JSContext *JS_NewContextRaw(JSRuntime *rt); +void JS_AddIntrinsicBaseObjects(JSContext *ctx); +void JS_AddIntrinsicDate(JSContext *ctx); +void JS_AddIntrinsicEval(JSContext *ctx); +void JS_AddIntrinsicStringNormalize(JSContext *ctx); +void JS_AddIntrinsicRegExpCompiler(JSContext *ctx); +void JS_AddIntrinsicRegExp(JSContext *ctx); +void JS_AddIntrinsicJSON(JSContext *ctx); +void JS_AddIntrinsicProxy(JSContext *ctx); +void JS_AddIntrinsicMapSet(JSContext *ctx); +void JS_AddIntrinsicTypedArrays(JSContext *ctx); +void JS_AddIntrinsicPromise(JSContext *ctx); +void JS_AddIntrinsicBigInt(JSContext *ctx); +void JS_AddIntrinsicBigFloat(JSContext *ctx); +void JS_AddIntrinsicBigDecimal(JSContext *ctx); +/* enable operator overloading */ +void JS_AddIntrinsicOperators(JSContext *ctx); +/* enable "use math" */ +void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable); + +JSValue js_string_codePointRange(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + +void *js_malloc_rt(JSRuntime *rt, size_t size); +void js_free_rt(JSRuntime *rt, void *ptr); +void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size); +size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr); +void *js_mallocz_rt(JSRuntime *rt, size_t size); + +void *js_malloc(JSContext *ctx, size_t size); +void js_free(JSContext *ctx, void *ptr); +void *js_realloc(JSContext *ctx, void *ptr, size_t size); +size_t js_malloc_usable_size(JSContext *ctx, const void *ptr); +void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack); +void *js_mallocz(JSContext *ctx, size_t size); +char *js_strdup(JSContext *ctx, const char *str); +char *js_strndup(JSContext *ctx, const char *s, size_t n); + +typedef struct JSMemoryUsage { + int64_t malloc_size, malloc_limit, memory_used_size; + int64_t malloc_count; + int64_t memory_used_count; + int64_t atom_count, atom_size; + int64_t str_count, str_size; + int64_t obj_count, obj_size; + int64_t prop_count, prop_size; + int64_t shape_count, shape_size; + int64_t js_func_count, js_func_size, js_func_code_size; + int64_t js_func_pc2line_count, js_func_pc2line_size; + int64_t c_func_count, array_count; + int64_t fast_array_count, fast_array_elements; + int64_t binary_object_count, binary_object_size; +} JSMemoryUsage; + +void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s); +void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); + +/* atom support */ +#define JS_ATOM_NULL 0 + +JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len); +JSAtom JS_NewAtom(JSContext *ctx, const char *str); +JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n); +JSAtom JS_DupAtom(JSContext *ctx, JSAtom v); +void JS_FreeAtom(JSContext *ctx, JSAtom v); +void JS_FreeAtomRT(JSRuntime *rt, JSAtom v); +JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom); +JSValue JS_AtomToString(JSContext *ctx, JSAtom atom); +const char *JS_AtomToCString(JSContext *ctx, JSAtom atom); +JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val); + +/* object class support */ + +typedef struct JSPropertyEnum { + JS_BOOL is_enumerable; + JSAtom atom; +} JSPropertyEnum; + +typedef struct JSPropertyDescriptor { + int flags; + JSValue value; + JSValue getter; + JSValue setter; +} JSPropertyDescriptor; + +typedef struct JSClassExoticMethods { + /* Return -1 if exception (can only happen in case of Proxy object), + FALSE if the property does not exists, TRUE if it exists. If 1 is + returned, the property descriptor 'desc' is filled if != NULL. */ + int (*get_own_property)(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop); + /* '*ptab' should hold the '*plen' property keys. Return 0 if OK, + -1 if exception. The 'is_enumerable' field is ignored. + */ + int (*get_own_property_names)(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSValueConst obj); + /* return < 0 if exception, or TRUE/FALSE */ + int (*delete_property)(JSContext* ctx, JSValueConst obj, JSAtom prop); + /* return < 0 if exception or TRUE/FALSE */ + int (*define_own_property)(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); + /* The following methods can be emulated with the previous ones, + so they are usually not needed */ + /* return < 0 if exception or TRUE/FALSE */ + int (*has_property)(JSContext* ctx, JSValueConst obj, JSAtom atom); + JSValue (*get_property)(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst receiver); + /* return < 0 if exception or TRUE/FALSE */ + int (*set_property)(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags); +} JSClassExoticMethods; + +typedef void JSClassFinalizer(JSRuntime* rt, JSValue val); +typedef void JSClassGCMark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0) +typedef JSValue JSClassCall(JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags); + +typedef struct JSClassDef { + const char* class_name; + JSClassFinalizer* finalizer; + JSClassGCMark* gc_mark; + /* if call != NULL, the object is a function. If (flags & + JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a + constructor. In this case, 'this_val' is new.target. A + constructor call only happens if the object constructor bit is + set (see JS_SetConstructorBit()). */ + JSClassCall* call; + /* XXX: suppress this indirection ? It is here only to save memory + because only a few classes need these methods */ + JSClassExoticMethods* exotic; +} JSClassDef; + +JSClassID JS_NewClassID(JSClassID *pclass_id); +int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); +int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); + +/* value handling */ + +static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) +{ + return JS_MKVAL(JS_TAG_BOOL, (val != 0)); +} + +static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) +{ + return JS_MKVAL(JS_TAG_INT, val); +} + +static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) +{ + return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); +} + +static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) { + JSValue v; + if (val == (int32_t)val) { + v = JS_NewInt32(ctx, val); + } else { + v = __JS_NewFloat64(ctx, val); + } + return v; +} + +static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) { + JSValue v; + if (val <= 0x7fffffff) { + v = JS_NewInt32(ctx, val); + } else { + v = __JS_NewFloat64(ctx, val); + } + return v; +} + +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); + +static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) { + JSValue v; + int32_t val; + union { + double d; + uint64_t u; + } u, t; + u.d = d; + val = (int32_t)d; + t.d = val; + /* -0 cannot be represented as integer, so we compare the bit + representation */ + if (u.u == t.u) { + v = JS_MKVAL(JS_TAG_INT, val); + } else { + v = __JS_NewFloat64(ctx, d); + } + return v; +} + +static inline JS_BOOL JS_IsNumber(JSValueConst v) { + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag); +} + +static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) { + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_INT; +} + +static inline JS_BOOL JS_IsBigFloat(JSValueConst v) { + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_FLOAT; +} + +static inline JS_BOOL JS_IsBigDecimal(JSValueConst v) { + int tag = JS_VALUE_GET_TAG(v); + return tag == JS_TAG_BIG_DECIMAL; +} + +static inline JS_BOOL JS_IsBool(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL; +} + +static inline JS_BOOL JS_IsNull(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_NULL; +} + +static inline JS_BOOL JS_IsUndefined(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; +} + +static inline JS_BOOL JS_IsException(JSValueConst v) +{ + return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION); +} + +static inline JS_BOOL JS_IsUninitialized(JSValueConst v) +{ + return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED); +} + +static inline JS_BOOL JS_IsString(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; +} + +static inline JS_BOOL JS_IsSymbol(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL; +} + +static inline JS_BOOL JS_IsObject(JSValueConst v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT; +} + +JSValue JS_Throw(JSContext *ctx, JSValue obj); +JSValue JS_GetException(JSContext *ctx); +JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); +void JS_ResetUncatchableError(JSContext *ctx); +JSValue JS_NewError(JSContext *ctx); +JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); +JSValue JS_ThrowOutOfMemory(JSContext *ctx); + +void __JS_FreeValue(JSContext *ctx, JSValue v); +static inline void JS_FreeValue(JSContext *ctx, JSValue v) { + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader* p = (JSRefCountHeader*)JS_VALUE_GET_PTR(v); + if (--p->ref_count <= 0) { + __JS_FreeValue(ctx, v); + } + } +} +void __JS_FreeValueRT(JSRuntime *rt, JSValue v); +static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v) { + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader* p = (JSRefCountHeader*)JS_VALUE_GET_PTR(v); + if (--p->ref_count <= 0) { + __JS_FreeValueRT(rt, v); + } + } +} + +static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) { + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader* p = (JSRefCountHeader*)JS_VALUE_GET_PTR(v); + p->ref_count++; + } + return (JSValue)v; +} + +static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) { + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader* p = (JSRefCountHeader*)JS_VALUE_GET_PTR(v); + p->ref_count++; + } + return (JSValue)v; +} + +int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ +int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); +static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) +{ + return JS_ToInt32(ctx, (int32_t*)pres, val); +} +int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val); +int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val); +int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val); +/* return an exception if 'val' is a Number */ +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val); +/* same as JS_ToInt64() but allow BigInt */ +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); + +JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); +JSValue JS_NewString(JSContext *ctx, const char *str); +JSValue JS_NewAtomString(JSContext *ctx, const char *str); +JSValue JS_ToString(JSContext *ctx, JSValueConst val); +JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); +const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8); +static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1) +{ + return JS_ToCStringLen2(ctx, plen, val1, 0); +} +static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) +{ + return JS_ToCStringLen2(ctx, NULL, val1, 0); +} +void JS_FreeCString(JSContext *ctx, const char *ptr); + +JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id); +JSValue JS_NewObjectClass(JSContext *ctx, int class_id); +JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); +JSValue JS_NewObject(JSContext *ctx); + +JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val); +JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val); +JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val); + +JSValue JS_NewArray(JSContext *ctx); +int JS_IsArray(JSContext *ctx, JSValueConst val); + +JSValue JS_GetPropertyInternal(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValueConst receiver, JS_BOOL throw_ref_error); +static js_force_inline JSValue JS_GetProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop) { + return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0); +} +JSValue JS_GetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop); +JSValue JS_GetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx); + +int JS_SetPropertyInternal(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags); +static inline int JS_SetProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val) { + return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); +} +int JS_SetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx, JSValue val); +int JS_SetPropertyInt64(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val); +int JS_SetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop, JSValue val); +int JS_HasProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop); +int JS_IsExtensible(JSContext *ctx, JSValueConst obj); +int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); +int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags); +int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val); +JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val); + +#define JS_GPN_STRING_MASK (1 << 0) +#define JS_GPN_SYMBOL_MASK (1 << 1) +#define JS_GPN_PRIVATE_MASK (1 << 2) +/* only include the enumerable properties */ +#define JS_GPN_ENUM_ONLY (1 << 4) +/* set theJSPropertyEnum.is_enumerable field */ +#define JS_GPN_SET_ENUM (1 << 5) + +int JS_GetOwnPropertyNames(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSValueConst obj, int flags); +int JS_GetOwnProperty(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop); + +JSValue JS_Call(JSContext* ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst* argv); +JSValue JS_Invoke(JSContext* ctx, JSValueConst this_val, JSAtom atom, int argc, JSValueConst* argv); +JSValue JS_CallConstructor(JSContext* ctx, JSValueConst func_obj, int argc, JSValueConst* argv); +JSValue JS_CallConstructor2(JSContext* ctx, JSValueConst func_obj, JSValueConst new_target, int argc, JSValueConst* argv); +JS_BOOL JS_DetectModule(const char* input, size_t input_len); +/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ +JSValue JS_Eval(JSContext* ctx, const char* input, size_t input_len, const char* filename, int eval_flags); +/* same as JS_Eval() but with an explicit 'this_obj' parameter */ +JSValue JS_EvalThis(JSContext* ctx, JSValueConst this_obj, const char* input, size_t input_len, const char* filename, int eval_flags); +JSValue JS_GetGlobalObject(JSContext* ctx); +int JS_IsInstanceOf(JSContext* ctx, JSValueConst val, JSValueConst obj); +int JS_DefineProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); +int JS_DefinePropertyValue(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags); +int JS_DefinePropertyValueUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx, JSValue val, int flags); +int JS_DefinePropertyValueStr(JSContext* ctx, JSValueConst this_obj, const char* prop, JSValue val, int flags); +int JS_DefinePropertyGetSet(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue getter, JSValue setter, int flags); +void JS_SetOpaque(JSValue obj, void* opaque); +void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); +void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); + +/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ +JSValue JS_ParseJSON(JSContext* ctx, const char* buf, size_t buf_len, const char* filename); +#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */ +JSValue JS_ParseJSON2(JSContext* ctx, const char* buf, size_t buf_len, const char* filename, int flags); +JSValue JS_JSONStringify(JSContext* ctx, JSValueConst obj, JSValueConst replacer, JSValueConst space0); + +typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr); +JSValue JS_NewArrayBuffer(JSContext* ctx, uint8_t* buf, size_t len, JSFreeArrayBufferDataFunc* free_func, void* opaque, JS_BOOL is_shared); +JSValue JS_NewArrayBufferCopy(JSContext* ctx, const uint8_t* buf, size_t len); +void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); +uint8_t* JS_GetArrayBuffer(JSContext* ctx, size_t* psize, JSValueConst obj); +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; + +/* return the module specifier (allocated with js_malloc()) or NULL if + exception */ +typedef char* JSModuleNormalizeFunc(JSContext* ctx, const char* module_base_name, const char* module_name, void* opaque); +typedef JSModuleDef* JSModuleLoaderFunc(JSContext* ctx, const char* module_name, void* opaque); + +/* module_normalize = NULL is allowed and invokes the default module + filename normalizer */ +void JS_SetModuleLoaderFunc(JSRuntime* rt, JSModuleNormalizeFunc* module_normalize, JSModuleLoaderFunc* module_loader, void* opaque); +/* return the import.meta object of a module */ +JSValue JS_GetImportMeta(JSContext* ctx, JSModuleDef* m); +JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); + +/* JS Job support */ + +typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv); +int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv); + +JS_BOOL JS_IsJobPending(JSRuntime *rt); +int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx); + +/* Object Writer/Reader (currently only used to handle precompiled code) */ +#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */ +#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */ +#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, + JS_CFUNC_generic_magic, + JS_CFUNC_constructor, + JS_CFUNC_constructor_magic, + JS_CFUNC_constructor_or_func, + JS_CFUNC_constructor_or_func_magic, + JS_CFUNC_f_f, + JS_CFUNC_f_f_f, + JS_CFUNC_getter, + JS_CFUNC_setter, + JS_CFUNC_getter_magic, + JS_CFUNC_setter_magic, + JS_CFUNC_iterator_next, +} JSCFunctionEnum; + +typedef union JSCFunctionType { + JSCFunction* generic; + JSValue (*generic_magic)(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); + JSCFunction* constructor; + JSValue (*constructor_magic)(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv, int magic); + JSCFunction* constructor_or_func; + double (*f_f)(double); + double (*f_f_f)(double, double); + JSValue (*getter)(JSContext* ctx, JSValueConst this_val); + JSValue (*setter)(JSContext* ctx, JSValueConst this_val, JSValueConst val); + JSValue (*getter_magic)(JSContext* ctx, JSValueConst this_val, int magic); + JSValue (*setter_magic)(JSContext* ctx, JSValueConst this_val, JSValueConst val, int magic); + JSValue (*iterator_next)(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int* pdone, int magic); +} JSCFunctionType; + +JSValue JS_NewCFunction2(JSContext* ctx, JSCFunction* func, const char* name, int length, JSCFunctionEnum cproto, int magic); +JSValue JS_NewCFunctionData(JSContext* ctx, JSCFunctionData* func, int length, int magic, int data_len, JSValueConst* data); + +static inline JSValue JS_NewCFunction(JSContext* ctx, JSCFunction* func, const char* name, int length) { + return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); +} + +static inline JSValue JS_NewCFunctionMagic(JSContext* ctx, JSCFunctionMagic* func, const char* name, int length, JSCFunctionEnum cproto, int magic) { + return JS_NewCFunction2(ctx, (JSCFunction*)func, name, length, cproto, magic); +} +void JS_SetConstructor(JSContext* ctx, JSValueConst func_obj, JSValueConst proto); + +/* C property definition */ + +typedef struct JSCFunctionListEntry { + const char* name; + uint8_t prop_flags; + uint8_t def_type; + int16_t magic; + union { + struct { + uint8_t length; /* XXX: should move outside union */ + uint8_t cproto; /* XXX: should move outside union */ + JSCFunctionType cfunc; + } func; + struct { + JSCFunctionType get; + JSCFunctionType set; + } getset; + struct { + const char* name; + int base; + } alias; + struct { + const struct JSCFunctionListEntry* tab; + int len; + } prop_list; + const char* str; + int32_t i32; + int64_t i64; + double f64; + } u; +} JSCFunctionListEntry; + +#define JS_DEF_CFUNC 0 +#define JS_DEF_CGETSET 1 +#define JS_DEF_CGETSET_MAGIC 2 +#define JS_DEF_PROP_STRING 3 +#define JS_DEF_PROP_INT32 4 +#define JS_DEF_PROP_INT64 5 +#define JS_DEF_PROP_DOUBLE 6 +#define JS_DEF_PROP_UNDEFINED 7 +#define JS_DEF_OBJECT 8 +#define JS_DEF_ALIAS 9 + +/* Note: c++ does not like nested designators */ +#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } +#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } } +#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } } +#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } } +#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } } +#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } } +#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } } +#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } } +#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } } +#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } } +#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } } +#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } } +#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } } +#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } } + +void JS_SetPropertyFunctionList(JSContext* ctx, JSValueConst obj, const JSCFunctionListEntry* tab, int len); + +/* C module definition */ + +typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m); + +JSModuleDef* JS_NewCModule(JSContext* ctx, const char* name_str, JSModuleInitFunc* func); +/* can only be called before the module is instantiated */ +int JS_AddModuleExport(JSContext* ctx, JSModuleDef* m, const char* name_str); +int JS_AddModuleExportList(JSContext* ctx, JSModuleDef* m, const JSCFunctionListEntry* tab, int len); +/* can only be called after the module is instantiated */ +int JS_SetModuleExport(JSContext* ctx, JSModuleDef* m, const char* export_name, JSValue val); +int JS_SetModuleExportList(JSContext* ctx, JSModuleDef* m, const JSCFunctionListEntry* tab, int len); + +#undef js_unlikely +#undef js_force_inline + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_H */ diff --git a/qjs.c b/qjs.c index d56b84336..5974442a0 100644 --- a/qjs.c +++ b/qjs.c @@ -1,570 +1,570 @@ -/* - * QuickJS stand alone interpreter - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -#include -#elif defined(__linux__) -#include -#endif - -#include "cutils.h" -#include "quickjs-libc.h" - -extern const uint8_t qjsc_repl[]; -extern const uint32_t qjsc_repl_size; -#ifdef CONFIG_BIGNUM -extern const uint8_t qjsc_qjscalc[]; -extern const uint32_t qjsc_qjscalc_size; -static int bignum_ext; -#endif - -static int eval_buf(JSContext *ctx, const void *buf, int buf_len, - const char *filename, int eval_flags) -{ - JSValue val; - int ret; - - if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { - /* for the modules, we compile then run to be able to set - import.meta */ - val = JS_Eval(ctx, buf, buf_len, filename, - eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); - if (!JS_IsException(val)) { - js_module_set_import_meta(ctx, val, TRUE, TRUE); - val = JS_EvalFunction(ctx, val); - } - } else { - val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); - } - if (JS_IsException(val)) { - js_std_dump_error(ctx); - ret = -1; - } else { - ret = 0; - } - JS_FreeValue(ctx, val); - return ret; -} - -static int eval_file(JSContext *ctx, const char *filename, int module) -{ - uint8_t *buf; - int ret, eval_flags; - size_t buf_len; - - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - perror(filename); - exit(1); - } - - if (module < 0) { - module = (has_suffix(filename, ".mjs") || - JS_DetectModule((const char *)buf, buf_len)); - } - if (module) - eval_flags = JS_EVAL_TYPE_MODULE; - else - eval_flags = JS_EVAL_TYPE_GLOBAL; - ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); - js_free(ctx, buf); - return ret; -} - -/* 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 -#define MALLOC_OVERHEAD 8 -#endif - -struct trace_malloc_data { - uint8_t *base; -}; - -static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, - struct trace_malloc_data *dp) -{ - return ptr - dp->base; -} - -/* default memory allocation functions with memory limitation */ -static inline size_t js_trace_malloc_usable_size(void *ptr) -{ -#if defined(__APPLE__) - return malloc_size(ptr); -#elif defined(_WIN32) - return _msize(ptr); -#elif defined(EMSCRIPTEN) - return 0; -#elif defined(__linux__) - return malloc_usable_size(ptr); -#else - /* change this to `return 0;` if compilation fails */ - return malloc_usable_size(ptr); -#endif -} - -static void -#ifdef _WIN32 -/* mingw printf is used */ -__attribute__((format(gnu_printf, 2, 3))) -#else -__attribute__((format(printf, 2, 3))) -#endif - js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...) -{ - va_list ap; - int c; - - va_start(ap, fmt); - while ((c = *fmt++) != '\0') { - if (c == '%') { - /* only handle %p and %zd */ - if (*fmt == 'p') { - uint8_t *ptr = va_arg(ap, void *); - if (ptr == NULL) { - printf("NULL"); - } else { - printf("H%+06lld.%zd", - js_trace_malloc_ptr_offset(ptr, s->opaque), - js_trace_malloc_usable_size(ptr)); - } - fmt++; - continue; - } - if (fmt[0] == 'z' && fmt[1] == 'd') { - size_t sz = va_arg(ap, size_t); - printf("%zd", sz); - fmt += 2; - continue; - } - } - putc(c, stdout); - } - va_end(ap); -} - -static void js_trace_malloc_init(struct trace_malloc_data *s) -{ - free(s->base = malloc(8)); -} - -static void *js_trace_malloc(JSMallocState *s, size_t size) -{ - void *ptr; - - /* Do not allocate zero bytes: behavior is platform dependent */ - assert(size != 0); - - if (unlikely(s->malloc_size + size > s->malloc_limit)) - return NULL; - ptr = malloc(size); - js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr); - if (ptr) { - s->malloc_count++; - s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - } - return ptr; -} - -static void js_trace_free(JSMallocState *s, void *ptr) -{ - if (!ptr) - return; - - js_trace_malloc_printf(s, "F %p\n", ptr); - s->malloc_count--; - s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - free(ptr); -} - -static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) -{ - size_t old_size; - - if (!ptr) { - if (size == 0) - return NULL; - return js_trace_malloc(s, size); - } - old_size = js_trace_malloc_usable_size(ptr); - if (size == 0) { - js_trace_malloc_printf(s, "R %zd %p\n", size, ptr); - s->malloc_count--; - s->malloc_size -= old_size + MALLOC_OVERHEAD; - free(ptr); - return NULL; - } - if (s->malloc_size + size - old_size > s->malloc_limit) - return NULL; - - js_trace_malloc_printf(s, "R %zd %p", size, ptr); - - ptr = realloc(ptr, size); - js_trace_malloc_printf(s, " -> %p\n", ptr); - if (ptr) { - s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size; - } - return ptr; -} - -static const JSMallocFunctions trace_mf = { - js_trace_malloc, - js_trace_free, - js_trace_realloc, -#if defined(__APPLE__) - malloc_size, -#elif defined(_WIN32) - (size_t (*)(const void *))_msize, -#elif defined(EMSCRIPTEN) - NULL, -#elif defined(__linux__) - (size_t (*)(const void *))malloc_usable_size, -#else - /* change this to `NULL,` if compilation fails */ - malloc_usable_size, -#endif -}; - -#define PROG_NAME "qjs" - -void help(void) -{ - printf("QuickJS version " CONFIG_VERSION "\n" - "usage: " PROG_NAME " [options] [file [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); -} - -int main(int argc, char **argv) -{ - JSRuntime *rt; - JSContext *ctx; - struct trace_malloc_data trace_data = { NULL }; - int optind; - char *expr = NULL; - int interactive = 0; - int dump_memory = 0; - int trace_memory = 0; - int empty_run = 0; - int module = -1; - int load_std = 0; - int dump_unhandled_promise_rejection = 0; - size_t memory_limit = 0; - 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' */ - { - const char *p, *exename; - exename = argv[0]; - p = strrchr(exename, '/'); - if (p) - exename = p + 1; - load_jscalc = !strcmp(exename, "qjscalc"); - } -#endif - - /* cannot use getopt because we want to pass the command line to - the script */ - optind = 1; - while (optind < argc && *argv[optind] == '-') { - char *arg = argv[optind] + 1; - const char *longopt = ""; - /* a single - is not an option, it also stops argument scanning */ - if (!*arg) - break; - optind++; - if (*arg == '-') { - longopt = arg + 1; - arg += strlen(arg); - /* -- stops argument scanning */ - if (!*longopt) - break; - } - for (; *arg || *longopt; longopt = "") { - char opt = *arg; - if (opt) - arg++; - if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { - help(); - continue; - } - if (opt == 'e' || !strcmp(longopt, "eval")) { - if (*arg) { - expr = arg; - break; - } - if (optind < argc) { - expr = argv[optind++]; - break; - } - fprintf(stderr, "qjs: missing expression for -e\n"); - exit(2); - } - if (opt == 'I' || !strcmp(longopt, "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; - } - if (opt == 'm' || !strcmp(longopt, "module")) { - module = 1; - continue; - } - if (!strcmp(longopt, "script")) { - module = 0; - continue; - } - if (opt == 'd' || !strcmp(longopt, "dump")) { - dump_memory++; - continue; - } - if (opt == 'T' || !strcmp(longopt, "trace")) { - trace_memory++; - continue; - } - if (!strcmp(longopt, "std")) { - load_std = 1; - continue; - } - if (!strcmp(longopt, "unhandled-rejection")) { - dump_unhandled_promise_rejection = 1; - continue; - } -#ifdef CONFIG_BIGNUM - if (!strcmp(longopt, "bignum")) { - bignum_ext = 1; - continue; - } - if (!strcmp(longopt, "qjscalc")) { - load_jscalc = 1; - continue; - } -#endif - if (opt == 'q' || !strcmp(longopt, "quit")) { - empty_run++; - continue; - } - if (!strcmp(longopt, "memory-limit")) { - if (optind >= argc) { - fprintf(stderr, "expecting memory limit"); - exit(1); - } - memory_limit = (size_t)strtod(argv[optind++], NULL); - continue; - } - if (!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 { - fprintf(stderr, "qjs: unknown option '--%s'\n", longopt); - } - help(); - } - } - - if (load_jscalc) - bignum_ext = 1; - - if (trace_memory) { - js_trace_malloc_init(&trace_data); - rt = JS_NewRuntime2(&trace_mf, &trace_data); - } else { - rt = JS_NewRuntime(); - } - if (!rt) { - fprintf(stderr, "qjs: cannot allocate JS runtime\n"); - exit(2); - } - if (memory_limit != 0) - JS_SetMemoryLimit(rt, memory_limit); - 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); - } - - /* loader for ES6 modules */ - JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); - - if (dump_unhandled_promise_rejection) { - JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, - NULL); - } - - if (!empty_run) { -#ifdef CONFIG_BIGNUM - if (load_jscalc) { - js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0); - } -#endif - js_std_add_helpers(ctx, argc - optind, argv + optind); - - /* make 'std' and 'os' visible to non module code */ - if (load_std) { - const char *str = "import * as std from 'std';\n" - "import * as os from 'os';\n" - "globalThis.std = std;\n" - "globalThis.os = os;\n"; - eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); - } - - 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; - } else - if (optind >= argc) { - /* interactive mode */ - interactive = 1; - } else { - const char *filename; - filename = argv[optind]; - if (eval_file(ctx, filename, module)) - goto fail; - } - if (interactive) { - js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); - } - js_std_loop(ctx); - } - - if (dump_memory) { - JSMemoryUsage stats; - JS_ComputeMemoryUsage(rt, &stats); - JS_DumpMemoryUsage(stdout, &stats, rt); - } - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - - if (empty_run && dump_memory) { - clock_t t[5]; - double best[5]; - int i, j; - for (i = 0; i < 100; i++) { - t[0] = clock(); - rt = JS_NewRuntime(); - t[1] = clock(); - ctx = JS_NewContext(rt); - t[2] = clock(); - JS_FreeContext(ctx); - t[3] = clock(); - JS_FreeRuntime(rt); - t[4] = clock(); - for (j = 4; j > 0; j--) { - double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC; - if (i == 0 || best[j] > ms) - best[j] = ms; - } - } - printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n", - best[1] + best[2] + best[3] + best[4], - best[1], best[2], best[3], best[4]); - } - return 0; - fail: - js_std_free_handlers(rt); - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return 1; -} +/* + * QuickJS stand alone interpreter + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#elif defined(__linux__) +#include +#endif + +#include "include/quickjs/cutils.h" +#include "quickjs-libc.h" + +extern const uint8_t qjsc_repl[]; +extern const uint32_t qjsc_repl_size; +#ifdef CONFIG_BIGNUM +extern const uint8_t qjsc_qjscalc[]; +extern const uint32_t qjsc_qjscalc_size; +static int bignum_ext; +#endif + +static int eval_buf(JSContext *ctx, const void *buf, int buf_len, + const char *filename, int eval_flags) +{ + JSValue val; + int ret; + + if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) { + /* for the modules, we compile then run to be able to set + import.meta */ + val = JS_Eval(ctx, buf, buf_len, filename, + eval_flags | JS_EVAL_FLAG_COMPILE_ONLY); + if (!JS_IsException(val)) { + js_module_set_import_meta(ctx, val, TRUE, TRUE); + val = JS_EvalFunction(ctx, val); + } + } else { + val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + } + if (JS_IsException(val)) { + js_std_dump_error(ctx); + ret = -1; + } else { + ret = 0; + } + JS_FreeValue(ctx, val); + return ret; +} + +static int eval_file(JSContext *ctx, const char *filename, int module) +{ + uint8_t *buf; + int ret, eval_flags; + size_t buf_len; + + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + perror(filename); + exit(1); + } + + if (module < 0) { + module = (has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags = JS_EVAL_TYPE_MODULE; + else + eval_flags = JS_EVAL_TYPE_GLOBAL; + ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); + js_free(ctx, buf); + return ret; +} + +/* 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 +#define MALLOC_OVERHEAD 8 +#endif + +struct trace_malloc_data { + uint8_t *base; +}; + +static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr, + struct trace_malloc_data *dp) +{ + return ptr - dp->base; +} + +/* default memory allocation functions with memory limitation */ +static inline size_t js_trace_malloc_usable_size(void *ptr) +{ +#if defined(__APPLE__) + return malloc_size(ptr); +#elif defined(_WIN32) + return _msize(ptr); +#elif defined(EMSCRIPTEN) + return 0; +#elif defined(__linux__) + return malloc_usable_size(ptr); +#else + /* change this to `return 0;` if compilation fails */ + return malloc_usable_size(ptr); +#endif +} + +static void +#ifdef _WIN32 +/* mingw printf is used */ +__attribute__((format(gnu_printf, 2, 3))) +#else +__attribute__((format(printf, 2, 3))) +#endif + js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...) +{ + va_list ap; + int c; + + va_start(ap, fmt); + while ((c = *fmt++) != '\0') { + if (c == '%') { + /* only handle %p and %zd */ + if (*fmt == 'p') { + uint8_t *ptr = va_arg(ap, void *); + if (ptr == NULL) { + printf("NULL"); + } else { + printf("H%+06lld.%zd", + js_trace_malloc_ptr_offset(ptr, s->opaque), + js_trace_malloc_usable_size(ptr)); + } + fmt++; + continue; + } + if (fmt[0] == 'z' && fmt[1] == 'd') { + size_t sz = va_arg(ap, size_t); + printf("%zd", sz); + fmt += 2; + continue; + } + } + putc(c, stdout); + } + va_end(ap); +} + +static void js_trace_malloc_init(struct trace_malloc_data *s) +{ + free(s->base = malloc(8)); +} + +static void *js_trace_malloc(JSMallocState *s, size_t size) +{ + void *ptr; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(size != 0); + + if (unlikely(s->malloc_size + size > s->malloc_limit)) + return NULL; + ptr = malloc(size); + js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr); + if (ptr) { + s->malloc_count++; + s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + } + return ptr; +} + +static void js_trace_free(JSMallocState *s, void *ptr) +{ + if (!ptr) + return; + + js_trace_malloc_printf(s, "F %p\n", ptr); + s->malloc_count--; + s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + free(ptr); +} + +static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size) +{ + size_t old_size; + + if (!ptr) { + if (size == 0) + return NULL; + return js_trace_malloc(s, size); + } + old_size = js_trace_malloc_usable_size(ptr); + if (size == 0) { + js_trace_malloc_printf(s, "R %zd %p\n", size, ptr); + s->malloc_count--; + s->malloc_size -= old_size + MALLOC_OVERHEAD; + free(ptr); + return NULL; + } + if (s->malloc_size + size - old_size > s->malloc_limit) + return NULL; + + js_trace_malloc_printf(s, "R %zd %p", size, ptr); + + ptr = realloc(ptr, size); + js_trace_malloc_printf(s, " -> %p\n", ptr); + if (ptr) { + s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size; + } + return ptr; +} + +static const JSMallocFunctions trace_mf = { + js_trace_malloc, + js_trace_free, + js_trace_realloc, +#if defined(__APPLE__) + malloc_size, +#elif defined(_WIN32) + (size_t (*)(const void *))_msize, +#elif defined(EMSCRIPTEN) + NULL, +#elif defined(__linux__) + (size_t (*)(const void *))malloc_usable_size, +#else + /* change this to `NULL,` if compilation fails */ + malloc_usable_size, +#endif +}; + +#define PROG_NAME "qjs" + +void help(void) +{ + printf("QuickJS version " CONFIG_VERSION "\n" + "usage: " PROG_NAME " [options] [file [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); +} + +int main(int argc, char **argv) +{ + JSRuntime *rt; + JSContext *ctx; + struct trace_malloc_data trace_data = { NULL }; + int optind; + char *expr = NULL; + int interactive = 0; + int dump_memory = 0; + int trace_memory = 0; + int empty_run = 0; + int module = -1; + int load_std = 0; + int dump_unhandled_promise_rejection = 0; + size_t memory_limit = 0; + 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' */ + { + const char *p, *exename; + exename = argv[0]; + p = strrchr(exename, '/'); + if (p) + exename = p + 1; + load_jscalc = !strcmp(exename, "qjscalc"); + } +#endif + + /* cannot use getopt because we want to pass the command line to + the script */ + optind = 1; + while (optind < argc && *argv[optind] == '-') { + char *arg = argv[optind] + 1; + const char *longopt = ""; + /* a single - is not an option, it also stops argument scanning */ + if (!*arg) + break; + optind++; + if (*arg == '-') { + longopt = arg + 1; + arg += strlen(arg); + /* -- stops argument scanning */ + if (!*longopt) + break; + } + for (; *arg || *longopt; longopt = "") { + char opt = *arg; + if (opt) + arg++; + if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) { + help(); + continue; + } + if (opt == 'e' || !strcmp(longopt, "eval")) { + if (*arg) { + expr = arg; + break; + } + if (optind < argc) { + expr = argv[optind++]; + break; + } + fprintf(stderr, "qjs: missing expression for -e\n"); + exit(2); + } + if (opt == 'I' || !strcmp(longopt, "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; + } + if (opt == 'm' || !strcmp(longopt, "module")) { + module = 1; + continue; + } + if (!strcmp(longopt, "script")) { + module = 0; + continue; + } + if (opt == 'd' || !strcmp(longopt, "dump")) { + dump_memory++; + continue; + } + if (opt == 'T' || !strcmp(longopt, "trace")) { + trace_memory++; + continue; + } + if (!strcmp(longopt, "std")) { + load_std = 1; + continue; + } + if (!strcmp(longopt, "unhandled-rejection")) { + dump_unhandled_promise_rejection = 1; + continue; + } +#ifdef CONFIG_BIGNUM + if (!strcmp(longopt, "bignum")) { + bignum_ext = 1; + continue; + } + if (!strcmp(longopt, "qjscalc")) { + load_jscalc = 1; + continue; + } +#endif + if (opt == 'q' || !strcmp(longopt, "quit")) { + empty_run++; + continue; + } + if (!strcmp(longopt, "memory-limit")) { + if (optind >= argc) { + fprintf(stderr, "expecting memory limit"); + exit(1); + } + memory_limit = (size_t)strtod(argv[optind++], NULL); + continue; + } + if (!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 { + fprintf(stderr, "qjs: unknown option '--%s'\n", longopt); + } + help(); + } + } + + if (load_jscalc) + bignum_ext = 1; + + if (trace_memory) { + js_trace_malloc_init(&trace_data); + rt = JS_NewRuntime2(&trace_mf, &trace_data); + } else { + rt = JS_NewRuntime(); + } + if (!rt) { + fprintf(stderr, "qjs: cannot allocate JS runtime\n"); + exit(2); + } + if (memory_limit != 0) + JS_SetMemoryLimit(rt, memory_limit); + 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); + } + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + + if (dump_unhandled_promise_rejection) { + JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, + NULL); + } + + if (!empty_run) { +#ifdef CONFIG_BIGNUM + if (load_jscalc) { + js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0); + } +#endif + js_std_add_helpers(ctx, argc - optind, argv + optind); + + /* make 'std' and 'os' visible to non module code */ + if (load_std) { + const char *str = "import * as std from 'std';\n" + "import * as os from 'os';\n" + "globalThis.std = std;\n" + "globalThis.os = os;\n"; + eval_buf(ctx, str, strlen(str), "", JS_EVAL_TYPE_MODULE); + } + + 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; + } else + if (optind >= argc) { + /* interactive mode */ + interactive = 1; + } else { + const char *filename; + filename = argv[optind]; + if (eval_file(ctx, filename, module)) + goto fail; + } + if (interactive) { + js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); + } + js_std_loop(ctx); + } + + if (dump_memory) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(stdout, &stats, rt); + } + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + if (empty_run && dump_memory) { + clock_t t[5]; + double best[5]; + int i, j; + for (i = 0; i < 100; i++) { + t[0] = clock(); + rt = JS_NewRuntime(); + t[1] = clock(); + ctx = JS_NewContext(rt); + t[2] = clock(); + JS_FreeContext(ctx); + t[3] = clock(); + JS_FreeRuntime(rt); + t[4] = clock(); + for (j = 4; j > 0; j--) { + double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC; + if (i == 0 || best[j] > ms) + best[j] = ms; + } + } + printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n", + best[1] + best[2] + best[3] + best[4], + best[1], best[2], best[3], best[4]); + } + return 0; + fail: + js_std_free_handlers(rt); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 1; +} diff --git a/qjsc.c b/qjsc.c index b9f1e4cd5..91c01410e 100644 --- a/qjsc.c +++ b/qjsc.c @@ -1,762 +1,762 @@ -/* - * QuickJS command line compiler - * - * Copyright (c) 2018-2021 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#if !defined(_WIN32) -#include -#endif - -#include "cutils.h" -#include "quickjs-libc.h" - -typedef struct { - char *name; - char *short_name; - int flags; -} namelist_entry_t; - -typedef struct namelist_t { - namelist_entry_t *array; - int count; - int size; -} namelist_t; - -typedef struct { - const char *option_name; - const char *init_name; -} FeatureEntry; - -static namelist_t cname_list; -static namelist_t cmodule_list; -static namelist_t init_module_list; -static uint64_t feature_bitmap; -static FILE *outfile; -static BOOL byte_swap; -static BOOL dynamic_export; -static const char *c_ident_prefix = "qjsc_"; - -#define FE_ALL (-1) - -static const FeatureEntry feature_list[] = { - { "date", "Date" }, - { "eval", "Eval" }, - { "string-normalize", "StringNormalize" }, - { "regexp", "RegExp" }, - { "json", "JSON" }, - { "proxy", "Proxy" }, - { "map", "MapSet" }, - { "typedarray", "TypedArrays" }, - { "promise", "Promise" }, -#define FE_MODULE_LOADER 9 - { "module-loader", NULL }, -#ifdef CONFIG_BIGNUM - { "bigint", "BigInt" }, -#endif -}; - -void namelist_add(namelist_t *lp, const char *name, const char *short_name, - int flags) -{ - namelist_entry_t *e; - if (lp->count == lp->size) { - size_t newsize = lp->size + (lp->size >> 1) + 4; - namelist_entry_t *a = - realloc(lp->array, sizeof(lp->array[0]) * newsize); - /* XXX: check for realloc failure */ - lp->array = a; - lp->size = newsize; - } - e = &lp->array[lp->count++]; - e->name = strdup(name); - if (short_name) - e->short_name = strdup(short_name); - else - e->short_name = NULL; - e->flags = flags; -} - -void namelist_free(namelist_t *lp) -{ - while (lp->count > 0) { - namelist_entry_t *e = &lp->array[--lp->count]; - free(e->name); - free(e->short_name); - } - free(lp->array); - lp->array = NULL; - lp->size = 0; -} - -namelist_entry_t *namelist_find(namelist_t *lp, const char *name) -{ - int i; - for(i = 0; i < lp->count; i++) { - namelist_entry_t *e = &lp->array[i]; - if (!strcmp(e->name, name)) - return e; - } - return NULL; -} - -static void get_c_name(char *buf, size_t buf_size, const char *file) -{ - const char *p, *r; - size_t len, i; - int c; - char *q; - - p = strrchr(file, '/'); - if (!p) - p = file; - else - p++; - r = strrchr(p, '.'); - if (!r) - len = strlen(p); - else - len = r - p; - pstrcpy(buf, buf_size, c_ident_prefix); - q = buf + strlen(buf); - for(i = 0; i < len; i++) { - c = p[i]; - if (!((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z'))) { - c = '_'; - } - if ((q - buf) < buf_size - 1) - *q++ = c; - } - *q = '\0'; -} - -static void dump_hex(FILE *f, const uint8_t *buf, size_t len) -{ - size_t i, col; - col = 0; - for(i = 0; i < len; i++) { - fprintf(f, " 0x%02x,", buf[i]); - if (++col == 8) { - fprintf(f, "\n"); - col = 0; - } - } - if (col != 0) - fprintf(f, "\n"); -} - -static void output_object_code(JSContext *ctx, - FILE *fo, JSValueConst obj, const char *c_name, - BOOL load_only) -{ - uint8_t *out_buf; - size_t out_buf_len; - int flags; - flags = JS_WRITE_OBJ_BYTECODE; - if (byte_swap) - flags |= JS_WRITE_OBJ_BSWAP; - out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); - if (!out_buf) { - js_std_dump_error(ctx); - exit(1); - } - - namelist_add(&cname_list, c_name, NULL, load_only); - - fprintf(fo, "const uint32_t %s_size = %u;\n\n", - c_name, (unsigned int)out_buf_len); - fprintf(fo, "const uint8_t %s[%u] = {\n", - c_name, (unsigned int)out_buf_len); - dump_hex(fo, out_buf, out_buf_len); - fprintf(fo, "};\n\n"); - - js_free(ctx, out_buf); -} - -static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m) -{ - /* should never be called when compiling JS code */ - abort(); -} - -static void find_unique_cname(char *cname, size_t cname_size) -{ - char cname1[1024]; - int suffix_num; - size_t len, max_len; - assert(cname_size >= 32); - /* find a C name not matching an existing module C name by - adding a numeric suffix */ - len = strlen(cname); - max_len = cname_size - 16; - if (len > max_len) - cname[max_len] = '\0'; - suffix_num = 1; - for(;;) { - snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num); - if (!namelist_find(&cname_list, cname1)) - break; - suffix_num++; - } - pstrcpy(cname, cname_size, cname1); -} - -JSModuleDef *jsc_module_loader(JSContext *ctx, - const char *module_name, void *opaque) -{ - JSModuleDef *m; - namelist_entry_t *e; - - /* check if it is a declared C or system module */ - e = namelist_find(&cmodule_list, module_name); - if (e) { - /* add in the static init module list */ - namelist_add(&init_module_list, e->name, e->short_name, 0); - /* create a dummy module */ - m = JS_NewCModule(ctx, module_name, js_module_dummy_init); - } else if (has_suffix(module_name, ".so")) { - fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n", module_name); - /* create a dummy module */ - m = JS_NewCModule(ctx, module_name, js_module_dummy_init); - /* the resulting executable will export its symbols for the - dynamic library */ - dynamic_export = TRUE; - } else { - size_t buf_len; - uint8_t *buf; - JSValue func_val; - char cname[1024]; - - buf = js_load_file(ctx, &buf_len, module_name); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s'", - module_name); - return NULL; - } - - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - get_c_name(cname, sizeof(cname), module_name); - if (namelist_find(&cname_list, cname)) { - find_unique_cname(cname, sizeof(cname)); - } - output_object_code(ctx, outfile, func_val, cname, TRUE); - - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); - } - return m; -} - -static void compile_file(JSContext *ctx, FILE *fo, - const char *filename, - const char *c_name1, - int module) -{ - uint8_t *buf; - char c_name[1024]; - int eval_flags; - JSValue obj; - size_t buf_len; - - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - fprintf(stderr, "Could not load '%s'\n", filename); - exit(1); - } - eval_flags = JS_EVAL_FLAG_COMPILE_ONLY; - if (module < 0) { - module = (has_suffix(filename, ".mjs") || - JS_DetectModule((const char *)buf, buf_len)); - } - if (module) - eval_flags |= JS_EVAL_TYPE_MODULE; - else - eval_flags |= JS_EVAL_TYPE_GLOBAL; - obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags); - if (JS_IsException(obj)) { - js_std_dump_error(ctx); - exit(1); - } - js_free(ctx, buf); - if (c_name1) { - pstrcpy(c_name, sizeof(c_name), c_name1); - } else { - get_c_name(c_name, sizeof(c_name), filename); - } - output_object_code(ctx, fo, obj, c_name, FALSE); - JS_FreeValue(ctx, obj); -} - -static const char main_c_template1[] = - "int main(int argc, char **argv)\n" - "{\n" - " JSRuntime *rt;\n" - " JSContext *ctx;\n" - " rt = JS_NewRuntime();\n" - " js_std_set_worker_new_context_func(JS_NewCustomContext);\n" - " js_std_init_handlers(rt);\n" - ; - -static const char main_c_template2[] = - " js_std_loop(ctx);\n" - " JS_FreeContext(ctx);\n" - " JS_FreeRuntime(rt);\n" - " return 0;\n" - "}\n"; - -#define PROG_NAME "qjsc" - -void help(void) -{ - printf("QuickJS Compiler version " CONFIG_VERSION "\n" - "usage: " PROG_NAME " [options] [files]\n" - "\n" - "options are:\n" - "-c only output bytecode in a C file\n" - "-e output main() and bytecode in a C file (default = executable output)\n" - "-o output set the output filename\n" - "-N cname set the C name of the generated data\n" - "-m compile as Javascript module (default=autodetect)\n" - "-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) - printf("|"); - printf("%s", feature_list[i].option_name); - } - printf("]\n" - " disable selected language features (smaller code size)\n"); - } -#endif - exit(1); -} - -#if defined(CONFIG_CC) && !defined(_WIN32) - -int exec_cmd(char **argv) -{ - int pid, status, ret; - - pid = fork(); - if (pid == 0) { - execvp(argv[0], argv); - exit(1); - } - - for(;;) { - ret = waitpid(pid, &status, 0); - if (ret == pid && WIFEXITED(status)) - break; - } - return WEXITSTATUS(status); -} - -static int output_executable(const char *out_filename, const char *cfilename, - BOOL use_lto, BOOL verbose, const char *exename) -{ - const char *argv[64]; - const char **arg, *bn_suffix, *lto_suffix; - char libjsname[1024]; - char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p; - int ret; - - /* get the directory of the executable */ - pstrcpy(exe_dir, sizeof(exe_dir), exename); - p = strrchr(exe_dir, '/'); - if (p) { - *p = '\0'; - } else { - pstrcpy(exe_dir, sizeof(exe_dir), "."); - } - - /* if 'quickjs.h' is present at the same path as the executable, we - use it as include and lib directory */ - snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir); - if (access(buf, R_OK) == 0) { - pstrcpy(inc_dir, sizeof(inc_dir), exe_dir); - pstrcpy(lib_dir, sizeof(lib_dir), exe_dir); - } else { - snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX); - snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX); - } - - lto_suffix = ""; - bn_suffix = ""; - - arg = argv; - *arg++ = CONFIG_CC; - *arg++ = "-O2"; -#ifdef CONFIG_LTO - if (use_lto) { - *arg++ = "-flto"; - lto_suffix = ".lto"; - } -#endif - /* XXX: use the executable path to find the includes files and - libraries */ - *arg++ = "-D"; - *arg++ = "_GNU_SOURCE"; - *arg++ = "-I"; - *arg++ = inc_dir; - *arg++ = "-o"; - *arg++ = out_filename; - if (dynamic_export) - *arg++ = "-rdynamic"; - *arg++ = cfilename; - snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a", - lib_dir, bn_suffix, lto_suffix); - *arg++ = libjsname; - *arg++ = "-lm"; - *arg++ = "-ldl"; - *arg++ = "-lpthread"; - *arg = NULL; - - if (verbose) { - for(arg = argv; *arg != NULL; arg++) - printf("%s ", *arg); - printf("\n"); - } - - ret = exec_cmd((char **)argv); - unlink(cfilename); - return ret; -} -#else -static int output_executable(const char *out_filename, const char *cfilename, - BOOL use_lto, BOOL verbose, const char *exename) -{ - fprintf(stderr, "Executable output is not supported for this target\n"); - exit(1); - return 0; -} -#endif - - -typedef enum { - OUTPUT_C, - OUTPUT_C_MAIN, - OUTPUT_EXECUTABLE, -} OutputTypeEnum; - -int main(int argc, char **argv) -{ - int c, i, verbose; - const char *out_filename, *cname; - char cfilename[1024]; - FILE *fo; - JSRuntime *rt; - JSContext *ctx; - BOOL use_lto; - 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; - cname = NULL; - feature_bitmap = FE_ALL; - module = -1; - 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:S:D:"); - if (c == -1) - break; - switch(c) { - case 'h': - help(); - case 'o': - out_filename = optarg; - break; - case 'c': - output_type = OUTPUT_C; - break; - case 'e': - output_type = OUTPUT_C_MAIN; - break; - case 'N': - cname = optarg; - break; - case 'f': - { - const char *p; - p = optarg; - if (!strcmp(optarg, "lto")) { - use_lto = TRUE; - } else if (strstart(p, "no-", &p)) { - use_lto = TRUE; - for(i = 0; i < countof(feature_list); i++) { - if (!strcmp(p, feature_list[i].option_name)) { - feature_bitmap &= ~((uint64_t)1 << i); - break; - } - } - if (i == countof(feature_list)) - goto bad_feature; - } else -#ifdef CONFIG_BIGNUM - if (!strcmp(optarg, "bignum")) { - bignum_ext = TRUE; - } else -#endif - { - bad_feature: - fprintf(stderr, "unsupported feature: %s\n", optarg); - exit(1); - } - } - break; - case 'm': - module = 1; - break; - case 'M': - { - char *p; - char path[1024]; - char cname[1024]; - pstrcpy(path, sizeof(path), optarg); - p = strchr(path, ','); - if (p) { - *p = '\0'; - pstrcpy(cname, sizeof(cname), p + 1); - } else { - get_c_name(cname, sizeof(cname), path); - } - namelist_add(&cmodule_list, path, cname, 0); - } - break; - case 'D': - namelist_add(&dynamic_module_list, optarg, NULL, 0); - break; - case 'x': - byte_swap = TRUE; - break; - case 'v': - verbose++; - break; - case 'p': - c_ident_prefix = optarg; - break; - case 'S': - stack_size = (size_t)strtod(optarg, NULL); - break; - default: - break; - } - } - - if (optind >= argc) - help(); - - if (!out_filename) { - if (output_type == OUTPUT_EXECUTABLE) { - out_filename = "a.out"; - } else { - out_filename = "out.c"; - } - } - - if (output_type == OUTPUT_EXECUTABLE) { -#if defined(_WIN32) || defined(__ANDROID__) - /* XXX: find a /tmp directory ? */ - snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid()); -#else - snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid()); -#endif - } else { - pstrcpy(cfilename, sizeof(cfilename), out_filename); - } - - fo = fopen(cfilename, "w"); - if (!fo) { - perror(cfilename); - exit(1); - } - outfile = fo; - - rt = JS_NewRuntime(); - ctx = JS_NewContext(rt); -#ifdef CONFIG_BIGNUM - if (bignum_ext) { - JS_AddIntrinsicBigFloat(ctx); - JS_AddIntrinsicBigDecimal(ctx); - JS_AddIntrinsicOperators(ctx); - JS_EnableBignumExt(ctx, TRUE); - } -#endif - - /* loader for ES6 modules */ - JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); - - fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n" - "\n" - ); - - if (output_type != OUTPUT_C) { - fprintf(fo, "#include \"quickjs-libc.h\"\n" - "\n" - ); - } else { - fprintf(fo, "#include \n" - "\n" - ); - } - - for(i = optind; i < argc; i++) { - const char *filename = argv[i]; - compile_file(ctx, fo, filename, cname, module); - cname = NULL; - } - - 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)) && - feature_list[i].init_name) { - fprintf(fo, " JS_AddIntrinsic%s(ctx);\n", - feature_list[i].init_name); - } - } -#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 */ - - fprintf(fo, - " {\n" - " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" - " js_init_module_%s(ctx, \"%s\");\n" - " }\n", - e->short_name, e->short_name, e->name); - } - for(i = 0; i < cname_list.count; i++) { - namelist_entry_t *e = &cname_list.array[i]; - 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]; - if (!e->flags) { - fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n", - e->name, e->name); - } - } - fputs(main_c_template2, fo); - } - - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - - fclose(fo); - - if (output_type == OUTPUT_EXECUTABLE) { - return output_executable(out_filename, cfilename, use_lto, verbose, - argv[0]); - } - namelist_free(&cname_list); - namelist_free(&cmodule_list); - namelist_free(&init_module_list); - return 0; -} +/* + * QuickJS command line compiler + * + * Copyright (c) 2018-2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(_WIN32) +#include +#endif + +#include "include/quickjs/cutils.h" +#include "quickjs-libc.h" + +typedef struct { + char *name; + char *short_name; + int flags; +} namelist_entry_t; + +typedef struct namelist_t { + namelist_entry_t *array; + int count; + int size; +} namelist_t; + +typedef struct { + const char *option_name; + const char *init_name; +} FeatureEntry; + +static namelist_t cname_list; +static namelist_t cmodule_list; +static namelist_t init_module_list; +static uint64_t feature_bitmap; +static FILE *outfile; +static BOOL byte_swap; +static BOOL dynamic_export; +static const char *c_ident_prefix = "qjsc_"; + +#define FE_ALL (-1) + +static const FeatureEntry feature_list[] = { + { "date", "Date" }, + { "eval", "Eval" }, + { "string-normalize", "StringNormalize" }, + { "regexp", "RegExp" }, + { "json", "JSON" }, + { "proxy", "Proxy" }, + { "map", "MapSet" }, + { "typedarray", "TypedArrays" }, + { "promise", "Promise" }, +#define FE_MODULE_LOADER 9 + { "module-loader", NULL }, +#ifdef CONFIG_BIGNUM + { "bigint", "BigInt" }, +#endif +}; + +void namelist_add(namelist_t *lp, const char *name, const char *short_name, + int flags) +{ + namelist_entry_t *e; + if (lp->count == lp->size) { + size_t newsize = lp->size + (lp->size >> 1) + 4; + namelist_entry_t *a = + realloc(lp->array, sizeof(lp->array[0]) * newsize); + /* XXX: check for realloc failure */ + lp->array = a; + lp->size = newsize; + } + e = &lp->array[lp->count++]; + e->name = strdup(name); + if (short_name) + e->short_name = strdup(short_name); + else + e->short_name = NULL; + e->flags = flags; +} + +void namelist_free(namelist_t *lp) +{ + while (lp->count > 0) { + namelist_entry_t *e = &lp->array[--lp->count]; + free(e->name); + free(e->short_name); + } + free(lp->array); + lp->array = NULL; + lp->size = 0; +} + +namelist_entry_t *namelist_find(namelist_t *lp, const char *name) +{ + int i; + for(i = 0; i < lp->count; i++) { + namelist_entry_t *e = &lp->array[i]; + if (!strcmp(e->name, name)) + return e; + } + return NULL; +} + +static void get_c_name(char *buf, size_t buf_size, const char *file) +{ + const char *p, *r; + size_t len, i; + int c; + char *q; + + p = strrchr(file, '/'); + if (!p) + p = file; + else + p++; + r = strrchr(p, '.'); + if (!r) + len = strlen(p); + else + len = r - p; + pstrcpy(buf, buf_size, c_ident_prefix); + q = buf + strlen(buf); + for(i = 0; i < len; i++) { + c = p[i]; + if (!((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) { + c = '_'; + } + if ((q - buf) < buf_size - 1) + *q++ = c; + } + *q = '\0'; +} + +static void dump_hex(FILE *f, const uint8_t *buf, size_t len) +{ + size_t i, col; + col = 0; + for(i = 0; i < len; i++) { + fprintf(f, " 0x%02x,", buf[i]); + if (++col == 8) { + fprintf(f, "\n"); + col = 0; + } + } + if (col != 0) + fprintf(f, "\n"); +} + +static void output_object_code(JSContext *ctx, + FILE *fo, JSValueConst obj, const char *c_name, + BOOL load_only) +{ + uint8_t *out_buf; + size_t out_buf_len; + int flags; + flags = JS_WRITE_OBJ_BYTECODE; + if (byte_swap) + flags |= JS_WRITE_OBJ_BSWAP; + out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags); + if (!out_buf) { + js_std_dump_error(ctx); + exit(1); + } + + namelist_add(&cname_list, c_name, NULL, load_only); + + fprintf(fo, "const uint32_t %s_size = %u;\n\n", + c_name, (unsigned int)out_buf_len); + fprintf(fo, "const uint8_t %s[%u] = {\n", + c_name, (unsigned int)out_buf_len); + dump_hex(fo, out_buf, out_buf_len); + fprintf(fo, "};\n\n"); + + js_free(ctx, out_buf); +} + +static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m) +{ + /* should never be called when compiling JS code */ + abort(); +} + +static void find_unique_cname(char *cname, size_t cname_size) +{ + char cname1[1024]; + int suffix_num; + size_t len, max_len; + assert(cname_size >= 32); + /* find a C name not matching an existing module C name by + adding a numeric suffix */ + len = strlen(cname); + max_len = cname_size - 16; + if (len > max_len) + cname[max_len] = '\0'; + suffix_num = 1; + for(;;) { + snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num); + if (!namelist_find(&cname_list, cname1)) + break; + suffix_num++; + } + pstrcpy(cname, cname_size, cname1); +} + +JSModuleDef *jsc_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + namelist_entry_t *e; + + /* check if it is a declared C or system module */ + e = namelist_find(&cmodule_list, module_name); + if (e) { + /* add in the static init module list */ + namelist_add(&init_module_list, e->name, e->short_name, 0); + /* create a dummy module */ + m = JS_NewCModule(ctx, module_name, js_module_dummy_init); + } else if (has_suffix(module_name, ".so")) { + fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n", module_name); + /* create a dummy module */ + m = JS_NewCModule(ctx, module_name, js_module_dummy_init); + /* the resulting executable will export its symbols for the + dynamic library */ + dynamic_export = TRUE; + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + char cname[1024]; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + get_c_name(cname, sizeof(cname), module_name); + if (namelist_find(&cname_list, cname)) { + find_unique_cname(cname, sizeof(cname)); + } + output_object_code(ctx, outfile, func_val, cname, TRUE); + + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static void compile_file(JSContext *ctx, FILE *fo, + const char *filename, + const char *c_name1, + int module) +{ + uint8_t *buf; + char c_name[1024]; + int eval_flags; + JSValue obj; + size_t buf_len; + + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + fprintf(stderr, "Could not load '%s'\n", filename); + exit(1); + } + eval_flags = JS_EVAL_FLAG_COMPILE_ONLY; + if (module < 0) { + module = (has_suffix(filename, ".mjs") || + JS_DetectModule((const char *)buf, buf_len)); + } + if (module) + eval_flags |= JS_EVAL_TYPE_MODULE; + else + eval_flags |= JS_EVAL_TYPE_GLOBAL; + obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags); + if (JS_IsException(obj)) { + js_std_dump_error(ctx); + exit(1); + } + js_free(ctx, buf); + if (c_name1) { + pstrcpy(c_name, sizeof(c_name), c_name1); + } else { + get_c_name(c_name, sizeof(c_name), filename); + } + output_object_code(ctx, fo, obj, c_name, FALSE); + JS_FreeValue(ctx, obj); +} + +static const char main_c_template1[] = + "int main(int argc, char **argv)\n" + "{\n" + " JSRuntime *rt;\n" + " JSContext *ctx;\n" + " rt = JS_NewRuntime();\n" + " js_std_set_worker_new_context_func(JS_NewCustomContext);\n" + " js_std_init_handlers(rt);\n" + ; + +static const char main_c_template2[] = + " js_std_loop(ctx);\n" + " JS_FreeContext(ctx);\n" + " JS_FreeRuntime(rt);\n" + " return 0;\n" + "}\n"; + +#define PROG_NAME "qjsc" + +void help(void) +{ + printf("QuickJS Compiler version " CONFIG_VERSION "\n" + "usage: " PROG_NAME " [options] [files]\n" + "\n" + "options are:\n" + "-c only output bytecode in a C file\n" + "-e output main() and bytecode in a C file (default = executable output)\n" + "-o output set the output filename\n" + "-N cname set the C name of the generated data\n" + "-m compile as Javascript module (default=autodetect)\n" + "-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) + printf("|"); + printf("%s", feature_list[i].option_name); + } + printf("]\n" + " disable selected language features (smaller code size)\n"); + } +#endif + exit(1); +} + +#if defined(CONFIG_CC) && !defined(_WIN32) + +int exec_cmd(char **argv) +{ + int pid, status, ret; + + pid = fork(); + if (pid == 0) { + execvp(argv[0], argv); + exit(1); + } + + for(;;) { + ret = waitpid(pid, &status, 0); + if (ret == pid && WIFEXITED(status)) + break; + } + return WEXITSTATUS(status); +} + +static int output_executable(const char *out_filename, const char *cfilename, + BOOL use_lto, BOOL verbose, const char *exename) +{ + const char *argv[64]; + const char **arg, *bn_suffix, *lto_suffix; + char libjsname[1024]; + char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p; + int ret; + + /* get the directory of the executable */ + pstrcpy(exe_dir, sizeof(exe_dir), exename); + p = strrchr(exe_dir, '/'); + if (p) { + *p = '\0'; + } else { + pstrcpy(exe_dir, sizeof(exe_dir), "."); + } + + /* if 'quickjs.h' is present at the same path as the executable, we + use it as include and lib directory */ + snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir); + if (access(buf, R_OK) == 0) { + pstrcpy(inc_dir, sizeof(inc_dir), exe_dir); + pstrcpy(lib_dir, sizeof(lib_dir), exe_dir); + } else { + snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX); + snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX); + } + + lto_suffix = ""; + bn_suffix = ""; + + arg = argv; + *arg++ = CONFIG_CC; + *arg++ = "-O2"; +#ifdef CONFIG_LTO + if (use_lto) { + *arg++ = "-flto"; + lto_suffix = ".lto"; + } +#endif + /* XXX: use the executable path to find the includes files and + libraries */ + *arg++ = "-D"; + *arg++ = "_GNU_SOURCE"; + *arg++ = "-I"; + *arg++ = inc_dir; + *arg++ = "-o"; + *arg++ = out_filename; + if (dynamic_export) + *arg++ = "-rdynamic"; + *arg++ = cfilename; + snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a", + lib_dir, bn_suffix, lto_suffix); + *arg++ = libjsname; + *arg++ = "-lm"; + *arg++ = "-ldl"; + *arg++ = "-lpthread"; + *arg = NULL; + + if (verbose) { + for(arg = argv; *arg != NULL; arg++) + printf("%s ", *arg); + printf("\n"); + } + + ret = exec_cmd((char **)argv); + unlink(cfilename); + return ret; +} +#else +static int output_executable(const char *out_filename, const char *cfilename, + BOOL use_lto, BOOL verbose, const char *exename) +{ + fprintf(stderr, "Executable output is not supported for this target\n"); + exit(1); + return 0; +} +#endif + + +typedef enum { + OUTPUT_C, + OUTPUT_C_MAIN, + OUTPUT_EXECUTABLE, +} OutputTypeEnum; + +int main(int argc, char **argv) +{ + int c, i, verbose; + const char *out_filename, *cname; + char cfilename[1024]; + FILE *fo; + JSRuntime *rt; + JSContext *ctx; + BOOL use_lto; + 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; + cname = NULL; + feature_bitmap = FE_ALL; + module = -1; + 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:S:D:"); + if (c == -1) + break; + switch(c) { + case 'h': + help(); + case 'o': + out_filename = optarg; + break; + case 'c': + output_type = OUTPUT_C; + break; + case 'e': + output_type = OUTPUT_C_MAIN; + break; + case 'N': + cname = optarg; + break; + case 'f': + { + const char *p; + p = optarg; + if (!strcmp(optarg, "lto")) { + use_lto = TRUE; + } else if (strstart(p, "no-", &p)) { + use_lto = TRUE; + for(i = 0; i < countof(feature_list); i++) { + if (!strcmp(p, feature_list[i].option_name)) { + feature_bitmap &= ~((uint64_t)1 << i); + break; + } + } + if (i == countof(feature_list)) + goto bad_feature; + } else +#ifdef CONFIG_BIGNUM + if (!strcmp(optarg, "bignum")) { + bignum_ext = TRUE; + } else +#endif + { + bad_feature: + fprintf(stderr, "unsupported feature: %s\n", optarg); + exit(1); + } + } + break; + case 'm': + module = 1; + break; + case 'M': + { + char *p; + char path[1024]; + char cname[1024]; + pstrcpy(path, sizeof(path), optarg); + p = strchr(path, ','); + if (p) { + *p = '\0'; + pstrcpy(cname, sizeof(cname), p + 1); + } else { + get_c_name(cname, sizeof(cname), path); + } + namelist_add(&cmodule_list, path, cname, 0); + } + break; + case 'D': + namelist_add(&dynamic_module_list, optarg, NULL, 0); + break; + case 'x': + byte_swap = TRUE; + break; + case 'v': + verbose++; + break; + case 'p': + c_ident_prefix = optarg; + break; + case 'S': + stack_size = (size_t)strtod(optarg, NULL); + break; + default: + break; + } + } + + if (optind >= argc) + help(); + + if (!out_filename) { + if (output_type == OUTPUT_EXECUTABLE) { + out_filename = "a.out"; + } else { + out_filename = "out.c"; + } + } + + if (output_type == OUTPUT_EXECUTABLE) { +#if defined(_WIN32) || defined(__ANDROID__) + /* XXX: find a /tmp directory ? */ + snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid()); +#else + snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid()); +#endif + } else { + pstrcpy(cfilename, sizeof(cfilename), out_filename); + } + + fo = fopen(cfilename, "w"); + if (!fo) { + perror(cfilename); + exit(1); + } + outfile = fo; + + rt = JS_NewRuntime(); + ctx = JS_NewContext(rt); +#ifdef CONFIG_BIGNUM + if (bignum_ext) { + JS_AddIntrinsicBigFloat(ctx); + JS_AddIntrinsicBigDecimal(ctx); + JS_AddIntrinsicOperators(ctx); + JS_EnableBignumExt(ctx, TRUE); + } +#endif + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); + + fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n" + "\n" + ); + + if (output_type != OUTPUT_C) { + fprintf(fo, "#include \"quickjs-libc.h\"\n" + "\n" + ); + } else { + fprintf(fo, "#include \n" + "\n" + ); + } + + for(i = optind; i < argc; i++) { + const char *filename = argv[i]; + compile_file(ctx, fo, filename, cname, module); + cname = NULL; + } + + 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)) && + feature_list[i].init_name) { + fprintf(fo, " JS_AddIntrinsic%s(ctx);\n", + feature_list[i].init_name); + } + } +#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 */ + + fprintf(fo, + " {\n" + " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" + " js_init_module_%s(ctx, \"%s\");\n" + " }\n", + e->short_name, e->short_name, e->name); + } + for(i = 0; i < cname_list.count; i++) { + namelist_entry_t *e = &cname_list.array[i]; + 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]; + if (!e->flags) { + fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, 0);\n", + e->name, e->name); + } + } + fputs(main_c_template2, fo); + } + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + fclose(fo); + + if (output_type == OUTPUT_EXECUTABLE) { + return output_executable(out_filename, cfilename, use_lto, verbose, + argv[0]); + } + namelist_free(&cname_list); + namelist_free(&cmodule_list); + namelist_free(&init_module_list); + return 0; +} diff --git a/qjscalc.js b/qjscalc.js index b1ad1e895..33a2f8558 100644 --- a/qjscalc.js +++ b/qjscalc.js @@ -1,2657 +1,2657 @@ -/* - * QuickJS Javascript Calculator - * - * Copyright (c) 2017-2020 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -"use strict"; -"use math"; - -var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunction, Series, Matrix; - -(function(global) { - global.Integer = global.BigInt; - global.Float = global.BigFloat; - global.algebraicMode = true; - - /* add non enumerable properties */ - function add_props(obj, props) { - var i, val, prop, tab, desc; - tab = Reflect.ownKeys(props); - for(i = 0; i < tab.length; i++) { - prop = tab[i]; - desc = Object.getOwnPropertyDescriptor(props, prop); - desc.enumerable = false; - if ("value" in desc) { - if (typeof desc.value !== "function") { - desc.writable = false; - desc.configurable = false; - } - } else { - /* getter/setter */ - desc.configurable = false; - } - Object.defineProperty(obj, prop, desc); - } - } - - /* 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 */ - - function generic_pow(a, b) { - var r, is_neg, i; - if (!Integer.isInteger(b)) { - return exp(log(a) * b); - } - if (Array.isArray(a) && !(a instanceof Polynomial || - a instanceof Series)) { - r = idn(Matrix.check_square(a)); - } else { - r = 1; - } - if (b == 0) - return r; - is_neg = false; - if (b < 0) { - is_neg = true; - b = -b; - } - r = a; - for(i = Integer.floorLog2(b) - 1; i >= 0; i--) { - r *= r; - if ((b >> i) & 1) - r *= a; - } - if (is_neg) { - if (typeof r.inverse != "function") - throw "negative powers are not supported for this type"; - r = r.inverse(); - } - return r; - } - - var small_primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499 ]; - - function miller_rabin_test(n, t) { - var d, r, s, i, j, a; - d = n - 1; - s = 0; - while ((d & 1) == 0) { - d >>= 1; - s++; - } - 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); - if (r == 1 || r == (n - 1)) - continue; - for(i = 1; i < s; i++) { - r = (r * r) % n; - if (r == 1) - return false; - if (r == (n - 1)) - continue loop; - } - return false; /* n is composite */ - } - return true; /* n is probably prime with probability (1-0.5^t) */ - } - - function fact_rec(a, b) { /* assumes a <= b */ - var i, r; - if ((b - a) <= 5) { - r = a; - for(i = a + 1; i <= b; i++) - r *= i; - return r; - } else { - /* to avoid a quadratic running time it is better to - multiply numbers of similar size */ - i = (a + b) >> 1; - return fact_rec(a, i) * fact_rec(i + 1, b); - } - } - - /* 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) { - /* integers are represented either as bigint or as number */ - return typeof a === "bigint" || - (typeof a === "number" && Number.isSafeInteger(a)); - }, - gcd(a, b) { - var r; - while (b != 0) { - r = a % b; - a = b; - b = r; - } - return a; - }, - fact(n) { - return n <= 0 ? 1 : fact_rec(1, n); - }, - /* binomial coefficient */ - comb(n, k) { - if (k < 0 || k > n) - return 0; - if (k > n - k) - k = n - k; - if (k == 0) - return 1; - return Integer.tdiv(fact_rec(n - k + 1, n), fact_rec(1, k)); - }, - /* inverse of x modulo y */ - invmod(x, y) { - var q, u, v, a, c, t; - u = x; - v = y; - c = 1; - a = 0; - while (u != 0) { - t = Integer.fdivrem(v, u); - q = t[0]; - v = u; - u = t[1]; - t = c; - c = a - q * c; - a = t; - } - /* v = gcd(x, y) */ - if (v != 1) - throw RangeError("not invertible"); - return a % y; - }, - /* return a ^ b modulo m */ - pmod(a, b, m) { - var r; - if (b == 0) - return 1; - if (b < 0) { - a = Integer.invmod(a, m); - b = -b; - } - r = 1; - for(;;) { - if (b & 1) { - r = (r * a) % m; - } - b >>= 1; - if (b == 0) - break; - a = (a * a) % m; - } - return r; - }, - - /* return true if n is prime (or probably prime with - probability 1-0.5^t) */ - isPrime(n, t) { - var i, d, n1; - if (!Integer.isInteger(n)) - throw TypeError("invalid type"); - if (n <= 1) - return false; - n1 = small_primes.length; - /* XXX: need Integer.sqrt() */ - for(i = 0; i < n1; i++) { - d = small_primes[i]; - if (d == n) - return true; - if (d > n) - return false; - if ((n % d) == 0) - return false; - } - if (n < d * d) - return true; - if (typeof t == "undefined") - t = 64; - return miller_rabin_test(n, t); - }, - nextPrime(n) { - if (!Integer.isInteger(n)) - throw TypeError("invalid type"); - if (n < 1) - n = 1; - for(;;) { - n++; - if (Integer.isPrime(n)) - return n; - } - }, - factor(n) { - var r, d; - if (!Integer.isInteger(n)) - throw TypeError("invalid type"); - r = []; - if (abs(n) <= 1) { - r.push(n); - return r; - } - if (n < 0) { - r.push(-1); - n = -n; - } - - while ((n % 2) == 0) { - n >>= 1; - r.push(2); - } - - d = 3; - while (n != 1) { - if (Integer.isPrime(n)) { - r.push(n); - break; - } - /* we are sure there is at least one divisor, so one test */ - for(;;) { - if ((n % d) == 0) - break; - d += 2; - } - for(;;) { - r.push(d); - n = Integer.tdiv(n, d); - if ((n % d) != 0) - break; - } - } - return r; - }, - }); - - add_props(Integer.prototype, { - inverse() { - return 1 / this; - }, - norm2() { - return this * this; - }, - abs() { - var v = this; - if (v < 0) - v = -v; - return v; - }, - conj() { - return this; - }, - arg() { - if (this >= 0) - return 0; - else - return Float.PI; - }, - exp() { - if (this == 0) - return 1; - else - return Float.exp(this); - }, - log() { - if (this == 1) - return 0; - else - return Float(this).log(); - }, - }); - - /* Fraction */ - - Fraction = function Fraction(a, b) - { - var d, r, obj; - - if (new.target) - throw TypeError("not a constructor"); - if (a instanceof Fraction) - return a; - if (!Integer.isInteger(a)) - throw TypeError("integer expected"); - if (typeof b === "undefined") { - b = 1; - } else { - if (!Integer.isInteger(b)) - throw TypeError("integer expected"); - if (b == 0) - throw RangeError("division by zero"); - d = Integer.gcd(a, b); - if (d != 1) { - a = Integer.tdiv(a, d); - b = Integer.tdiv(b, d); - } - - /* the fractions are normalized with den > 0 */ - if (b < 0) { - a = -a; - b = -b; - } - } - obj = Object.create(Fraction.prototype); - obj.num = a; - obj.den = b; - return obj; - } - - 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, { - /* (internal use) simplify 'a' to an integer when possible */ - toFraction(a, b) { - var r = Fraction(a, b); - if (algebraicMode && r.den == 1) - return r.num; - else - return r; - }, - }); - - add_props(Fraction.prototype, { - [Symbol.toPrimitive](hint) { - if (hint === "string") { - return this.toString(); - } else { - return Float(this.num) / this.den; - } - }, - inverse() { - return Fraction(this.den, this.num); - }, - toString() { - return this.num + "/" + this.den; - }, - norm2() { - return this * this; - }, - abs() { - if (this.num < 0) - return -this; - else - return this; - }, - conj() { - return this; - }, - arg() { - if (this.num >= 0) - return 0; - else - return Float.PI; - }, - exp() { - return Float.exp(Float(this)); - }, - log() { - return Float(this).log(); - }, - }); - - /* Number (Float64) */ - - add_props(Number.prototype, { - inverse() { - return 1 / this; - }, - norm2() { - return this * this; - }, - abs() { - return Math.abs(this); - }, - conj() { - return this; - }, - arg() { - if (this >= 0) - return 0; - else - return Float.PI; - }, - exp() { - return Float.exp(this); - }, - log() { - if (this < 0) { - return Complex(this).log(); - } else { - return Float.log(this); - } - }, - }); - - /* Float */ - - var const_tab = []; - - /* we cache the constants for small precisions */ - function get_const(n) { - var t, c, p; - t = const_tab[n]; - p = BigFloatEnv.prec; - if (t && t.prec == p) { - return t.val; - } else { - switch(n) { - case 0: c = Float.exp(1); break; - case 1: c = Float.log(10); break; -// case 2: c = Float.log(2); break; - case 3: c = 1/Float.log(2); break; - case 4: c = 1/Float.log(10); break; -// case 5: c = Float.atan(1) * 4; break; - case 6: c = Float.sqrt(0.5); break; - case 7: c = Float.sqrt(2); break; - } - if (p <= 1024) { - const_tab[n] = { prec: p, val: c }; - } - return c; - } - } - - add_props(Float, { - isFloat(a) { - return typeof a === "number" || typeof a === "bigfloat"; - }, - bestappr(u, b) { - var num1, num0, den1, den0, u, num, den, n; - - if (typeof b === "undefined") - throw TypeError("second argument expected"); - num1 = 1; - num0 = 0; - den1 = 0; - den0 = 1; - for(;;) { - n = Integer(Float.floor(u)); - num = n * num1 + num0; - den = n * den1 + den0; - if (den > b) - break; - u = 1.0 / (u - n); - num0 = num1; - num1 = num; - den0 = den1; - den1 = den; - } - return Fraction(num1, den1); - }, - /* similar constants as Math.x */ - get E() { return get_const(0); }, - get LN10() { return get_const(1); }, -// get LN2() { return get_const(2); }, - get LOG2E() { return get_const(3); }, - get LOG10E() { return get_const(4); }, -// get PI() { return get_const(5); }, - get SQRT1_2() { return get_const(6); }, - get SQRT2() { return get_const(7); }, - }); - - add_props(Float.prototype, { - inverse() { - return 1.0 / this; - }, - norm2() { - return this * this; - }, - abs() { - return Float.abs(this); - }, - conj() { - return this; - }, - arg() { - if (this >= 0) - return 0; - else - return Float.PI; - }, - exp() { - return Float.exp(this); - }, - log() { - if (this < 0) { - return Complex(this).log(); - } else { - return Float.log(this); - } - }, - }); - - /* Complex */ - - Complex = function Complex(re, im) - { - var obj; - if (new.target) - throw TypeError("not a constructor"); - if (re instanceof Complex) - return re; - if (typeof im === "undefined") { - im = 0; - } - obj = Object.create(Complex.prototype); - obj.re = re; - obj.im = im; - return obj; - } - - - 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, { - /* simplify to real number when possible */ - toComplex(re, im) { - if (algebraicMode && im == 0) - return re; - else - return Complex(re, im); - }, - }); - - add_props(Complex.prototype, { - inverse() { - var c = this.norm2(); - return Complex(this.re / c, -this.im / c); - }, - toString() { - var v, s = "", a = this; - if (a.re != 0) - s += a.re.toString(); - if (a.im == 1) { - if (s != "") - s += "+"; - s += "I"; - } else if (a.im == -1) { - s += "-I"; - } else { - v = a.im.toString(); - if (v[0] != "-" && s != "") - s += "+"; - s += v + "*I"; - } - return s; - }, - norm2() { - return this.re * this.re + this.im * this.im; - }, - abs() { - return Float.sqrt(norm2(this)); - }, - conj() { - return Complex(this.re, -this.im); - }, - arg() { - return Float.atan2(this.im, this.re); - }, - exp() { - var arg = this.im, r = this.re.exp(); - return Complex(r * cos(arg), r * sin(arg)); - }, - log() { - return Complex(abs(this).log(), atan2(this.im, this.re)); - }, - }); - - /* Mod */ - - Mod = function Mod(a, m) { - var obj, t; - if (new.target) - throw TypeError("not a constructor"); - obj = Object.create(Mod.prototype); - if (Integer.isInteger(m)) { - if (m <= 0) - throw RangeError("the modulo cannot be <= 0"); - if (Integer.isInteger(a)) { - a %= m; - } else if (a instanceof Fraction) { - return Mod(a.num, m) / a.den; - } else { - throw TypeError("invalid types"); - } - } else { - throw TypeError("invalid types"); - } - obj.res = a; - obj.mod = m; - return obj; - }; - - 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, { - inverse() { - var a = this, m = a.mod; - if (Integer.isInteger(m)) { - return Mod(Integer.invmod(a.res, m), m); - } else { - throw TypeError("unsupported type"); - } - }, - toString() { - return "Mod(" + this.res + "," + this.mod + ")"; - }, - }); - - /* 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) - throw TypeError("not a constructor"); - if (a instanceof Polynomial) { - return a; - } else if (Array.isArray(a)) { - if (a.length == 0) - a = [ 0 ]; - Object.setPrototypeOf(a, Polynomial.prototype); - return a.trim(); - } else if (polynomial_is_scalar(a)) { - a = [a]; - Object.setPrototypeOf(a, Polynomial.prototype); - return a; - } else { - throw TypeError("invalid type"); - } - } - - function number_need_paren(c) - { - return !(Integer.isInteger(c) || - Float.isFloat(c) || - c instanceof Fraction || - (c instanceof Complex && c.re == 0)); - } - - /* string for c*X^i */ - function monomial_toString(c, i) - { - var str1; - if (i == 0) { - str1 = c.toString(); - } else { - if (c == 1) { - str1 = ""; - } else if (c == -1) { - str1 = "-"; - } else { - if (number_need_paren(c)) { - str1 = "(" + c + ")"; - } else { - str1 = String(c); - } - str1 += "*"; - } - str1 += "X"; - if (i != 1) { - str1 += "^" + i; - } - } - return str1; - } - - /* find one complex root of 'p' starting from z at precision eps using - at most max_it iterations. Return null if could not find root. */ - function poly_root_laguerre1(p, z, max_it) - { - var p1, p2, i, z0, z1, z2, d, t0, t1, d1, d2, e, el, zl; - - d = p.deg(); - if (d == 1) { - /* monomial case */ - return -p[0] / p[1]; - } - /* trivial zero */ - if (p[0] == 0) - return 0.0; - - p1 = p.deriv(); - p2 = p1.deriv(); - el = 0.0; - zl = 0.0; - for(i = 0; i < max_it; i++) { - z0 = p.apply(z); - if (z0 == 0) - return z; /* simple exit case */ - - /* Ward stopping criteria */ - e = abs(z - zl); -// print("e", i, e); - if (i >= 2 && e >= el) { - if (abs(zl) < 1e-4) { - if (e < 1e-7) - return zl; - } else { - if (e < abs(zl) * 1e-3) - return zl; - } - } - el = e; - zl = z; - - z1 = p1.apply(z); - z2 = p2.apply(z); - t0 = (d - 1) * z1; - t0 = t0 * t0; - t1 = d * (d - 1) * z0 * z2; - t0 = sqrt(t0 - t1); - d1 = z1 + t0; - d2 = z1 - t0; - if (norm2(d2) > norm2(d1)) - d1 = d2; - if (d1 == 0) - return null; - z = z - d * z0 / d1; - } - return null; - } - - function poly_roots(p) - { - var d, i, roots, j, z, eps; - var start_points = [ 0.1, -1.4, 1.7 ]; - - if (!(p instanceof Polynomial)) - throw TypeError("polynomial expected"); - d = p.deg(); - if (d <= 0) - return []; - eps = 2.0 ^ (-BigFloatEnv.prec); - roots = []; - for(i = 0; i < d; i++) { - /* XXX: should select another start point if error */ - for(j = 0; j < 3; j++) { - z = poly_root_laguerre1(p, start_points[j], 100); - if (z !== null) - break; - } - if (j == 3) - throw RangeError("error in root finding algorithm"); - roots[i] = z; - p = Polynomial.divrem(p, X - z)[0]; - } - return roots; - } - - add_props(Polynomial.prototype, { - trim() { - var a = this, i; - i = a.length; - while (i > 1 && a[i - 1] == 0) - i--; - a.length = i; - return a; - }, - conj() { - var r, i, n, a; - a = this; - n = a.length; - r = []; - for(i = 0; i < n; i++) - r[i] = a[i].conj(); - return Polynomial(r); - }, - inverse() { - return RationalFunction(Polynomial([1]), this); - }, - toString() { - var i, str, str1, c, a = this; - if (a.length == 1) { - return a[0].toString(); - } - str=""; - for(i = a.length - 1; i >= 0; i--) { - c = a[i]; - if (c == 0 || - (c instanceof Mod) && c.res == 0) - continue; - str1 = monomial_toString(c, i); - if (str1[0] != "-") { - if (str != "") - str += "+"; - } - str += str1; - } - return str; - }, - deg() { - if (this.length == 1 && this[0] == 0) - return -Infinity; - else - return this.length - 1; - }, - apply(b) { - var i, n, r, a = this; - n = a.length - 1; - r = a[n]; - while (n > 0) { - n--; - r = r * b + a[n]; - } - return r; - }, - deriv() { - var a = this, n, r, i; - n = a.length; - if (n == 1) { - return Polynomial(0); - } else { - r = []; - for(i = 1; i < n; i++) { - r[i - 1] = i * a[i]; - } - return Polynomial(r); - } - }, - integ() { - var a = this, n, r, i; - n = a.length; - r = [0]; - for(i = 0; i < n; i++) { - r[i + 1] = a[i] / (i + 1); - } - return Polynomial(r); - }, - norm2() { - var a = this, n, r, i; - n = a.length; - r = 0; - for(i = 0; i < n; i++) { - r += a[i].norm2(); - } - return r; - }, - }); - - - 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, { - divrem(a, b) { - var n1, n2, i, j, q, r, n, c; - if (b.deg() < 0) - throw RangeError("division by zero"); - n1 = a.length; - n2 = b.length; - if (n1 < n2) - return [Polynomial([0]), a]; - r = Array.prototype.dup.call(a); - q = []; - n2--; - n = n1 - n2; - for(i = 0; i < n; i++) - q[i] = 0; - for(i = n - 1; i >= 0; i--) { - c = r[i + n2]; - if (c != 0) { - c = c / b[n2]; - r[i + n2] = 0; - for(j = 0; j < n2; j++) { - r[i + j] -= b[j] * c; - } - q[i] = c; - } - } - return [Polynomial(q), Polynomial(r)]; - }, - gcd(a, b) { - var t; - while (b.deg() >= 0) { - t = Polynomial.divrem(a, b); - a = b; - b = t[1]; - } - /* convert to monic form */ - return a / a[a.length - 1]; - }, - invmod(x, y) { - var q, u, v, a, c, t; - u = x; - v = y; - c = Polynomial([1]); - a = Polynomial([0]); - while (u.deg() >= 0) { - t = Polynomial.divrem(v, u); - q = t[0]; - v = u; - u = t[1]; - t = c; - c = a - q * c; - a = t; - } - /* v = gcd(x, y) */ - if (v.deg() > 0) - throw RangeError("not invertible"); - return Polynomial.divrem(a, y)[1]; - }, - roots(p) { - return poly_roots(p); - } - }); - - /* Polynomial Modulo Q */ - - PolyMod = function PolyMod(a, m) { - var obj, t; - if (new.target) - throw TypeError("not a constructor"); - obj = Object.create(PolyMod.prototype); - if (m instanceof Polynomial) { - if (m.deg() <= 0) - throw RangeError("the modulo cannot have a degree <= 0"); - if (a instanceof RationalFunction) { - return PolyMod(a.num, m) / a.den; - } else { - a = Polynomial(a); - t = Polynomial.divrem(a, m); - a = t[1]; - } - } else { - throw TypeError("invalid types"); - } - obj.res = a; - obj.mod = m; - return obj; - }; - - 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, { - inverse() { - var a = this, m = a.mod; - if (m instanceof Polynomial) { - return PolyMod(Polynomial.invmod(a.res, m), m); - } else { - throw TypeError("unsupported type"); - } - }, - toString() { - return "PolyMod(" + this.res + "," + this.mod + ")"; - }, - }); - - /* Rational function */ - - RationalFunction = function RationalFunction(a, b) - { - var t, r, d, obj; - if (new.target) - throw TypeError("not a constructor"); - if (!(a instanceof Polynomial) || - !(b instanceof Polynomial)) - throw TypeError("polynomial expected"); - t = Polynomial.divrem(a, b); - r = t[1]; - if (r.deg() < 0) - return t[0]; /* no need for a fraction */ - d = Polynomial.gcd(b, r); - if (d.deg() > 0) { - a = Polynomial.divrem(a, d)[0]; - b = Polynomial.divrem(b, d)[0]; - } - obj = Object.create(RationalFunction.prototype); - obj.num = a; - obj.den = b; - return obj; - } - - add_props(RationalFunction.prototype, { - inverse() { - return RationalFunction(this.den, this.num); - }, - conj() { - return RationalFunction(this.num.conj(), this.den.conj()); - }, - toString() { - var str; - if (this.num.deg() <= 0 && - !number_need_paren(this.num[0])) - str = this.num.toString(); - else - str = "(" + this.num.toString() + ")"; - str += "/(" + this.den.toString() + ")" - return str; - }, - apply(b) { - return this.num.apply(b) / this.den.apply(b); - }, - deriv() { - var n = this.num, d = this.den; - return RationalFunction(n.deriv() * d - n * d.deriv(), d * d); - }, - }); - - 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, { - /* This function always return a RationalFunction object even - if it could simplified to a polynomial, so it is not - equivalent to RationalFunction(a) */ - toRationalFunction(a) { - var obj; - if (a instanceof RationalFunction) { - return a; - } else { - obj = Object.create(RationalFunction.prototype); - obj.num = Polynomial(a); - obj.den = Polynomial(1); - return obj; - } - }, - }); - - /* Power series */ - - /* 'a' is an array */ - function get_emin(a) { - var i, n; - n = a.length; - for(i = 0; i < n; i++) { - if (a[i] != 0) - return i; - } - return n; - }; - - 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) { - var emin, r, i; - - if (a instanceof Series) { - return a; - } else if (series_is_scalar_or_polynomial(a)) { - if (n <= 0) { - /* XXX: should still use the polynomial degree */ - return Series.zero(0, 0); - } else { - a = Polynomial(a); - emin = get_emin(a); - r = Series.zero(n, emin); - n = Math.min(a.length - emin, n); - for(i = 0; i < n; i++) - r[i] = a[i + emin]; - return r; - } - } else if (a instanceof RationalFunction) { - return Series(a.num, n) / a.den; - } else { - throw TypeError("invalid type"); - } - }; - - 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 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; - obj = Series.zero(this.length, this.emin); - for(i = 0; i < n; i++) { - obj[i] = this[i].conj(); - } - return obj; - }, - inverse() { - var r, n, i, j, sum, v1 = this; - n = v1.length; - if (n == 0) - throw RangeError("division by zero"); - r = Series.zero(n, -v1.emin); - r[0] = 1 / v1[0]; - for(i = 1; i < n; i++) { - sum = 0; - for(j = 1; j <= i; j++) { - sum += v1[j] * r[i - j]; - } - r[i] = -sum * r[0]; - } - return r; - }, - /* remove leading zero terms */ - trim() { - var i, j, n, r, v1 = this; - n = v1.length; - i = 0; - while (i < n && v1[i] == 0) - i++; - if (i == 0) - return v1; - for(j = i; j < n; j++) - v1[j - i] = v1[j]; - v1.length = n - i; - v1.__proto__.emin += i; - return v1; - }, - toString() { - var i, j, str, str1, c, a = this, emin, n; - str=""; - emin = this.emin; - n = this.length; - for(j = 0; j < n; j++) { - i = j + emin; - c = a[j]; - if (c != 0) { - str1 = monomial_toString(c, i); - if (str1[0] != "-") { - if (str != "") - str += "+"; - } - str += str1; - } - } - if (str != "") - str += "+"; - str += "O(" + monomial_toString(1, n + emin) + ")"; - return str; - }, - apply(b) { - var i, n, r, a = this; - n = a.length; - if (n == 0) - return 0; - r = a[--n]; - while (n > 0) { - n--; - r = r * b + a[n]; - } - if (a.emin != 0) - r *= b ^ a.emin; - return r; - }, - deriv() { - var a = this, n = a.length, emin = a.emin, r, i, j; - if (n == 0 && emin == 0) { - return Series.zero(0, 0); - } else { - r = Series.zero(n, emin - 1); - for(i = 0; i < n; i++) { - j = emin + i; - if (j == 0) - r[i] = 0; - else - r[i] = j * a[i]; - } - return r.trim(); - } - }, - integ() { - var a = this, n = a.length, emin = a.emin, i, j, r; - r = Series.zero(n, emin + 1); - for(i = 0; i < n; i++) { - j = emin + i; - if (j == -1) { - if (a[i] != 0) - throw RangeError("cannot represent integ(1/X)"); - } else { - r[i] = a[i] / (j + 1); - } - } - return r.trim(); - }, - exp() { - var c, i, r, n, a = this; - if (a.emin < 0) - throw RangeError("negative exponent in exp"); - n = a.emin + a.length; - if (a.emin > 0 || a[0] == 0) { - c = 1; - } else { - c = global.exp(a[0]); - a -= a[0]; - } - r = Series.zero(n, 0); - for(i = 0; i < n; i++) { - r[i] = c / fact(i); - } - return r.apply(a); - }, - log() { - var a = this, r; - if (a.emin != 0) - throw RangeError("log argument must have a non zero constant term"); - r = integ(deriv(a) / a); - /* add the constant term */ - r += global.log(a[0]); - return r; - }, - }); - - add_props(Series, { - /* new series of length n and first exponent emin */ - zero(n, emin) { - var r, i, obj; - - r = []; - for(i = 0; i < n; i++) - r[i] = 0; - /* we return an array and store emin in its prototype */ - obj = Object.create(Series.prototype); - obj.emin = emin; - Object.setPrototypeOf(r, obj); - return r; - }, - O(a) { - function ErrorO() { - return TypeError("invalid O() argument"); - } - var n; - if (series_is_scalar_or_polynomial(a)) { - a = Polynomial(a); - n = a.deg(); - if (n < 0) - throw ErrorO(); - } else if (a instanceof RationalFunction) { - if (a.num.deg() != 0) - throw ErrorO(); - n = a.den.deg(); - if (n < 0) - throw ErrorO(); - n = -n; - } else - throw ErrorO(); - return Series.zero(0, n); - }, - }); - - /* Array (Matrix) */ - - Matrix = function Matrix(h, w) { - var i, j, r, rl; - if (typeof w === "undefined") - w = h; - r = []; - for(i = 0; i < h; i++) { - rl = []; - for(j = 0; j < w; j++) - rl[j] = 0; - r[i] = rl; - } - return r; - }; - - add_props(Matrix, { - idn(n) { - var r, i; - r = Matrix(n, n); - for(i = 0; i < n; i++) - r[i][i] = 1; - return r; - }, - diag(a) { - var r, i, n; - n = a.length; - r = Matrix(n, n); - for(i = 0; i < n; i++) - r[i][i] = a[i]; - return r; - }, - hilbert(n) { - var i, j, r; - r = Matrix(n); - for(i = 0; i < n; i++) { - for(j = 0; j < n; j++) { - r[i][j] = 1 / (1 + i + j); - } - } - return r; - }, - trans(a) { - var h, w, r, i, j; - if (!Array.isArray(a)) - throw TypeError("matrix expected"); - h = a.length; - if (!Array.isArray(a[0])) { - w = 1; - r = Matrix(w, h); - for(i = 0; i < h; i++) { - r[0][i] = a[i]; - } - } else { - w = a[0].length; - r = Matrix(w, h); - for(i = 0; i < h; i++) { - for(j = 0; j < w; j++) { - r[j][i] = a[i][j]; - } - } - } - return r; - }, - check_square(a) { - var a, n; - if (!Array.isArray(a)) - throw TypeError("array expected"); - n = a.length; - if (!Array.isArray(a[0]) || n != a[0].length) - throw TypeError("square matrix expected"); - return n; - }, - trace(a) { - var n, r, i; - n = Matrix.check_square(a); - r = a[0][0]; - for(i = 1; i < n; i++) { - r += a[i][i]; - } - return r; - }, - charpoly(a) { - var n, p, c, i, j, coef; - n = Matrix.check_square(a); - p = []; - for(i = 0; i < n + 1; i++) - p[i] = 0; - p[n] = 1; - c = Matrix.idn(n); - for(i = 0; i < n; i++) { - c = c * a; - coef = -trace(c) / (i + 1); - p[n - i - 1] = coef; - for(j = 0; j < n; j++) - c[j][j] += coef; - } - return Polynomial(p); - }, - eigenvals(a) { - return Polynomial.roots(Matrix.charpoly(a)); - }, - det(a) { - var n, i, j, k, s, src, v, c; - - n = Matrix.check_square(a); - s = 1; - src = a.dup(); - for(i=0;i= 0; i--) { + r *= r; + if ((b >> i) & 1) + r *= a; + } + if (is_neg) { + if (typeof r.inverse != "function") + throw "negative powers are not supported for this type"; + r = r.inverse(); + } + return r; + } + + var small_primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499 ]; + + function miller_rabin_test(n, t) { + var d, r, s, i, j, a; + d = n - 1; + s = 0; + while ((d & 1) == 0) { + d >>= 1; + s++; + } + 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); + if (r == 1 || r == (n - 1)) + continue; + for(i = 1; i < s; i++) { + r = (r * r) % n; + if (r == 1) + return false; + if (r == (n - 1)) + continue loop; + } + return false; /* n is composite */ + } + return true; /* n is probably prime with probability (1-0.5^t) */ + } + + function fact_rec(a, b) { /* assumes a <= b */ + var i, r; + if ((b - a) <= 5) { + r = a; + for(i = a + 1; i <= b; i++) + r *= i; + return r; + } else { + /* to avoid a quadratic running time it is better to + multiply numbers of similar size */ + i = (a + b) >> 1; + return fact_rec(a, i) * fact_rec(i + 1, b); + } + } + + /* 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) { + /* integers are represented either as bigint or as number */ + return typeof a === "bigint" || + (typeof a === "number" && Number.isSafeInteger(a)); + }, + gcd(a, b) { + var r; + while (b != 0) { + r = a % b; + a = b; + b = r; + } + return a; + }, + fact(n) { + return n <= 0 ? 1 : fact_rec(1, n); + }, + /* binomial coefficient */ + comb(n, k) { + if (k < 0 || k > n) + return 0; + if (k > n - k) + k = n - k; + if (k == 0) + return 1; + return Integer.tdiv(fact_rec(n - k + 1, n), fact_rec(1, k)); + }, + /* inverse of x modulo y */ + invmod(x, y) { + var q, u, v, a, c, t; + u = x; + v = y; + c = 1; + a = 0; + while (u != 0) { + t = Integer.fdivrem(v, u); + q = t[0]; + v = u; + u = t[1]; + t = c; + c = a - q * c; + a = t; + } + /* v = gcd(x, y) */ + if (v != 1) + throw RangeError("not invertible"); + return a % y; + }, + /* return a ^ b modulo m */ + pmod(a, b, m) { + var r; + if (b == 0) + return 1; + if (b < 0) { + a = Integer.invmod(a, m); + b = -b; + } + r = 1; + for(;;) { + if (b & 1) { + r = (r * a) % m; + } + b >>= 1; + if (b == 0) + break; + a = (a * a) % m; + } + return r; + }, + + /* return true if n is prime (or probably prime with + probability 1-0.5^t) */ + isPrime(n, t) { + var i, d, n1; + if (!Integer.isInteger(n)) + throw TypeError("invalid type"); + if (n <= 1) + return false; + n1 = small_primes.length; + /* XXX: need Integer.sqrt() */ + for(i = 0; i < n1; i++) { + d = small_primes[i]; + if (d == n) + return true; + if (d > n) + return false; + if ((n % d) == 0) + return false; + } + if (n < d * d) + return true; + if (typeof t == "undefined") + t = 64; + return miller_rabin_test(n, t); + }, + nextPrime(n) { + if (!Integer.isInteger(n)) + throw TypeError("invalid type"); + if (n < 1) + n = 1; + for(;;) { + n++; + if (Integer.isPrime(n)) + return n; + } + }, + factor(n) { + var r, d; + if (!Integer.isInteger(n)) + throw TypeError("invalid type"); + r = []; + if (abs(n) <= 1) { + r.push(n); + return r; + } + if (n < 0) { + r.push(-1); + n = -n; + } + + while ((n % 2) == 0) { + n >>= 1; + r.push(2); + } + + d = 3; + while (n != 1) { + if (Integer.isPrime(n)) { + r.push(n); + break; + } + /* we are sure there is at least one divisor, so one test */ + for(;;) { + if ((n % d) == 0) + break; + d += 2; + } + for(;;) { + r.push(d); + n = Integer.tdiv(n, d); + if ((n % d) != 0) + break; + } + } + return r; + }, + }); + + add_props(Integer.prototype, { + inverse() { + return 1 / this; + }, + norm2() { + return this * this; + }, + abs() { + var v = this; + if (v < 0) + v = -v; + return v; + }, + conj() { + return this; + }, + arg() { + if (this >= 0) + return 0; + else + return Float.PI; + }, + exp() { + if (this == 0) + return 1; + else + return Float.exp(this); + }, + log() { + if (this == 1) + return 0; + else + return Float(this).log(); + }, + }); + + /* Fraction */ + + Fraction = function Fraction(a, b) + { + var d, r, obj; + + if (new.target) + throw TypeError("not a constructor"); + if (a instanceof Fraction) + return a; + if (!Integer.isInteger(a)) + throw TypeError("integer expected"); + if (typeof b === "undefined") { + b = 1; + } else { + if (!Integer.isInteger(b)) + throw TypeError("integer expected"); + if (b == 0) + throw RangeError("division by zero"); + d = Integer.gcd(a, b); + if (d != 1) { + a = Integer.tdiv(a, d); + b = Integer.tdiv(b, d); + } + + /* the fractions are normalized with den > 0 */ + if (b < 0) { + a = -a; + b = -b; + } + } + obj = Object.create(Fraction.prototype); + obj.num = a; + obj.den = b; + return obj; + } + + 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, { + /* (internal use) simplify 'a' to an integer when possible */ + toFraction(a, b) { + var r = Fraction(a, b); + if (algebraicMode && r.den == 1) + return r.num; + else + return r; + }, + }); + + add_props(Fraction.prototype, { + [Symbol.toPrimitive](hint) { + if (hint === "string") { + return this.toString(); + } else { + return Float(this.num) / this.den; + } + }, + inverse() { + return Fraction(this.den, this.num); + }, + toString() { + return this.num + "/" + this.den; + }, + norm2() { + return this * this; + }, + abs() { + if (this.num < 0) + return -this; + else + return this; + }, + conj() { + return this; + }, + arg() { + if (this.num >= 0) + return 0; + else + return Float.PI; + }, + exp() { + return Float.exp(Float(this)); + }, + log() { + return Float(this).log(); + }, + }); + + /* Number (Float64) */ + + add_props(Number.prototype, { + inverse() { + return 1 / this; + }, + norm2() { + return this * this; + }, + abs() { + return Math.abs(this); + }, + conj() { + return this; + }, + arg() { + if (this >= 0) + return 0; + else + return Float.PI; + }, + exp() { + return Float.exp(this); + }, + log() { + if (this < 0) { + return Complex(this).log(); + } else { + return Float.log(this); + } + }, + }); + + /* Float */ + + var const_tab = []; + + /* we cache the constants for small precisions */ + function get_const(n) { + var t, c, p; + t = const_tab[n]; + p = BigFloatEnv.prec; + if (t && t.prec == p) { + return t.val; + } else { + switch(n) { + case 0: c = Float.exp(1); break; + case 1: c = Float.log(10); break; +// case 2: c = Float.log(2); break; + case 3: c = 1/Float.log(2); break; + case 4: c = 1/Float.log(10); break; +// case 5: c = Float.atan(1) * 4; break; + case 6: c = Float.sqrt(0.5); break; + case 7: c = Float.sqrt(2); break; + } + if (p <= 1024) { + const_tab[n] = { prec: p, val: c }; + } + return c; + } + } + + add_props(Float, { + isFloat(a) { + return typeof a === "number" || typeof a === "bigfloat"; + }, + bestappr(u, b) { + var num1, num0, den1, den0, u, num, den, n; + + if (typeof b === "undefined") + throw TypeError("second argument expected"); + num1 = 1; + num0 = 0; + den1 = 0; + den0 = 1; + for(;;) { + n = Integer(Float.floor(u)); + num = n * num1 + num0; + den = n * den1 + den0; + if (den > b) + break; + u = 1.0 / (u - n); + num0 = num1; + num1 = num; + den0 = den1; + den1 = den; + } + return Fraction(num1, den1); + }, + /* similar constants as Math.x */ + get E() { return get_const(0); }, + get LN10() { return get_const(1); }, +// get LN2() { return get_const(2); }, + get LOG2E() { return get_const(3); }, + get LOG10E() { return get_const(4); }, +// get PI() { return get_const(5); }, + get SQRT1_2() { return get_const(6); }, + get SQRT2() { return get_const(7); }, + }); + + add_props(Float.prototype, { + inverse() { + return 1.0 / this; + }, + norm2() { + return this * this; + }, + abs() { + return Float.abs(this); + }, + conj() { + return this; + }, + arg() { + if (this >= 0) + return 0; + else + return Float.PI; + }, + exp() { + return Float.exp(this); + }, + log() { + if (this < 0) { + return Complex(this).log(); + } else { + return Float.log(this); + } + }, + }); + + /* Complex */ + + Complex = function Complex(re, im) + { + var obj; + if (new.target) + throw TypeError("not a constructor"); + if (re instanceof Complex) + return re; + if (typeof im === "undefined") { + im = 0; + } + obj = Object.create(Complex.prototype); + obj.re = re; + obj.im = im; + return obj; + } + + + 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, { + /* simplify to real number when possible */ + toComplex(re, im) { + if (algebraicMode && im == 0) + return re; + else + return Complex(re, im); + }, + }); + + add_props(Complex.prototype, { + inverse() { + var c = this.norm2(); + return Complex(this.re / c, -this.im / c); + }, + toString() { + var v, s = "", a = this; + if (a.re != 0) + s += a.re.toString(); + if (a.im == 1) { + if (s != "") + s += "+"; + s += "I"; + } else if (a.im == -1) { + s += "-I"; + } else { + v = a.im.toString(); + if (v[0] != "-" && s != "") + s += "+"; + s += v + "*I"; + } + return s; + }, + norm2() { + return this.re * this.re + this.im * this.im; + }, + abs() { + return Float.sqrt(norm2(this)); + }, + conj() { + return Complex(this.re, -this.im); + }, + arg() { + return Float.atan2(this.im, this.re); + }, + exp() { + var arg = this.im, r = this.re.exp(); + return Complex(r * cos(arg), r * sin(arg)); + }, + log() { + return Complex(abs(this).log(), atan2(this.im, this.re)); + }, + }); + + /* Mod */ + + Mod = function Mod(a, m) { + var obj, t; + if (new.target) + throw TypeError("not a constructor"); + obj = Object.create(Mod.prototype); + if (Integer.isInteger(m)) { + if (m <= 0) + throw RangeError("the modulo cannot be <= 0"); + if (Integer.isInteger(a)) { + a %= m; + } else if (a instanceof Fraction) { + return Mod(a.num, m) / a.den; + } else { + throw TypeError("invalid types"); + } + } else { + throw TypeError("invalid types"); + } + obj.res = a; + obj.mod = m; + return obj; + }; + + 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, { + inverse() { + var a = this, m = a.mod; + if (Integer.isInteger(m)) { + return Mod(Integer.invmod(a.res, m), m); + } else { + throw TypeError("unsupported type"); + } + }, + toString() { + return "Mod(" + this.res + "," + this.mod + ")"; + }, + }); + + /* 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) + throw TypeError("not a constructor"); + if (a instanceof Polynomial) { + return a; + } else if (Array.isArray(a)) { + if (a.length == 0) + a = [ 0 ]; + Object.setPrototypeOf(a, Polynomial.prototype); + return a.trim(); + } else if (polynomial_is_scalar(a)) { + a = [a]; + Object.setPrototypeOf(a, Polynomial.prototype); + return a; + } else { + throw TypeError("invalid type"); + } + } + + function number_need_paren(c) + { + return !(Integer.isInteger(c) || + Float.isFloat(c) || + c instanceof Fraction || + (c instanceof Complex && c.re == 0)); + } + + /* string for c*X^i */ + function monomial_toString(c, i) + { + var str1; + if (i == 0) { + str1 = c.toString(); + } else { + if (c == 1) { + str1 = ""; + } else if (c == -1) { + str1 = "-"; + } else { + if (number_need_paren(c)) { + str1 = "(" + c + ")"; + } else { + str1 = String(c); + } + str1 += "*"; + } + str1 += "X"; + if (i != 1) { + str1 += "^" + i; + } + } + return str1; + } + + /* find one complex root of 'p' starting from z at precision eps using + at most max_it iterations. Return null if could not find root. */ + function poly_root_laguerre1(p, z, max_it) + { + var p1, p2, i, z0, z1, z2, d, t0, t1, d1, d2, e, el, zl; + + d = p.deg(); + if (d == 1) { + /* monomial case */ + return -p[0] / p[1]; + } + /* trivial zero */ + if (p[0] == 0) + return 0.0; + + p1 = p.deriv(); + p2 = p1.deriv(); + el = 0.0; + zl = 0.0; + for(i = 0; i < max_it; i++) { + z0 = p.apply(z); + if (z0 == 0) + return z; /* simple exit case */ + + /* Ward stopping criteria */ + e = abs(z - zl); +// print("e", i, e); + if (i >= 2 && e >= el) { + if (abs(zl) < 1e-4) { + if (e < 1e-7) + return zl; + } else { + if (e < abs(zl) * 1e-3) + return zl; + } + } + el = e; + zl = z; + + z1 = p1.apply(z); + z2 = p2.apply(z); + t0 = (d - 1) * z1; + t0 = t0 * t0; + t1 = d * (d - 1) * z0 * z2; + t0 = sqrt(t0 - t1); + d1 = z1 + t0; + d2 = z1 - t0; + if (norm2(d2) > norm2(d1)) + d1 = d2; + if (d1 == 0) + return null; + z = z - d * z0 / d1; + } + return null; + } + + function poly_roots(p) + { + var d, i, roots, j, z, eps; + var start_points = [ 0.1, -1.4, 1.7 ]; + + if (!(p instanceof Polynomial)) + throw TypeError("polynomial expected"); + d = p.deg(); + if (d <= 0) + return []; + eps = 2.0 ^ (-BigFloatEnv.prec); + roots = []; + for(i = 0; i < d; i++) { + /* XXX: should select another start point if error */ + for(j = 0; j < 3; j++) { + z = poly_root_laguerre1(p, start_points[j], 100); + if (z !== null) + break; + } + if (j == 3) + throw RangeError("error in root finding algorithm"); + roots[i] = z; + p = Polynomial.divrem(p, X - z)[0]; + } + return roots; + } + + add_props(Polynomial.prototype, { + trim() { + var a = this, i; + i = a.length; + while (i > 1 && a[i - 1] == 0) + i--; + a.length = i; + return a; + }, + conj() { + var r, i, n, a; + a = this; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = a[i].conj(); + return Polynomial(r); + }, + inverse() { + return RationalFunction(Polynomial([1]), this); + }, + toString() { + var i, str, str1, c, a = this; + if (a.length == 1) { + return a[0].toString(); + } + str=""; + for(i = a.length - 1; i >= 0; i--) { + c = a[i]; + if (c == 0 || + (c instanceof Mod) && c.res == 0) + continue; + str1 = monomial_toString(c, i); + if (str1[0] != "-") { + if (str != "") + str += "+"; + } + str += str1; + } + return str; + }, + deg() { + if (this.length == 1 && this[0] == 0) + return -Infinity; + else + return this.length - 1; + }, + apply(b) { + var i, n, r, a = this; + n = a.length - 1; + r = a[n]; + while (n > 0) { + n--; + r = r * b + a[n]; + } + return r; + }, + deriv() { + var a = this, n, r, i; + n = a.length; + if (n == 1) { + return Polynomial(0); + } else { + r = []; + for(i = 1; i < n; i++) { + r[i - 1] = i * a[i]; + } + return Polynomial(r); + } + }, + integ() { + var a = this, n, r, i; + n = a.length; + r = [0]; + for(i = 0; i < n; i++) { + r[i + 1] = a[i] / (i + 1); + } + return Polynomial(r); + }, + norm2() { + var a = this, n, r, i; + n = a.length; + r = 0; + for(i = 0; i < n; i++) { + r += a[i].norm2(); + } + return r; + }, + }); + + + 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, { + divrem(a, b) { + var n1, n2, i, j, q, r, n, c; + if (b.deg() < 0) + throw RangeError("division by zero"); + n1 = a.length; + n2 = b.length; + if (n1 < n2) + return [Polynomial([0]), a]; + r = Array.prototype.dup.call(a); + q = []; + n2--; + n = n1 - n2; + for(i = 0; i < n; i++) + q[i] = 0; + for(i = n - 1; i >= 0; i--) { + c = r[i + n2]; + if (c != 0) { + c = c / b[n2]; + r[i + n2] = 0; + for(j = 0; j < n2; j++) { + r[i + j] -= b[j] * c; + } + q[i] = c; + } + } + return [Polynomial(q), Polynomial(r)]; + }, + gcd(a, b) { + var t; + while (b.deg() >= 0) { + t = Polynomial.divrem(a, b); + a = b; + b = t[1]; + } + /* convert to monic form */ + return a / a[a.length - 1]; + }, + invmod(x, y) { + var q, u, v, a, c, t; + u = x; + v = y; + c = Polynomial([1]); + a = Polynomial([0]); + while (u.deg() >= 0) { + t = Polynomial.divrem(v, u); + q = t[0]; + v = u; + u = t[1]; + t = c; + c = a - q * c; + a = t; + } + /* v = gcd(x, y) */ + if (v.deg() > 0) + throw RangeError("not invertible"); + return Polynomial.divrem(a, y)[1]; + }, + roots(p) { + return poly_roots(p); + } + }); + + /* Polynomial Modulo Q */ + + PolyMod = function PolyMod(a, m) { + var obj, t; + if (new.target) + throw TypeError("not a constructor"); + obj = Object.create(PolyMod.prototype); + if (m instanceof Polynomial) { + if (m.deg() <= 0) + throw RangeError("the modulo cannot have a degree <= 0"); + if (a instanceof RationalFunction) { + return PolyMod(a.num, m) / a.den; + } else { + a = Polynomial(a); + t = Polynomial.divrem(a, m); + a = t[1]; + } + } else { + throw TypeError("invalid types"); + } + obj.res = a; + obj.mod = m; + return obj; + }; + + 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, { + inverse() { + var a = this, m = a.mod; + if (m instanceof Polynomial) { + return PolyMod(Polynomial.invmod(a.res, m), m); + } else { + throw TypeError("unsupported type"); + } + }, + toString() { + return "PolyMod(" + this.res + "," + this.mod + ")"; + }, + }); + + /* Rational function */ + + RationalFunction = function RationalFunction(a, b) + { + var t, r, d, obj; + if (new.target) + throw TypeError("not a constructor"); + if (!(a instanceof Polynomial) || + !(b instanceof Polynomial)) + throw TypeError("polynomial expected"); + t = Polynomial.divrem(a, b); + r = t[1]; + if (r.deg() < 0) + return t[0]; /* no need for a fraction */ + d = Polynomial.gcd(b, r); + if (d.deg() > 0) { + a = Polynomial.divrem(a, d)[0]; + b = Polynomial.divrem(b, d)[0]; + } + obj = Object.create(RationalFunction.prototype); + obj.num = a; + obj.den = b; + return obj; + } + + add_props(RationalFunction.prototype, { + inverse() { + return RationalFunction(this.den, this.num); + }, + conj() { + return RationalFunction(this.num.conj(), this.den.conj()); + }, + toString() { + var str; + if (this.num.deg() <= 0 && + !number_need_paren(this.num[0])) + str = this.num.toString(); + else + str = "(" + this.num.toString() + ")"; + str += "/(" + this.den.toString() + ")" + return str; + }, + apply(b) { + return this.num.apply(b) / this.den.apply(b); + }, + deriv() { + var n = this.num, d = this.den; + return RationalFunction(n.deriv() * d - n * d.deriv(), d * d); + }, + }); + + 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, { + /* This function always return a RationalFunction object even + if it could simplified to a polynomial, so it is not + equivalent to RationalFunction(a) */ + toRationalFunction(a) { + var obj; + if (a instanceof RationalFunction) { + return a; + } else { + obj = Object.create(RationalFunction.prototype); + obj.num = Polynomial(a); + obj.den = Polynomial(1); + return obj; + } + }, + }); + + /* Power series */ + + /* 'a' is an array */ + function get_emin(a) { + var i, n; + n = a.length; + for(i = 0; i < n; i++) { + if (a[i] != 0) + return i; + } + return n; + }; + + 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) { + var emin, r, i; + + if (a instanceof Series) { + return a; + } else if (series_is_scalar_or_polynomial(a)) { + if (n <= 0) { + /* XXX: should still use the polynomial degree */ + return Series.zero(0, 0); + } else { + a = Polynomial(a); + emin = get_emin(a); + r = Series.zero(n, emin); + n = Math.min(a.length - emin, n); + for(i = 0; i < n; i++) + r[i] = a[i + emin]; + return r; + } + } else if (a instanceof RationalFunction) { + return Series(a.num, n) / a.den; + } else { + throw TypeError("invalid type"); + } + }; + + 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 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; + obj = Series.zero(this.length, this.emin); + for(i = 0; i < n; i++) { + obj[i] = this[i].conj(); + } + return obj; + }, + inverse() { + var r, n, i, j, sum, v1 = this; + n = v1.length; + if (n == 0) + throw RangeError("division by zero"); + r = Series.zero(n, -v1.emin); + r[0] = 1 / v1[0]; + for(i = 1; i < n; i++) { + sum = 0; + for(j = 1; j <= i; j++) { + sum += v1[j] * r[i - j]; + } + r[i] = -sum * r[0]; + } + return r; + }, + /* remove leading zero terms */ + trim() { + var i, j, n, r, v1 = this; + n = v1.length; + i = 0; + while (i < n && v1[i] == 0) + i++; + if (i == 0) + return v1; + for(j = i; j < n; j++) + v1[j - i] = v1[j]; + v1.length = n - i; + v1.__proto__.emin += i; + return v1; + }, + toString() { + var i, j, str, str1, c, a = this, emin, n; + str=""; + emin = this.emin; + n = this.length; + for(j = 0; j < n; j++) { + i = j + emin; + c = a[j]; + if (c != 0) { + str1 = monomial_toString(c, i); + if (str1[0] != "-") { + if (str != "") + str += "+"; + } + str += str1; + } + } + if (str != "") + str += "+"; + str += "O(" + monomial_toString(1, n + emin) + ")"; + return str; + }, + apply(b) { + var i, n, r, a = this; + n = a.length; + if (n == 0) + return 0; + r = a[--n]; + while (n > 0) { + n--; + r = r * b + a[n]; + } + if (a.emin != 0) + r *= b ^ a.emin; + return r; + }, + deriv() { + var a = this, n = a.length, emin = a.emin, r, i, j; + if (n == 0 && emin == 0) { + return Series.zero(0, 0); + } else { + r = Series.zero(n, emin - 1); + for(i = 0; i < n; i++) { + j = emin + i; + if (j == 0) + r[i] = 0; + else + r[i] = j * a[i]; + } + return r.trim(); + } + }, + integ() { + var a = this, n = a.length, emin = a.emin, i, j, r; + r = Series.zero(n, emin + 1); + for(i = 0; i < n; i++) { + j = emin + i; + if (j == -1) { + if (a[i] != 0) + throw RangeError("cannot represent integ(1/X)"); + } else { + r[i] = a[i] / (j + 1); + } + } + return r.trim(); + }, + exp() { + var c, i, r, n, a = this; + if (a.emin < 0) + throw RangeError("negative exponent in exp"); + n = a.emin + a.length; + if (a.emin > 0 || a[0] == 0) { + c = 1; + } else { + c = global.exp(a[0]); + a -= a[0]; + } + r = Series.zero(n, 0); + for(i = 0; i < n; i++) { + r[i] = c / fact(i); + } + return r.apply(a); + }, + log() { + var a = this, r; + if (a.emin != 0) + throw RangeError("log argument must have a non zero constant term"); + r = integ(deriv(a) / a); + /* add the constant term */ + r += global.log(a[0]); + return r; + }, + }); + + add_props(Series, { + /* new series of length n and first exponent emin */ + zero(n, emin) { + var r, i, obj; + + r = []; + for(i = 0; i < n; i++) + r[i] = 0; + /* we return an array and store emin in its prototype */ + obj = Object.create(Series.prototype); + obj.emin = emin; + Object.setPrototypeOf(r, obj); + return r; + }, + O(a) { + function ErrorO() { + return TypeError("invalid O() argument"); + } + var n; + if (series_is_scalar_or_polynomial(a)) { + a = Polynomial(a); + n = a.deg(); + if (n < 0) + throw ErrorO(); + } else if (a instanceof RationalFunction) { + if (a.num.deg() != 0) + throw ErrorO(); + n = a.den.deg(); + if (n < 0) + throw ErrorO(); + n = -n; + } else + throw ErrorO(); + return Series.zero(0, n); + }, + }); + + /* Array (Matrix) */ + + Matrix = function Matrix(h, w) { + var i, j, r, rl; + if (typeof w === "undefined") + w = h; + r = []; + for(i = 0; i < h; i++) { + rl = []; + for(j = 0; j < w; j++) + rl[j] = 0; + r[i] = rl; + } + return r; + }; + + add_props(Matrix, { + idn(n) { + var r, i; + r = Matrix(n, n); + for(i = 0; i < n; i++) + r[i][i] = 1; + return r; + }, + diag(a) { + var r, i, n; + n = a.length; + r = Matrix(n, n); + for(i = 0; i < n; i++) + r[i][i] = a[i]; + return r; + }, + hilbert(n) { + var i, j, r; + r = Matrix(n); + for(i = 0; i < n; i++) { + for(j = 0; j < n; j++) { + r[i][j] = 1 / (1 + i + j); + } + } + return r; + }, + trans(a) { + var h, w, r, i, j; + if (!Array.isArray(a)) + throw TypeError("matrix expected"); + h = a.length; + if (!Array.isArray(a[0])) { + w = 1; + r = Matrix(w, h); + for(i = 0; i < h; i++) { + r[0][i] = a[i]; + } + } else { + w = a[0].length; + r = Matrix(w, h); + for(i = 0; i < h; i++) { + for(j = 0; j < w; j++) { + r[j][i] = a[i][j]; + } + } + } + return r; + }, + check_square(a) { + var a, n; + if (!Array.isArray(a)) + throw TypeError("array expected"); + n = a.length; + if (!Array.isArray(a[0]) || n != a[0].length) + throw TypeError("square matrix expected"); + return n; + }, + trace(a) { + var n, r, i; + n = Matrix.check_square(a); + r = a[0][0]; + for(i = 1; i < n; i++) { + r += a[i][i]; + } + return r; + }, + charpoly(a) { + var n, p, c, i, j, coef; + n = Matrix.check_square(a); + p = []; + for(i = 0; i < n + 1; i++) + p[i] = 0; + p[n] = 1; + c = Matrix.idn(n); + for(i = 0; i < n; i++) { + c = c * a; + coef = -trace(c) / (i + 1); + p[n - i - 1] = coef; + for(j = 0; j < n; j++) + c[j][j] += coef; + } + return Polynomial(p); + }, + eigenvals(a) { + return Polynomial.roots(Matrix.charpoly(a)); + }, + det(a) { + var n, i, j, k, s, src, v, c; + + n = Matrix.check_square(a); + s = 1; + src = a.dup(); + for(i=0;i -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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" - -/* TODO: - - add socket calls -*/ - -typedef struct { - struct list_head link; - int fd; - JSValue rw_func[2]; -} JSOSRWHandler; - -typedef struct { - struct list_head link; - int sig_num; - JSValue func; -} JSOSSignalHandler; - -typedef struct { - struct list_head link; - BOOL has_object; - int64_t timeout; - JSValue func; -} JSOSTimer; - -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 (*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) -{ - char fmtbuf[32]; - uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; - JSValue res; - DynBuf dbuf; - const char *fmt_str; - const uint8_t *fmt, *fmt_end; - const uint8_t *p; - char *q; - int i, c, len, mod; - size_t fmt_len; - int32_t int32_arg; - int64_t int64_arg; - double double_arg; - const char *string_arg; - /* Use indirect call to dbuf_printf to prevent gcc warning */ - int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf; - - js_std_dbuf_init(ctx, &dbuf); - - if (argc > 0) { - fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]); - if (!fmt_str) - goto fail; - - i = 1; - fmt = (const uint8_t *)fmt_str; - fmt_end = fmt + fmt_len; - while (fmt < fmt_end) { - for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++) - continue; - dbuf_put(&dbuf, p, fmt - p); - if (fmt >= fmt_end) - break; - q = fmtbuf; - *q++ = *fmt++; /* copy '%' */ - - /* 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; - *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); - fmt++; - } else { - while (my_isdigit(*fmt)) { - if (q >= fmtbuf + sizeof(fmtbuf) - 1) - goto invalid; - *q++ = *fmt++; - } - } - } - - /* 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; - int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); - JS_FreeCString(ctx, string_arg); - } else { - if (JS_ToInt32(ctx, &int32_arg, argv[i++])) - goto fail; - } - /* handle utf-8 encoding explicitly */ - if ((unsigned)int32_arg > 0x10FFFF) - int32_arg = 0xFFFD; - /* ignore conversion flags, width and precision */ - len = unicode_to_utf8(cbuf, int32_arg); - dbuf_put(&dbuf, cbuf, len); - break; - - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - if (i >= argc) - goto missing; - if (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); - } - if (dbuf.error) { - res = JS_ThrowOutOfMemory(ctx); - } else { - if (fp) { - len = fwrite(dbuf.buf, 1, dbuf.size, fp); - res = JS_NewInt32(ctx, len); - } else { - res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); - } - } - dbuf_free(&dbuf); - return res; - -fail: - dbuf_free(&dbuf); - return JS_EXCEPTION; -} - -uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) -{ - FILE *f; - uint8_t *buf; - size_t buf_len; - long lret; - - f = fopen(filename, "rb"); - if (!f) - return NULL; - if (fseek(f, 0, SEEK_END) < 0) - goto fail; - lret = ftell(f); - if (lret < 0) - goto fail; - /* XXX: on Linux, ftell() return LONG_MAX for directories */ - if (lret == LONG_MAX) { - errno = EISDIR; - goto fail; - } - buf_len = lret; - if (fseek(f, 0, SEEK_SET) < 0) - goto fail; - if (ctx) - buf = js_malloc(ctx, buf_len + 1); - else - buf = malloc(buf_len + 1); - if (!buf) - goto fail; - if (fread(buf, 1, buf_len, f) != buf_len) { - errno = EIO; - if (ctx) - js_free(ctx, buf); - else - free(buf); - fail: - fclose(f); - return NULL; - } - buf[buf_len] = '\0'; - fclose(f); - *pbuf_len = buf_len; - return buf; -} - -/* load and evaluate a file */ -static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint8_t *buf; - const char *filename; - JSValue ret; - size_t buf_len; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - buf = js_load_file(ctx, &buf_len, filename); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load '%s'", filename); - JS_FreeCString(ctx, filename); - return JS_EXCEPTION; - } - ret = JS_Eval(ctx, (char *)buf, buf_len, filename, - JS_EVAL_TYPE_GLOBAL); - js_free(ctx, buf); - JS_FreeCString(ctx, filename); - return ret; -} - -/* 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); - - -#if defined(_WIN32) -static JSModuleDef *js_module_loader_so(JSContext *ctx, - const char *module_name) -{ - JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); - return NULL; -} -#else -static JSModuleDef *js_module_loader_so(JSContext *ctx, - const char *module_name) -{ - JSModuleDef *m; - void *hd; - JSInitModuleFunc *init; - char *filename; - - if (!strchr(module_name, '/')) { - /* must add a '/' so that the DLL is not searched in the - system library paths */ - filename = js_malloc(ctx, strlen(module_name) + 2 + 1); - if (!filename) - return NULL; - strcpy(filename, "./"); - strcpy(filename + 2, module_name); - } else { - filename = (char *)module_name; - } - - /* C module */ - hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); - if (filename != module_name) - js_free(ctx, filename); - if (!hd) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library", - module_name); - goto fail; - } - - init = dlsym(hd, "js_init_module"); - if (!init) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", - module_name); - goto fail; - } - - m = init(ctx, module_name); - if (!m) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", - module_name); - fail: - if (hd) - dlclose(hd); - return NULL; - } - return m; -} -#endif /* !_WIN32 */ - -int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, - JS_BOOL use_realpath, JS_BOOL is_main) -{ - JSModuleDef *m; - char buf[PATH_MAX + 16]; - JSValue meta_obj; - JSAtom module_name_atom; - const char *module_name; - - assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); - m = JS_VALUE_GET_PTR(func_val); - - module_name_atom = JS_GetModuleName(ctx, m); - module_name = JS_AtomToCString(ctx, module_name_atom); - JS_FreeAtom(ctx, module_name_atom); - if (!module_name) - return -1; - if (!strchr(module_name, ':')) { - strcpy(buf, "file://"); -#if !defined(_WIN32) - /* realpath() cannot be used with modules compiled with qjsc - because the corresponding module source code is not - necessarily present */ - if (use_realpath) { - char *res = realpath(module_name, buf + strlen(buf)); - if (!res) { - JS_ThrowTypeError(ctx, "realpath failure"); - JS_FreeCString(ctx, module_name); - return -1; - } - } else -#endif - { - pstrcat(buf, sizeof(buf), module_name); - } - } else { - pstrcpy(buf, sizeof(buf), module_name); - } - JS_FreeCString(ctx, module_name); - - meta_obj = JS_GetImportMeta(ctx, m); - if (JS_IsException(meta_obj)) - return -1; - JS_DefinePropertyValueStr(ctx, meta_obj, "url", - JS_NewString(ctx, buf), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, meta_obj, "main", - JS_NewBool(ctx, is_main), - JS_PROP_C_W_E); - JS_FreeValue(ctx, meta_obj); - return 0; -} - -JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque) -{ - JSModuleDef *m; - - if (has_suffix(module_name, ".so")) { - m = js_module_loader_so(ctx, module_name); - } else { - size_t buf_len; - uint8_t *buf; - JSValue func_val; - - buf = js_load_file(ctx, &buf_len, module_name); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s'", - module_name); - return NULL; - } - - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - /* XXX: could propagate the exception */ - js_module_set_import_meta(ctx, func_val, TRUE, FALSE); - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); - } - return m; -} - -static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int status; - if (JS_ToInt32(ctx, &status, argv[0])) - status = -1; - exit(status); - return JS_UNDEFINED; -} - -static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *name, *str; - name = JS_ToCString(ctx, argv[0]); - if (!name) - return JS_EXCEPTION; - str = getenv(name); - JS_FreeCString(ctx, name); - if (!str) - return JS_UNDEFINED; - else - return JS_NewString(ctx, str); -} - -#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) -{ - JS_RunGC(JS_GetRuntime(ctx)); - return JS_UNDEFINED; -} - -static int interrupt_handler(JSRuntime *rt, void *opaque) -{ - return (os_pending_signals >> SIGINT) & 1; -} - -static int get_bool_option(JSContext *ctx, BOOL *pbool, - JSValueConst obj, - const char *option) -{ - JSValue val; - val = JS_GetPropertyStr(ctx, obj, option); - if (JS_IsException(val)) - return -1; - if (!JS_IsUndefined(val)) { - *pbool = JS_ToBool(ctx, val); - } - JS_FreeValue(ctx, val); - return 0; -} - -static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - 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 (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { - /* install the interrupt handler */ - JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); - } - flags = JS_EVAL_TYPE_GLOBAL; - if (backtrace_barrier) - flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; - ret = JS_Eval(ctx, str, len, "", flags); - JS_FreeCString(ctx, str); - if (!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); - /* convert the uncatchable "interrupted" error into a normal error - so that it can be caught by the REPL */ - if (JS_IsException(ret)) - JS_ResetUncatchableError(ctx); - } - return ret; -} - -static JSClassID js_std_file_class_id; - -typedef struct { - FILE *f; - BOOL close_in_finalizer; - BOOL is_popen; -} JSSTDFile; - -static void js_std_file_finalizer(JSRuntime *rt, JSValue val) -{ - JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id); - if (s) { - if (s->f && s->close_in_finalizer) { - if (s->is_popen) - pclose(s->f); - else - fclose(s->f); - } - js_free_rt(rt, s); - } -} - -static ssize_t js_get_errno(ssize_t ret) -{ - if (ret == -1) - ret = -errno; - return ret; -} - -static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int err; - if (JS_ToInt32(ctx, &err, argv[0])) - return JS_EXCEPTION; - return JS_NewString(ctx, strerror(err)); -} - -static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue 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, - BOOL close_in_finalizer, - BOOL is_popen) -{ - JSSTDFile *s; - JSValue obj; - obj = JS_NewObjectClass(ctx, js_std_file_class_id); - if (JS_IsException(obj)) - return obj; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - s->close_in_finalizer = close_in_finalizer; - s->is_popen = is_popen; - s->f = f; - JS_SetOpaque(obj, s); - return obj; -} - -static 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; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (mode[strspn(mode, "rwa+b")] != '\0') { - 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_NULL; - return js_new_std_file(ctx, f, TRUE, FALSE); - fail: - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} - -static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename, *mode = NULL; - FILE *f; - int err; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - goto fail; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (mode[strspn(mode, "rw")] != '\0') { - JS_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_NULL; - return js_new_std_file(ctx, f, TRUE, TRUE); - fail: - JS_FreeCString(ctx, filename); - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} - -static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *mode; - FILE *f; - int fd, err; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - mode = JS_ToCString(ctx, argv[1]); - if (!mode) - goto fail; - if (mode[strspn(mode, "rwa+")] != '\0') { - JS_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_NULL; - return js_new_std_file(ctx, f, TRUE, FALSE); - fail: - JS_FreeCString(ctx, mode); - return JS_EXCEPTION; -} - -static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f; - f = tmpfile(); - if (argc >= 1) - js_set_error_object(ctx, argv[0], f ? 0 : errno); - if (!f) - return JS_NULL; - return js_new_std_file(ctx, f, TRUE, FALSE); -} - -static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_printf_internal(ctx, argc, argv, NULL); -} - -static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_printf_internal(ctx, argc, argv, stdout); -} - -static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj) -{ - JSSTDFile *s = JS_GetOpaque2(ctx, obj, js_std_file_class_id); - if (!s) - return NULL; - if (!s->f) { - JS_ThrowTypeError(ctx, "invalid file handle"); - return NULL; - } - return s->f; -} - -static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - FILE *f; - int i; - const char *str; - size_t len; - - if (magic == 0) { - f = stdout; - } else { - f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - } - - for(i = 0; i < argc; i++) { - str = JS_ToCStringLen(ctx, &len, argv[i]); - if (!str) - return JS_EXCEPTION; - fwrite(str, 1, len, f); - JS_FreeCString(ctx, str); - } - return JS_UNDEFINED; -} - -static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSSTDFile *s = JS_GetOpaque2(ctx, this_val, js_std_file_class_id); - int err; - if (!s) - return JS_EXCEPTION; - if (!s->f) - return JS_ThrowTypeError(ctx, "invalid file handle"); - if (s->is_popen) - err = js_get_errno(pclose(s->f)); - else - err = js_get_errno(fclose(s->f)); - s->f = NULL; - return JS_NewInt32(ctx, err); -} - -static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return js_printf_internal(ctx, argc, argv, f); -} - -static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - fflush(f); - return JS_UNDEFINED; -} - -static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int is_bigint) -{ - FILE *f = js_std_file_get(ctx, this_val); - int64_t pos; - if (!f) - return JS_EXCEPTION; -#if defined(__linux__) - pos = ftello(f); -#else - pos = ftell(f); -#endif - 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, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int64_t pos; - int whence, ret; - if (!f) - return JS_EXCEPTION; - if (JS_ToInt64Ext(ctx, &pos, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &whence, argv[1])) - return JS_EXCEPTION; -#if defined(__linux__) - ret = fseeko(f, pos, whence); -#else - ret = fseek(f, pos, whence); -#endif - if (ret < 0) - ret = -errno; - return JS_NewInt32(ctx, ret); -} - -static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewBool(ctx, feof(f)); -} - -static JSValue js_std_file_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) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewInt32(ctx, fileno(f)); -} - -static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - FILE *f = js_std_file_get(ctx, this_val); - uint64_t pos, len; - size_t size, ret; - uint8_t *buf; - - if (!f) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &pos, argv[1])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &len, argv[2])) - return JS_EXCEPTION; - buf = JS_GetArrayBuffer(ctx, &size, argv[0]); - if (!buf) - return JS_EXCEPTION; - if (pos + len > size) - return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); - if (magic) - ret = fwrite(buf + pos, 1, len, f); - else - ret = fread(buf + pos, 1, len, f); - return JS_NewInt64(ctx, ret); -} - -/* XXX: could use less memory and go faster */ -static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - DynBuf dbuf; - JSValue obj; - - if (!f) - return JS_EXCEPTION; - - js_std_dbuf_init(ctx, &dbuf); - for(;;) { - c = fgetc(f); - if (c == EOF) { - if (dbuf.size == 0) { - /* EOF */ - dbuf_free(&dbuf); - return JS_NULL; - } else { - break; - } - } - if (c == '\n') - break; - if (dbuf_putc(&dbuf, c)) { - dbuf_free(&dbuf); - return JS_ThrowOutOfMemory(ctx); - } - } - obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); - dbuf_free(&dbuf); - return obj; -} - -/* XXX: could use less memory and go faster */ -static JSValue js_std_file_readAsString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - DynBuf dbuf; - JSValue obj; - uint64_t max_size64; - size_t max_size; - JSValueConst max_size_val; - - if (!f) - return JS_EXCEPTION; - - if (argc >= 1) - max_size_val = argv[0]; - else - max_size_val = JS_UNDEFINED; - max_size = (size_t)-1; - if (!JS_IsUndefined(max_size_val)) { - if (JS_ToIndex(ctx, &max_size64, max_size_val)) - return JS_EXCEPTION; - if (max_size64 < max_size) - max_size = max_size64; - } - - js_std_dbuf_init(ctx, &dbuf); - while (max_size != 0) { - c = fgetc(f); - if (c == EOF) - break; - if (dbuf_putc(&dbuf, c)) { - dbuf_free(&dbuf); - return JS_EXCEPTION; - } - max_size--; - } - obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); - dbuf_free(&dbuf); - return obj; -} - -static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - if (!f) - return JS_EXCEPTION; - return JS_NewInt32(ctx, fgetc(f)); -} - -static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - FILE *f = js_std_file_get(ctx, this_val); - int c; - if (!f) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &c, argv[0])) - return JS_EXCEPTION; - c = fputc(c, f); - return JS_NewInt32(ctx, c); -} - -/* urlGet */ - -#define URL_GET_PROGRAM "curl -s -i" -#define URL_GET_BUF_SIZE 4096 - -static int http_get_header_line(FILE *f, char *buf, size_t buf_size, - DynBuf *dbuf) -{ - int c; - char *p; - - p = buf; - for(;;) { - c = fgetc(f); - if (c < 0) - return -1; - if ((p - buf) < buf_size - 1) - *p++ = c; - if (dbuf) - dbuf_putc(dbuf, c); - if (c == '\n') - break; - } - *p = '\0'; - return 0; -} - -static int http_get_status(const char *buf) -{ - const char *p = buf; - while (*p != ' ' && *p != '\0') - p++; - if (*p != ' ') - return 0; - while (*p == ' ') - p++; - return atoi(p); -} - -static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *url; - DynBuf cmd_buf; - DynBuf data_buf_s, *data_buf = &data_buf_s; - DynBuf header_buf_s, *header_buf = &header_buf_s; - char *buf; - size_t i, len; - int c, status; - JSValue response = JS_UNDEFINED, ret_obj; - JSValueConst options_obj; - FILE *f; - BOOL binary_flag, full_flag; - - url = JS_ToCString(ctx, argv[0]); - if (!url) - return JS_EXCEPTION; - - binary_flag = FALSE; - full_flag = FALSE; - - if (argc >= 2) { - options_obj = argv[1]; - - if (get_bool_option(ctx, &binary_flag, options_obj, "binary")) - goto fail_obj; - - if (get_bool_option(ctx, &full_flag, options_obj, "full")) { - fail_obj: - JS_FreeCString(ctx, url); - return JS_EXCEPTION; - } - } - - js_std_dbuf_init(ctx, &cmd_buf); - dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM); - len = strlen(url); - for(i = 0; i < len; i++) { - c = url[i]; - if (c == '\'' || c == '\\') - dbuf_putc(&cmd_buf, '\\'); - dbuf_putc(&cmd_buf, c); - } - JS_FreeCString(ctx, url); - dbuf_putstr(&cmd_buf, "''"); - dbuf_putc(&cmd_buf, '\0'); - if (dbuf_error(&cmd_buf)) { - dbuf_free(&cmd_buf); - return JS_EXCEPTION; - } - // printf("%s\n", (char *)cmd_buf.buf); - f = popen((char *)cmd_buf.buf, "r"); - dbuf_free(&cmd_buf); - if (!f) { - return JS_ThrowTypeError(ctx, "could not start curl"); - } - - js_std_dbuf_init(ctx, data_buf); - js_std_dbuf_init(ctx, header_buf); - - buf = js_malloc(ctx, URL_GET_BUF_SIZE); - if (!buf) - goto fail; - - /* get the HTTP status */ - if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) { - status = 0; - goto bad_header; - } - status = http_get_status(buf); - if (!full_flag && !(status >= 200 && status <= 299)) { - goto bad_header; - } - - /* wait until there is an empty line */ - for(;;) { - if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { - bad_header: - response = JS_NULL; - goto done; - } - if (!strcmp(buf, "\r\n")) - break; - } - if (dbuf_error(header_buf)) - goto fail; - header_buf->size -= 2; /* remove the trailing CRLF */ - - /* download the data */ - for(;;) { - len = fread(buf, 1, URL_GET_BUF_SIZE, f); - if (len == 0) - break; - dbuf_put(data_buf, (uint8_t *)buf, len); - } - if (dbuf_error(data_buf)) - goto fail; - if (binary_flag) { - response = JS_NewArrayBufferCopy(ctx, - data_buf->buf, data_buf->size); - } else { - response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size); - } - 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); - if (JS_IsException(ret_obj)) - goto fail; - JS_DefinePropertyValueStr(ctx, ret_obj, "response", - response, - 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; - } - dbuf_free(header_buf); - return ret_obj; - fail: - if (f) - pclose(f); - js_free(ctx, buf); - if (data_buf) - dbuf_free(data_buf); - if (header_buf) - dbuf_free(header_buf); - JS_FreeValue(ctx, response); - return JS_EXCEPTION; -} - -static JSClassDef js_std_file_class = { - "FILE", - .finalizer = js_std_file_finalizer, -}; - -static const JSCFunctionListEntry js_std_error_props[] = { - /* various errno values */ -#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) - DEF(EINVAL), - DEF(EIO), - DEF(EACCES), - DEF(EEXIST), - DEF(ENOSPC), - DEF(ENOSYS), - DEF(EBUSY), - DEF(ENOENT), - DEF(EPERM), - DEF(EPIPE), - DEF(EBADF), -#undef DEF -}; - -static const JSCFunctionListEntry js_std_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[] = { - 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_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, ... */ -}; - -static int js_std_init(JSContext *ctx, JSModuleDef *m) -{ - JSValue proto; - - /* FILE class */ - /* the class ID is created once */ - JS_NewClassID(&js_std_file_class_id); - /* the class is created once per runtime */ - JS_NewClass(JS_GetRuntime(ctx), js_std_file_class_id, &js_std_file_class); - proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs, - countof(js_std_file_proto_funcs)); - JS_SetClassProto(ctx, js_std_file_class_id, proto); - - JS_SetModuleExportList(ctx, m, js_std_funcs, - countof(js_std_funcs)); - JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE)); - JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE)); - JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE)); - return 0; -} - -JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_std_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs)); - JS_AddModuleExport(ctx, m, "in"); - JS_AddModuleExport(ctx, m, "out"); - JS_AddModuleExport(ctx, m, "err"); - return m; -} - -/**********************************************************/ -/* 'os' object */ - -static JSValue js_os_open(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename; - int flags, mode, ret; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &flags, argv[1])) - goto fail; - if (argc >= 3 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32(ctx, &mode, argv[2])) { - fail: - JS_FreeCString(ctx, filename); - return JS_EXCEPTION; - } - } else { - mode = 0666; - } -#if defined(_WIN32) - /* force binary mode by default */ - if (!(flags & O_TEXT)) - flags |= O_BINARY; -#endif - ret = js_get_errno(open(filename, flags, mode)); - JS_FreeCString(ctx, filename); - return JS_NewInt32(ctx, ret); -} - -static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, ret; - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - ret = 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; - int64_t pos, ret; - BOOL is_bigint; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - 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); - 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, - int argc, JSValueConst *argv, int magic) -{ - int fd; - uint64_t pos, len; - size_t size; - ssize_t ret; - uint8_t *buf; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &pos, argv[2])) - return JS_EXCEPTION; - if (JS_ToIndex(ctx, &len, argv[3])) - return JS_EXCEPTION; - buf = JS_GetArrayBuffer(ctx, &size, argv[1]); - if (!buf) - return JS_EXCEPTION; - if (pos + len > size) - return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); - if (magic) - ret = js_get_errno(write(fd, buf + pos, len)); - else - ret = js_get_errno(read(fd, buf + pos, len)); - return JS_NewInt64(ctx, ret); -} - -static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - return JS_NewBool(ctx, (isatty(fd) != 0)); -} - -#if defined(_WIN32) -static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - HANDLE handle; - CONSOLE_SCREEN_BUFFER_INFO info; - JSValue obj; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - handle = (HANDLE)_get_osfhandle(fd); - - if (!GetConsoleScreenBufferInfo(handle, &info)) - return JS_NULL; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E); - return obj; -} - -/* Windows 10 built-in VT100 emulation */ -#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 -#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 - -static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - HANDLE handle; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - handle = (HANDLE)_get_osfhandle(fd); - SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT); - _setmode(fd, _O_BINARY); - if (fd == 0) { - handle = (HANDLE)_get_osfhandle(1); /* corresponding output */ - SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING); - } - return JS_UNDEFINED; -} -#else -static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd; - struct winsize ws; - JSValue obj; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && - ws.ws_col >= 4 && ws.ws_row >= 4) { - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E); - return obj; - } else { - return JS_NULL; - } -} - -static struct termios oldtty; - -static void term_exit(void) -{ - tcsetattr(0, TCSANOW, &oldtty); -} - -/* XXX: should add a way to go back to normal mode */ -static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - struct termios tty; - int fd; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - - memset(&tty, 0, sizeof(tty)); - tcgetattr(fd, &tty); - oldtty = tty; - - tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP - |INLCR|IGNCR|ICRNL|IXON); - tty.c_oflag |= OPOST; - tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); - tty.c_cflag &= ~(CSIZE|PARENB); - tty.c_cflag |= CS8; - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; - - tcsetattr(fd, TCSANOW, &tty); - - atexit(term_exit); - return JS_UNDEFINED; -} - -#endif /* !_WIN32 */ - -static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *filename; - int ret; - - filename = JS_ToCString(ctx, argv[0]); - if (!filename) - return JS_EXCEPTION; -#if defined(_WIN32) - { - struct stat st; - if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode)) { - ret = rmdir(filename); - } else { - ret = unlink(filename); - } - } -#else - ret = remove(filename); -#endif - ret = js_get_errno(ret); - JS_FreeCString(ctx, filename); - return JS_NewInt32(ctx, ret); -} - -static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *oldpath, *newpath; - int ret; - - oldpath = JS_ToCString(ctx, argv[0]); - if (!oldpath) - return JS_EXCEPTION; - newpath = JS_ToCString(ctx, argv[1]); - if (!newpath) { - JS_FreeCString(ctx, oldpath); - return JS_EXCEPTION; - } - ret = js_get_errno(rename(oldpath, newpath)); - JS_FreeCString(ctx, oldpath); - JS_FreeCString(ctx, newpath); - return JS_NewInt32(ctx, ret); -} - -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, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == fd) - return rh; - } - return NULL; -} - -static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) -{ - int i; - list_del(&rh->link); - for(i = 0; i < 2; i++) { - JS_FreeValueRT(rt, rh->rw_func[i]); - } - js_free_rt(rt, rh); -} - -static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - JSOSRWHandler *rh; - int fd; - JSValueConst func; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - func = argv[1]; - if (JS_IsNull(func)) { - rh = find_rh(ts, fd); - if (rh) { - JS_FreeValue(ctx, rh->rw_func[magic]); - rh->rw_func[magic] = JS_NULL; - if (JS_IsNull(rh->rw_func[0]) && - JS_IsNull(rh->rw_func[1])) { - /* remove the entry */ - free_rw_handler(JS_GetRuntime(ctx), rh); - } - } - } else { - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - rh = find_rh(ts, fd); - if (!rh) { - rh = js_mallocz(ctx, sizeof(*rh)); - if (!rh) - return JS_EXCEPTION; - rh->fd = fd; - rh->rw_func[0] = JS_NULL; - rh->rw_func[1] = JS_NULL; - list_add_tail(&rh->link, &ts->os_rw_handlers); - } - JS_FreeValue(ctx, rh->rw_func[magic]); - rh->rw_func[magic] = JS_DupValue(ctx, func); - } - return JS_UNDEFINED; -} - -static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num) -{ - JSOSSignalHandler *sh; - struct list_head *el; - list_for_each(el, &ts->os_signal_handlers) { - sh = list_entry(el, JSOSSignalHandler, link); - if (sh->sig_num == sig_num) - return sh; - } - return NULL; -} - -static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh) -{ - list_del(&sh->link); - JS_FreeValueRT(rt, sh->func); - js_free_rt(rt, sh); -} - -static void os_signal_handler(int sig_num) -{ - os_pending_signals |= ((uint64_t)1 << sig_num); -} - -#if defined(_WIN32) -typedef void (*sighandler_t)(int sig_num); -#endif - -static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - 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; - if (sig_num >= 64) - return JS_ThrowRangeError(ctx, "invalid signal number"); - func = argv[1]; - /* func = null: SIG_DFL, func = undefined, SIG_IGN */ - if (JS_IsNull(func) || JS_IsUndefined(func)) { - sh = find_sh(ts, sig_num); - if (sh) { - free_sh(JS_GetRuntime(ctx), sh); - } - if (JS_IsNull(func)) - handler = SIG_DFL; - else - handler = SIG_IGN; - signal(sig_num, handler); - } else { - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - sh = find_sh(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, &ts->os_signal_handlers); - } - JS_FreeValue(ctx, sh->func); - sh->func = JS_DupValue(ctx, func); - signal(sig_num, os_signal_handler); - } - return JS_UNDEFINED; -} - -#if defined(__linux__) || defined(__APPLE__) -static int64_t get_time_ms(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); -} -#else -/* more portable, but does not work if the date is updated */ -static int64_t get_time_ms(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); -} -#endif - -static void unlink_timer(JSRuntime *rt, JSOSTimer *th) -{ - if (th->link.prev) { - list_del(&th->link); - th->link.prev = th->link.next = NULL; - } -} - -static void free_timer(JSRuntime *rt, JSOSTimer *th) -{ - JS_FreeValueRT(rt, th->func); - js_free_rt(rt, th); -} - -static JSClassID js_os_timer_class_id; - -static void js_os_timer_finalizer(JSRuntime *rt, JSValue val) -{ - JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); - if (th) { - th->has_object = FALSE; - if (!th->link.prev) - free_timer(rt, th); - } -} - -static void js_os_timer_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); - if (th) { - JS_MarkValue(rt, th->func, mark_func); - } -} - -static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - int64_t delay; - JSValueConst func; - JSOSTimer *th; - JSValue obj; - - func = argv[0]; - if (!JS_IsFunction(ctx, func)) - return JS_ThrowTypeError(ctx, "not a function"); - if (JS_ToInt64(ctx, &delay, argv[1])) - return JS_EXCEPTION; - obj = JS_NewObjectClass(ctx, js_os_timer_class_id); - if (JS_IsException(obj)) - return obj; - th = js_mallocz(ctx, sizeof(*th)); - if (!th) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - th->has_object = TRUE; - th->timeout = get_time_ms() + delay; - th->func = JS_DupValue(ctx, func); - list_add_tail(&th->link, &ts->os_timers); - JS_SetOpaque(obj, th); - return obj; -} - -static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id); - if (!th) - return JS_EXCEPTION; - unlink_timer(JS_GetRuntime(ctx), th); - return JS_UNDEFINED; -} - -static JSClassDef js_os_timer_class = { - "OSTimer", - .finalizer = js_os_timer_finalizer, - .gc_mark = js_os_timer_mark, -}; - -static void call_handler(JSContext *ctx, JSValueConst func) -{ - JSValue ret, func1; - /* 'func' might be destroyed when calling itself (if it frees the - handler), so must take extra care */ - func1 = JS_DupValue(ctx, func); - ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); - JS_FreeValue(ctx, func1); - if (JS_IsException(ret)) - js_std_dump_error(ctx); - JS_FreeValue(ctx, ret); -} - -#if defined(_WIN32) - -static int js_os_poll(JSContext *ctx) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = JS_GetRuntimeOpaque(rt); - int min_delay, console_fd; - int64_t cur_time, delay; - JSOSRWHandler *rh; - struct list_head *el; - - /* XXX: handle signals if useful */ - - if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers)) - return -1; /* no more events */ - - /* XXX: only timers and basic console input are supported */ - if (!list_empty(&ts->os_timers)) { - cur_time = get_time_ms(); - min_delay = 10000; - list_for_each(el, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - delay = th->timeout - cur_time; - if (delay <= 0) { - JSValue func; - /* the timer expired */ - func = th->func; - th->func = JS_UNDEFINED; - unlink_timer(rt, th); - if (!th->has_object) - free_timer(rt, th); - call_handler(ctx, func); - JS_FreeValue(ctx, func); - return 0; - } else if (delay < min_delay) { - min_delay = delay; - } - } - } else { - min_delay = -1; - } - - console_fd = -1; - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { - console_fd = rh->fd; - break; - } - } - - if (console_fd >= 0) { - DWORD ti, ret; - HANDLE handle; - if (min_delay == -1) - ti = INFINITE; - else - ti = min_delay; - handle = (HANDLE)_get_osfhandle(console_fd); - ret = WaitForSingleObject(handle, ti); - if (ret == WAIT_OBJECT_0) { - list_for_each(el, &ts->os_rw_handlers) { - rh = list_entry(el, JSOSRWHandler, link); - if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { - call_handler(ctx, rh->rw_func[0]); - /* must stop because the list may have been modified */ - break; - } - } - } - } else { - Sleep(min_delay); - } - 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; - JSOSRWHandler *rh; - struct list_head *el; - struct timeval tv, *tvp; - - /* 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, &ts->os_signal_handlers) { - sh = list_entry(el, JSOSSignalHandler, link); - mask = (uint64_t)1 << sh->sig_num; - if (os_pending_signals & mask) { - os_pending_signals &= ~mask; - call_handler(ctx, sh->func); - return 0; - } - } - } - - if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && - list_empty(&ts->port_list)) - return -1; /* no more events */ - - if (!list_empty(&ts->os_timers)) { - cur_time = get_time_ms(); - min_delay = 10000; - list_for_each(el, &ts->os_timers) { - JSOSTimer *th = list_entry(el, JSOSTimer, link); - delay = th->timeout - cur_time; - if (delay <= 0) { - JSValue func; - /* the timer expired */ - func = th->func; - th->func = JS_UNDEFINED; - unlink_timer(rt, th); - if (!th->has_object) - free_timer(rt, th); - call_handler(ctx, func); - JS_FreeValue(ctx, func); - return 0; - } else if (delay < min_delay) { - min_delay = delay; - } - } - tv.tv_sec = min_delay / 1000; - tv.tv_usec = (min_delay % 1000) * 1000; - tvp = &tv; - } else { - tvp = NULL; - } - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - fd_max = -1; - list_for_each(el, &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])) - FD_SET(rh->fd, &rfds); - 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, &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 */ - goto done; - } - 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 */ - 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 */ - -static JSValue make_obj_error(JSContext *ctx, - JSValue obj, - int err) -{ - JSValue arr; - if (JS_IsException(obj)) - return obj; - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - return JS_EXCEPTION; - JS_DefinePropertyValueUint32(ctx, arr, 0, obj, - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err), - JS_PROP_C_W_E); - return arr; -} - -static JSValue make_string_error(JSContext *ctx, - const char *buf, - int err) -{ - return make_obj_error(ctx, JS_NewString(ctx, buf), err); -} - -/* return [cwd, errorcode] */ -static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - char buf[PATH_MAX]; - int err; - - if (!getcwd(buf, sizeof(buf))) { - buf[0] = '\0'; - err = errno; - } else { - err = 0; - } - return make_string_error(ctx, buf, err); -} - -static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *target; - int err; - - target = JS_ToCString(ctx, argv[0]); - if (!target) - return JS_EXCEPTION; - 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, - int argc, JSValueConst *argv) -{ - int mode, ret; - const char *path; - - if (argc >= 2) { - if (JS_ToInt32(ctx, &mode, argv[1])) - return JS_EXCEPTION; - } else { - mode = 0777; - } - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; -#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_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, - int argc, JSValueConst *argv, int is_lstat) -{ - const char *path; - int err, res; - struct stat st; - JSValue obj; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; -#if 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; - obj = JS_NULL; - } else { - err = 0; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - JS_DefinePropertyValueStr(ctx, obj, "dev", - JS_NewInt64(ctx, st.st_dev), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ino", - JS_NewInt64(ctx, st.st_ino), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mode", - JS_NewInt32(ctx, st.st_mode), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "nlink", - JS_NewInt64(ctx, st.st_nlink), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "uid", - JS_NewInt64(ctx, st.st_uid), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "gid", - JS_NewInt64(ctx, st.st_gid), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "rdev", - JS_NewInt64(ctx, st.st_rdev), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "size", - JS_NewInt64(ctx, st.st_size), - JS_PROP_C_W_E); -#if !defined(_WIN32) - JS_DefinePropertyValueStr(ctx, obj, "blocks", - JS_NewInt64(ctx, st.st_blocks), - JS_PROP_C_W_E); -#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); - JS_DefinePropertyValueStr(ctx, obj, "mtime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ctime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)), - JS_PROP_C_W_E); -#else - JS_DefinePropertyValueStr(ctx, obj, "atime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "mtime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)), - JS_PROP_C_W_E); - JS_DefinePropertyValueStr(ctx, obj, "ctime", - JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)), - JS_PROP_C_W_E); -#endif - } - return make_obj_error(ctx, obj, err); -} - -#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); -} - -/* sleep(delay_ms) */ -static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t delay; - int ret; - - if (JS_ToInt64(ctx, &delay, argv[0])) - return JS_EXCEPTION; - if (delay < 0) - delay = 0; -#if defined(_WIN32) - { - if (delay > INT32_MAX) - delay = INT32_MAX; - Sleep(delay); - ret = 0; - } -#else - { - struct timespec ts; - - ts.tv_sec = delay / 1000; - ts.tv_nsec = (delay % 1000) * 1000000; - ret = js_get_errno(nanosleep(&ts, NULL)); - } -#endif - return JS_NewInt32(ctx, ret); -} - -#if defined(_WIN32) -static char *realpath(const char *path, char *buf) -{ - if (!_fullpath(buf, path, PATH_MAX)) { - errno = ENOENT; - return NULL; - } else { - return buf; - } -} -#endif - -/* 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); -} - -#if !defined(_WIN32) -static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *target, *linkpath; - int err; - - target = JS_ToCString(ctx, argv[0]); - if (!target) - return JS_EXCEPTION; - linkpath = JS_ToCString(ctx, argv[1]); - if (!linkpath) { - JS_FreeCString(ctx, target); - return JS_EXCEPTION; - } - err = js_get_errno(symlink(target, linkpath)); - JS_FreeCString(ctx, target); - JS_FreeCString(ctx, linkpath); - return JS_NewInt32(ctx, err); -} - -/* return [path, errorcode] */ -static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *path; - char buf[PATH_MAX]; - int err; - ssize_t res; - - path = JS_ToCString(ctx, argv[0]); - if (!path) - return JS_EXCEPTION; - res = readlink(path, buf, sizeof(buf) - 1); - if (res < 0) { - buf[0] = '\0'; - err = errno; - } else { - buf[res] = '\0'; - err = 0; - } - JS_FreeCString(ctx, path); - return make_string_error(ctx, buf, err); -} - -static char **build_envp(JSContext *ctx, JSValueConst obj) -{ - uint32_t len, i; - JSPropertyEnum *tab; - char **envp, *pair; - const char *key, *str; - JSValue val; - size_t key_len, str_len; - - if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) - return NULL; - 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; - } - 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); - } - done: - 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; -} - -/* execvpe is not available on non GNU systems */ -static int my_execvpe(const char *filename, char **argv, char **envp) -{ - char *path, *p, *p_next, *p1; - char buf[PATH_MAX]; - size_t filename_len, path_len; - BOOL eacces_error; - - 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) - 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 */ -static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst options, args = argv[0]; - JSValue val, ret_val; - const char **exec_argv, *file = NULL, *str, *cwd = NULL; - 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)) - return JS_EXCEPTION; - ret = JS_ToUint32(ctx, &exec_argc, val); - JS_FreeValue(ctx, val); - if (ret) - return JS_EXCEPTION; - /* arbitrary limit to avoid overflow */ - if (exec_argc < 1 || exec_argc > 65535) { - return JS_ThrowTypeError(ctx, "invalid number of arguments"); - } - exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1)); - if (!exec_argv) - return JS_EXCEPTION; - for(i = 0; i < exec_argc; i++) { - val = JS_GetPropertyUint32(ctx, args, i); - if (JS_IsException(val)) - goto exception; - str = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!str) - goto exception; - exec_argv[i] = str; - } - exec_argv[exec_argc] = NULL; - - for(i = 0; i < 3; i++) - std_fds[i] = i; - - /* get the options, if any */ - if (argc >= 2) { - options = argv[1]; - - if (get_bool_option(ctx, &block_flag, options, "block")) - goto exception; - if (get_bool_option(ctx, &use_path, options, "usePath")) - goto exception; - - val = JS_GetPropertyStr(ctx, options, "file"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - file = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!file) - goto exception; - } - - val = JS_GetPropertyStr(ctx, options, "cwd"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - cwd = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!cwd) - goto exception; - } - - /* stdin/stdout/stderr handles */ - for(i = 0; i < 3; i++) { - val = JS_GetPropertyStr(ctx, options, std_name[i]); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - int fd; - ret = JS_ToInt32(ctx, &fd, val); - JS_FreeValue(ctx, val); - if (ret) - goto exception; - std_fds[i] = fd; - } - } - - 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(); - if (pid < 0) { - JS_ThrowTypeError(ctx, "fork error"); - goto exception; - } - if (pid == 0) { - /* child */ - int fd_max = sysconf(_SC_OPEN_MAX); - - /* remap the stdin/stdout/stderr handles if necessary */ - for(i = 0; i < 3; i++) { - if (std_fds[i] != i) { - if (dup2(std_fds[i], i) < 0) - _exit(127); - } - } - - for(i = 3; i < fd_max; i++) - close(i); - if (cwd) { - if (chdir(cwd) < 0) - _exit(127); - } - if (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 = my_execvpe(file, (char **)exec_argv, envp); - else - ret = execve(file, (char **)exec_argv, envp); - _exit(127); - } - /* parent */ - if (block_flag) { - for(;;) { - ret = waitpid(pid, &status, 0); - if (ret == pid) { - if (WIFEXITED(status)) { - ret = WEXITSTATUS(status); - break; - } else if (WIFSIGNALED(status)) { - ret = -WTERMSIG(status); - break; - } - } - } - } else { - ret = pid; - } - ret_val = JS_NewInt32(ctx, ret); - done: - JS_FreeCString(ctx, file); - JS_FreeCString(ctx, cwd); - for(i = 0; i < exec_argc; i++) - JS_FreeCString(ctx, exec_argv[i]); - js_free(ctx, exec_argv); - 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; - goto done; -} - -/* waitpid(pid, block) -> [pid, status] */ -static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid, status, options, ret; - JSValue obj; - - if (JS_ToInt32(ctx, &pid, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &options, argv[1])) - return JS_EXCEPTION; - - ret = waitpid(pid, &status, options); - if (ret < 0) { - ret = -errno; - status = 0; - } - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret), - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), - JS_PROP_C_W_E); - return obj; -} - -/* pipe() -> [read_fd, write_fd] or null if error */ -static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pipe_fds[2], ret; - JSValue obj; - - ret = pipe(pipe_fds); - if (ret < 0) - return JS_NULL; - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), - JS_PROP_C_W_E); - return obj; -} - -/* kill(pid, sig) */ -static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid, sig, ret; - - if (JS_ToInt32(ctx, &pid, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &sig, argv[1])) - return JS_EXCEPTION; - ret = js_get_errno(kill(pid, sig)); - return JS_NewInt32(ctx, ret); -} - -/* dup(fd) */ -static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, ret; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - ret = js_get_errno(dup(fd)); - return JS_NewInt32(ctx, ret); -} - -/* dup2(fd) */ -static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int fd, fd2, ret; - - if (JS_ToInt32(ctx, &fd, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &fd2, argv[1])) - return JS_EXCEPTION; - ret = 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__) -#define OS_PLATFORM "darwin" -#elif defined(EMSCRIPTEN) -#define OS_PLATFORM "js" -#else -#define OS_PLATFORM "linux" -#endif - -#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) - -static const JSCFunctionListEntry js_os_funcs[] = { - JS_CFUNC_DEF("open", 2, js_os_open ), - OS_FLAG(O_RDONLY), - OS_FLAG(O_WRONLY), - OS_FLAG(O_RDWR), - OS_FLAG(O_APPEND), - OS_FLAG(O_CREAT), - OS_FLAG(O_EXCL), - OS_FLAG(O_TRUNC), -#if defined(_WIN32) - OS_FLAG(O_BINARY), - OS_FLAG(O_TEXT), -#endif - JS_CFUNC_DEF("close", 1, js_os_close ), - JS_CFUNC_DEF("seek", 3, js_os_seek ), - JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ), - JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ), - JS_CFUNC_DEF("isatty", 1, js_os_isatty ), - JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ), - JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), - JS_CFUNC_DEF("remove", 1, js_os_remove ), - JS_CFUNC_DEF("rename", 2, js_os_rename ), - JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), - JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), - JS_CFUNC_DEF("signal", 2, js_os_signal ), - OS_FLAG(SIGINT), - OS_FLAG(SIGABRT), - OS_FLAG(SIGFPE), - OS_FLAG(SIGILL), - OS_FLAG(SIGSEGV), - OS_FLAG(SIGTERM), -#if !defined(_WIN32) - OS_FLAG(SIGQUIT), - OS_FLAG(SIGPIPE), - OS_FLAG(SIGALRM), - OS_FLAG(SIGUSR1), - OS_FLAG(SIGUSR2), - OS_FLAG(SIGCHLD), - OS_FLAG(SIGCONT), - OS_FLAG(SIGSTOP), - OS_FLAG(SIGTSTP), - OS_FLAG(SIGTTIN), - OS_FLAG(SIGTTOU), -#endif - JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ), - JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), - JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), - JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), - JS_CFUNC_DEF("chdir", 0, js_os_chdir ), - JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), - JS_CFUNC_DEF("readdir", 1, js_os_readdir ), - /* st_mode constants */ - OS_FLAG(S_IFMT), - OS_FLAG(S_IFIFO), - OS_FLAG(S_IFCHR), - OS_FLAG(S_IFDIR), - OS_FLAG(S_IFBLK), - OS_FLAG(S_IFREG), -#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 ), - JS_CFUNC_DEF("sleep", 1, js_os_sleep ), - JS_CFUNC_DEF("realpath", 1, js_os_realpath ), -#if !defined(_WIN32) - JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), - JS_CFUNC_DEF("symlink", 2, js_os_symlink ), - JS_CFUNC_DEF("readlink", 1, js_os_readlink ), - JS_CFUNC_DEF("exec", 1, js_os_exec ), - JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), - OS_FLAG(WNOHANG), - JS_CFUNC_DEF("pipe", 0, js_os_pipe ), - JS_CFUNC_DEF("kill", 2, js_os_kill ), - JS_CFUNC_DEF("dup", 1, js_os_dup ), - JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), -#endif -}; - -static int js_os_init(JSContext *ctx, JSModuleDef *m) -{ - os_poll_func = js_os_poll; - - /* OSTimer class */ - JS_NewClassID(&js_os_timer_class_id); - JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class); - -#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)); -} - -JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) -{ - JSModuleDef *m; - m = JS_NewCModule(ctx, module_name, js_os_init); - if (!m) - return NULL; - JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); -#ifdef USE_WORKER - JS_AddModuleExport(ctx, m, "Worker"); -#endif - return m; -} - -/**********************************************************/ - -static JSValue js_print(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int i; - const char *str; - size_t len; - - for(i = 0; i < argc; i++) { - if (i != 0) - putchar(' '); - str = JS_ToCStringLen(ctx, &len, argv[i]); - if (!str) - return JS_EXCEPTION; - fwrite(str, 1, len, stdout); - JS_FreeCString(ctx, str); - } - putchar('\n'); - return JS_UNDEFINED; -} - -void js_std_add_helpers(JSContext *ctx, int argc, char **argv) -{ - JSValue global_obj, console, args; - int i; - - /* XXX: should these global definitions be enumerable? */ - global_obj = JS_GetGlobalObject(ctx); - - console = JS_NewObject(ctx); - JS_SetPropertyStr(ctx, console, "log", - JS_NewCFunction(ctx, js_print, "log", 1)); - JS_SetPropertyStr(ctx, global_obj, "console", console); - - /* same methods as the mozilla JS shell */ - 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, "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); -} - -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, &ts->os_rw_handlers) { - JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); - free_rw_handler(rt, rh); - } - - 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, &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; - - exception_val = JS_GetException(ctx); - 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) -{ - JSContext *ctx1; - int err; - - for(;;) { - /* execute the pending jobs */ - for(;;) { - err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); - if (err <= 0) { - if (err < 0) { - js_std_dump_error(ctx1); - } - break; - } - } - - if (!os_poll_func || os_poll_func(ctx)) - break; - } -} - -void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int load_only) -{ - JSValue obj, val; - obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); - if (JS_IsException(obj)) - goto exception; - if (load_only) { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - js_module_set_import_meta(ctx, obj, FALSE, FALSE); - } - } else { - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - if (JS_ResolveModule(ctx, obj) < 0) { - JS_FreeValue(ctx, obj); - goto exception; - } - js_module_set_import_meta(ctx, obj, FALSE, TRUE); - } - val = JS_EvalFunction(ctx, obj); - if (JS_IsException(val)) { - exception: - js_std_dump_error(ctx); - exit(1); - } - JS_FreeValue(ctx, val); - } -} +/* + * QuickJS C library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 "include/quickjs/cutils.h" +#include "include/quickjs/list.h" +#include "quickjs-libc.h" + +/* TODO: + - add socket calls +*/ + +typedef struct { + struct list_head link; + int fd; + JSValue rw_func[2]; +} JSOSRWHandler; + +typedef struct { + struct list_head link; + int sig_num; + JSValue func; +} JSOSSignalHandler; + +typedef struct { + struct list_head link; + BOOL has_object; + int64_t timeout; + JSValue func; +} JSOSTimer; + +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 (*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) +{ + char fmtbuf[32]; + uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; + JSValue res; + DynBuf dbuf; + const char *fmt_str; + const uint8_t *fmt, *fmt_end; + const uint8_t *p; + char *q; + int i, c, len, mod; + size_t fmt_len; + int32_t int32_arg; + int64_t int64_arg; + double double_arg; + const char *string_arg; + /* Use indirect call to dbuf_printf to prevent gcc warning */ + int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf; + + js_std_dbuf_init(ctx, &dbuf); + + if (argc > 0) { + fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]); + if (!fmt_str) + goto fail; + + i = 1; + fmt = (const uint8_t *)fmt_str; + fmt_end = fmt + fmt_len; + while (fmt < fmt_end) { + for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++) + continue; + dbuf_put(&dbuf, p, fmt - p); + if (fmt >= fmt_end) + break; + q = fmtbuf; + *q++ = *fmt++; /* copy '%' */ + + /* 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; + *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); + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + } + } + } + + /* 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; + int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); + JS_FreeCString(ctx, string_arg); + } else { + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + } + /* handle utf-8 encoding explicitly */ + if ((unsigned)int32_arg > 0x10FFFF) + int32_arg = 0xFFFD; + /* ignore conversion flags, width and precision */ + len = unicode_to_utf8(cbuf, int32_arg); + dbuf_put(&dbuf, cbuf, len); + break; + + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (i >= argc) + goto missing; + if (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); + } + if (dbuf.error) { + res = JS_ThrowOutOfMemory(ctx); + } else { + if (fp) { + len = fwrite(dbuf.buf, 1, dbuf.size, fp); + res = JS_NewInt32(ctx, len); + } else { + res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + } + } + dbuf_free(&dbuf); + return res; + +fail: + dbuf_free(&dbuf); + return JS_EXCEPTION; +} + +uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) +{ + FILE *f; + uint8_t *buf; + size_t buf_len; + long lret; + + f = fopen(filename, "rb"); + if (!f) + return NULL; + if (fseek(f, 0, SEEK_END) < 0) + goto fail; + lret = ftell(f); + if (lret < 0) + goto fail; + /* XXX: on Linux, ftell() return LONG_MAX for directories */ + if (lret == LONG_MAX) { + errno = EISDIR; + goto fail; + } + buf_len = lret; + if (fseek(f, 0, SEEK_SET) < 0) + goto fail; + if (ctx) + buf = js_malloc(ctx, buf_len + 1); + else + buf = malloc(buf_len + 1); + if (!buf) + goto fail; + if (fread(buf, 1, buf_len, f) != buf_len) { + errno = EIO; + if (ctx) + js_free(ctx, buf); + else + free(buf); + fail: + fclose(f); + return NULL; + } + buf[buf_len] = '\0'; + fclose(f); + *pbuf_len = buf_len; + return buf; +} + +/* load and evaluate a file */ +static JSValue js_loadScript(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint8_t *buf; + const char *filename; + JSValue ret; + size_t buf_len; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load '%s'", filename); + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + ret = JS_Eval(ctx, (char *)buf, buf_len, filename, + JS_EVAL_TYPE_GLOBAL); + js_free(ctx, buf); + JS_FreeCString(ctx, filename); + return ret; +} + +/* 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); + + +#if defined(_WIN32) +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); + return NULL; +} +#else +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JSModuleDef *m; + void *hd; + JSInitModuleFunc *init; + char *filename; + + if (!strchr(module_name, '/')) { + /* must add a '/' so that the DLL is not searched in the + system library paths */ + filename = js_malloc(ctx, strlen(module_name) + 2 + 1); + if (!filename) + return NULL; + strcpy(filename, "./"); + strcpy(filename + 2, module_name); + } else { + filename = (char *)module_name; + } + + /* C module */ + hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); + if (filename != module_name) + js_free(ctx, filename); + if (!hd) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library", + module_name); + goto fail; + } + + init = dlsym(hd, "js_init_module"); + if (!init) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", + module_name); + goto fail; + } + + m = init(ctx, module_name); + if (!m) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", + module_name); + fail: + if (hd) + dlclose(hd); + return NULL; + } + return m; +} +#endif /* !_WIN32 */ + +int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, + JS_BOOL use_realpath, JS_BOOL is_main) +{ + JSModuleDef *m; + char buf[PATH_MAX + 16]; + JSValue meta_obj; + JSAtom module_name_atom; + const char *module_name; + + assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); + m = JS_VALUE_GET_PTR(func_val); + + module_name_atom = JS_GetModuleName(ctx, m); + module_name = JS_AtomToCString(ctx, module_name_atom); + JS_FreeAtom(ctx, module_name_atom); + if (!module_name) + return -1; + if (!strchr(module_name, ':')) { + strcpy(buf, "file://"); +#if !defined(_WIN32) + /* realpath() cannot be used with modules compiled with qjsc + because the corresponding module source code is not + necessarily present */ + if (use_realpath) { + char *res = realpath(module_name, buf + strlen(buf)); + if (!res) { + JS_ThrowTypeError(ctx, "realpath failure"); + JS_FreeCString(ctx, module_name); + return -1; + } + } else +#endif + { + pstrcat(buf, sizeof(buf), module_name); + } + } else { + pstrcpy(buf, sizeof(buf), module_name); + } + JS_FreeCString(ctx, module_name); + + meta_obj = JS_GetImportMeta(ctx, m); + if (JS_IsException(meta_obj)) + return -1; + JS_DefinePropertyValueStr(ctx, meta_obj, "url", + JS_NewString(ctx, buf), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, meta_obj, "main", + JS_NewBool(ctx, is_main), + JS_PROP_C_W_E); + JS_FreeValue(ctx, meta_obj); + return 0; +} + +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + + if (has_suffix(module_name, ".so")) { + m = js_module_loader_so(ctx, module_name); + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + /* XXX: could propagate the exception */ + js_module_set_import_meta(ctx, func_val, TRUE, FALSE); + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static JSValue js_std_exit(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int status; + if (JS_ToInt32(ctx, &status, argv[0])) + status = -1; + exit(status); + return JS_UNDEFINED; +} + +static JSValue js_std_getenv(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *name, *str; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + str = getenv(name); + JS_FreeCString(ctx, name); + if (!str) + return JS_UNDEFINED; + else + return JS_NewString(ctx, str); +} + +#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) +{ + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +static int interrupt_handler(JSRuntime *rt, void *opaque) +{ + return (os_pending_signals >> SIGINT) & 1; +} + +static int get_bool_option(JSContext *ctx, BOOL *pbool, + JSValueConst obj, + const char *option) +{ + JSValue val; + val = JS_GetPropertyStr(ctx, obj, option); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) { + *pbool = JS_ToBool(ctx, val); + } + JS_FreeValue(ctx, val); + return 0; +} + +static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + 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 (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { + /* install the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); + } + flags = JS_EVAL_TYPE_GLOBAL; + if (backtrace_barrier) + flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; + ret = JS_Eval(ctx, str, len, "", flags); + JS_FreeCString(ctx, str); + if (!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); + /* convert the uncatchable "interrupted" error into a normal error + so that it can be caught by the REPL */ + if (JS_IsException(ret)) + JS_ResetUncatchableError(ctx); + } + return ret; +} + +static JSClassID js_std_file_class_id; + +typedef struct { + FILE *f; + BOOL close_in_finalizer; + BOOL is_popen; +} JSSTDFile; + +static void js_std_file_finalizer(JSRuntime *rt, JSValue val) +{ + JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id); + if (s) { + if (s->f && s->close_in_finalizer) { + if (s->is_popen) + pclose(s->f); + else + fclose(s->f); + } + js_free_rt(rt, s); + } +} + +static ssize_t js_get_errno(ssize_t ret) +{ + if (ret == -1) + ret = -errno; + return ret; +} + +static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int err; + if (JS_ToInt32(ctx, &err, argv[0])) + return JS_EXCEPTION; + return JS_NewString(ctx, strerror(err)); +} + +static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue 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, + BOOL close_in_finalizer, + BOOL is_popen) +{ + JSSTDFile *s; + JSValue obj; + obj = JS_NewObjectClass(ctx, js_std_file_class_id); + if (JS_IsException(obj)) + return obj; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + s->close_in_finalizer = close_in_finalizer; + s->is_popen = is_popen; + s->f = f; + JS_SetOpaque(obj, s); + return obj; +} + +static 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; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+b")] != '\0') { + 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_NULL; + return js_new_std_file(ctx, f, TRUE, FALSE); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +static JSValue js_std_popen(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + int err; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rw")] != '\0') { + JS_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_NULL; + return js_new_std_file(ctx, f, TRUE, TRUE); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *mode; + FILE *f; + int fd, err; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+")] != '\0') { + JS_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_NULL; + return js_new_std_file(ctx, f, TRUE, FALSE); + fail: + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f; + f = tmpfile(); + if (argc >= 1) + js_set_error_object(ctx, argv[0], f ? 0 : errno); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, TRUE, FALSE); +} + +static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_printf_internal(ctx, argc, argv, NULL); +} + +static JSValue js_std_printf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_printf_internal(ctx, argc, argv, stdout); +} + +static FILE *js_std_file_get(JSContext *ctx, JSValueConst obj) +{ + JSSTDFile *s = JS_GetOpaque2(ctx, obj, js_std_file_class_id); + if (!s) + return NULL; + if (!s->f) { + JS_ThrowTypeError(ctx, "invalid file handle"); + return NULL; + } + return s->f; +} + +static JSValue js_std_file_puts(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + FILE *f; + int i; + const char *str; + size_t len; + + if (magic == 0) { + f = stdout; + } else { + f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + } + + for(i = 0; i < argc; i++) { + str = JS_ToCStringLen(ctx, &len, argv[i]); + if (!str) + return JS_EXCEPTION; + fwrite(str, 1, len, f); + JS_FreeCString(ctx, str); + } + return JS_UNDEFINED; +} + +static JSValue js_std_file_close(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSSTDFile *s = JS_GetOpaque2(ctx, this_val, js_std_file_class_id); + int err; + if (!s) + return JS_EXCEPTION; + if (!s->f) + return JS_ThrowTypeError(ctx, "invalid file handle"); + if (s->is_popen) + err = js_get_errno(pclose(s->f)); + else + err = js_get_errno(fclose(s->f)); + s->f = NULL; + return JS_NewInt32(ctx, err); +} + +static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return js_printf_internal(ctx, argc, argv, f); +} + +static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + fflush(f); + return JS_UNDEFINED; +} + +static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_bigint) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + if (!f) + return JS_EXCEPTION; +#if defined(__linux__) + pos = ftello(f); +#else + pos = ftell(f); +#endif + 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, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + int whence, ret; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt64Ext(ctx, &pos, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[1])) + return JS_EXCEPTION; +#if defined(__linux__) + ret = fseeko(f, pos, whence); +#else + ret = fseek(f, pos, whence); +#endif + if (ret < 0) + ret = -errno; + return JS_NewInt32(ctx, ret); +} + +static JSValue js_std_file_eof(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewBool(ctx, feof(f)); +} + +static JSValue js_std_file_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) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fileno(f)); +} + +static JSValue js_std_file_read_write(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + FILE *f = js_std_file_get(ctx, this_val); + uint64_t pos, len; + size_t size, ret; + uint8_t *buf; + + if (!f) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[0]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = fwrite(buf + pos, 1, len, f); + else + ret = fread(buf + pos, 1, len, f); + return JS_NewInt64(ctx, ret); +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_getline(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + + if (!f) + return JS_EXCEPTION; + + js_std_dbuf_init(ctx, &dbuf); + for(;;) { + c = fgetc(f); + if (c == EOF) { + if (dbuf.size == 0) { + /* EOF */ + dbuf_free(&dbuf); + return JS_NULL; + } else { + break; + } + } + if (c == '\n') + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_ThrowOutOfMemory(ctx); + } + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_readAsString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + uint64_t max_size64; + size_t max_size; + JSValueConst max_size_val; + + if (!f) + return JS_EXCEPTION; + + if (argc >= 1) + max_size_val = argv[0]; + else + max_size_val = JS_UNDEFINED; + max_size = (size_t)-1; + if (!JS_IsUndefined(max_size_val)) { + if (JS_ToIndex(ctx, &max_size64, max_size_val)) + return JS_EXCEPTION; + if (max_size64 < max_size) + max_size = max_size64; + } + + js_std_dbuf_init(ctx, &dbuf); + while (max_size != 0) { + c = fgetc(f); + if (c == EOF) + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_EXCEPTION; + } + max_size--; + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +static JSValue js_std_file_getByte(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fgetc(f)); +} + +static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &c, argv[0])) + return JS_EXCEPTION; + c = fputc(c, f); + return JS_NewInt32(ctx, c); +} + +/* urlGet */ + +#define URL_GET_PROGRAM "curl -s -i" +#define URL_GET_BUF_SIZE 4096 + +static int http_get_header_line(FILE *f, char *buf, size_t buf_size, + DynBuf *dbuf) +{ + int c; + char *p; + + p = buf; + for(;;) { + c = fgetc(f); + if (c < 0) + return -1; + if ((p - buf) < buf_size - 1) + *p++ = c; + if (dbuf) + dbuf_putc(dbuf, c); + if (c == '\n') + break; + } + *p = '\0'; + return 0; +} + +static int http_get_status(const char *buf) +{ + const char *p = buf; + while (*p != ' ' && *p != '\0') + p++; + if (*p != ' ') + return 0; + while (*p == ' ') + p++; + return atoi(p); +} + +static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *url; + DynBuf cmd_buf; + DynBuf data_buf_s, *data_buf = &data_buf_s; + DynBuf header_buf_s, *header_buf = &header_buf_s; + char *buf; + size_t i, len; + int c, status; + JSValue response = JS_UNDEFINED, ret_obj; + JSValueConst options_obj; + FILE *f; + BOOL binary_flag, full_flag; + + url = JS_ToCString(ctx, argv[0]); + if (!url) + return JS_EXCEPTION; + + binary_flag = FALSE; + full_flag = FALSE; + + if (argc >= 2) { + options_obj = argv[1]; + + if (get_bool_option(ctx, &binary_flag, options_obj, "binary")) + goto fail_obj; + + if (get_bool_option(ctx, &full_flag, options_obj, "full")) { + fail_obj: + JS_FreeCString(ctx, url); + return JS_EXCEPTION; + } + } + + js_std_dbuf_init(ctx, &cmd_buf); + dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM); + len = strlen(url); + for(i = 0; i < len; i++) { + c = url[i]; + if (c == '\'' || c == '\\') + dbuf_putc(&cmd_buf, '\\'); + dbuf_putc(&cmd_buf, c); + } + JS_FreeCString(ctx, url); + dbuf_putstr(&cmd_buf, "''"); + dbuf_putc(&cmd_buf, '\0'); + if (dbuf_error(&cmd_buf)) { + dbuf_free(&cmd_buf); + return JS_EXCEPTION; + } + // printf("%s\n", (char *)cmd_buf.buf); + f = popen((char *)cmd_buf.buf, "r"); + dbuf_free(&cmd_buf); + if (!f) { + return JS_ThrowTypeError(ctx, "could not start curl"); + } + + js_std_dbuf_init(ctx, data_buf); + js_std_dbuf_init(ctx, header_buf); + + buf = js_malloc(ctx, URL_GET_BUF_SIZE); + if (!buf) + goto fail; + + /* get the HTTP status */ + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) { + status = 0; + goto bad_header; + } + status = http_get_status(buf); + if (!full_flag && !(status >= 200 && status <= 299)) { + goto bad_header; + } + + /* wait until there is an empty line */ + for(;;) { + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { + bad_header: + response = JS_NULL; + goto done; + } + if (!strcmp(buf, "\r\n")) + break; + } + if (dbuf_error(header_buf)) + goto fail; + header_buf->size -= 2; /* remove the trailing CRLF */ + + /* download the data */ + for(;;) { + len = fread(buf, 1, URL_GET_BUF_SIZE, f); + if (len == 0) + break; + dbuf_put(data_buf, (uint8_t *)buf, len); + } + if (dbuf_error(data_buf)) + goto fail; + if (binary_flag) { + response = JS_NewArrayBufferCopy(ctx, + data_buf->buf, data_buf->size); + } else { + response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size); + } + 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); + if (JS_IsException(ret_obj)) + goto fail; + JS_DefinePropertyValueStr(ctx, ret_obj, "response", + response, + 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; + } + dbuf_free(header_buf); + return ret_obj; + fail: + if (f) + pclose(f); + js_free(ctx, buf); + if (data_buf) + dbuf_free(data_buf); + if (header_buf) + dbuf_free(header_buf); + JS_FreeValue(ctx, response); + return JS_EXCEPTION; +} + +static JSClassDef js_std_file_class = { + "FILE", + .finalizer = js_std_file_finalizer, +}; + +static const JSCFunctionListEntry js_std_error_props[] = { + /* various errno values */ +#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + DEF(EINVAL), + DEF(EIO), + DEF(EACCES), + DEF(EEXIST), + DEF(ENOSPC), + DEF(ENOSYS), + DEF(EBUSY), + DEF(ENOENT), + DEF(EPERM), + DEF(EPIPE), + DEF(EBADF), +#undef DEF +}; + +static const JSCFunctionListEntry js_std_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[] = { + 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_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, ... */ +}; + +static int js_std_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue proto; + + /* FILE class */ + /* the class ID is created once */ + JS_NewClassID(&js_std_file_class_id); + /* the class is created once per runtime */ + JS_NewClass(JS_GetRuntime(ctx), js_std_file_class_id, &js_std_file_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs, + countof(js_std_file_proto_funcs)); + JS_SetClassProto(ctx, js_std_file_class_id, proto); + + JS_SetModuleExportList(ctx, m, js_std_funcs, + countof(js_std_funcs)); + JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE)); + JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE)); + JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE)); + return 0; +} + +JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_std_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs)); + JS_AddModuleExport(ctx, m, "in"); + JS_AddModuleExport(ctx, m, "out"); + JS_AddModuleExport(ctx, m, "err"); + return m; +} + +/**********************************************************/ +/* 'os' object */ + +static JSValue js_os_open(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + int flags, mode, ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &flags, argv[1])) + goto fail; + if (argc >= 3 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32(ctx, &mode, argv[2])) { + fail: + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + } else { + mode = 0666; + } +#if defined(_WIN32) + /* force binary mode by default */ + if (!(flags & O_TEXT)) + flags |= O_BINARY; +#endif + ret = js_get_errno(open(filename, flags, mode)); + JS_FreeCString(ctx, filename); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_close(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, ret; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = 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; + int64_t pos, ret; + BOOL is_bigint; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + 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); + 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, + int argc, JSValueConst *argv, int magic) +{ + int fd; + uint64_t pos, len; + size_t size; + ssize_t ret; + uint8_t *buf; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[2])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[3])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[1]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = js_get_errno(write(fd, buf + pos, len)); + else + ret = js_get_errno(read(fd, buf + pos, len)); + return JS_NewInt64(ctx, ret); +} + +static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + return JS_NewBool(ctx, (isatty(fd) != 0)); +} + +#if defined(_WIN32) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + + if (!GetConsoleScreenBufferInfo(handle, &info)) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E); + return obj; +} + +/* Windows 10 built-in VT100 emulation */ +#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 + +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + HANDLE handle; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT); + _setmode(fd, _O_BINARY); + if (fd == 0) { + handle = (HANDLE)_get_osfhandle(1); /* corresponding output */ + SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } + return JS_UNDEFINED; +} +#else +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd; + struct winsize ws; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && + ws.ws_col >= 4 && ws.ws_row >= 4) { + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E); + return obj; + } else { + return JS_NULL; + } +} + +static struct termios oldtty; + +static void term_exit(void) +{ + tcsetattr(0, TCSANOW, &oldtty); +} + +/* XXX: should add a way to go back to normal mode */ +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + struct termios tty; + int fd; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + + memset(&tty, 0, sizeof(tty)); + tcgetattr(fd, &tty); + oldtty = tty; + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + tcsetattr(fd, TCSANOW, &tty); + + atexit(term_exit); + return JS_UNDEFINED; +} + +#endif /* !_WIN32 */ + +static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *filename; + int ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; +#if defined(_WIN32) + { + struct stat st; + if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode)) { + ret = rmdir(filename); + } else { + ret = unlink(filename); + } + } +#else + ret = remove(filename); +#endif + ret = js_get_errno(ret); + JS_FreeCString(ctx, filename); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_rename(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *oldpath, *newpath; + int ret; + + oldpath = JS_ToCString(ctx, argv[0]); + if (!oldpath) + return JS_EXCEPTION; + newpath = JS_ToCString(ctx, argv[1]); + if (!newpath) { + JS_FreeCString(ctx, oldpath); + return JS_EXCEPTION; + } + ret = js_get_errno(rename(oldpath, newpath)); + JS_FreeCString(ctx, oldpath); + JS_FreeCString(ctx, newpath); + return JS_NewInt32(ctx, ret); +} + +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, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == fd) + return rh; + } + return NULL; +} + +static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) +{ + int i; + list_del(&rh->link); + for(i = 0; i < 2; i++) { + JS_FreeValueRT(rt, rh->rw_func[i]); + } + js_free_rt(rt, rh); +} + +static JSValue js_os_setReadHandler(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSOSRWHandler *rh; + int fd; + JSValueConst func; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + func = argv[1]; + if (JS_IsNull(func)) { + rh = find_rh(ts, fd); + if (rh) { + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_NULL; + if (JS_IsNull(rh->rw_func[0]) && + JS_IsNull(rh->rw_func[1])) { + /* remove the entry */ + free_rw_handler(JS_GetRuntime(ctx), rh); + } + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + rh = find_rh(ts, fd); + if (!rh) { + rh = js_mallocz(ctx, sizeof(*rh)); + if (!rh) + return JS_EXCEPTION; + rh->fd = fd; + rh->rw_func[0] = JS_NULL; + rh->rw_func[1] = JS_NULL; + list_add_tail(&rh->link, &ts->os_rw_handlers); + } + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num) +{ + JSOSSignalHandler *sh; + struct list_head *el; + list_for_each(el, &ts->os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + if (sh->sig_num == sig_num) + return sh; + } + return NULL; +} + +static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh) +{ + list_del(&sh->link); + JS_FreeValueRT(rt, sh->func); + js_free_rt(rt, sh); +} + +static void os_signal_handler(int sig_num) +{ + os_pending_signals |= ((uint64_t)1 << sig_num); +} + +#if defined(_WIN32) +typedef void (*sighandler_t)(int sig_num); +#endif + +static JSValue js_os_signal(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + 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; + if (sig_num >= 64) + return JS_ThrowRangeError(ctx, "invalid signal number"); + func = argv[1]; + /* func = null: SIG_DFL, func = undefined, SIG_IGN */ + if (JS_IsNull(func) || JS_IsUndefined(func)) { + sh = find_sh(ts, sig_num); + if (sh) { + free_sh(JS_GetRuntime(ctx), sh); + } + if (JS_IsNull(func)) + handler = SIG_DFL; + else + handler = SIG_IGN; + signal(sig_num, handler); + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + sh = find_sh(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, &ts->os_signal_handlers); + } + JS_FreeValue(ctx, sh->func); + sh->func = JS_DupValue(ctx, func); + signal(sig_num, os_signal_handler); + } + return JS_UNDEFINED; +} + +#if defined(__linux__) || defined(__APPLE__) +static int64_t get_time_ms(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); +} +#else +/* more portable, but does not work if the date is updated */ +static int64_t get_time_ms(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); +} +#endif + +static void unlink_timer(JSRuntime *rt, JSOSTimer *th) +{ + if (th->link.prev) { + list_del(&th->link); + th->link.prev = th->link.next = NULL; + } +} + +static void free_timer(JSRuntime *rt, JSOSTimer *th) +{ + JS_FreeValueRT(rt, th->func); + js_free_rt(rt, th); +} + +static JSClassID js_os_timer_class_id; + +static void js_os_timer_finalizer(JSRuntime *rt, JSValue val) +{ + JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); + if (th) { + th->has_object = FALSE; + if (!th->link.prev) + free_timer(rt, th); + } +} + +static void js_os_timer_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); + if (th) { + JS_MarkValue(rt, th->func, mark_func); + } +} + +static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + int64_t delay; + JSValueConst func; + JSOSTimer *th; + JSValue obj; + + func = argv[0]; + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (JS_ToInt64(ctx, &delay, argv[1])) + return JS_EXCEPTION; + obj = JS_NewObjectClass(ctx, js_os_timer_class_id); + if (JS_IsException(obj)) + return obj; + th = js_mallocz(ctx, sizeof(*th)); + if (!th) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + th->has_object = TRUE; + th->timeout = get_time_ms() + delay; + th->func = JS_DupValue(ctx, func); + list_add_tail(&th->link, &ts->os_timers); + JS_SetOpaque(obj, th); + return obj; +} + +static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id); + if (!th) + return JS_EXCEPTION; + unlink_timer(JS_GetRuntime(ctx), th); + return JS_UNDEFINED; +} + +static JSClassDef js_os_timer_class = { + "OSTimer", + .finalizer = js_os_timer_finalizer, + .gc_mark = js_os_timer_mark, +}; + +static void call_handler(JSContext *ctx, JSValueConst func) +{ + JSValue ret, func1; + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func1 = JS_DupValue(ctx, func); + ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); + JS_FreeValue(ctx, func1); + if (JS_IsException(ret)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, ret); +} + +#if defined(_WIN32) + +static int js_os_poll(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + int min_delay, console_fd; + int64_t cur_time, delay; + JSOSRWHandler *rh; + struct list_head *el; + + /* XXX: handle signals if useful */ + + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers)) + return -1; /* no more events */ + + /* XXX: only timers and basic console input are supported */ + if (!list_empty(&ts->os_timers)) { + cur_time = get_time_ms(); + min_delay = 10000; + list_for_each(el, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + delay = th->timeout - cur_time; + if (delay <= 0) { + JSValue func; + /* the timer expired */ + func = th->func; + th->func = JS_UNDEFINED; + unlink_timer(rt, th); + if (!th->has_object) + free_timer(rt, th); + call_handler(ctx, func); + JS_FreeValue(ctx, func); + return 0; + } else if (delay < min_delay) { + min_delay = delay; + } + } + } else { + min_delay = -1; + } + + console_fd = -1; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + console_fd = rh->fd; + break; + } + } + + if (console_fd >= 0) { + DWORD ti, ret; + HANDLE handle; + if (min_delay == -1) + ti = INFINITE; + else + ti = min_delay; + handle = (HANDLE)_get_osfhandle(console_fd); + ret = WaitForSingleObject(handle, ti); + if (ret == WAIT_OBJECT_0) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { + call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + break; + } + } + } + } else { + Sleep(min_delay); + } + 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; + JSOSRWHandler *rh; + struct list_head *el; + struct timeval tv, *tvp; + + /* 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, &ts->os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + mask = (uint64_t)1 << sh->sig_num; + if (os_pending_signals & mask) { + os_pending_signals &= ~mask; + call_handler(ctx, sh->func); + return 0; + } + } + } + + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) && + list_empty(&ts->port_list)) + return -1; /* no more events */ + + if (!list_empty(&ts->os_timers)) { + cur_time = get_time_ms(); + min_delay = 10000; + list_for_each(el, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + delay = th->timeout - cur_time; + if (delay <= 0) { + JSValue func; + /* the timer expired */ + func = th->func; + th->func = JS_UNDEFINED; + unlink_timer(rt, th); + if (!th->has_object) + free_timer(rt, th); + call_handler(ctx, func); + JS_FreeValue(ctx, func); + return 0; + } else if (delay < min_delay) { + min_delay = delay; + } + } + tv.tv_sec = min_delay / 1000; + tv.tv_usec = (min_delay % 1000) * 1000; + tvp = &tv; + } else { + tvp = NULL; + } + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + fd_max = -1; + list_for_each(el, &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])) + FD_SET(rh->fd, &rfds); + 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, &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 */ + goto done; + } + 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 */ + 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 */ + +static JSValue make_obj_error(JSContext *ctx, + JSValue obj, + int err) +{ + JSValue arr; + if (JS_IsException(obj)) + return obj; + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return JS_EXCEPTION; + JS_DefinePropertyValueUint32(ctx, arr, 0, obj, + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err), + JS_PROP_C_W_E); + return arr; +} + +static JSValue make_string_error(JSContext *ctx, + const char *buf, + int err) +{ + return make_obj_error(ctx, JS_NewString(ctx, buf), err); +} + +/* return [cwd, errorcode] */ +static JSValue js_os_getcwd(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + char buf[PATH_MAX]; + int err; + + if (!getcwd(buf, sizeof(buf))) { + buf[0] = '\0'; + err = errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} + +static JSValue js_os_chdir(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *target; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + 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, + int argc, JSValueConst *argv) +{ + int mode, ret; + const char *path; + + if (argc >= 2) { + if (JS_ToInt32(ctx, &mode, argv[1])) + return JS_EXCEPTION; + } else { + mode = 0777; + } + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#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_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, + int argc, JSValueConst *argv, int is_lstat) +{ + const char *path; + int err, res; + struct stat st; + JSValue obj; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if 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; + obj = JS_NULL; + } else { + err = 0; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JS_DefinePropertyValueStr(ctx, obj, "dev", + JS_NewInt64(ctx, st.st_dev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ino", + JS_NewInt64(ctx, st.st_ino), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mode", + JS_NewInt32(ctx, st.st_mode), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "nlink", + JS_NewInt64(ctx, st.st_nlink), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "uid", + JS_NewInt64(ctx, st.st_uid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "gid", + JS_NewInt64(ctx, st.st_gid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "rdev", + JS_NewInt64(ctx, st.st_rdev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "size", + JS_NewInt64(ctx, st.st_size), + JS_PROP_C_W_E); +#if !defined(_WIN32) + JS_DefinePropertyValueStr(ctx, obj, "blocks", + JS_NewInt64(ctx, st.st_blocks), + JS_PROP_C_W_E); +#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); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)), + JS_PROP_C_W_E); +#else + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)), + JS_PROP_C_W_E); +#endif + } + return make_obj_error(ctx, obj, err); +} + +#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); +} + +/* sleep(delay_ms) */ +static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t delay; + int ret; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + if (delay < 0) + delay = 0; +#if defined(_WIN32) + { + if (delay > INT32_MAX) + delay = INT32_MAX; + Sleep(delay); + ret = 0; + } +#else + { + struct timespec ts; + + ts.tv_sec = delay / 1000; + ts.tv_nsec = (delay % 1000) * 1000000; + ret = js_get_errno(nanosleep(&ts, NULL)); + } +#endif + return JS_NewInt32(ctx, ret); +} + +#if defined(_WIN32) +static char *realpath(const char *path, char *buf) +{ + if (!_fullpath(buf, path, PATH_MAX)) { + errno = ENOENT; + return NULL; + } else { + return buf; + } +} +#endif + +/* 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); +} + +#if !defined(_WIN32) +static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *target, *linkpath; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + linkpath = JS_ToCString(ctx, argv[1]); + if (!linkpath) { + JS_FreeCString(ctx, target); + return JS_EXCEPTION; + } + err = js_get_errno(symlink(target, linkpath)); + JS_FreeCString(ctx, target); + JS_FreeCString(ctx, linkpath); + return JS_NewInt32(ctx, err); +} + +/* return [path, errorcode] */ +static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *path; + char buf[PATH_MAX]; + int err; + ssize_t res; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = readlink(path, buf, sizeof(buf) - 1); + if (res < 0) { + buf[0] = '\0'; + err = errno; + } else { + buf[res] = '\0'; + err = 0; + } + JS_FreeCString(ctx, path); + return make_string_error(ctx, buf, err); +} + +static char **build_envp(JSContext *ctx, JSValueConst obj) +{ + uint32_t len, i; + JSPropertyEnum *tab; + char **envp, *pair; + const char *key, *str; + JSValue val; + size_t key_len, str_len; + + if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) + return NULL; + 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; + } + 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); + } + done: + 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; +} + +/* execvpe is not available on non GNU systems */ +static int my_execvpe(const char *filename, char **argv, char **envp) +{ + char *path, *p, *p_next, *p1; + char buf[PATH_MAX]; + size_t filename_len, path_len; + BOOL eacces_error; + + 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) + 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 */ +static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst options, args = argv[0]; + JSValue val, ret_val; + const char **exec_argv, *file = NULL, *str, *cwd = NULL; + 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)) + return JS_EXCEPTION; + ret = JS_ToUint32(ctx, &exec_argc, val); + JS_FreeValue(ctx, val); + if (ret) + return JS_EXCEPTION; + /* arbitrary limit to avoid overflow */ + if (exec_argc < 1 || exec_argc > 65535) { + return JS_ThrowTypeError(ctx, "invalid number of arguments"); + } + exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1)); + if (!exec_argv) + return JS_EXCEPTION; + for(i = 0; i < exec_argc; i++) { + val = JS_GetPropertyUint32(ctx, args, i); + if (JS_IsException(val)) + goto exception; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto exception; + exec_argv[i] = str; + } + exec_argv[exec_argc] = NULL; + + for(i = 0; i < 3; i++) + std_fds[i] = i; + + /* get the options, if any */ + if (argc >= 2) { + options = argv[1]; + + if (get_bool_option(ctx, &block_flag, options, "block")) + goto exception; + if (get_bool_option(ctx, &use_path, options, "usePath")) + goto exception; + + val = JS_GetPropertyStr(ctx, options, "file"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + file = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!file) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "cwd"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + cwd = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!cwd) + goto exception; + } + + /* stdin/stdout/stderr handles */ + for(i = 0; i < 3; i++) { + val = JS_GetPropertyStr(ctx, options, std_name[i]); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + int fd; + ret = JS_ToInt32(ctx, &fd, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + std_fds[i] = fd; + } + } + + 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(); + if (pid < 0) { + JS_ThrowTypeError(ctx, "fork error"); + goto exception; + } + if (pid == 0) { + /* child */ + int fd_max = sysconf(_SC_OPEN_MAX); + + /* remap the stdin/stdout/stderr handles if necessary */ + for(i = 0; i < 3; i++) { + if (std_fds[i] != i) { + if (dup2(std_fds[i], i) < 0) + _exit(127); + } + } + + for(i = 3; i < fd_max; i++) + close(i); + if (cwd) { + if (chdir(cwd) < 0) + _exit(127); + } + if (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 = my_execvpe(file, (char **)exec_argv, envp); + else + ret = execve(file, (char **)exec_argv, envp); + _exit(127); + } + /* parent */ + if (block_flag) { + for(;;) { + ret = waitpid(pid, &status, 0); + if (ret == pid) { + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + break; + } else if (WIFSIGNALED(status)) { + ret = -WTERMSIG(status); + break; + } + } + } + } else { + ret = pid; + } + ret_val = JS_NewInt32(ctx, ret); + done: + JS_FreeCString(ctx, file); + JS_FreeCString(ctx, cwd); + for(i = 0; i < exec_argc; i++) + JS_FreeCString(ctx, exec_argv[i]); + js_free(ctx, exec_argv); + 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; + goto done; +} + +/* waitpid(pid, block) -> [pid, status] */ +static JSValue js_os_waitpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid, status, options, ret; + JSValue obj; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &options, argv[1])) + return JS_EXCEPTION; + + ret = waitpid(pid, &status, options); + if (ret < 0) { + ret = -errno; + status = 0; + } + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + return obj; +} + +/* pipe() -> [read_fd, write_fd] or null if error */ +static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pipe_fds[2], ret; + JSValue obj; + + ret = pipe(pipe_fds); + if (ret < 0) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), + JS_PROP_C_W_E); + return obj; +} + +/* kill(pid, sig) */ +static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid, sig, ret; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &sig, argv[1])) + return JS_EXCEPTION; + ret = js_get_errno(kill(pid, sig)); + return JS_NewInt32(ctx, ret); +} + +/* dup(fd) */ +static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = js_get_errno(dup(fd)); + return JS_NewInt32(ctx, ret); +} + +/* dup2(fd) */ +static JSValue js_os_dup2(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int fd, fd2, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &fd2, argv[1])) + return JS_EXCEPTION; + ret = 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__) +#define OS_PLATFORM "darwin" +#elif defined(EMSCRIPTEN) +#define OS_PLATFORM "js" +#else +#define OS_PLATFORM "linux" +#endif + +#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + +static const JSCFunctionListEntry js_os_funcs[] = { + JS_CFUNC_DEF("open", 2, js_os_open ), + OS_FLAG(O_RDONLY), + OS_FLAG(O_WRONLY), + OS_FLAG(O_RDWR), + OS_FLAG(O_APPEND), + OS_FLAG(O_CREAT), + OS_FLAG(O_EXCL), + OS_FLAG(O_TRUNC), +#if defined(_WIN32) + OS_FLAG(O_BINARY), + OS_FLAG(O_TEXT), +#endif + JS_CFUNC_DEF("close", 1, js_os_close ), + JS_CFUNC_DEF("seek", 3, js_os_seek ), + JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ), + JS_CFUNC_DEF("isatty", 1, js_os_isatty ), + JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ), + JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), + JS_CFUNC_DEF("remove", 1, js_os_remove ), + JS_CFUNC_DEF("rename", 2, js_os_rename ), + JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), + JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), + JS_CFUNC_DEF("signal", 2, js_os_signal ), + OS_FLAG(SIGINT), + OS_FLAG(SIGABRT), + OS_FLAG(SIGFPE), + OS_FLAG(SIGILL), + OS_FLAG(SIGSEGV), + OS_FLAG(SIGTERM), +#if !defined(_WIN32) + OS_FLAG(SIGQUIT), + OS_FLAG(SIGPIPE), + OS_FLAG(SIGALRM), + OS_FLAG(SIGUSR1), + OS_FLAG(SIGUSR2), + OS_FLAG(SIGCHLD), + OS_FLAG(SIGCONT), + OS_FLAG(SIGSTOP), + OS_FLAG(SIGTSTP), + OS_FLAG(SIGTTIN), + OS_FLAG(SIGTTOU), +#endif + JS_CFUNC_DEF("setTimeout", 2, js_os_setTimeout ), + JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), + JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), + JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), + JS_CFUNC_DEF("chdir", 0, js_os_chdir ), + JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), + JS_CFUNC_DEF("readdir", 1, js_os_readdir ), + /* st_mode constants */ + OS_FLAG(S_IFMT), + OS_FLAG(S_IFIFO), + OS_FLAG(S_IFCHR), + OS_FLAG(S_IFDIR), + OS_FLAG(S_IFBLK), + OS_FLAG(S_IFREG), +#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 ), + JS_CFUNC_DEF("sleep", 1, js_os_sleep ), + JS_CFUNC_DEF("realpath", 1, js_os_realpath ), +#if !defined(_WIN32) + JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), + JS_CFUNC_DEF("symlink", 2, js_os_symlink ), + JS_CFUNC_DEF("readlink", 1, js_os_readlink ), + JS_CFUNC_DEF("exec", 1, js_os_exec ), + JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), + OS_FLAG(WNOHANG), + JS_CFUNC_DEF("pipe", 0, js_os_pipe ), + JS_CFUNC_DEF("kill", 2, js_os_kill ), + JS_CFUNC_DEF("dup", 1, js_os_dup ), + JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), +#endif +}; + +static int js_os_init(JSContext *ctx, JSModuleDef *m) +{ + os_poll_func = js_os_poll; + + /* OSTimer class */ + JS_NewClassID(&js_os_timer_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class); + +#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)); +} + +JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_os_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); +#ifdef USE_WORKER + JS_AddModuleExport(ctx, m, "Worker"); +#endif + return m; +} + +/**********************************************************/ + +static JSValue js_print(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int i; + const char *str; + size_t len; + + for(i = 0; i < argc; i++) { + if (i != 0) + putchar(' '); + str = JS_ToCStringLen(ctx, &len, argv[i]); + if (!str) + return JS_EXCEPTION; + fwrite(str, 1, len, stdout); + JS_FreeCString(ctx, str); + } + putchar('\n'); + return JS_UNDEFINED; +} + +void js_std_add_helpers(JSContext *ctx, int argc, char **argv) +{ + JSValue global_obj, console, args; + int i; + + /* XXX: should these global definitions be enumerable? */ + global_obj = JS_GetGlobalObject(ctx); + + console = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, console, "log", + JS_NewCFunction(ctx, js_print, "log", 1)); + JS_SetPropertyStr(ctx, global_obj, "console", console); + + /* same methods as the mozilla JS shell */ + 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, "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); +} + +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, &ts->os_rw_handlers) { + JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); + free_rw_handler(rt, rh); + } + + 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, &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; + + exception_val = JS_GetException(ctx); + 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) +{ + JSContext *ctx1; + int err; + + for(;;) { + /* execute the pending jobs */ + for(;;) { + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err <= 0) { + if (err < 0) { + js_std_dump_error(ctx1); + } + break; + } + } + + if (!os_poll_func || os_poll_func(ctx)) + break; + } +} + +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int load_only) +{ + JSValue obj, val; + obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + goto exception; + if (load_only) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + js_module_set_import_meta(ctx, obj, FALSE, FALSE); + } + } else { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + if (JS_ResolveModule(ctx, obj) < 0) { + JS_FreeValue(ctx, obj); + goto exception; + } + js_module_set_import_meta(ctx, obj, FALSE, TRUE); + } + val = JS_EvalFunction(ctx, obj); + if (JS_IsException(val)) { + exception: + js_std_dump_error(ctx); + exit(1); + } + JS_FreeValue(ctx, val); + } +} diff --git a/quickjs-libc.h b/quickjs-libc.h index fbbe5b016..ac5eae6c8 100644 --- a/quickjs-libc.h +++ b/quickjs-libc.h @@ -1,59 +1,59 @@ -/* - * QuickJS C library - * - * Copyright (c) 2017-2018 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef QUICKJS_LIBC_H -#define QUICKJS_LIBC_H - -#include -#include - -#include "quickjs.h" - -#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); -int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, - JS_BOOL use_realpath, JS_BOOL is_main); -JSModuleDef *js_module_loader(JSContext *ctx, - const char *module_name, void *opaque); -void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, - int flags); -void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, - JSValueConst reason, - JS_BOOL is_handled, void *opaque); -void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); - -#ifdef __cplusplus -} /* extern "C" { */ -#endif - -#endif /* QUICKJS_LIBC_H */ +/* + * QuickJS C library + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_LIBC_H +#define QUICKJS_LIBC_H + +#include +#include + +#include "include/quickjs/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); +int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val, + JS_BOOL use_realpath, JS_BOOL is_main); +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque); +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags); +void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise, + JSValueConst reason, + JS_BOOL is_handled, void *opaque); +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_LIBC_H */ diff --git a/readme.txt b/readme.txt index 789521d53..74fef48b3 100644 --- a/readme.txt +++ b/readme.txt @@ -1 +1 @@ -The main documentation is in doc/quickjs.pdf or doc/quickjs.html. +The main documentation is in doc/quickjs.pdf or doc/quickjs.html. diff --git a/release.sh b/release.sh index 26fba1b03..0266ae45d 100755 --- a/release.sh +++ b/release.sh @@ -1,158 +1,158 @@ -#!/bin/sh -# Release the QuickJS source code - -set -e - -version=`cat VERSION` - -if [ "$1" = "-h" ] ; then - echo "release.sh [release_list]" - echo "" - echo "release_list: extras binary win_binary quickjs" - - exit 1 -fi - -release_list="extras binary win_binary quickjs" - -if [ "$1" != "" ] ; then - release_list="$1" -fi - -#################################################" -# extras - -if echo $release_list | grep -w -q extras ; then - -d="quickjs-${version}" -name="quickjs-extras-${version}" -outdir="/tmp/${d}" - -rm -rf $outdir -mkdir -p $outdir $outdir/unicode $outdir/tests - -cp unicode/* $outdir/unicode -cp -a tests/bench-v8 $outdir/tests - -( cd /tmp && tar Jcvf /tmp/${name}.tar.xz ${d} ) - -fi - -#################################################" -# Windows binary release - -if echo $release_list | grep -w -q win_binary ; then - -# 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 - -make CONFIG_WIN32=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 . ) - -make CONFIG_WIN32=y clean - -# 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 echo $release_list | grep -w -q quickjs ; then - -make build_doc - -d="quickjs-${version}" -outdir="/tmp/${d}" - -rm -rf $outdir -mkdir -p $outdir $outdir/doc $outdir/tests $outdir/examples - -cp Makefile VERSION TODO Changelog readme.txt LICENSE \ - release.sh unicode_download.sh \ - qjs.c qjsc.c qjscalc.js repl.js \ - quickjs.c quickjs.h quickjs-atom.h \ - quickjs-libc.c quickjs-libc.h quickjs-opcode.h \ - cutils.c cutils.h list.h \ - libregexp.c libregexp.h libregexp-opcode.h \ - libunicode.c libunicode.h libunicode-table.h \ - libbf.c libbf.h \ - unicode_gen.c unicode_gen_def.h \ - run-test262.c test262o.conf test262.conf \ - test262o_errors.txt test262_errors.txt \ - $outdir - -cp tests/*.js tests/*.patch tests/bjson.c $outdir/tests - -cp examples/*.js examples/*.c $outdir/examples - -cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \ - doc/jsbignum.texi doc/jsbignum.html doc/jsbignum.pdf \ - $outdir/doc - -( cd /tmp && tar Jcvf /tmp/${d}.tar.xz ${d} ) - -fi +#!/bin/sh +# Release the QuickJS source code + +set -e + +version=`cat VERSION` + +if [ "$1" = "-h" ] ; then + echo "release.sh [release_list]" + echo "" + echo "release_list: extras binary win_binary quickjs" + + exit 1 +fi + +release_list="extras binary win_binary quickjs" + +if [ "$1" != "" ] ; then + release_list="$1" +fi + +#################################################" +# extras + +if echo $release_list | grep -w -q extras ; then + +d="quickjs-${version}" +name="quickjs-extras-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir $outdir/unicode $outdir/tests + +cp unicode/* $outdir/unicode +cp -a tests/bench-v8 $outdir/tests + +( cd /tmp && tar Jcvf /tmp/${name}.tar.xz ${d} ) + +fi + +#################################################" +# Windows binary release + +if echo $release_list | grep -w -q win_binary ; then + +# 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 + +make CONFIG_WIN32=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 . ) + +make CONFIG_WIN32=y clean + +# 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 echo $release_list | grep -w -q quickjs ; then + +make build_doc + +d="quickjs-${version}" +outdir="/tmp/${d}" + +rm -rf $outdir +mkdir -p $outdir $outdir/doc $outdir/tests $outdir/examples + +cp Makefile VERSION TODO Changelog readme.txt LICENSE \ + release.sh unicode_download.sh \ + qjs.c qjsc.c qjscalc.js repl.js \ + quickjs.c quickjs.h quickjs-atom.h \ + quickjs-libc.c quickjs-libc.h quickjs-opcode.h \ + cutils.c cutils.h list.h \ + libregexp.c libregexp.h libregexp-opcode.h \ + libunicode.c libunicode.h libunicode-table.h \ + libbf.c libbf.h \ + unicode_gen.c unicode_gen_def.h \ + run-test262.c test262o.conf test262.conf \ + test262o_errors.txt test262_errors.txt \ + $outdir + +cp tests/*.js tests/*.patch tests/bjson.c $outdir/tests + +cp examples/*.js examples/*.c $outdir/examples + +cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \ + doc/jsbignum.texi doc/jsbignum.html doc/jsbignum.pdf \ + $outdir/doc + +( cd /tmp && tar Jcvf /tmp/${d}.tar.xz ${d} ) + +fi diff --git a/repl.js b/repl.js index 484269e16..2fd693cb0 100644 --- a/repl.js +++ b/repl.js @@ -1,1566 +1,1566 @@ -/* - * QuickJS Read Eval Print Loop - * - * Copyright (c) 2017-2020 Fabrice Bellard - * Copyright (c) 2017-2020 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -"use strip"; - -import * as std from "std"; -import * as os from "os"; - -(function(g) { - /* add 'os' and 'std' bindings */ - g.os = os; - g.std = std; - - /* close global objects */ - var Object = g.Object; - var String = g.String; - var Array = g.Array; - var Date = g.Date; - var Math = g.Math; - var isFinite = g.isFinite; - var parseFloat = g.parseFloat; - - /* XXX: use preprocessor ? */ - var config_numcalc = (typeof os.open === "undefined"); - var has_jscalc = (typeof Fraction === "function"); - var has_bignum = (typeof BigFloat === "function"); - - var colors = { - none: "\x1b[0m", - black: "\x1b[30m", - red: "\x1b[31m", - green: "\x1b[32m", - yellow: "\x1b[33m", - blue: "\x1b[34m", - magenta: "\x1b[35m", - cyan: "\x1b[36m", - white: "\x1b[37m", - gray: "\x1b[30;1m", - grey: "\x1b[30;1m", - bright_red: "\x1b[31;1m", - bright_green: "\x1b[32;1m", - bright_yellow: "\x1b[33;1m", - bright_blue: "\x1b[34;1m", - bright_magenta: "\x1b[35;1m", - bright_cyan: "\x1b[36;1m", - bright_white: "\x1b[37;1m", - }; - - var styles; - if (config_numcalc) { - styles = { - 'default': 'black', - 'comment': 'white', - 'string': 'green', - 'regex': 'cyan', - 'number': 'green', - 'keyword': 'blue', - 'function': 'gray', - 'type': 'bright_magenta', - 'identifier': 'yellow', - 'error': 'bright_red', - 'result': 'black', - 'error_msg': 'bright_red', - }; - } else { - styles = { - 'default': 'bright_green', - 'comment': 'white', - 'string': 'bright_cyan', - 'regex': 'cyan', - 'number': 'green', - 'keyword': 'bright_white', - 'function': 'bright_yellow', - 'type': 'bright_magenta', - 'identifier': 'bright_green', - 'error': 'red', - 'result': 'bright_white', - 'error_msg': 'bright_red', - }; - } - - var history = []; - var clip_board = ""; - var prec; - var expBits; - var log2_10; - - var pstate = ""; - var prompt = ""; - var plen = 0; - var ps1; - if (config_numcalc) - ps1 = "> "; - else - ps1 = "qjs > "; - var ps2 = " ... "; - var utf8 = true; - var show_time = false; - var show_colors = true; - var eval_time = 0; - - var mexpr = ""; - var level = 0; - var cmd = ""; - var cursor_pos = 0; - var last_cmd = ""; - var last_cursor_pos = 0; - var history_index; - var this_fun, last_fun; - var quote_flag = false; - - var utf8_state = 0; - var utf8_val = 0; - - var term_fd; - var term_read_buf; - var term_width; - /* current X position of the cursor in the terminal */ - var term_cursor_x = 0; - - function termInit() { - var tab; - term_fd = std.in.fileno(); - - /* get the terminal size */ - term_width = 80; - if (os.isatty(term_fd)) { - if (os.ttyGetWinSize) { - tab = os.ttyGetWinSize(term_fd); - if (tab) - term_width = tab[0]; - } - if (os.ttySetRaw) { - /* set the TTY to raw mode */ - os.ttySetRaw(term_fd); - } - } - - /* install a Ctrl-C signal handler */ - os.signal(os.SIGINT, sigint_handler); - - /* install a handler to read stdin */ - term_read_buf = new Uint8Array(64); - os.setReadHandler(term_fd, term_read_handler); - } - - function sigint_handler() { - /* send Ctrl-C to readline */ - handle_byte(3); - } - - function term_read_handler() { - var l, i; - l = os.read(term_fd, term_read_buf.buffer, 0, term_read_buf.length); - for(i = 0; i < l; i++) - handle_byte(term_read_buf[i]); - } - - function handle_byte(c) { - if (!utf8) { - handle_char(c); - } else if (utf8_state !== 0 && (c >= 0x80 && c < 0xc0)) { - utf8_val = (utf8_val << 6) | (c & 0x3F); - utf8_state--; - if (utf8_state === 0) { - handle_char(utf8_val); - } - } else if (c >= 0xc0 && c < 0xf8) { - utf8_state = 1 + (c >= 0xe0) + (c >= 0xf0); - utf8_val = c & ((1 << (6 - utf8_state)) - 1); - } else { - utf8_state = 0; - handle_char(c); - } - } - - function is_alpha(c) { - return typeof c === "string" && - ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); - } - - function is_digit(c) { - return typeof c === "string" && (c >= '0' && c <= '9'); - } - - function is_word(c) { - return typeof c === "string" && - (is_alpha(c) || is_digit(c) || c == '_' || c == '$'); - } - - function 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 "()": - case "[]": - case "{}": - return true; - } - return false; - } - - function print_color_text(str, start, style_names) { - var i, j; - for (j = start; j < str.length;) { - var style = style_names[i = j]; - while (++j < str.length && style_names[j] == style) - continue; - std.puts(colors[styles[style] || 'default']); - std.puts(str.substring(i, j)); - std.puts(colors['none']); - } - } - - function print_csi(n, code) { - std.puts("\x1b[" + ((n != 1) ? n : "") + code); - } - - /* XXX: handle double-width characters */ - function move_cursor(delta) { - var i, l; - if (delta > 0) { - while (delta != 0) { - if (term_cursor_x == (term_width - 1)) { - std.puts("\n"); /* translated to CRLF */ - term_cursor_x = 0; - delta--; - } else { - l = Math.min(term_width - 1 - term_cursor_x, delta); - print_csi(l, "C"); /* right */ - delta -= l; - term_cursor_x += l; - } - } - } else { - delta = -delta; - while (delta != 0) { - if (term_cursor_x == 0) { - print_csi(1, "A"); /* up */ - print_csi(term_width - 1, "C"); /* right */ - delta--; - term_cursor_x = term_width - 1; - } else { - l = Math.min(delta, term_cursor_x); - print_csi(l, "D"); /* left */ - delta -= l; - term_cursor_x -= l; - } - } - } - } - - function update() { - var i, 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(-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; - var colorstate = colorize_js(str); - print_color_text(str, start, colorstate[2]); - } else { - std.puts(cmd); - } - } - 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"); - } - /* remove the trailing characters */ - std.puts("\x1b[J"); - last_cmd = cmd; - last_cursor_pos = cmd.length; - } - 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(); - } - - /* editing commands */ - function insert(str) { - if (str) { - cmd = cmd.substring(0, cursor_pos) + str + cmd.substring(cursor_pos); - cursor_pos += str.length; - } - } - - function quoted_insert() { - quote_flag = true; - } - - function abort() { - cmd = ""; - cursor_pos = 0; - return -2; - } - - function alert() { - } - - function beginning_of_line() { - cursor_pos = 0; - } - - function end_of_line() { - cursor_pos = cmd.length; - } - - function forward_char() { - if (cursor_pos < cmd.length) { - cursor_pos++; - while (is_trailing_surrogate(cmd.charAt(cursor_pos))) - cursor_pos++; - } - } - - function backward_char() { - if (cursor_pos > 0) { - cursor_pos--; - while (is_trailing_surrogate(cmd.charAt(cursor_pos))) - cursor_pos--; - } - } - - function skip_word_forward(pos) { - while (pos < cmd.length && !is_word(cmd.charAt(pos))) - pos++; - while (pos < cmd.length && is_word(cmd.charAt(pos))) - pos++; - return pos; - } - - function skip_word_backward(pos) { - while (pos > 0 && !is_word(cmd.charAt(pos - 1))) - pos--; - while (pos > 0 && is_word(cmd.charAt(pos - 1))) - pos--; - return pos; - } - - function forward_word() { - cursor_pos = skip_word_forward(cursor_pos); - } - - function backward_word() { - cursor_pos = skip_word_backward(cursor_pos); - } - - function accept_line() { - std.puts("\n"); - history_add(cmd); - return -1; - } - - function history_add(str) { - if (str) { - history.push(str); - } - history_index = history.length; - } - - function previous_history() { - if (history_index > 0) { - if (history_index == history.length) { - history.push(cmd); - } - history_index--; - cmd = history[history_index]; - cursor_pos = cmd.length; - } - } - - function next_history() { - if (history_index < history.length - 1) { - history_index++; - cmd = history[history_index]; - cursor_pos = cmd.length; - } - } - - function history_search(dir) { - var pos = cursor_pos; - for (var i = 1; i <= history.length; i++) { - var index = (history.length + i * dir + history_index) % history.length; - if (history[index].substring(0, pos) == cmd.substring(0, pos)) { - history_index = index; - cmd = history[index]; - return; - } - } - } - - function history_search_backward() { - return history_search(-1); - } - - function history_search_forward() { - return history_search(1); - } - - function delete_char_dir(dir) { - var start, 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); - } else { - cmd = cmd.substring(0, start) + cmd.substring(end); - cursor_pos = start; - } - } - } - - function delete_char() { - delete_char_dir(1); - } - - function control_d() { - if (cmd.length == 0) { - std.puts("\n"); - return -3; /* exit read eval print loop */ - } else { - delete_char_dir(1); - } - } - - function backward_delete_char() { - delete_char_dir(-1); - } - - function transpose_chars() { - var pos = cursor_pos; - if (cmd.length > 1 && pos > 0) { - if (pos == cmd.length) - pos--; - cmd = cmd.substring(0, pos - 1) + cmd.substring(pos, pos + 1) + - cmd.substring(pos - 1, pos) + cmd.substring(pos + 1); - cursor_pos = pos + 1; - } - } - - function transpose_words() { - var p1 = skip_word_backward(cursor_pos); - var p2 = skip_word_forward(p1); - var p4 = skip_word_forward(cursor_pos); - var p3 = skip_word_backward(p4); - - if (p1 < p2 && p2 <= cursor_pos && cursor_pos <= p3 && p3 < p4) { - cmd = cmd.substring(0, p1) + cmd.substring(p3, p4) + - cmd.substring(p2, p3) + cmd.substring(p1, p2); - cursor_pos = p4; - } - } - - function upcase_word() { - var end = skip_word_forward(cursor_pos); - cmd = cmd.substring(0, cursor_pos) + - cmd.substring(cursor_pos, end).toUpperCase() + - cmd.substring(end); - } - - function downcase_word() { - var end = skip_word_forward(cursor_pos); - cmd = cmd.substring(0, cursor_pos) + - cmd.substring(cursor_pos, end).toLowerCase() + - cmd.substring(end); - } - - function kill_region(start, end, dir) { - var s = cmd.substring(start, end); - if (last_fun !== kill_region) - clip_board = s; - else if (dir < 0) - clip_board = s + clip_board; - else - clip_board = clip_board + s; - - cmd = cmd.substring(0, start) + cmd.substring(end); - if (cursor_pos > end) - cursor_pos -= end - start; - else if (cursor_pos > start) - cursor_pos = start; - this_fun = kill_region; - } - - function kill_line() { - kill_region(cursor_pos, cmd.length, 1); - } - - function backward_kill_line() { - kill_region(0, cursor_pos, -1); - } - - function kill_word() { - kill_region(cursor_pos, skip_word_forward(cursor_pos), 1); - } - - function backward_kill_word() { - kill_region(skip_word_backward(cursor_pos), cursor_pos, -1); - } - - function yank() { - insert(clip_board); - } - - function control_c() { - if (last_fun === control_c) { - std.puts("\n"); - std.exit(0); - } else { - std.puts("\n(Press Ctrl-C again to quit)\n"); - readline_print_prompt(); - } - } - - function reset() { - cmd = ""; - cursor_pos = 0; - } - - function get_context_word(line, pos) { - var s = ""; - while (pos > 0 && is_word(line[pos - 1])) { - pos--; - s = line[pos] + s; - } - return s; - } - function get_context_object(line, pos) { - var obj, base, c; - if (pos <= 0 || " ~!%^&*(-+={[|:;,<>?/".indexOf(line[pos - 1]) >= 0) - return g; - if (pos >= 2 && line[pos - 1] === ".") { - pos--; - obj = {}; - switch (c = line[pos - 1]) { - case '\'': - case '\"': - return "a"; - case ']': - return []; - case '}': - return {}; - case '/': - return / /; - default: - if (is_word(c)) { - base = get_context_word(line, pos); - if (["true", "false", "null", "this"].includes(base) || !isNaN(+base)) - return eval(base); - obj = get_context_object(line, pos - base.length); - if (obj === null || obj === void 0) - return obj; - if (obj === g && obj[base] === void 0) - return eval(base); - else - return obj[base]; - } - return {}; - } - } - return void 0; - } - - function get_completions(line, pos) { - var s, obj, ctx_obj, r, i, j, paren; - - s = get_context_word(line, pos); - ctx_obj = get_context_object(line, pos - s.length); - r = []; - /* enumerate properties from object and its prototype chain, - add non-numeric regular properties with s as e prefix - */ - for (i = 0, obj = ctx_obj; i < 10 && obj !== null && obj !== void 0; i++) { - var props = Object.getOwnPropertyNames(obj); - /* add non-numeric regular properties */ - for (j = 0; j < props.length; j++) { - var prop = props[j]; - if (typeof prop == "string" && ""+(+prop) != prop && prop.startsWith(s)) - r.push(prop); - } - obj = Object.getPrototypeOf(obj); - } - if (r.length > 1) { - /* sort list with internal names last and remove duplicates */ - function symcmp(a, b) { - if (a[0] != b[0]) { - if (a[0] == '_') - return 1; - if (b[0] == '_') - return -1; - } - if (a < b) - return -1; - if (a > b) - return +1; - return 0; - } - r.sort(symcmp); - for(i = j = 1; i < r.length; i++) { - if (r[i] != r[i - 1]) - r[j++] = r[i]; - } - r.length = j; - } - /* 'tab' = list of completions, 'pos' = cursor position inside - the completions */ - return { tab: r, pos: s.length, ctx: ctx_obj }; - } - - function completion() { - var tab, res, s, i, j, len, t, max_width, col, n_cols, row, n_rows; - res = get_completions(cmd, cursor_pos); - tab = res.tab; - if (tab.length === 0) - return; - s = tab[0]; - len = s.length; - /* add the chars which are identical in all the completions */ - for(i = 1; i < tab.length; i++) { - t = tab[i]; - for(j = 0; j < len; j++) { - if (t[j] !== s[j]) { - len = j; - break; - } - } - } - for(i = res.pos; i < len; i++) { - insert(s[i]); - } - if (last_fun === completion && tab.length == 1) { - /* append parentheses to function names */ - var m = res.ctx[tab[0]]; - if (typeof m == "function") { - insert('('); - if (m.length == 0) - insert(')'); - } else if (typeof m == "object") { - insert('.'); - } - } - /* show the possible completions */ - if (last_fun === completion && tab.length >= 2) { - max_width = 0; - for(i = 0; i < tab.length; i++) - max_width = Math.max(max_width, tab[i].length); - max_width += 2; - n_cols = Math.max(1, Math.floor((term_width + 1) / max_width)); - n_rows = Math.ceil(tab.length / n_cols); - std.puts("\n"); - /* display the sorted list column-wise */ - for (row = 0; row < n_rows; row++) { - for (col = 0; col < n_cols; col++) { - i = col * n_rows + row; - if (i >= tab.length) - break; - s = tab[i]; - if (col != n_cols - 1) - s = s.padEnd(max_width); - std.puts(s); - } - std.puts("\n"); - } - /* show a new prompt */ - readline_print_prompt(); - } - } - - var commands = { /* command table */ - "\x01": beginning_of_line, /* ^A - bol */ - "\x02": backward_char, /* ^B - backward-char */ - "\x03": control_c, /* ^C - abort */ - "\x04": control_d, /* ^D - delete-char or exit */ - "\x05": end_of_line, /* ^E - eol */ - "\x06": forward_char, /* ^F - forward-char */ - "\x07": abort, /* ^G - bell */ - "\x08": backward_delete_char, /* ^H - backspace */ - "\x09": completion, /* ^I - history-search-backward */ - "\x0a": accept_line, /* ^J - newline */ - "\x0b": kill_line, /* ^K - delete to end of line */ - "\x0d": accept_line, /* ^M - enter */ - "\x0e": next_history, /* ^N - down */ - "\x10": previous_history, /* ^P - up */ - "\x11": quoted_insert, /* ^Q - quoted-insert */ - "\x12": alert, /* ^R - reverse-search */ - "\x13": alert, /* ^S - search */ - "\x14": transpose_chars, /* ^T - transpose */ - "\x18": reset, /* ^X - cancel */ - "\x19": yank, /* ^Y - yank */ - "\x1bOA": previous_history, /* ^[OA - up */ - "\x1bOB": next_history, /* ^[OB - down */ - "\x1bOC": forward_char, /* ^[OC - right */ - "\x1bOD": backward_char, /* ^[OD - left */ - "\x1bOF": forward_word, /* ^[OF - ctrl-right */ - "\x1bOH": backward_word, /* ^[OH - ctrl-left */ - "\x1b[1;5C": forward_word, /* ^[[1;5C - ctrl-right */ - "\x1b[1;5D": backward_word, /* ^[[1;5D - ctrl-left */ - "\x1b[1~": beginning_of_line, /* ^[[1~ - bol */ - "\x1b[3~": delete_char, /* ^[[3~ - delete */ - "\x1b[4~": end_of_line, /* ^[[4~ - eol */ - "\x1b[5~": history_search_backward,/* ^[[5~ - page up */ - "\x1b[6~": history_search_forward, /* ^[[5~ - page down */ - "\x1b[A": previous_history, /* ^[[A - up */ - "\x1b[B": next_history, /* ^[[B - down */ - "\x1b[C": forward_char, /* ^[[C - right */ - "\x1b[D": backward_char, /* ^[[D - left */ - "\x1b[F": end_of_line, /* ^[[F - end */ - "\x1b[H": beginning_of_line, /* ^[[H - home */ - "\x1b\x7f": backward_kill_word, /* M-C-? - backward_kill_word */ - "\x1bb": backward_word, /* M-b - backward_word */ - "\x1bd": kill_word, /* M-d - kill_word */ - "\x1bf": forward_word, /* M-f - backward_word */ - "\x1bk": backward_kill_line, /* M-k - backward_kill_line */ - "\x1bl": downcase_word, /* M-l - downcase_word */ - "\x1bt": transpose_words, /* M-t - transpose_words */ - "\x1bu": upcase_word, /* M-u - upcase_word */ - "\x7f": backward_delete_char, /* ^? - delete */ - }; - - function dupstr(str, count) { - var res = ""; - while (count-- > 0) - res += str; - return res; - } - - var readline_keys; - var readline_state; - var readline_cb; - - function readline_print_prompt() - { - std.puts(prompt); - term_cursor_x = ucs_length(prompt) % term_width; - last_cmd = ""; - last_cursor_pos = 0; - } - - function readline_start(defstr, cb) { - cmd = defstr || ""; - cursor_pos = cmd.length; - history_index = history.length; - readline_cb = cb; - - prompt = pstate; - - if (mexpr) { - prompt += dupstr(" ", plen - prompt.length); - prompt += ps2; - } else { - if (show_time) { - var t = Math.round(eval_time) + " "; - eval_time = 0; - t = dupstr("0", 5 - t.length) + t; - prompt += t.substring(0, t.length - 4) + "." + t.substring(t.length - 4); - } - plen = prompt.length; - prompt += ps1; - } - readline_print_prompt(); - update(); - readline_state = 0; - } - - function handle_char(c1) { - var c; - c = String.fromCodePoint(c1); - switch(readline_state) { - case 0: - if (c == '\x1b') { /* '^[' - ESC */ - readline_keys = c; - readline_state = 1; - } else { - handle_key(c); - } - break; - case 1: /* '^[ */ - readline_keys += c; - if (c == '[') { - readline_state = 2; - } else if (c == 'O') { - readline_state = 3; - } else { - handle_key(readline_keys); - readline_state = 0; - } - break; - case 2: /* '^[[' - CSI */ - readline_keys += c; - if (!(c == ';' || (c >= '0' && c <= '9'))) { - handle_key(readline_keys); - readline_state = 0; - } - break; - case 3: /* '^[O' - ESC2 */ - readline_keys += c; - handle_key(readline_keys); - readline_state = 0; - break; - } - } - - function handle_key(keys) { - var fun; - - if (quote_flag) { - if (ucs_length(keys) === 1) - insert(keys); - quote_flag = false; - } else if (fun = commands[keys]) { - this_fun = fun; - switch (fun(keys)) { - case -1: - readline_cb(cmd); - return; - case -2: - readline_cb(null); - return; - case -3: - /* uninstall a Ctrl-C signal handler */ - os.signal(os.SIGINT, null); - /* uninstall the stdin read handler */ - os.setReadHandler(term_fd, null); - return; - } - last_fun = this_fun; - } else if (ucs_length(keys) === 1 && keys >= ' ') { - insert(keys); - last_fun = insert; - } else { - alert(); /* beep! */ - } - - cursor_pos = (cursor_pos < 0) ? 0 : - (cursor_pos > cmd.length) ? cmd.length : cursor_pos; - update(); - } - - var hex_mode = false; - var eval_mode = "std"; - - function number_to_string(a, radix) { - var s; - if (!isFinite(a)) { - /* NaN, Infinite */ - return a.toString(); - } else { - if (a == 0) { - if (1 / a < 0) - s = "-0"; - else - s = "0"; - } else { - if (radix == 16 && a === Math.floor(a)) { - var s; - if (a < 0) { - a = -a; - s = "-"; - } else { - s = ""; - } - s += "0x" + a.toString(16); - } else { - s = a.toString(); - } - } - return s; - } - } - - function bigfloat_to_string(a, radix) { - var s; - if (!BigFloat.isFinite(a)) { - /* NaN, Infinite */ - if (eval_mode !== "math") { - return "BigFloat(" + a.toString() + ")"; - } else { - return a.toString(); - } - } else { - if (a == 0) { - if (1 / a < 0) - s = "-0"; - else - s = "0"; - } else { - if (radix == 16) { - var s; - if (a < 0) { - a = -a; - s = "-"; - } else { - s = ""; - } - s += "0x" + a.toString(16); - } else { - s = a.toString(); - } - } - if (typeof a === "bigfloat" && eval_mode !== "math") { - s += "l"; - } else if (eval_mode !== "std" && s.indexOf(".") < 0 && - ((radix == 16 && s.indexOf("p") < 0) || - (radix == 10 && s.indexOf("e") < 0))) { - /* add a decimal point so that the floating point type - is visible */ - s += ".0"; - } - return s; - } - } - - function bigint_to_string(a, radix) { - var s; - if (radix == 16) { - var s; - if (a < 0) { - a = -a; - s = "-"; - } else { - s = ""; - } - s += "0x" + a.toString(16); - } else { - s = a.toString(); - } - if (eval_mode === "std") - s += "n"; - return s; - } - - function print(a) { - var stack = []; - - function print_rec(a) { - var n, i, keys, key, type, s; - - type = typeof(a); - if (type === "object") { - if (a === null) { - std.puts(a); - } else if (stack.indexOf(a) >= 0) { - std.puts("[circular]"); - } else if (has_jscalc && (a instanceof Fraction || - a instanceof Complex || - a instanceof Mod || - a instanceof Polynomial || - a instanceof PolyMod || - a instanceof RationalFunction || - a instanceof Series)) { - std.puts(a.toString()); - } else { - stack.push(a); - if (Array.isArray(a)) { - n = a.length; - std.puts("[ "); - for(i = 0; i < n; i++) { - if (i !== 0) - std.puts(", "); - if (i in a) { - print_rec(a[i]); - } else { - std.puts(""); - } - if (i > 20) { - std.puts("..."); - break; - } - } - std.puts(" ]"); - } else if (Object.__getClass(a) === "RegExp") { - std.puts(a.toString()); - } else { - keys = Object.keys(a); - n = keys.length; - std.puts("{ "); - for(i = 0; i < n; i++) { - if (i !== 0) - std.puts(", "); - key = keys[i]; - std.puts(key, ": "); - print_rec(a[key]); - } - std.puts(" }"); - } - stack.pop(a); - } - } else if (type === "string") { - s = a.__quote(); - if (s.length > 79) - s = s.substring(0, 75) + "...\""; - std.puts(s); - } else if (type === "number") { - std.puts(number_to_string(a, hex_mode ? 16 : 10)); - } else if (type === "bigint") { - std.puts(bigint_to_string(a, hex_mode ? 16 : 10)); - } else if (type === "bigfloat") { - std.puts(bigfloat_to_string(a, hex_mode ? 16 : 10)); - } else if (type === "bigdecimal") { - std.puts(a.toString() + "m"); - } else if (type === "symbol") { - std.puts(String(a)); - } else if (type === "function") { - std.puts("function " + a.name + "()"); - } else { - std.puts(a); - } - } - print_rec(a); - } - - function extract_directive(a) { - var pos; - if (a[0] !== '\\') - return ""; - for (pos = 1; pos < a.length; pos++) { - if (!is_alpha(a[pos])) - break; - } - return a.substring(1, pos); - } - - /* return true if the string after cmd can be evaluted as JS */ - function handle_directive(cmd, expr) { - var param, prec1, expBits1; - - if (cmd === "h" || cmd === "?" || cmd == "help") { - help(); - } else if (cmd === "load") { - var filename = expr.substring(cmd.length + 1).trim(); - if (filename.lastIndexOf(".") <= filename.lastIndexOf("/")) - filename += ".js"; - std.loadScript(filename); - return false; - } else if (cmd === "x") { - hex_mode = true; - } else if (cmd === "d") { - hex_mode = false; - } else if (cmd === "t") { - show_time = !show_time; - } else if (has_bignum && cmd === "p") { - param = expr.substring(cmd.length + 1).trim().split(" "); - if (param.length === 1 && param[0] === "") { - std.puts("BigFloat precision=" + prec + " bits (~" + - Math.floor(prec / log2_10) + - " digits), exponent size=" + expBits + " bits\n"); - } else if (param[0] === "f16") { - prec = 11; - expBits = 5; - } else if (param[0] === "f32") { - prec = 24; - expBits = 8; - } else if (param[0] === "f64") { - prec = 53; - expBits = 11; - } else if (param[0] === "f128") { - prec = 113; - expBits = 15; - } else { - prec1 = parseInt(param[0]); - if (param.length >= 2) - expBits1 = parseInt(param[1]); - else - expBits1 = BigFloatEnv.expBitsMax; - if (Number.isNaN(prec1) || - prec1 < BigFloatEnv.precMin || - prec1 > BigFloatEnv.precMax) { - std.puts("Invalid precision\n"); - return false; - } - if (Number.isNaN(expBits1) || - expBits1 < BigFloatEnv.expBitsMin || - expBits1 > BigFloatEnv.expBitsMax) { - std.puts("Invalid exponent bits\n"); - return false; - } - prec = prec1; - expBits = expBits1; - } - return false; - } else if (has_bignum && cmd === "digits") { - param = expr.substring(cmd.length + 1).trim(); - prec1 = Math.ceil(parseFloat(param) * log2_10); - if (prec1 < BigFloatEnv.precMin || - prec1 > BigFloatEnv.precMax) { - std.puts("Invalid precision\n"); - return false; - } - prec = prec1; - expBits = BigFloatEnv.expBitsMax; - return false; - } else if (has_bignum && cmd === "mode") { - param = expr.substring(cmd.length + 1).trim(); - if (param === "") { - std.puts("Running mode=" + eval_mode + "\n"); - } else if (param === "std" || param === "math") { - eval_mode = param; - } else { - std.puts("Invalid mode\n"); - } - return false; - } else if (cmd === "clear") { - std.puts("\x1b[H\x1b[J"); - } else if (cmd === "q") { - std.exit(0); - } else if (has_jscalc && cmd === "a") { - algebraicMode = true; - } else if (has_jscalc && cmd === "n") { - algebraicMode = false; - } else { - std.puts("Unknown directive: " + cmd + "\n"); - return false; - } - return true; - } - - if (config_numcalc) { - /* called by the GUI */ - g.execCmd = function (cmd) { - switch(cmd) { - case "dec": - hex_mode = false; - break; - case "hex": - hex_mode = true; - break; - case "num": - algebraicMode = false; - break; - case "alg": - algebraicMode = true; - break; - } - } - } - - function help() { - function sel(n) { - return n ? "*": " "; - } - std.puts("\\h this help\n" + - "\\x " + sel(hex_mode) + "hexadecimal number display\n" + - "\\d " + sel(!hex_mode) + "decimal number display\n" + - "\\t " + sel(show_time) + "toggle timing display\n" + - "\\clear clear the terminal\n"); - if (has_jscalc) { - std.puts("\\a " + sel(algebraicMode) + "algebraic mode\n" + - "\\n " + sel(!algebraicMode) + "numeric mode\n"); - } - if (has_bignum) { - std.puts("\\p [m [e]] set the BigFloat precision to 'm' bits\n" + - "\\digits n set the BigFloat precision to 'ceil(n*log2(10))' bits\n"); - if (!has_jscalc) { - std.puts("\\mode [std|math] change the running mode (current = " + eval_mode + ")\n"); - } - } - if (!config_numcalc) { - std.puts("\\q exit\n"); - } - } - - function eval_and_print(expr) { - var result; - - try { - if (eval_mode === "math") - expr = '"use math"; void 0;' + expr; - var now = (new Date).getTime(); - /* eval as a script */ - result = std.evalScript(expr, { backtrace_barrier: true }); - eval_time = (new Date).getTime() - now; - std.puts(colors[styles.result]); - print(result); - std.puts("\n"); - std.puts(colors.none); - /* set the last result */ - g._ = result; - } catch (error) { - std.puts(colors[styles.error_msg]); - if (error instanceof Error) { - console.log(error); - if (error.stack) { - std.puts(error.stack); - } - } else { - std.puts("Throw: "); - console.log(error); - } - std.puts(colors.none); - } - } - - function cmd_start() { - if (!config_numcalc) { - if (has_jscalc) - std.puts('QJSCalc - Type "\\h" for help\n'); - else - std.puts('QuickJS - Type "\\h" for help\n'); - } - if (has_bignum) { - log2_10 = Math.log(10) / Math.log(2); - prec = 113; - expBits = 15; - if (has_jscalc) { - eval_mode = "math"; - /* XXX: numeric mode should always be the default ? */ - g.algebraicMode = config_numcalc; - } - } - - cmd_readline_start(); - } - - function cmd_readline_start() { - readline_start(dupstr(" ", level), readline_handle_cmd); - } - - function readline_handle_cmd(expr) { - handle_cmd(expr); - cmd_readline_start(); - } - - function handle_cmd(expr) { - var colorstate, cmd; - - if (expr === null) { - expr = ""; - return; - } - if (expr === "?") { - help(); - return; - } - cmd = extract_directive(expr); - if (cmd.length > 0) { - if (!handle_directive(cmd, expr)) - return; - expr = expr.substring(cmd.length + 1); - } - if (expr === "") - return; - - if (mexpr) - expr = mexpr + '\n' + expr; - colorstate = colorize_js(expr); - pstate = colorstate[0]; - level = colorstate[1]; - if (pstate) { - mexpr = expr; - return; - } - mexpr = ""; - - if (has_bignum) { - BigFloatEnv.setPrec(eval_and_print.bind(null, expr), - prec, expBits); - } else { - eval_and_print(expr); - } - level = 0; - - /* run the garbage collector after each command */ - std.gc(); - } - - function colorize_js(str) { - var i, c, start, n = str.length; - var style, state = "", level = 0; - var primary, can_regex = 1; - var r = []; - - function push_state(c) { state += c; } - function last_state(c) { return state.substring(state.length - 1); } - function pop_state(c) { - var c = last_state(); - state = state.substring(0, state.length - 1); - return c; - } - - function parse_block_comment() { - style = 'comment'; - push_state('/'); - for (i++; i < n - 1; i++) { - if (str[i] == '*' && str[i + 1] == '/') { - i += 2; - pop_state('/'); - break; - } - } - } - - function parse_line_comment() { - style = 'comment'; - for (i++; i < n; i++) { - if (str[i] == '\n') { - break; - } - } - } - - function parse_string(delim) { - style = 'string'; - push_state(delim); - while (i < n) { - c = str[i++]; - if (c == '\n') { - style = 'error'; - continue; - } - if (c == '\\') { - if (i >= n) - break; - i++; - } else - if (c == delim) { - pop_state(); - break; - } - } - } - - function parse_regex() { - style = 'regex'; - push_state('/'); - while (i < n) { - c = str[i++]; - if (c == '\n') { - style = 'error'; - continue; - } - if (c == '\\') { - if (i < n) { - i++; - } - continue; - } - if (last_state() == '[') { - if (c == ']') { - pop_state() - } - // ECMA 5: ignore '/' inside char classes - continue; - } - if (c == '[') { - push_state('['); - if (str[i] == '[' || str[i] == ']') - i++; - continue; - } - if (c == '/') { - pop_state(); - while (i < n && is_word(str[i])) - i++; - break; - } - } - } - - function parse_number() { - style = 'number'; - while (i < n && (is_word(str[i]) || (str[i] == '.' && (i == n - 1 || str[i + 1] != '.')))) { - i++; - } - } - - var js_keywords = "|" + - "break|case|catch|continue|debugger|default|delete|do|" + - "else|finally|for|function|if|in|instanceof|new|" + - "return|switch|this|throw|try|typeof|while|with|" + - "class|const|enum|import|export|extends|super|" + - "implements|interface|let|package|private|protected|" + - "public|static|yield|" + - "undefined|null|true|false|Infinity|NaN|" + - "eval|arguments|" + - "await|"; - - var js_no_regex = "|this|super|undefined|null|true|false|Infinity|NaN|arguments|"; - var js_types = "|void|var|"; - - function parse_identifier() { - can_regex = 1; - - while (i < n && is_word(str[i])) - i++; - - var w = '|' + str.substring(start, i) + '|'; - - if (js_keywords.indexOf(w) >= 0) { - style = 'keyword'; - if (js_no_regex.indexOf(w) >= 0) - can_regex = 0; - return; - } - - var i1 = i; - while (i1 < n && str[i1] == ' ') - i1++; - - if (i1 < n && str[i1] == '(') { - style = 'function'; - return; - } - - if (js_types.indexOf(w) >= 0) { - style = 'type'; - return; - } - - style = 'identifier'; - can_regex = 0; - } - - function set_style(from, to) { - while (r.length < from) - r.push('default'); - while (r.length < to) - r.push(style); - } - - for (i = 0; i < n;) { - style = null; - start = i; - switch (c = str[i++]) { - case ' ': - case '\t': - case '\r': - case '\n': - continue; - case '+': - case '-': - if (i < n && str[i] == c) { - i++; - continue; - } - can_regex = 1; - continue; - case '/': - if (i < n && str[i] == '*') { // block comment - parse_block_comment(); - break; - } - if (i < n && str[i] == '/') { // line comment - parse_line_comment(); - break; - } - if (can_regex) { - parse_regex(); - can_regex = 0; - break; - } - can_regex = 1; - continue; - case '\'': - case '\"': - case '`': - parse_string(c); - can_regex = 0; - break; - case '(': - case '[': - case '{': - can_regex = 1; - level++; - push_state(c); - continue; - case ')': - case ']': - case '}': - can_regex = 0; - if (level > 0 && is_balanced(last_state(), c)) { - level--; - pop_state(); - continue; - } - style = 'error'; - break; - default: - if (is_digit(c)) { - parse_number(); - can_regex = 0; - break; - } - if (is_word(c) || c == '$') { - parse_identifier(); - break; - } - can_regex = 1; - continue; - } - if (style) - set_style(start, i); - } - set_style(n, n); - return [ state, level, r ]; - } - - termInit(); - - cmd_start(); - -})(globalThis); +/* + * QuickJS Read Eval Print Loop + * + * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2020 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +"use strip"; + +import * as std from "std"; +import * as os from "os"; + +(function(g) { + /* add 'os' and 'std' bindings */ + g.os = os; + g.std = std; + + /* close global objects */ + var Object = g.Object; + var String = g.String; + var Array = g.Array; + var Date = g.Date; + var Math = g.Math; + var isFinite = g.isFinite; + var parseFloat = g.parseFloat; + + /* XXX: use preprocessor ? */ + var config_numcalc = (typeof os.open === "undefined"); + var has_jscalc = (typeof Fraction === "function"); + var has_bignum = (typeof BigFloat === "function"); + + var colors = { + none: "\x1b[0m", + black: "\x1b[30m", + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + blue: "\x1b[34m", + magenta: "\x1b[35m", + cyan: "\x1b[36m", + white: "\x1b[37m", + gray: "\x1b[30;1m", + grey: "\x1b[30;1m", + bright_red: "\x1b[31;1m", + bright_green: "\x1b[32;1m", + bright_yellow: "\x1b[33;1m", + bright_blue: "\x1b[34;1m", + bright_magenta: "\x1b[35;1m", + bright_cyan: "\x1b[36;1m", + bright_white: "\x1b[37;1m", + }; + + var styles; + if (config_numcalc) { + styles = { + 'default': 'black', + 'comment': 'white', + 'string': 'green', + 'regex': 'cyan', + 'number': 'green', + 'keyword': 'blue', + 'function': 'gray', + 'type': 'bright_magenta', + 'identifier': 'yellow', + 'error': 'bright_red', + 'result': 'black', + 'error_msg': 'bright_red', + }; + } else { + styles = { + 'default': 'bright_green', + 'comment': 'white', + 'string': 'bright_cyan', + 'regex': 'cyan', + 'number': 'green', + 'keyword': 'bright_white', + 'function': 'bright_yellow', + 'type': 'bright_magenta', + 'identifier': 'bright_green', + 'error': 'red', + 'result': 'bright_white', + 'error_msg': 'bright_red', + }; + } + + var history = []; + var clip_board = ""; + var prec; + var expBits; + var log2_10; + + var pstate = ""; + var prompt = ""; + var plen = 0; + var ps1; + if (config_numcalc) + ps1 = "> "; + else + ps1 = "qjs > "; + var ps2 = " ... "; + var utf8 = true; + var show_time = false; + var show_colors = true; + var eval_time = 0; + + var mexpr = ""; + var level = 0; + var cmd = ""; + var cursor_pos = 0; + var last_cmd = ""; + var last_cursor_pos = 0; + var history_index; + var this_fun, last_fun; + var quote_flag = false; + + var utf8_state = 0; + var utf8_val = 0; + + var term_fd; + var term_read_buf; + var term_width; + /* current X position of the cursor in the terminal */ + var term_cursor_x = 0; + + function termInit() { + var tab; + term_fd = std.in.fileno(); + + /* get the terminal size */ + term_width = 80; + if (os.isatty(term_fd)) { + if (os.ttyGetWinSize) { + tab = os.ttyGetWinSize(term_fd); + if (tab) + term_width = tab[0]; + } + if (os.ttySetRaw) { + /* set the TTY to raw mode */ + os.ttySetRaw(term_fd); + } + } + + /* install a Ctrl-C signal handler */ + os.signal(os.SIGINT, sigint_handler); + + /* install a handler to read stdin */ + term_read_buf = new Uint8Array(64); + os.setReadHandler(term_fd, term_read_handler); + } + + function sigint_handler() { + /* send Ctrl-C to readline */ + handle_byte(3); + } + + function term_read_handler() { + var l, i; + l = os.read(term_fd, term_read_buf.buffer, 0, term_read_buf.length); + for(i = 0; i < l; i++) + handle_byte(term_read_buf[i]); + } + + function handle_byte(c) { + if (!utf8) { + handle_char(c); + } else if (utf8_state !== 0 && (c >= 0x80 && c < 0xc0)) { + utf8_val = (utf8_val << 6) | (c & 0x3F); + utf8_state--; + if (utf8_state === 0) { + handle_char(utf8_val); + } + } else if (c >= 0xc0 && c < 0xf8) { + utf8_state = 1 + (c >= 0xe0) + (c >= 0xf0); + utf8_val = c & ((1 << (6 - utf8_state)) - 1); + } else { + utf8_state = 0; + handle_char(c); + } + } + + function is_alpha(c) { + return typeof c === "string" && + ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); + } + + function is_digit(c) { + return typeof c === "string" && (c >= '0' && c <= '9'); + } + + function is_word(c) { + return typeof c === "string" && + (is_alpha(c) || is_digit(c) || c == '_' || c == '$'); + } + + function 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 "()": + case "[]": + case "{}": + return true; + } + return false; + } + + function print_color_text(str, start, style_names) { + var i, j; + for (j = start; j < str.length;) { + var style = style_names[i = j]; + while (++j < str.length && style_names[j] == style) + continue; + std.puts(colors[styles[style] || 'default']); + std.puts(str.substring(i, j)); + std.puts(colors['none']); + } + } + + function print_csi(n, code) { + std.puts("\x1b[" + ((n != 1) ? n : "") + code); + } + + /* XXX: handle double-width characters */ + function move_cursor(delta) { + var i, l; + if (delta > 0) { + while (delta != 0) { + if (term_cursor_x == (term_width - 1)) { + std.puts("\n"); /* translated to CRLF */ + term_cursor_x = 0; + delta--; + } else { + l = Math.min(term_width - 1 - term_cursor_x, delta); + print_csi(l, "C"); /* right */ + delta -= l; + term_cursor_x += l; + } + } + } else { + delta = -delta; + while (delta != 0) { + if (term_cursor_x == 0) { + print_csi(1, "A"); /* up */ + print_csi(term_width - 1, "C"); /* right */ + delta--; + term_cursor_x = term_width - 1; + } else { + l = Math.min(delta, term_cursor_x); + print_csi(l, "D"); /* left */ + delta -= l; + term_cursor_x -= l; + } + } + } + } + + function update() { + var i, 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(-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; + var colorstate = colorize_js(str); + print_color_text(str, start, colorstate[2]); + } else { + std.puts(cmd); + } + } + 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"); + } + /* remove the trailing characters */ + std.puts("\x1b[J"); + last_cmd = cmd; + last_cursor_pos = cmd.length; + } + 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(); + } + + /* editing commands */ + function insert(str) { + if (str) { + cmd = cmd.substring(0, cursor_pos) + str + cmd.substring(cursor_pos); + cursor_pos += str.length; + } + } + + function quoted_insert() { + quote_flag = true; + } + + function abort() { + cmd = ""; + cursor_pos = 0; + return -2; + } + + function alert() { + } + + function beginning_of_line() { + cursor_pos = 0; + } + + function end_of_line() { + cursor_pos = cmd.length; + } + + function forward_char() { + if (cursor_pos < cmd.length) { + cursor_pos++; + while (is_trailing_surrogate(cmd.charAt(cursor_pos))) + cursor_pos++; + } + } + + function backward_char() { + if (cursor_pos > 0) { + cursor_pos--; + while (is_trailing_surrogate(cmd.charAt(cursor_pos))) + cursor_pos--; + } + } + + function skip_word_forward(pos) { + while (pos < cmd.length && !is_word(cmd.charAt(pos))) + pos++; + while (pos < cmd.length && is_word(cmd.charAt(pos))) + pos++; + return pos; + } + + function skip_word_backward(pos) { + while (pos > 0 && !is_word(cmd.charAt(pos - 1))) + pos--; + while (pos > 0 && is_word(cmd.charAt(pos - 1))) + pos--; + return pos; + } + + function forward_word() { + cursor_pos = skip_word_forward(cursor_pos); + } + + function backward_word() { + cursor_pos = skip_word_backward(cursor_pos); + } + + function accept_line() { + std.puts("\n"); + history_add(cmd); + return -1; + } + + function history_add(str) { + if (str) { + history.push(str); + } + history_index = history.length; + } + + function previous_history() { + if (history_index > 0) { + if (history_index == history.length) { + history.push(cmd); + } + history_index--; + cmd = history[history_index]; + cursor_pos = cmd.length; + } + } + + function next_history() { + if (history_index < history.length - 1) { + history_index++; + cmd = history[history_index]; + cursor_pos = cmd.length; + } + } + + function history_search(dir) { + var pos = cursor_pos; + for (var i = 1; i <= history.length; i++) { + var index = (history.length + i * dir + history_index) % history.length; + if (history[index].substring(0, pos) == cmd.substring(0, pos)) { + history_index = index; + cmd = history[index]; + return; + } + } + } + + function history_search_backward() { + return history_search(-1); + } + + function history_search_forward() { + return history_search(1); + } + + function delete_char_dir(dir) { + var start, 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); + } else { + cmd = cmd.substring(0, start) + cmd.substring(end); + cursor_pos = start; + } + } + } + + function delete_char() { + delete_char_dir(1); + } + + function control_d() { + if (cmd.length == 0) { + std.puts("\n"); + return -3; /* exit read eval print loop */ + } else { + delete_char_dir(1); + } + } + + function backward_delete_char() { + delete_char_dir(-1); + } + + function transpose_chars() { + var pos = cursor_pos; + if (cmd.length > 1 && pos > 0) { + if (pos == cmd.length) + pos--; + cmd = cmd.substring(0, pos - 1) + cmd.substring(pos, pos + 1) + + cmd.substring(pos - 1, pos) + cmd.substring(pos + 1); + cursor_pos = pos + 1; + } + } + + function transpose_words() { + var p1 = skip_word_backward(cursor_pos); + var p2 = skip_word_forward(p1); + var p4 = skip_word_forward(cursor_pos); + var p3 = skip_word_backward(p4); + + if (p1 < p2 && p2 <= cursor_pos && cursor_pos <= p3 && p3 < p4) { + cmd = cmd.substring(0, p1) + cmd.substring(p3, p4) + + cmd.substring(p2, p3) + cmd.substring(p1, p2); + cursor_pos = p4; + } + } + + function upcase_word() { + var end = skip_word_forward(cursor_pos); + cmd = cmd.substring(0, cursor_pos) + + cmd.substring(cursor_pos, end).toUpperCase() + + cmd.substring(end); + } + + function downcase_word() { + var end = skip_word_forward(cursor_pos); + cmd = cmd.substring(0, cursor_pos) + + cmd.substring(cursor_pos, end).toLowerCase() + + cmd.substring(end); + } + + function kill_region(start, end, dir) { + var s = cmd.substring(start, end); + if (last_fun !== kill_region) + clip_board = s; + else if (dir < 0) + clip_board = s + clip_board; + else + clip_board = clip_board + s; + + cmd = cmd.substring(0, start) + cmd.substring(end); + if (cursor_pos > end) + cursor_pos -= end - start; + else if (cursor_pos > start) + cursor_pos = start; + this_fun = kill_region; + } + + function kill_line() { + kill_region(cursor_pos, cmd.length, 1); + } + + function backward_kill_line() { + kill_region(0, cursor_pos, -1); + } + + function kill_word() { + kill_region(cursor_pos, skip_word_forward(cursor_pos), 1); + } + + function backward_kill_word() { + kill_region(skip_word_backward(cursor_pos), cursor_pos, -1); + } + + function yank() { + insert(clip_board); + } + + function control_c() { + if (last_fun === control_c) { + std.puts("\n"); + std.exit(0); + } else { + std.puts("\n(Press Ctrl-C again to quit)\n"); + readline_print_prompt(); + } + } + + function reset() { + cmd = ""; + cursor_pos = 0; + } + + function get_context_word(line, pos) { + var s = ""; + while (pos > 0 && is_word(line[pos - 1])) { + pos--; + s = line[pos] + s; + } + return s; + } + function get_context_object(line, pos) { + var obj, base, c; + if (pos <= 0 || " ~!%^&*(-+={[|:;,<>?/".indexOf(line[pos - 1]) >= 0) + return g; + if (pos >= 2 && line[pos - 1] === ".") { + pos--; + obj = {}; + switch (c = line[pos - 1]) { + case '\'': + case '\"': + return "a"; + case ']': + return []; + case '}': + return {}; + case '/': + return / /; + default: + if (is_word(c)) { + base = get_context_word(line, pos); + if (["true", "false", "null", "this"].includes(base) || !isNaN(+base)) + return eval(base); + obj = get_context_object(line, pos - base.length); + if (obj === null || obj === void 0) + return obj; + if (obj === g && obj[base] === void 0) + return eval(base); + else + return obj[base]; + } + return {}; + } + } + return void 0; + } + + function get_completions(line, pos) { + var s, obj, ctx_obj, r, i, j, paren; + + s = get_context_word(line, pos); + ctx_obj = get_context_object(line, pos - s.length); + r = []; + /* enumerate properties from object and its prototype chain, + add non-numeric regular properties with s as e prefix + */ + for (i = 0, obj = ctx_obj; i < 10 && obj !== null && obj !== void 0; i++) { + var props = Object.getOwnPropertyNames(obj); + /* add non-numeric regular properties */ + for (j = 0; j < props.length; j++) { + var prop = props[j]; + if (typeof prop == "string" && ""+(+prop) != prop && prop.startsWith(s)) + r.push(prop); + } + obj = Object.getPrototypeOf(obj); + } + if (r.length > 1) { + /* sort list with internal names last and remove duplicates */ + function symcmp(a, b) { + if (a[0] != b[0]) { + if (a[0] == '_') + return 1; + if (b[0] == '_') + return -1; + } + if (a < b) + return -1; + if (a > b) + return +1; + return 0; + } + r.sort(symcmp); + for(i = j = 1; i < r.length; i++) { + if (r[i] != r[i - 1]) + r[j++] = r[i]; + } + r.length = j; + } + /* 'tab' = list of completions, 'pos' = cursor position inside + the completions */ + return { tab: r, pos: s.length, ctx: ctx_obj }; + } + + function completion() { + var tab, res, s, i, j, len, t, max_width, col, n_cols, row, n_rows; + res = get_completions(cmd, cursor_pos); + tab = res.tab; + if (tab.length === 0) + return; + s = tab[0]; + len = s.length; + /* add the chars which are identical in all the completions */ + for(i = 1; i < tab.length; i++) { + t = tab[i]; + for(j = 0; j < len; j++) { + if (t[j] !== s[j]) { + len = j; + break; + } + } + } + for(i = res.pos; i < len; i++) { + insert(s[i]); + } + if (last_fun === completion && tab.length == 1) { + /* append parentheses to function names */ + var m = res.ctx[tab[0]]; + if (typeof m == "function") { + insert('('); + if (m.length == 0) + insert(')'); + } else if (typeof m == "object") { + insert('.'); + } + } + /* show the possible completions */ + if (last_fun === completion && tab.length >= 2) { + max_width = 0; + for(i = 0; i < tab.length; i++) + max_width = Math.max(max_width, tab[i].length); + max_width += 2; + n_cols = Math.max(1, Math.floor((term_width + 1) / max_width)); + n_rows = Math.ceil(tab.length / n_cols); + std.puts("\n"); + /* display the sorted list column-wise */ + for (row = 0; row < n_rows; row++) { + for (col = 0; col < n_cols; col++) { + i = col * n_rows + row; + if (i >= tab.length) + break; + s = tab[i]; + if (col != n_cols - 1) + s = s.padEnd(max_width); + std.puts(s); + } + std.puts("\n"); + } + /* show a new prompt */ + readline_print_prompt(); + } + } + + var commands = { /* command table */ + "\x01": beginning_of_line, /* ^A - bol */ + "\x02": backward_char, /* ^B - backward-char */ + "\x03": control_c, /* ^C - abort */ + "\x04": control_d, /* ^D - delete-char or exit */ + "\x05": end_of_line, /* ^E - eol */ + "\x06": forward_char, /* ^F - forward-char */ + "\x07": abort, /* ^G - bell */ + "\x08": backward_delete_char, /* ^H - backspace */ + "\x09": completion, /* ^I - history-search-backward */ + "\x0a": accept_line, /* ^J - newline */ + "\x0b": kill_line, /* ^K - delete to end of line */ + "\x0d": accept_line, /* ^M - enter */ + "\x0e": next_history, /* ^N - down */ + "\x10": previous_history, /* ^P - up */ + "\x11": quoted_insert, /* ^Q - quoted-insert */ + "\x12": alert, /* ^R - reverse-search */ + "\x13": alert, /* ^S - search */ + "\x14": transpose_chars, /* ^T - transpose */ + "\x18": reset, /* ^X - cancel */ + "\x19": yank, /* ^Y - yank */ + "\x1bOA": previous_history, /* ^[OA - up */ + "\x1bOB": next_history, /* ^[OB - down */ + "\x1bOC": forward_char, /* ^[OC - right */ + "\x1bOD": backward_char, /* ^[OD - left */ + "\x1bOF": forward_word, /* ^[OF - ctrl-right */ + "\x1bOH": backward_word, /* ^[OH - ctrl-left */ + "\x1b[1;5C": forward_word, /* ^[[1;5C - ctrl-right */ + "\x1b[1;5D": backward_word, /* ^[[1;5D - ctrl-left */ + "\x1b[1~": beginning_of_line, /* ^[[1~ - bol */ + "\x1b[3~": delete_char, /* ^[[3~ - delete */ + "\x1b[4~": end_of_line, /* ^[[4~ - eol */ + "\x1b[5~": history_search_backward,/* ^[[5~ - page up */ + "\x1b[6~": history_search_forward, /* ^[[5~ - page down */ + "\x1b[A": previous_history, /* ^[[A - up */ + "\x1b[B": next_history, /* ^[[B - down */ + "\x1b[C": forward_char, /* ^[[C - right */ + "\x1b[D": backward_char, /* ^[[D - left */ + "\x1b[F": end_of_line, /* ^[[F - end */ + "\x1b[H": beginning_of_line, /* ^[[H - home */ + "\x1b\x7f": backward_kill_word, /* M-C-? - backward_kill_word */ + "\x1bb": backward_word, /* M-b - backward_word */ + "\x1bd": kill_word, /* M-d - kill_word */ + "\x1bf": forward_word, /* M-f - backward_word */ + "\x1bk": backward_kill_line, /* M-k - backward_kill_line */ + "\x1bl": downcase_word, /* M-l - downcase_word */ + "\x1bt": transpose_words, /* M-t - transpose_words */ + "\x1bu": upcase_word, /* M-u - upcase_word */ + "\x7f": backward_delete_char, /* ^? - delete */ + }; + + function dupstr(str, count) { + var res = ""; + while (count-- > 0) + res += str; + return res; + } + + var readline_keys; + var readline_state; + var readline_cb; + + function readline_print_prompt() + { + std.puts(prompt); + term_cursor_x = ucs_length(prompt) % term_width; + last_cmd = ""; + last_cursor_pos = 0; + } + + function readline_start(defstr, cb) { + cmd = defstr || ""; + cursor_pos = cmd.length; + history_index = history.length; + readline_cb = cb; + + prompt = pstate; + + if (mexpr) { + prompt += dupstr(" ", plen - prompt.length); + prompt += ps2; + } else { + if (show_time) { + var t = Math.round(eval_time) + " "; + eval_time = 0; + t = dupstr("0", 5 - t.length) + t; + prompt += t.substring(0, t.length - 4) + "." + t.substring(t.length - 4); + } + plen = prompt.length; + prompt += ps1; + } + readline_print_prompt(); + update(); + readline_state = 0; + } + + function handle_char(c1) { + var c; + c = String.fromCodePoint(c1); + switch(readline_state) { + case 0: + if (c == '\x1b') { /* '^[' - ESC */ + readline_keys = c; + readline_state = 1; + } else { + handle_key(c); + } + break; + case 1: /* '^[ */ + readline_keys += c; + if (c == '[') { + readline_state = 2; + } else if (c == 'O') { + readline_state = 3; + } else { + handle_key(readline_keys); + readline_state = 0; + } + break; + case 2: /* '^[[' - CSI */ + readline_keys += c; + if (!(c == ';' || (c >= '0' && c <= '9'))) { + handle_key(readline_keys); + readline_state = 0; + } + break; + case 3: /* '^[O' - ESC2 */ + readline_keys += c; + handle_key(readline_keys); + readline_state = 0; + break; + } + } + + function handle_key(keys) { + var fun; + + if (quote_flag) { + if (ucs_length(keys) === 1) + insert(keys); + quote_flag = false; + } else if (fun = commands[keys]) { + this_fun = fun; + switch (fun(keys)) { + case -1: + readline_cb(cmd); + return; + case -2: + readline_cb(null); + return; + case -3: + /* uninstall a Ctrl-C signal handler */ + os.signal(os.SIGINT, null); + /* uninstall the stdin read handler */ + os.setReadHandler(term_fd, null); + return; + } + last_fun = this_fun; + } else if (ucs_length(keys) === 1 && keys >= ' ') { + insert(keys); + last_fun = insert; + } else { + alert(); /* beep! */ + } + + cursor_pos = (cursor_pos < 0) ? 0 : + (cursor_pos > cmd.length) ? cmd.length : cursor_pos; + update(); + } + + var hex_mode = false; + var eval_mode = "std"; + + function number_to_string(a, radix) { + var s; + if (!isFinite(a)) { + /* NaN, Infinite */ + return a.toString(); + } else { + if (a == 0) { + if (1 / a < 0) + s = "-0"; + else + s = "0"; + } else { + if (radix == 16 && a === Math.floor(a)) { + var s; + if (a < 0) { + a = -a; + s = "-"; + } else { + s = ""; + } + s += "0x" + a.toString(16); + } else { + s = a.toString(); + } + } + return s; + } + } + + function bigfloat_to_string(a, radix) { + var s; + if (!BigFloat.isFinite(a)) { + /* NaN, Infinite */ + if (eval_mode !== "math") { + return "BigFloat(" + a.toString() + ")"; + } else { + return a.toString(); + } + } else { + if (a == 0) { + if (1 / a < 0) + s = "-0"; + else + s = "0"; + } else { + if (radix == 16) { + var s; + if (a < 0) { + a = -a; + s = "-"; + } else { + s = ""; + } + s += "0x" + a.toString(16); + } else { + s = a.toString(); + } + } + if (typeof a === "bigfloat" && eval_mode !== "math") { + s += "l"; + } else if (eval_mode !== "std" && s.indexOf(".") < 0 && + ((radix == 16 && s.indexOf("p") < 0) || + (radix == 10 && s.indexOf("e") < 0))) { + /* add a decimal point so that the floating point type + is visible */ + s += ".0"; + } + return s; + } + } + + function bigint_to_string(a, radix) { + var s; + if (radix == 16) { + var s; + if (a < 0) { + a = -a; + s = "-"; + } else { + s = ""; + } + s += "0x" + a.toString(16); + } else { + s = a.toString(); + } + if (eval_mode === "std") + s += "n"; + return s; + } + + function print(a) { + var stack = []; + + function print_rec(a) { + var n, i, keys, key, type, s; + + type = typeof(a); + if (type === "object") { + if (a === null) { + std.puts(a); + } else if (stack.indexOf(a) >= 0) { + std.puts("[circular]"); + } else if (has_jscalc && (a instanceof Fraction || + a instanceof Complex || + a instanceof Mod || + a instanceof Polynomial || + a instanceof PolyMod || + a instanceof RationalFunction || + a instanceof Series)) { + std.puts(a.toString()); + } else { + stack.push(a); + if (Array.isArray(a)) { + n = a.length; + std.puts("[ "); + for(i = 0; i < n; i++) { + if (i !== 0) + std.puts(", "); + if (i in a) { + print_rec(a[i]); + } else { + std.puts(""); + } + if (i > 20) { + std.puts("..."); + break; + } + } + std.puts(" ]"); + } else if (Object.__getClass(a) === "RegExp") { + std.puts(a.toString()); + } else { + keys = Object.keys(a); + n = keys.length; + std.puts("{ "); + for(i = 0; i < n; i++) { + if (i !== 0) + std.puts(", "); + key = keys[i]; + std.puts(key, ": "); + print_rec(a[key]); + } + std.puts(" }"); + } + stack.pop(a); + } + } else if (type === "string") { + s = a.__quote(); + if (s.length > 79) + s = s.substring(0, 75) + "...\""; + std.puts(s); + } else if (type === "number") { + std.puts(number_to_string(a, hex_mode ? 16 : 10)); + } else if (type === "bigint") { + std.puts(bigint_to_string(a, hex_mode ? 16 : 10)); + } else if (type === "bigfloat") { + std.puts(bigfloat_to_string(a, hex_mode ? 16 : 10)); + } else if (type === "bigdecimal") { + std.puts(a.toString() + "m"); + } else if (type === "symbol") { + std.puts(String(a)); + } else if (type === "function") { + std.puts("function " + a.name + "()"); + } else { + std.puts(a); + } + } + print_rec(a); + } + + function extract_directive(a) { + var pos; + if (a[0] !== '\\') + return ""; + for (pos = 1; pos < a.length; pos++) { + if (!is_alpha(a[pos])) + break; + } + return a.substring(1, pos); + } + + /* return true if the string after cmd can be evaluted as JS */ + function handle_directive(cmd, expr) { + var param, prec1, expBits1; + + if (cmd === "h" || cmd === "?" || cmd == "help") { + help(); + } else if (cmd === "load") { + var filename = expr.substring(cmd.length + 1).trim(); + if (filename.lastIndexOf(".") <= filename.lastIndexOf("/")) + filename += ".js"; + std.loadScript(filename); + return false; + } else if (cmd === "x") { + hex_mode = true; + } else if (cmd === "d") { + hex_mode = false; + } else if (cmd === "t") { + show_time = !show_time; + } else if (has_bignum && cmd === "p") { + param = expr.substring(cmd.length + 1).trim().split(" "); + if (param.length === 1 && param[0] === "") { + std.puts("BigFloat precision=" + prec + " bits (~" + + Math.floor(prec / log2_10) + + " digits), exponent size=" + expBits + " bits\n"); + } else if (param[0] === "f16") { + prec = 11; + expBits = 5; + } else if (param[0] === "f32") { + prec = 24; + expBits = 8; + } else if (param[0] === "f64") { + prec = 53; + expBits = 11; + } else if (param[0] === "f128") { + prec = 113; + expBits = 15; + } else { + prec1 = parseInt(param[0]); + if (param.length >= 2) + expBits1 = parseInt(param[1]); + else + expBits1 = BigFloatEnv.expBitsMax; + if (Number.isNaN(prec1) || + prec1 < BigFloatEnv.precMin || + prec1 > BigFloatEnv.precMax) { + std.puts("Invalid precision\n"); + return false; + } + if (Number.isNaN(expBits1) || + expBits1 < BigFloatEnv.expBitsMin || + expBits1 > BigFloatEnv.expBitsMax) { + std.puts("Invalid exponent bits\n"); + return false; + } + prec = prec1; + expBits = expBits1; + } + return false; + } else if (has_bignum && cmd === "digits") { + param = expr.substring(cmd.length + 1).trim(); + prec1 = Math.ceil(parseFloat(param) * log2_10); + if (prec1 < BigFloatEnv.precMin || + prec1 > BigFloatEnv.precMax) { + std.puts("Invalid precision\n"); + return false; + } + prec = prec1; + expBits = BigFloatEnv.expBitsMax; + return false; + } else if (has_bignum && cmd === "mode") { + param = expr.substring(cmd.length + 1).trim(); + if (param === "") { + std.puts("Running mode=" + eval_mode + "\n"); + } else if (param === "std" || param === "math") { + eval_mode = param; + } else { + std.puts("Invalid mode\n"); + } + return false; + } else if (cmd === "clear") { + std.puts("\x1b[H\x1b[J"); + } else if (cmd === "q") { + std.exit(0); + } else if (has_jscalc && cmd === "a") { + algebraicMode = true; + } else if (has_jscalc && cmd === "n") { + algebraicMode = false; + } else { + std.puts("Unknown directive: " + cmd + "\n"); + return false; + } + return true; + } + + if (config_numcalc) { + /* called by the GUI */ + g.execCmd = function (cmd) { + switch(cmd) { + case "dec": + hex_mode = false; + break; + case "hex": + hex_mode = true; + break; + case "num": + algebraicMode = false; + break; + case "alg": + algebraicMode = true; + break; + } + } + } + + function help() { + function sel(n) { + return n ? "*": " "; + } + std.puts("\\h this help\n" + + "\\x " + sel(hex_mode) + "hexadecimal number display\n" + + "\\d " + sel(!hex_mode) + "decimal number display\n" + + "\\t " + sel(show_time) + "toggle timing display\n" + + "\\clear clear the terminal\n"); + if (has_jscalc) { + std.puts("\\a " + sel(algebraicMode) + "algebraic mode\n" + + "\\n " + sel(!algebraicMode) + "numeric mode\n"); + } + if (has_bignum) { + std.puts("\\p [m [e]] set the BigFloat precision to 'm' bits\n" + + "\\digits n set the BigFloat precision to 'ceil(n*log2(10))' bits\n"); + if (!has_jscalc) { + std.puts("\\mode [std|math] change the running mode (current = " + eval_mode + ")\n"); + } + } + if (!config_numcalc) { + std.puts("\\q exit\n"); + } + } + + function eval_and_print(expr) { + var result; + + try { + if (eval_mode === "math") + expr = '"use math"; void 0;' + expr; + var now = (new Date).getTime(); + /* eval as a script */ + result = std.evalScript(expr, { backtrace_barrier: true }); + eval_time = (new Date).getTime() - now; + std.puts(colors[styles.result]); + print(result); + std.puts("\n"); + std.puts(colors.none); + /* set the last result */ + g._ = result; + } catch (error) { + std.puts(colors[styles.error_msg]); + if (error instanceof Error) { + console.log(error); + if (error.stack) { + std.puts(error.stack); + } + } else { + std.puts("Throw: "); + console.log(error); + } + std.puts(colors.none); + } + } + + function cmd_start() { + if (!config_numcalc) { + if (has_jscalc) + std.puts('QJSCalc - Type "\\h" for help\n'); + else + std.puts('QuickJS - Type "\\h" for help\n'); + } + if (has_bignum) { + log2_10 = Math.log(10) / Math.log(2); + prec = 113; + expBits = 15; + if (has_jscalc) { + eval_mode = "math"; + /* XXX: numeric mode should always be the default ? */ + g.algebraicMode = config_numcalc; + } + } + + cmd_readline_start(); + } + + function cmd_readline_start() { + readline_start(dupstr(" ", level), readline_handle_cmd); + } + + function readline_handle_cmd(expr) { + handle_cmd(expr); + cmd_readline_start(); + } + + function handle_cmd(expr) { + var colorstate, cmd; + + if (expr === null) { + expr = ""; + return; + } + if (expr === "?") { + help(); + return; + } + cmd = extract_directive(expr); + if (cmd.length > 0) { + if (!handle_directive(cmd, expr)) + return; + expr = expr.substring(cmd.length + 1); + } + if (expr === "") + return; + + if (mexpr) + expr = mexpr + '\n' + expr; + colorstate = colorize_js(expr); + pstate = colorstate[0]; + level = colorstate[1]; + if (pstate) { + mexpr = expr; + return; + } + mexpr = ""; + + if (has_bignum) { + BigFloatEnv.setPrec(eval_and_print.bind(null, expr), + prec, expBits); + } else { + eval_and_print(expr); + } + level = 0; + + /* run the garbage collector after each command */ + std.gc(); + } + + function colorize_js(str) { + var i, c, start, n = str.length; + var style, state = "", level = 0; + var primary, can_regex = 1; + var r = []; + + function push_state(c) { state += c; } + function last_state(c) { return state.substring(state.length - 1); } + function pop_state(c) { + var c = last_state(); + state = state.substring(0, state.length - 1); + return c; + } + + function parse_block_comment() { + style = 'comment'; + push_state('/'); + for (i++; i < n - 1; i++) { + if (str[i] == '*' && str[i + 1] == '/') { + i += 2; + pop_state('/'); + break; + } + } + } + + function parse_line_comment() { + style = 'comment'; + for (i++; i < n; i++) { + if (str[i] == '\n') { + break; + } + } + } + + function parse_string(delim) { + style = 'string'; + push_state(delim); + while (i < n) { + c = str[i++]; + if (c == '\n') { + style = 'error'; + continue; + } + if (c == '\\') { + if (i >= n) + break; + i++; + } else + if (c == delim) { + pop_state(); + break; + } + } + } + + function parse_regex() { + style = 'regex'; + push_state('/'); + while (i < n) { + c = str[i++]; + if (c == '\n') { + style = 'error'; + continue; + } + if (c == '\\') { + if (i < n) { + i++; + } + continue; + } + if (last_state() == '[') { + if (c == ']') { + pop_state() + } + // ECMA 5: ignore '/' inside char classes + continue; + } + if (c == '[') { + push_state('['); + if (str[i] == '[' || str[i] == ']') + i++; + continue; + } + if (c == '/') { + pop_state(); + while (i < n && is_word(str[i])) + i++; + break; + } + } + } + + function parse_number() { + style = 'number'; + while (i < n && (is_word(str[i]) || (str[i] == '.' && (i == n - 1 || str[i + 1] != '.')))) { + i++; + } + } + + var js_keywords = "|" + + "break|case|catch|continue|debugger|default|delete|do|" + + "else|finally|for|function|if|in|instanceof|new|" + + "return|switch|this|throw|try|typeof|while|with|" + + "class|const|enum|import|export|extends|super|" + + "implements|interface|let|package|private|protected|" + + "public|static|yield|" + + "undefined|null|true|false|Infinity|NaN|" + + "eval|arguments|" + + "await|"; + + var js_no_regex = "|this|super|undefined|null|true|false|Infinity|NaN|arguments|"; + var js_types = "|void|var|"; + + function parse_identifier() { + can_regex = 1; + + while (i < n && is_word(str[i])) + i++; + + var w = '|' + str.substring(start, i) + '|'; + + if (js_keywords.indexOf(w) >= 0) { + style = 'keyword'; + if (js_no_regex.indexOf(w) >= 0) + can_regex = 0; + return; + } + + var i1 = i; + while (i1 < n && str[i1] == ' ') + i1++; + + if (i1 < n && str[i1] == '(') { + style = 'function'; + return; + } + + if (js_types.indexOf(w) >= 0) { + style = 'type'; + return; + } + + style = 'identifier'; + can_regex = 0; + } + + function set_style(from, to) { + while (r.length < from) + r.push('default'); + while (r.length < to) + r.push(style); + } + + for (i = 0; i < n;) { + style = null; + start = i; + switch (c = str[i++]) { + case ' ': + case '\t': + case '\r': + case '\n': + continue; + case '+': + case '-': + if (i < n && str[i] == c) { + i++; + continue; + } + can_regex = 1; + continue; + case '/': + if (i < n && str[i] == '*') { // block comment + parse_block_comment(); + break; + } + if (i < n && str[i] == '/') { // line comment + parse_line_comment(); + break; + } + if (can_regex) { + parse_regex(); + can_regex = 0; + break; + } + can_regex = 1; + continue; + case '\'': + case '\"': + case '`': + parse_string(c); + can_regex = 0; + break; + case '(': + case '[': + case '{': + can_regex = 1; + level++; + push_state(c); + continue; + case ')': + case ']': + case '}': + can_regex = 0; + if (level > 0 && is_balanced(last_state(), c)) { + level--; + pop_state(); + continue; + } + style = 'error'; + break; + default: + if (is_digit(c)) { + parse_number(); + can_regex = 0; + break; + } + if (is_word(c) || c == '$') { + parse_identifier(); + break; + } + can_regex = 1; + continue; + } + if (style) + set_style(start, i); + } + set_style(n, n); + return [ state, level, r ]; + } + + termInit(); + + cmd_start(); + +})(globalThis); diff --git a/run-test262.c b/run-test262.c index 2092cacaf..19856d8ef 100644 --- a/run-test262.c +++ b/run-test262.c @@ -1,2107 +1,2107 @@ -/* - * ECMA Test 262 Runner for QuickJS - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cutils.h" -#include "list.h" -#include "quickjs-libc.h" - -/* enable test262 thread support to test SharedArrayBuffer and Atomics */ -#define CONFIG_AGENT - -#define CMD_NAME "run-test262" - -typedef struct namelist_t { - char **array; - int count; - int size; - unsigned int sorted : 1; -} namelist_t; - -namelist_t test_list; -namelist_t exclude_list; -namelist_t exclude_dir_list; - -FILE *outfile; -enum test_mode_t { - TEST_DEFAULT_NOSTRICT, /* run tests as nostrict unless test is flagged as strictonly */ - TEST_DEFAULT_STRICT, /* run tests as strict unless test is flagged as nostrict */ - TEST_NOSTRICT, /* run tests as nostrict, skip strictonly tests */ - TEST_STRICT, /* run tests as strict, skip nostrict tests */ - TEST_ALL, /* run tests in both strict and nostrict, unless restricted by spec */ -} test_mode = TEST_DEFAULT_NOSTRICT; -int skip_async; -int skip_module; -int new_style; -int dump_memory; -int stats_count; -JSMemoryUsage stats_all, stats_avg, stats_min, stats_max; -char *stats_min_filename; -char *stats_max_filename; -int verbose; -char *harness_dir; -char *harness_exclude; -char *harness_features; -char *harness_skip_features; -char *error_filename; -char *error_file; -FILE *error_out; -char *report_filename; -int update_errors; -int test_count, test_failed, test_index, test_skipped, test_excluded; -int new_errors, changed_errors, fixed_errors; -int async_done; - -void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); -void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); - -void warning(const char *fmt, ...) -{ - va_list ap; - - fflush(stdout); - fprintf(stderr, "%s: ", CMD_NAME); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fputc('\n', stderr); -} - -void fatal(int errcode, const char *fmt, ...) -{ - va_list ap; - - fflush(stdout); - fprintf(stderr, "%s: ", CMD_NAME); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fputc('\n', stderr); - - exit(errcode); -} - -void perror_exit(int errcode, const char *s) -{ - fflush(stdout); - fprintf(stderr, "%s: ", CMD_NAME); - perror(s); - exit(errcode); -} - -char *strdup_len(const char *str, int len) -{ - char *p = malloc(len + 1); - memcpy(p, str, len); - p[len] = '\0'; - return p; -} - -static inline int str_equal(const char *a, const char *b) { - return !strcmp(a, b); -} - -char *str_append(char **pp, const char *sep, const char *str) { - char *res, *p; - size_t len = 0; - p = *pp; - if (p) { - len = strlen(p) + strlen(sep); - } - res = malloc(len + strlen(str) + 1); - if (p) { - strcpy(res, p); - strcat(res, sep); - } - strcpy(res + len, str); - free(p); - return *pp = res; -} - -char *str_strip(char *p) -{ - size_t len = strlen(p); - while (len > 0 && isspace((unsigned char)p[len - 1])) - p[--len] = '\0'; - while (isspace((unsigned char)*p)) - p++; - return p; -} - -int has_prefix(const char *str, const char *prefix) -{ - return !strncmp(str, prefix, strlen(prefix)); -} - -char *skip_prefix(const char *str, const char *prefix) -{ - int i; - for (i = 0;; i++) { - if (prefix[i] == '\0') { /* skip the prefix */ - str += i; - break; - } - if (str[i] != prefix[i]) - break; - } - return (char *)str; -} - -char *get_basename(const char *filename) -{ - char *p; - - p = strrchr(filename, '/'); - if (!p) - return NULL; - return strdup_len(filename, p - filename); -} - -char *compose_path(const char *path, const char *name) -{ - int path_len, name_len; - char *d, *q; - - if (!path || path[0] == '\0' || *name == '/') { - d = strdup(name); - } else { - path_len = strlen(path); - name_len = strlen(name); - d = malloc(path_len + 1 + name_len + 1); - if (d) { - q = d; - memcpy(q, path, path_len); - q += path_len; - if (path[path_len - 1] != '/') - *q++ = '/'; - memcpy(q, name, name_len + 1); - } - } - return d; -} - -int namelist_cmp(const char *a, const char *b) -{ - /* compare strings in modified lexicographical order */ - for (;;) { - int ca = (unsigned char)*a++; - int cb = (unsigned char)*b++; - if (isdigit(ca) && isdigit(cb)) { - int na = ca - '0'; - int nb = cb - '0'; - while (isdigit(ca = (unsigned char)*a++)) - na = na * 10 + ca - '0'; - while (isdigit(cb = (unsigned char)*b++)) - nb = nb * 10 + cb - '0'; - if (na < nb) - return -1; - if (na > nb) - return +1; - } - if (ca < cb) - return -1; - if (ca > cb) - return +1; - if (ca == '\0') - return 0; - } -} - -int namelist_cmp_indirect(const void *a, const void *b) -{ - return namelist_cmp(*(const char **)a, *(const char **)b); -} - -void namelist_sort(namelist_t *lp) -{ - int i, count; - if (lp->count > 1) { - qsort(lp->array, lp->count, sizeof(*lp->array), namelist_cmp_indirect); - /* remove duplicates */ - for (count = i = 1; i < lp->count; i++) { - if (namelist_cmp(lp->array[count - 1], lp->array[i]) == 0) { - free(lp->array[i]); - } else { - lp->array[count++] = lp->array[i]; - } - } - lp->count = count; - } - lp->sorted = 1; -} - -int namelist_find(namelist_t *lp, const char *name) -{ - int a, b, m, cmp; - - if (!lp->sorted) { - namelist_sort(lp); - } - for (a = 0, b = lp->count; a < b;) { - m = a + (b - a) / 2; - cmp = namelist_cmp(lp->array[m], name); - if (cmp < 0) - a = m + 1; - else if (cmp > 0) - b = m; - else - return m; - } - return -1; -} - -void namelist_add(namelist_t *lp, const char *base, const char *name) -{ - char *s; - - s = compose_path(base, name); - if (!s) - goto fail; - if (lp->count == lp->size) { - size_t newsize = lp->size + (lp->size >> 1) + 4; - char **a = realloc(lp->array, sizeof(lp->array[0]) * newsize); - if (!a) - goto fail; - lp->array = a; - lp->size = newsize; - } - lp->array[lp->count] = s; - lp->count++; - return; -fail: - fatal(1, "allocation failure\n"); -} - -void namelist_load(namelist_t *lp, const char *filename) -{ - char buf[1024]; - char *base_name; - FILE *f; - - f = fopen(filename, "rb"); - if (!f) { - perror_exit(1, filename); - } - base_name = get_basename(filename); - - while (fgets(buf, sizeof(buf), f) != NULL) { - char *p = str_strip(buf); - if (*p == '#' || *p == ';' || *p == '\0') - continue; /* line comment */ - - namelist_add(lp, base_name, p); - } - free(base_name); - fclose(f); -} - -void namelist_add_from_error_file(namelist_t *lp, const char *file) -{ - const char *p, *p0; - char *pp; - - for (p = file; (p = strstr(p, ".js:")) != NULL; p++) { - for (p0 = p; p0 > file && p0[-1] != '\n'; p0--) - continue; - pp = strdup_len(p0, p + 3 - p0); - namelist_add(lp, NULL, pp); - free(pp); - } -} - -void namelist_free(namelist_t *lp) -{ - while (lp->count > 0) { - free(lp->array[--lp->count]); - } - free(lp->array); - lp->array = NULL; - lp->size = 0; -} - -static int add_test_file(const char *filename, const struct stat *ptr, int flag) -{ - namelist_t *lp = &test_list; - if (has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js")) - namelist_add(lp, NULL, filename); - return 0; -} - -/* find js files from the directory tree and sort the list */ -static void enumerate_tests(const char *path) -{ - namelist_t *lp = &test_list; - int start = lp->count; - ftw(path, add_test_file, 100); - qsort(lp->array + start, lp->count - start, sizeof(*lp->array), - namelist_cmp_indirect); -} - -static JSValue js_print(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int i; - const char *str; - - if (outfile) { - for (i = 0; i < argc; i++) { - if (i != 0) - fputc(' ', outfile); - str = JS_ToCString(ctx, argv[i]); - if (!str) - return JS_EXCEPTION; - if (!strcmp(str, "Test262:AsyncTestComplete")) { - async_done++; - } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) { - async_done = 2; /* force an error */ - } - fputs(str, outfile); - JS_FreeCString(ctx, str); - } - fputc('\n', outfile); - } - return JS_UNDEFINED; -} - -static JSValue js_detachArrayBuffer(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - JS_DetachArrayBuffer(ctx, argv[0]); - return JS_UNDEFINED; -} - -static JSValue js_evalScript(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - const char *str; - size_t len; - JSValue ret; - str = JS_ToCStringLen(ctx, &len, argv[0]); - if (!str) - return JS_EXCEPTION; - ret = JS_Eval(ctx, str, len, "", JS_EVAL_TYPE_GLOBAL); - JS_FreeCString(ctx, str); - return ret; -} - -#ifdef CONFIG_AGENT - -#include - -typedef struct { - struct list_head link; - pthread_t tid; - char *script; - JSValue broadcast_func; - BOOL broadcast_pending; - JSValue broadcast_sab; /* in the main context */ - uint8_t *broadcast_sab_buf; - size_t broadcast_sab_size; - int32_t broadcast_val; -} Test262Agent; - -typedef struct { - struct list_head link; - char *str; -} AgentReport; - -static JSValue add_helpers1(JSContext *ctx); -static void add_helpers(JSContext *ctx); - -static pthread_mutex_t agent_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t agent_cond = PTHREAD_COND_INITIALIZER; -/* list of Test262Agent.link */ -static struct list_head agent_list = LIST_HEAD_INIT(agent_list); - -static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER; -/* list of AgentReport.link */ -static struct list_head report_list = LIST_HEAD_INIT(report_list); - -static void *agent_start(void *arg) -{ - Test262Agent *agent = arg; - JSRuntime *rt; - JSContext *ctx; - JSValue ret_val; - int ret; - - rt = JS_NewRuntime(); - if (rt == NULL) { - fatal(1, "JS_NewRuntime failure"); - } - ctx = JS_NewContext(rt); - if (ctx == NULL) { - JS_FreeRuntime(rt); - fatal(1, "JS_NewContext failure"); - } - JS_SetContextOpaque(ctx, agent); - JS_SetRuntimeInfo(rt, "agent"); - JS_SetCanBlock(rt, TRUE); - - add_helpers(ctx); - ret_val = JS_Eval(ctx, agent->script, strlen(agent->script), - "", JS_EVAL_TYPE_GLOBAL); - free(agent->script); - agent->script = NULL; - if (JS_IsException(ret_val)) - js_std_dump_error(ctx); - JS_FreeValue(ctx, ret_val); - - for(;;) { - JSContext *ctx1; - ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); - if (ret < 0) { - js_std_dump_error(ctx); - break; - } else if (ret == 0) { - if (JS_IsUndefined(agent->broadcast_func)) { - break; - } else { - JSValue args[2]; - - pthread_mutex_lock(&agent_mutex); - while (!agent->broadcast_pending) { - pthread_cond_wait(&agent_cond, &agent_mutex); - } - - agent->broadcast_pending = FALSE; - pthread_cond_signal(&agent_cond); - - pthread_mutex_unlock(&agent_mutex); - - args[0] = JS_NewArrayBuffer(ctx, agent->broadcast_sab_buf, - agent->broadcast_sab_size, - NULL, NULL, TRUE); - args[1] = JS_NewInt32(ctx, agent->broadcast_val); - ret_val = JS_Call(ctx, agent->broadcast_func, JS_UNDEFINED, - 2, (JSValueConst *)args); - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); - if (JS_IsException(ret_val)) - js_std_dump_error(ctx); - JS_FreeValue(ctx, ret_val); - JS_FreeValue(ctx, agent->broadcast_func); - agent->broadcast_func = JS_UNDEFINED; - } - } - } - JS_FreeValue(ctx, agent->broadcast_func); - - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return NULL; -} - -static JSValue js_agent_start(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - const char *script; - Test262Agent *agent; - - if (JS_GetContextOpaque(ctx) != NULL) - return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); - - script = JS_ToCString(ctx, argv[0]); - if (!script) - return JS_EXCEPTION; - agent = malloc(sizeof(*agent)); - memset(agent, 0, sizeof(*agent)); - agent->broadcast_func = JS_UNDEFINED; - agent->broadcast_sab = JS_UNDEFINED; - agent->script = strdup(script); - JS_FreeCString(ctx, script); - list_add_tail(&agent->link, &agent_list); - pthread_create(&agent->tid, NULL, agent_start, agent); - return JS_UNDEFINED; -} - -static void js_agent_free(JSContext *ctx) -{ - struct list_head *el, *el1; - Test262Agent *agent; - - list_for_each_safe(el, el1, &agent_list) { - agent = list_entry(el, Test262Agent, link); - pthread_join(agent->tid, NULL); - JS_FreeValue(ctx, agent->broadcast_sab); - list_del(&agent->link); - free(agent); - } -} - -static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - Test262Agent *agent = JS_GetContextOpaque(ctx); - if (!agent) - return JS_ThrowTypeError(ctx, "must be called inside an agent"); - /* nothing to do */ - return JS_UNDEFINED; -} - -static BOOL is_broadcast_pending(void) -{ - struct list_head *el; - Test262Agent *agent; - list_for_each(el, &agent_list) { - agent = list_entry(el, Test262Agent, link); - if (agent->broadcast_pending) - return TRUE; - } - return FALSE; -} - -static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - JSValueConst sab = argv[0]; - struct list_head *el; - Test262Agent *agent; - uint8_t *buf; - size_t buf_size; - int32_t val; - - if (JS_GetContextOpaque(ctx) != NULL) - return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); - - buf = JS_GetArrayBuffer(ctx, &buf_size, sab); - if (!buf) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &val, argv[1])) - return JS_EXCEPTION; - - /* broadcast the values and wait until all agents have started - calling their callbacks */ - pthread_mutex_lock(&agent_mutex); - list_for_each(el, &agent_list) { - agent = list_entry(el, Test262Agent, link); - agent->broadcast_pending = TRUE; - /* the shared array buffer is used by the thread, so increment - its refcount */ - agent->broadcast_sab = JS_DupValue(ctx, sab); - agent->broadcast_sab_buf = buf; - agent->broadcast_sab_size = buf_size; - agent->broadcast_val = val; - } - pthread_cond_broadcast(&agent_cond); - - while (is_broadcast_pending()) { - pthread_cond_wait(&agent_cond, &agent_mutex); - } - pthread_mutex_unlock(&agent_mutex); - return JS_UNDEFINED; -} - -static JSValue js_agent_receiveBroadcast(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - Test262Agent *agent = JS_GetContextOpaque(ctx); - if (!agent) - return JS_ThrowTypeError(ctx, "must be called inside an agent"); - if (!JS_IsFunction(ctx, argv[0])) - return JS_ThrowTypeError(ctx, "expecting function"); - JS_FreeValue(ctx, agent->broadcast_func); - agent->broadcast_func = JS_DupValue(ctx, argv[0]); - return JS_UNDEFINED; -} - -static JSValue js_agent_sleep(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - uint32_t duration; - if (JS_ToUint32(ctx, &duration, argv[0])) - return JS_EXCEPTION; - usleep(duration * 1000); - return JS_UNDEFINED; -} - -static int64_t get_clock_ms(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); -} - -static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - return JS_NewInt64(ctx, get_clock_ms()); -} - -static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - AgentReport *rep; - JSValue ret; - - pthread_mutex_lock(&report_mutex); - if (list_empty(&report_list)) { - rep = NULL; - } else { - rep = list_entry(report_list.next, AgentReport, link); - list_del(&rep->link); - } - pthread_mutex_unlock(&report_mutex); - if (rep) { - ret = JS_NewString(ctx, rep->str); - free(rep->str); - free(rep); - } else { - ret = JS_NULL; - } - return ret; -} - -static JSValue js_agent_report(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - const char *str; - AgentReport *rep; - - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - rep = malloc(sizeof(*rep)); - rep->str = strdup(str); - JS_FreeCString(ctx, str); - - pthread_mutex_lock(&report_mutex); - list_add_tail(&rep->link, &report_list); - pthread_mutex_unlock(&report_mutex); - return JS_UNDEFINED; -} - -static const JSCFunctionListEntry js_agent_funcs[] = { - /* only in main */ - JS_CFUNC_DEF("start", 1, js_agent_start ), - JS_CFUNC_DEF("getReport", 0, js_agent_getReport ), - JS_CFUNC_DEF("broadcast", 2, js_agent_broadcast ), - /* only in agent */ - JS_CFUNC_DEF("report", 1, js_agent_report ), - JS_CFUNC_DEF("leaving", 0, js_agent_leaving ), - JS_CFUNC_DEF("receiveBroadcast", 1, js_agent_receiveBroadcast ), - /* in both */ - JS_CFUNC_DEF("sleep", 1, js_agent_sleep ), - JS_CFUNC_DEF("monotonicNow", 0, js_agent_monotonicNow ), -}; - -static JSValue js_new_agent(JSContext *ctx) -{ - JSValue agent; - agent = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, agent, js_agent_funcs, - countof(js_agent_funcs)); - return agent; -} -#endif - -static JSValue js_createRealm(JSContext *ctx, JSValue this_val, - int argc, JSValue *argv) -{ - JSContext *ctx1; - JSValue ret; - - ctx1 = JS_NewContext(JS_GetRuntime(ctx)); - if (!ctx1) - return JS_ThrowOutOfMemory(ctx); - 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; -} - -static JSValue add_helpers1(JSContext *ctx) -{ - JSValue global_obj; - JSValue obj262, obj; - - global_obj = JS_GetGlobalObject(ctx); - - JS_SetPropertyStr(ctx, global_obj, "print", - JS_NewCFunction(ctx, js_print, "print", 1)); - - /* $262 special object used by the tests */ - obj262 = JS_NewObject(ctx); - JS_SetPropertyStr(ctx, obj262, "detachArrayBuffer", - JS_NewCFunction(ctx, js_detachArrayBuffer, - "detachArrayBuffer", 1)); - JS_SetPropertyStr(ctx, obj262, "evalScript", - JS_NewCFunction(ctx, js_evalScript, - "evalScript", 1)); - JS_SetPropertyStr(ctx, obj262, "codePointRange", - JS_NewCFunction(ctx, js_string_codePointRange, - "codePointRange", 2)); -#ifdef CONFIG_AGENT - JS_SetPropertyStr(ctx, obj262, "agent", js_new_agent(ctx)); -#endif - - JS_SetPropertyStr(ctx, obj262, "global", - JS_DupValue(ctx, global_obj)); - JS_SetPropertyStr(ctx, obj262, "createRealm", - JS_NewCFunction(ctx, js_createRealm, - "createRealm", 0)); - 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)); - - JS_FreeValue(ctx, global_obj); - return obj262; -} - -static void add_helpers(JSContext *ctx) -{ - JS_FreeValue(ctx, add_helpers1(ctx)); -} - -static char *load_file(const char *filename, size_t *lenp) -{ - char *buf; - size_t buf_len; - buf = (char *)js_load_file(NULL, &buf_len, filename); - if (!buf) - perror_exit(1, filename); - if (lenp) - *lenp = buf_len; - return buf; -} - -static JSModuleDef *js_module_loader_test(JSContext *ctx, - const char *module_name, void *opaque) -{ - size_t buf_len; - uint8_t *buf; - JSModuleDef *m; - JSValue func_val; - - buf = js_load_file(ctx, &buf_len, module_name); - if (!buf) { - JS_ThrowReferenceError(ctx, "could not load module filename '%s'", - module_name); - return NULL; - } - - /* compile the module */ - func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, - JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); - js_free(ctx, buf); - if (JS_IsException(func_val)) - return NULL; - /* the module is already referenced, so we must free it */ - m = JS_VALUE_GET_PTR(func_val); - JS_FreeValue(ctx, func_val); - return m; -} - -int is_line_sep(char c) -{ - return (c == '\0' || c == '\n' || c == '\r'); -} - -char *find_line(const char *str, const char *line) -{ - if (str) { - const char *p; - int len = strlen(line); - for (p = str; (p = strstr(p, line)) != NULL; p += len + 1) { - if ((p == str || is_line_sep(p[-1])) && is_line_sep(p[len])) - return (char *)p; - } - } - return NULL; -} - -int is_word_sep(char c) -{ - return (c == '\0' || isspace((unsigned char)c) || c == ','); -} - -char *find_word(const char *str, const char *word) -{ - const char *p; - int len = strlen(word); - if (str && len) { - for (p = str; (p = strstr(p, word)) != NULL; p += len) { - if ((p == str || is_word_sep(p[-1])) && is_word_sep(p[len])) - return (char *)p; - } - } - return NULL; -} - -/* handle exclude directories */ -void update_exclude_dirs(void) -{ - namelist_t *lp = &test_list; - namelist_t *ep = &exclude_list; - namelist_t *dp = &exclude_dir_list; - char *name; - int i, j, count; - - /* split directpries from exclude_list */ - for (count = i = 0; i < ep->count; i++) { - name = ep->array[i]; - if (has_suffix(name, "/")) { - namelist_add(dp, NULL, name); - free(name); - } else { - ep->array[count++] = name; - } - } - ep->count = count; - - namelist_sort(dp); - - /* filter out excluded directories */ - for (count = i = 0; i < lp->count; i++) { - name = lp->array[i]; - for (j = 0; j < dp->count; j++) { - if (has_prefix(name, dp->array[j])) { - test_excluded++; - free(name); - name = NULL; - break; - } - } - if (name) { - lp->array[count++] = name; - } - } - lp->count = count; -} - -void load_config(const char *filename) -{ - char buf[1024]; - FILE *f; - char *base_name; - enum { - SECTION_NONE = 0, - SECTION_CONFIG, - SECTION_EXCLUDE, - SECTION_FEATURES, - SECTION_TESTS, - } section = SECTION_NONE; - int lineno = 0; - - f = fopen(filename, "rb"); - if (!f) { - perror_exit(1, filename); - } - base_name = get_basename(filename); - - while (fgets(buf, sizeof(buf), f) != NULL) { - char *p, *q; - lineno++; - p = str_strip(buf); - if (*p == '#' || *p == ';' || *p == '\0') - continue; /* line comment */ - - if (*p == "[]"[0]) { - /* new section */ - p++; - p[strcspn(p, "]")] = '\0'; - if (str_equal(p, "config")) - section = SECTION_CONFIG; - else if (str_equal(p, "exclude")) - section = SECTION_EXCLUDE; - else if (str_equal(p, "features")) - section = SECTION_FEATURES; - else if (str_equal(p, "tests")) - section = SECTION_TESTS; - else - section = SECTION_NONE; - continue; - } - q = strchr(p, '='); - if (q) { - /* setting: name=value */ - *q++ = '\0'; - q = str_strip(q); - } - switch (section) { - case SECTION_CONFIG: - if (!q) { - printf("%s:%d: syntax error\n", filename, lineno); - continue; - } - if (str_equal(p, "style")) { - new_style = str_equal(q, "new"); - continue; - } - if (str_equal(p, "testdir")) { - char *testdir = compose_path(base_name, q); - enumerate_tests(testdir); - free(testdir); - continue; - } - if (str_equal(p, "harnessdir")) { - harness_dir = compose_path(base_name, q); - continue; - } - if (str_equal(p, "harnessexclude")) { - str_append(&harness_exclude, " ", q); - continue; - } - if (str_equal(p, "features")) { - str_append(&harness_features, " ", q); - continue; - } - if (str_equal(p, "skip-features")) { - str_append(&harness_skip_features, " ", q); - continue; - } - if (str_equal(p, "mode")) { - if (str_equal(q, "default") || str_equal(q, "default-nostrict")) - test_mode = TEST_DEFAULT_NOSTRICT; - else if (str_equal(q, "default-strict")) - test_mode = TEST_DEFAULT_STRICT; - else if (str_equal(q, "nostrict")) - test_mode = TEST_NOSTRICT; - else if (str_equal(q, "strict")) - test_mode = TEST_STRICT; - else if (str_equal(q, "all") || str_equal(q, "both")) - test_mode = TEST_ALL; - else - fatal(2, "unknown test mode: %s", q); - continue; - } - if (str_equal(p, "strict")) { - if (str_equal(q, "skip") || str_equal(q, "no")) - test_mode = TEST_NOSTRICT; - continue; - } - if (str_equal(p, "nostrict")) { - if (str_equal(q, "skip") || str_equal(q, "no")) - test_mode = TEST_STRICT; - continue; - } - if (str_equal(p, "async")) { - skip_async = !str_equal(q, "yes"); - continue; - } - if (str_equal(p, "module")) { - skip_module = !str_equal(q, "yes"); - continue; - } - if (str_equal(p, "verbose")) { - verbose = str_equal(q, "yes"); - continue; - } - if (str_equal(p, "errorfile")) { - error_filename = compose_path(base_name, q); - continue; - } - if (str_equal(p, "excludefile")) { - char *path = compose_path(base_name, q); - namelist_load(&exclude_list, path); - free(path); - continue; - } - if (str_equal(p, "reportfile")) { - report_filename = compose_path(base_name, q); - continue; - } - case SECTION_EXCLUDE: - namelist_add(&exclude_list, base_name, p); - break; - case SECTION_FEATURES: - if (!q || str_equal(q, "yes")) - str_append(&harness_features, " ", p); - else - str_append(&harness_skip_features, " ", p); - break; - case SECTION_TESTS: - namelist_add(&test_list, base_name, p); - break; - default: - /* ignore settings in other sections */ - break; - } - } - fclose(f); - free(base_name); -} - -char *find_error(const char *filename, int *pline, int is_strict) -{ - if (error_file) { - size_t len = strlen(filename); - const char *p, *q, *r; - int line; - - for (p = error_file; (p = strstr(p, filename)) != NULL; p += len) { - if ((p == error_file || p[-1] == '\n' || p[-1] == '(') && p[len] == ':') { - q = p + len; - line = 1; - if (*q == ':') { - line = strtol(q + 1, (char**)&q, 10); - if (*q == ':') - q++; - } - while (*q == ' ') { - q++; - } - /* check strict mode indicator */ - if (!strstart(q, "strict mode: ", &q) != !is_strict) - continue; - r = q = skip_prefix(q, "unexpected error: "); - r += strcspn(r, "\n"); - while (r[0] == '\n' && r[1] && strncmp(r + 1, filename, 8)) { - r++; - r += strcspn(r, "\n"); - } - if (pline) - *pline = line; - return strdup_len(q, r - q); - } - } - } - return NULL; -} - -int skip_comments(const char *str, int line, int *pline) -{ - const char *p; - int c; - - p = str; - while ((c = (unsigned char)*p++) != '\0') { - if (isspace(c)) { - if (c == '\n') - line++; - continue; - } - if (c == '/' && *p == '/') { - while (*++p && *p != '\n') - continue; - continue; - } - if (c == '/' && *p == '*') { - for (p += 1; *p; p++) { - if (*p == '\n') { - line++; - continue; - } - if (*p == '*' && p[1] == '/') { - p += 2; - break; - } - } - continue; - } - break; - } - if (pline) - *pline = line; - - return p - str; -} - -int longest_match(const char *str, const char *find, int pos, int *ppos, int line, int *pline) -{ - int len, maxlen; - - maxlen = 0; - - if (*find) { - const char *p; - for (p = str + pos; *p; p++) { - if (*p == *find) { - for (len = 1; p[len] && p[len] == find[len]; len++) - continue; - if (len > maxlen) { - maxlen = len; - if (ppos) - *ppos = p - str; - if (pline) - *pline = line; - if (!find[len]) - break; - } - } - if (*p == '\n') - line++; - } - } - return maxlen; -} - -static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename, int is_test, int is_negative, - const char *error_type, FILE *outfile, int eval_flags, - int is_async) -{ - JSValue res_val, exception_val; - int ret, error_line, pos, pos_line; - BOOL is_error, has_error_line; - const char *error_name; - - pos = skip_comments(buf, 1, &pos_line); - error_line = pos_line; - has_error_line = FALSE; - exception_val = JS_UNDEFINED; - error_name = NULL; - - async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */ - - res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); - - if (is_async && !JS_IsException(res_val)) { - JS_FreeValue(ctx, res_val); - for(;;) { - JSContext *ctx1; - ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); - if (ret < 0) { - res_val = JS_EXCEPTION; - break; - } else if (ret == 0) { - /* test if the test called $DONE() once */ - if (async_done != 1) { - res_val = JS_ThrowTypeError(ctx, "$DONE() not called"); - } else { - res_val = JS_UNDEFINED; - } - break; - } - } - } - - if (JS_IsException(res_val)) { - exception_val = JS_GetException(ctx); - is_error = JS_IsError(ctx, exception_val); - /* XXX: should get the filename and line number */ - if (outfile) { - if (!is_error) - fprintf(outfile, "%sThrow: ", (eval_flags & JS_EVAL_FLAG_STRICT) ? - "strict mode: " : ""); - js_print(ctx, JS_NULL, 1, &exception_val); - } - if (is_error) { - JSValue name, stack; - const char *stack_str; - - name = JS_GetPropertyStr(ctx, exception_val, "name"); - error_name = JS_ToCString(ctx, name); - stack = JS_GetPropertyStr(ctx, exception_val, "stack"); - if (!JS_IsUndefined(stack)) { - stack_str = JS_ToCString(ctx, stack); - if (stack_str) { - const char *p; - int len; - - if (outfile) - fprintf(outfile, "%s", stack_str); - - len = strlen(filename); - p = strstr(stack_str, filename); - if (p != NULL && p[len] == ':') { - error_line = atoi(p + len + 1); - has_error_line = TRUE; - } - JS_FreeCString(ctx, stack_str); - } - } - JS_FreeValue(ctx, stack); - JS_FreeValue(ctx, name); - } - if (is_negative) { - ret = 0; - if (error_type) { - char *error_class; - const char *msg; - - msg = JS_ToCString(ctx, exception_val); - error_class = strdup_len(msg, strcspn(msg, ":")); - if (!str_equal(error_class, error_type)) - ret = -1; - free(error_class); - JS_FreeCString(ctx, msg); - } - } else { - ret = -1; - } - } else { - if (is_negative) - ret = -1; - else - ret = 0; - } - - if (verbose && is_test) { - JSValue msg_val = JS_UNDEFINED; - const char *msg = NULL; - int s_line; - char *s = find_error(filename, &s_line, eval_flags & JS_EVAL_FLAG_STRICT); - const char *strict_mode = (eval_flags & JS_EVAL_FLAG_STRICT) ? "strict mode: " : ""; - - if (!JS_IsUndefined(exception_val)) { - msg_val = JS_ToString(ctx, exception_val); - msg = JS_ToCString(ctx, msg_val); - } - if (is_negative) { // expect error - if (ret == 0) { - if (msg && s && - (str_equal(s, "expected error") || - strstart(s, "unexpected error type:", NULL) || - str_equal(s, msg))) { // did not have error yet - if (!has_error_line) { - longest_match(buf, msg, pos, &pos, pos_line, &error_line); - } - printf("%s:%d: %sOK, now has error %s\n", - filename, error_line, strict_mode, msg); - fixed_errors++; - } - } else { - if (!s) { // not yet reported - if (msg) { - fprintf(error_out, "%s:%d: %sunexpected error type: %s\n", - filename, error_line, strict_mode, msg); - } else { - fprintf(error_out, "%s:%d: %sexpected error\n", - filename, error_line, strict_mode); - } - new_errors++; - } - } - } else { // should not have error - if (msg) { - if (!s || !str_equal(s, msg)) { - if (!has_error_line) { - char *p = skip_prefix(msg, "Test262 Error: "); - if (strstr(p, "Test case returned non-true value!")) { - longest_match(buf, "runTestCase", pos, &pos, pos_line, &error_line); - } else { - longest_match(buf, p, pos, &pos, pos_line, &error_line); - } - } - fprintf(error_out, "%s:%d: %s%s%s\n", filename, error_line, strict_mode, - error_file ? "unexpected error: " : "", msg); - - if (s && (!str_equal(s, msg) || error_line != s_line)) { - printf("%s:%d: %sprevious error: %s\n", filename, s_line, strict_mode, s); - changed_errors++; - } else { - new_errors++; - } - } - } else { - if (s) { - printf("%s:%d: %sOK, fixed error: %s\n", filename, s_line, strict_mode, s); - fixed_errors++; - } - } - } - JS_FreeValue(ctx, msg_val); - JS_FreeCString(ctx, msg); - free(s); - } - JS_FreeCString(ctx, error_name); - JS_FreeValue(ctx, exception_val); - JS_FreeValue(ctx, res_val); - return ret; -} - -static int eval_file(JSContext *ctx, const char *base, const char *p, - int eval_flags) -{ - char *buf; - size_t buf_len; - char *filename = compose_path(base, p); - - buf = load_file(filename, &buf_len); - if (!buf) { - warning("cannot load %s", filename); - goto fail; - } - if (eval_buf(ctx, buf, buf_len, filename, FALSE, FALSE, NULL, stderr, - eval_flags, FALSE)) { - warning("error evaluating %s", filename); - goto fail; - } - free(buf); - free(filename); - return 0; - -fail: - free(buf); - free(filename); - return 1; -} - -char *extract_desc(const char *buf, char style) -{ - const char *p, *desc_start; - char *desc; - int len; - - p = buf; - while (*p != '\0') { - if (p[0] == '/' && p[1] == '*' && p[2] == style && p[3] != '/') { - p += 3; - desc_start = p; - while (*p != '\0' && (p[0] != '*' || p[1] != '/')) - p++; - if (*p == '\0') { - warning("Expecting end of desc comment"); - return NULL; - } - len = p - desc_start; - desc = malloc(len + 1); - memcpy(desc, desc_start, len); - desc[len] = '\0'; - return desc; - } else { - p++; - } - } - return NULL; -} - -static char *find_tag(char *desc, const char *tag, int *state) -{ - char *p; - p = strstr(desc, tag); - if (p) { - p += strlen(tag); - *state = 0; - } - return p; -} - -static char *get_option(char **pp, int *state) -{ - char *p, *p0, *option = NULL; - if (*pp) { - for (p = *pp;; p++) { - switch (*p) { - case '[': - *state += 1; - continue; - case ']': - *state -= 1; - if (*state > 0) - continue; - p = NULL; - break; - case ' ': - case '\t': - case '\r': - case ',': - case '-': - continue; - case '\n': - if (*state > 0 || p[1] == ' ') - continue; - p = NULL; - break; - case '\0': - p = NULL; - break; - default: - p0 = p; - p += strcspn(p0, " \t\r\n,]"); - option = strdup_len(p0, p - p0); - break; - } - break; - } - *pp = p; - } - return option; -} - -void update_stats(JSRuntime *rt, const char *filename) { - JSMemoryUsage stats; - JS_ComputeMemoryUsage(rt, &stats); - if (stats_count++ == 0) { - stats_avg = stats_all = stats_min = stats_max = stats; - stats_min_filename = strdup(filename); - stats_max_filename = strdup(filename); - } else { - if (stats_max.malloc_size < stats.malloc_size) { - stats_max = stats; - free(stats_max_filename); - stats_max_filename = strdup(filename); - } - if (stats_min.malloc_size > stats.malloc_size) { - stats_min = stats; - free(stats_min_filename); - stats_min_filename = strdup(filename); - } - -#define update(f) stats_avg.f = (stats_all.f += stats.f) / stats_count - update(malloc_count); - update(malloc_size); - update(memory_used_count); - update(memory_used_size); - update(atom_count); - update(atom_size); - update(str_count); - update(str_size); - update(obj_count); - update(obj_size); - update(prop_count); - update(prop_size); - update(shape_count); - update(shape_size); - update(js_func_count); - update(js_func_size); - update(js_func_code_size); - update(js_func_pc2line_count); - update(js_func_pc2line_size); - update(c_func_count); - update(array_count); - update(fast_array_count); - update(fast_array_elements); - } -#undef update -} - -int run_test_buf(const char *filename, char *harness, namelist_t *ip, - char *buf, size_t buf_len, const char* error_type, - int eval_flags, BOOL is_negative, BOOL is_async, - BOOL can_block) -{ - JSRuntime *rt; - JSContext *ctx; - int i, ret; - - rt = JS_NewRuntime(); - if (rt == NULL) { - fatal(1, "JS_NewRuntime failure"); - } - ctx = JS_NewContext(rt); - if (ctx == NULL) { - JS_FreeRuntime(rt); - fatal(1, "JS_NewContext failure"); - } - JS_SetRuntimeInfo(rt, filename); - - JS_SetCanBlock(rt, can_block); - - /* loader for ES6 modules */ - JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL); - - add_helpers(ctx); - - for (i = 0; i < ip->count; i++) { - if (eval_file(ctx, harness, ip->array[i], - JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRIP)) { - fatal(1, "error including %s for %s", ip->array[i], filename); - } - } - - ret = eval_buf(ctx, buf, buf_len, filename, TRUE, is_negative, - error_type, outfile, eval_flags, is_async); - ret = (ret != 0); - - if (dump_memory) { - update_stats(rt, filename); - } -#ifdef CONFIG_AGENT - js_agent_free(ctx); -#endif - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - - test_count++; - if (ret) { - test_failed++; - if (outfile) { - /* do not output a failure number to minimize diff */ - fprintf(outfile, " FAILED\n"); - } - } - return ret; -} - -int run_test(const char *filename, int index) -{ - char harnessbuf[1024]; - char *harness; - char *buf; - size_t buf_len; - char *desc, *p; - char *error_type; - int ret, eval_flags, use_strict, use_nostrict; - BOOL is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip; - BOOL can_block; - namelist_t include_list = { 0 }, *ip = &include_list; - - is_nostrict = is_onlystrict = is_negative = is_async = is_module = skip = FALSE; - can_block = TRUE; - error_type = NULL; - buf = load_file(filename, &buf_len); - - harness = harness_dir; - - if (new_style) { - if (!harness) { - p = strstr(filename, "test/"); - if (p) { - snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s", - (int)(p - filename), filename, "harness"); - } - harness = harnessbuf; - } - namelist_add(ip, NULL, "sta.js"); - namelist_add(ip, NULL, "assert.js"); - /* extract the YAML frontmatter */ - desc = extract_desc(buf, '-'); - if (desc) { - char *ifile, *option; - int state; - p = find_tag(desc, "includes:", &state); - if (p) { - while ((ifile = get_option(&p, &state)) != NULL) { - // skip unsupported harness files - if (find_word(harness_exclude, ifile)) { - skip |= 1; - } else { - namelist_add(ip, NULL, ifile); - } - free(ifile); - } - } - p = find_tag(desc, "flags:", &state); - if (p) { - while ((option = get_option(&p, &state)) != NULL) { - if (str_equal(option, "noStrict") || - str_equal(option, "raw")) { - is_nostrict = TRUE; - skip |= (test_mode == TEST_STRICT); - } - else if (str_equal(option, "onlyStrict")) { - is_onlystrict = TRUE; - skip |= (test_mode == TEST_NOSTRICT); - } - else if (str_equal(option, "async")) { - is_async = TRUE; - skip |= skip_async; - } - else if (str_equal(option, "module")) { - is_module = TRUE; - skip |= skip_module; - } - else if (str_equal(option, "CanBlockIsFalse")) { - can_block = FALSE; - } - free(option); - } - } - p = find_tag(desc, "negative:", &state); - if (p) { - /* XXX: should extract the phase */ - char *q = find_tag(p, "type:", &state); - if (q) { - while (isspace(*q)) - q++; - error_type = strdup_len(q, strcspn(q, " \n")); - } - is_negative = TRUE; - } - p = find_tag(desc, "features:", &state); - if (p) { - while ((option = get_option(&p, &state)) != NULL) { - if (find_word(harness_features, option)) { - /* feature is enabled */ - } else if (find_word(harness_skip_features, option)) { - /* skip disabled feature */ - skip |= 1; - } else { - /* feature is not listed: skip and warn */ - printf("%s:%d: unknown feature: %s\n", filename, 1, option); - skip |= 1; - } - free(option); - } - } - free(desc); - } - if (is_async) - namelist_add(ip, NULL, "doneprintHandle.js"); - } else { - char *ifile; - - if (!harness) { - p = strstr(filename, "test/"); - if (p) { - snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s", - (int)(p - filename), filename, "test/harness"); - } - harness = harnessbuf; - } - - namelist_add(ip, NULL, "sta.js"); - - /* include extra harness files */ - for (p = buf; (p = strstr(p, "$INCLUDE(\"")) != NULL; p++) { - p += 10; - ifile = strdup_len(p, strcspn(p, "\"")); - // skip unsupported harness files - if (find_word(harness_exclude, ifile)) { - skip |= 1; - } else { - namelist_add(ip, NULL, ifile); - } - free(ifile); - } - - /* locate the old style configuration comment */ - desc = extract_desc(buf, '*'); - if (desc) { - if (strstr(desc, "@noStrict")) { - is_nostrict = TRUE; - skip |= (test_mode == TEST_STRICT); - } - if (strstr(desc, "@onlyStrict")) { - is_onlystrict = TRUE; - skip |= (test_mode == TEST_NOSTRICT); - } - if (strstr(desc, "@negative")) { - /* XXX: should extract the regex to check error type */ - is_negative = TRUE; - } - free(desc); - } - } - - if (outfile && index >= 0) { - fprintf(outfile, "%d: %s%s%s%s%s%s%s\n", index, filename, - is_nostrict ? " @noStrict" : "", - is_onlystrict ? " @onlyStrict" : "", - is_async ? " async" : "", - is_module ? " module" : "", - is_negative ? " @negative" : "", - skip ? " SKIPPED" : ""); - fflush(outfile); - } - - use_strict = use_nostrict = 0; - /* XXX: should remove 'test_mode' or simplify it just to force - strict or non strict mode for single file tests */ - switch (test_mode) { - case TEST_DEFAULT_NOSTRICT: - if (is_onlystrict) - use_strict = 1; - else - use_nostrict = 1; - break; - case TEST_DEFAULT_STRICT: - if (is_nostrict) - use_nostrict = 1; - else - use_strict = 1; - break; - case TEST_NOSTRICT: - if (!is_onlystrict) - use_nostrict = 1; - break; - case TEST_STRICT: - if (!is_nostrict) - use_strict = 1; - break; - case TEST_ALL: - if (is_module) { - use_nostrict = 1; - } else { - if (!is_nostrict) - use_strict = 1; - if (!is_onlystrict) - use_nostrict = 1; - } - break; - } - - if (skip || use_strict + use_nostrict == 0) { - test_skipped++; - ret = -2; - } else { - clock_t clocks; - - if (is_module) { - eval_flags = JS_EVAL_TYPE_MODULE; - } else { - eval_flags = JS_EVAL_TYPE_GLOBAL; - } - clocks = clock(); - ret = 0; - if (use_nostrict) { - ret = run_test_buf(filename, harness, ip, buf, buf_len, - error_type, eval_flags, is_negative, is_async, - can_block); - } - if (use_strict) { - ret |= run_test_buf(filename, harness, ip, buf, buf_len, - error_type, eval_flags | JS_EVAL_FLAG_STRICT, - is_negative, is_async, can_block); - } - clocks = clock() - clocks; - if (outfile && index >= 0 && clocks >= CLOCKS_PER_SEC / 10) { - /* output timings for tests that take more than 100 ms */ - fprintf(outfile, " time: %d ms\n", (int)(clocks * 1000LL / CLOCKS_PER_SEC)); - } - } - namelist_free(&include_list); - free(error_type); - free(buf); - - return ret; -} - -/* run a test when called by test262-harness+eshost */ -int run_test262_harness_test(const char *filename, BOOL is_module) -{ - JSRuntime *rt; - JSContext *ctx; - char *buf; - size_t buf_len; - int eval_flags, ret_code, ret; - JSValue res_val; - BOOL can_block; - - outfile = stdout; /* for js_print */ - - rt = JS_NewRuntime(); - if (rt == NULL) { - fatal(1, "JS_NewRuntime failure"); - } - ctx = JS_NewContext(rt); - if (ctx == NULL) { - JS_FreeRuntime(rt); - fatal(1, "JS_NewContext failure"); - } - JS_SetRuntimeInfo(rt, filename); - - can_block = TRUE; - JS_SetCanBlock(rt, can_block); - - /* loader for ES6 modules */ - JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL); - - add_helpers(ctx); - - buf = load_file(filename, &buf_len); - - if (is_module) { - eval_flags = JS_EVAL_TYPE_MODULE; - } else { - eval_flags = JS_EVAL_TYPE_GLOBAL; - } - res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); - ret_code = 0; - if (JS_IsException(res_val)) { - js_std_dump_error(ctx); - ret_code = 1; - } else { - JS_FreeValue(ctx, res_val); - for(;;) { - JSContext *ctx1; - ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); - if (ret < 0) { - js_std_dump_error(ctx1); - ret_code = 1; - } else if (ret == 0) { - break; - } - } - } - free(buf); -#ifdef CONFIG_AGENT - js_agent_free(ctx); -#endif - JS_FreeContext(ctx); - JS_FreeRuntime(rt); - return ret_code; -} - -clock_t last_clock; - -void show_progress(int force) { - clock_t t = clock(); - if (force || !last_clock || (t - last_clock) > CLOCKS_PER_SEC / 20) { - last_clock = t; - /* output progress indicator: erase end of line and return to col 0 */ - fprintf(stderr, "%d/%d/%d\033[K\r", - test_failed, test_count, test_skipped); - fflush(stderr); - } -} - -static int slow_test_threshold; - -void run_test_dir_list(namelist_t *lp, int start_index, int stop_index) -{ - int i; - - namelist_sort(lp); - for (i = 0; i < lp->count; i++) { - const char *p = lp->array[i]; - if (namelist_find(&exclude_list, p) >= 0) { - test_excluded++; - } else if (test_index < start_index) { - test_skipped++; - } else if (stop_index >= 0 && test_index > stop_index) { - test_skipped++; - } else { - int ti; - if (slow_test_threshold != 0) { - ti = get_clock_ms(); - } else { - ti = 0; - } - run_test(p, test_index); - if (slow_test_threshold != 0) { - ti = get_clock_ms() - ti; - if (ti >= slow_test_threshold) - fprintf(stderr, "\n%s (%d ms)\n", p, ti); - } - show_progress(FALSE); - } - test_index++; - } - show_progress(TRUE); -} - -void help(void) -{ - printf("run-test262 version " CONFIG_VERSION "\n" - "usage: run-test262 [options] {-f file ... | [dir_list] [index range]}\n" - "-h help\n" - "-a run tests in strict and nostrict modes\n" - "-m print memory usage summary\n" - "-n use new style harness\n" - "-N run test prepared by test262-harness+eshost\n" - "-s run tests in strict mode, skip @nostrict tests\n" - "-E only run tests from the error file\n" - "-u update error file\n" - "-v verbose: output error messages\n" - "-T duration display tests taking more than 'duration' ms\n" - "-c file read configuration from 'file'\n" - "-d dir run all test files in directory tree 'dir'\n" - "-e file load the known errors from 'file'\n" - "-f file execute single test from 'file'\n" - "-r file set the report file name (default=none)\n" - "-x file exclude tests listed in 'file'\n"); - exit(1); -} - -char *get_opt_arg(const char *option, char *arg) -{ - if (!arg) { - fatal(2, "missing argument for option %s", option); - } - return arg; -} - -int main(int argc, char **argv) -{ - int optind, start_index, stop_index; - BOOL is_dir_list; - BOOL only_check_errors = FALSE; - const char *filename; - BOOL is_test262_harness = FALSE; - BOOL is_module = FALSE; - -#if !defined(_WIN32) - /* Date tests assume California local time */ - setenv("TZ", "America/Los_Angeles", 1); -#endif - - /* cannot use getopt because we want to pass the command line to - the script */ - optind = 1; - is_dir_list = TRUE; - while (optind < argc) { - char *arg = argv[optind]; - if (*arg != '-') - break; - optind++; - if (str_equal(arg, "-h")) { - help(); - } else if (str_equal(arg, "-m")) { - dump_memory++; - } else if (str_equal(arg, "-n")) { - new_style++; - } else if (str_equal(arg, "-s")) { - test_mode = TEST_STRICT; - } else if (str_equal(arg, "-a")) { - test_mode = TEST_ALL; - } else if (str_equal(arg, "-u")) { - update_errors++; - } else if (str_equal(arg, "-v")) { - verbose++; - } else if (str_equal(arg, "-c")) { - load_config(get_opt_arg(arg, argv[optind++])); - } else if (str_equal(arg, "-d")) { - enumerate_tests(get_opt_arg(arg, argv[optind++])); - } else if (str_equal(arg, "-e")) { - error_filename = get_opt_arg(arg, argv[optind++]); - } else if (str_equal(arg, "-x")) { - namelist_load(&exclude_list, get_opt_arg(arg, argv[optind++])); - } else if (str_equal(arg, "-f")) { - is_dir_list = FALSE; - } else if (str_equal(arg, "-r")) { - report_filename = get_opt_arg(arg, argv[optind++]); - } else if (str_equal(arg, "-E")) { - only_check_errors = TRUE; - } else if (str_equal(arg, "-T")) { - slow_test_threshold = atoi(get_opt_arg(arg, argv[optind++])); - } else if (str_equal(arg, "-N")) { - is_test262_harness = TRUE; - } else if (str_equal(arg, "--module")) { - is_module = TRUE; - } else { - fatal(1, "unknown option: %s", arg); - break; - } - } - - if (optind >= argc && !test_list.count) - help(); - - if (is_test262_harness) { - return run_test262_harness_test(argv[optind], is_module); - } - - error_out = stdout; - if (error_filename) { - error_file = load_file(error_filename, NULL); - if (only_check_errors && error_file) { - namelist_free(&test_list); - namelist_add_from_error_file(&test_list, error_file); - } - if (update_errors) { - free(error_file); - error_file = NULL; - error_out = fopen(error_filename, "w"); - if (!error_out) { - perror_exit(1, error_filename); - } - } - } - - update_exclude_dirs(); - - if (is_dir_list) { - if (optind < argc && !isdigit(argv[optind][0])) { - filename = argv[optind++]; - namelist_load(&test_list, filename); - } - start_index = 0; - stop_index = -1; - if (optind < argc) { - start_index = atoi(argv[optind++]); - if (optind < argc) { - stop_index = atoi(argv[optind++]); - } - } - if (!report_filename || str_equal(report_filename, "none")) { - outfile = NULL; - } else if (str_equal(report_filename, "-")) { - outfile = stdout; - } else { - outfile = fopen(report_filename, "wb"); - if (!outfile) { - perror_exit(1, report_filename); - } - } - run_test_dir_list(&test_list, start_index, stop_index); - - if (outfile && outfile != stdout) { - fclose(outfile); - outfile = NULL; - } - } else { - outfile = stdout; - while (optind < argc) { - run_test(argv[optind++], -1); - } - } - - if (dump_memory) { - if (dump_memory > 1 && stats_count > 1) { - printf("\nMininum memory statistics for %s:\n\n", stats_min_filename); - JS_DumpMemoryUsage(stdout, &stats_min, NULL); - printf("\nMaximum memory statistics for %s:\n\n", stats_max_filename); - JS_DumpMemoryUsage(stdout, &stats_max, NULL); - } - printf("\nAverage memory statistics for %d tests:\n\n", stats_count); - JS_DumpMemoryUsage(stdout, &stats_avg, NULL); - printf("\n"); - } - - if (is_dir_list) { - fprintf(stderr, "Result: %d/%d error%s", - test_failed, test_count, test_count != 1 ? "s" : ""); - if (test_excluded) - fprintf(stderr, ", %d excluded", test_excluded); - if (test_skipped) - fprintf(stderr, ", %d skipped", test_skipped); - if (error_file) { - if (new_errors) - fprintf(stderr, ", %d new", new_errors); - if (changed_errors) - fprintf(stderr, ", %d changed", changed_errors); - if (fixed_errors) - fprintf(stderr, ", %d fixed", fixed_errors); - } - fprintf(stderr, "\n"); - } - - if (error_out && error_out != stdout) { - fclose(error_out); - error_out = NULL; - } - - namelist_free(&test_list); - namelist_free(&exclude_list); - namelist_free(&exclude_dir_list); - free(harness_dir); - free(harness_features); - free(harness_exclude); - free(error_file); - - return 0; -} +/* + * ECMA Test 262 Runner for QuickJS + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "list.h" +#include "quickjs-libc.h" + +/* enable test262 thread support to test SharedArrayBuffer and Atomics */ +#define CONFIG_AGENT + +#define CMD_NAME "run-test262" + +typedef struct namelist_t { + char **array; + int count; + int size; + unsigned int sorted : 1; +} namelist_t; + +namelist_t test_list; +namelist_t exclude_list; +namelist_t exclude_dir_list; + +FILE *outfile; +enum test_mode_t { + TEST_DEFAULT_NOSTRICT, /* run tests as nostrict unless test is flagged as strictonly */ + TEST_DEFAULT_STRICT, /* run tests as strict unless test is flagged as nostrict */ + TEST_NOSTRICT, /* run tests as nostrict, skip strictonly tests */ + TEST_STRICT, /* run tests as strict, skip nostrict tests */ + TEST_ALL, /* run tests in both strict and nostrict, unless restricted by spec */ +} test_mode = TEST_DEFAULT_NOSTRICT; +int skip_async; +int skip_module; +int new_style; +int dump_memory; +int stats_count; +JSMemoryUsage stats_all, stats_avg, stats_min, stats_max; +char *stats_min_filename; +char *stats_max_filename; +int verbose; +char *harness_dir; +char *harness_exclude; +char *harness_features; +char *harness_skip_features; +char *error_filename; +char *error_file; +FILE *error_out; +char *report_filename; +int update_errors; +int test_count, test_failed, test_index, test_skipped, test_excluded; +int new_errors, changed_errors, fixed_errors; +int async_done; + +void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); +void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); + +void warning(const char *fmt, ...) +{ + va_list ap; + + fflush(stdout); + fprintf(stderr, "%s: ", CMD_NAME); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +void fatal(int errcode, const char *fmt, ...) +{ + va_list ap; + + fflush(stdout); + fprintf(stderr, "%s: ", CMD_NAME); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + + exit(errcode); +} + +void perror_exit(int errcode, const char *s) +{ + fflush(stdout); + fprintf(stderr, "%s: ", CMD_NAME); + perror(s); + exit(errcode); +} + +char *strdup_len(const char *str, int len) +{ + char *p = malloc(len + 1); + memcpy(p, str, len); + p[len] = '\0'; + return p; +} + +static inline int str_equal(const char *a, const char *b) { + return !strcmp(a, b); +} + +char *str_append(char **pp, const char *sep, const char *str) { + char *res, *p; + size_t len = 0; + p = *pp; + if (p) { + len = strlen(p) + strlen(sep); + } + res = malloc(len + strlen(str) + 1); + if (p) { + strcpy(res, p); + strcat(res, sep); + } + strcpy(res + len, str); + free(p); + return *pp = res; +} + +char *str_strip(char *p) +{ + size_t len = strlen(p); + while (len > 0 && isspace((unsigned char)p[len - 1])) + p[--len] = '\0'; + while (isspace((unsigned char)*p)) + p++; + return p; +} + +int has_prefix(const char *str, const char *prefix) +{ + return !strncmp(str, prefix, strlen(prefix)); +} + +char *skip_prefix(const char *str, const char *prefix) +{ + int i; + for (i = 0;; i++) { + if (prefix[i] == '\0') { /* skip the prefix */ + str += i; + break; + } + if (str[i] != prefix[i]) + break; + } + return (char *)str; +} + +char *get_basename(const char *filename) +{ + char *p; + + p = strrchr(filename, '/'); + if (!p) + return NULL; + return strdup_len(filename, p - filename); +} + +char *compose_path(const char *path, const char *name) +{ + int path_len, name_len; + char *d, *q; + + if (!path || path[0] == '\0' || *name == '/') { + d = strdup(name); + } else { + path_len = strlen(path); + name_len = strlen(name); + d = malloc(path_len + 1 + name_len + 1); + if (d) { + q = d; + memcpy(q, path, path_len); + q += path_len; + if (path[path_len - 1] != '/') + *q++ = '/'; + memcpy(q, name, name_len + 1); + } + } + return d; +} + +int namelist_cmp(const char *a, const char *b) +{ + /* compare strings in modified lexicographical order */ + for (;;) { + int ca = (unsigned char)*a++; + int cb = (unsigned char)*b++; + if (isdigit(ca) && isdigit(cb)) { + int na = ca - '0'; + int nb = cb - '0'; + while (isdigit(ca = (unsigned char)*a++)) + na = na * 10 + ca - '0'; + while (isdigit(cb = (unsigned char)*b++)) + nb = nb * 10 + cb - '0'; + if (na < nb) + return -1; + if (na > nb) + return +1; + } + if (ca < cb) + return -1; + if (ca > cb) + return +1; + if (ca == '\0') + return 0; + } +} + +int namelist_cmp_indirect(const void *a, const void *b) +{ + return namelist_cmp(*(const char **)a, *(const char **)b); +} + +void namelist_sort(namelist_t *lp) +{ + int i, count; + if (lp->count > 1) { + qsort(lp->array, lp->count, sizeof(*lp->array), namelist_cmp_indirect); + /* remove duplicates */ + for (count = i = 1; i < lp->count; i++) { + if (namelist_cmp(lp->array[count - 1], lp->array[i]) == 0) { + free(lp->array[i]); + } else { + lp->array[count++] = lp->array[i]; + } + } + lp->count = count; + } + lp->sorted = 1; +} + +int namelist_find(namelist_t *lp, const char *name) +{ + int a, b, m, cmp; + + if (!lp->sorted) { + namelist_sort(lp); + } + for (a = 0, b = lp->count; a < b;) { + m = a + (b - a) / 2; + cmp = namelist_cmp(lp->array[m], name); + if (cmp < 0) + a = m + 1; + else if (cmp > 0) + b = m; + else + return m; + } + return -1; +} + +void namelist_add(namelist_t *lp, const char *base, const char *name) +{ + char *s; + + s = compose_path(base, name); + if (!s) + goto fail; + if (lp->count == lp->size) { + size_t newsize = lp->size + (lp->size >> 1) + 4; + char **a = realloc(lp->array, sizeof(lp->array[0]) * newsize); + if (!a) + goto fail; + lp->array = a; + lp->size = newsize; + } + lp->array[lp->count] = s; + lp->count++; + return; +fail: + fatal(1, "allocation failure\n"); +} + +void namelist_load(namelist_t *lp, const char *filename) +{ + char buf[1024]; + char *base_name; + FILE *f; + + f = fopen(filename, "rb"); + if (!f) { + perror_exit(1, filename); + } + base_name = get_basename(filename); + + while (fgets(buf, sizeof(buf), f) != NULL) { + char *p = str_strip(buf); + if (*p == '#' || *p == ';' || *p == '\0') + continue; /* line comment */ + + namelist_add(lp, base_name, p); + } + free(base_name); + fclose(f); +} + +void namelist_add_from_error_file(namelist_t *lp, const char *file) +{ + const char *p, *p0; + char *pp; + + for (p = file; (p = strstr(p, ".js:")) != NULL; p++) { + for (p0 = p; p0 > file && p0[-1] != '\n'; p0--) + continue; + pp = strdup_len(p0, p + 3 - p0); + namelist_add(lp, NULL, pp); + free(pp); + } +} + +void namelist_free(namelist_t *lp) +{ + while (lp->count > 0) { + free(lp->array[--lp->count]); + } + free(lp->array); + lp->array = NULL; + lp->size = 0; +} + +static int add_test_file(const char *filename, const struct stat *ptr, int flag) +{ + namelist_t *lp = &test_list; + if (has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js")) + namelist_add(lp, NULL, filename); + return 0; +} + +/* find js files from the directory tree and sort the list */ +static void enumerate_tests(const char *path) +{ + namelist_t *lp = &test_list; + int start = lp->count; + ftw(path, add_test_file, 100); + qsort(lp->array + start, lp->count - start, sizeof(*lp->array), + namelist_cmp_indirect); +} + +static JSValue js_print(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int i; + const char *str; + + if (outfile) { + for (i = 0; i < argc; i++) { + if (i != 0) + fputc(' ', outfile); + str = JS_ToCString(ctx, argv[i]); + if (!str) + return JS_EXCEPTION; + if (!strcmp(str, "Test262:AsyncTestComplete")) { + async_done++; + } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) { + async_done = 2; /* force an error */ + } + fputs(str, outfile); + JS_FreeCString(ctx, str); + } + fputc('\n', outfile); + } + return JS_UNDEFINED; +} + +static JSValue js_detachArrayBuffer(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JS_DetachArrayBuffer(ctx, argv[0]); + return JS_UNDEFINED; +} + +static JSValue js_evalScript(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *str; + size_t len; + JSValue ret; + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + ret = JS_Eval(ctx, str, len, "", JS_EVAL_TYPE_GLOBAL); + JS_FreeCString(ctx, str); + return ret; +} + +#ifdef CONFIG_AGENT + +#include + +typedef struct { + struct list_head link; + pthread_t tid; + char *script; + JSValue broadcast_func; + BOOL broadcast_pending; + JSValue broadcast_sab; /* in the main context */ + uint8_t *broadcast_sab_buf; + size_t broadcast_sab_size; + int32_t broadcast_val; +} Test262Agent; + +typedef struct { + struct list_head link; + char *str; +} AgentReport; + +static JSValue add_helpers1(JSContext *ctx); +static void add_helpers(JSContext *ctx); + +static pthread_mutex_t agent_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t agent_cond = PTHREAD_COND_INITIALIZER; +/* list of Test262Agent.link */ +static struct list_head agent_list = LIST_HEAD_INIT(agent_list); + +static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER; +/* list of AgentReport.link */ +static struct list_head report_list = LIST_HEAD_INIT(report_list); + +static void *agent_start(void *arg) +{ + Test262Agent *agent = arg; + JSRuntime *rt; + JSContext *ctx; + JSValue ret_val; + int ret; + + rt = JS_NewRuntime(); + if (rt == NULL) { + fatal(1, "JS_NewRuntime failure"); + } + ctx = JS_NewContext(rt); + if (ctx == NULL) { + JS_FreeRuntime(rt); + fatal(1, "JS_NewContext failure"); + } + JS_SetContextOpaque(ctx, agent); + JS_SetRuntimeInfo(rt, "agent"); + JS_SetCanBlock(rt, TRUE); + + add_helpers(ctx); + ret_val = JS_Eval(ctx, agent->script, strlen(agent->script), + "", JS_EVAL_TYPE_GLOBAL); + free(agent->script); + agent->script = NULL; + if (JS_IsException(ret_val)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, ret_val); + + for(;;) { + JSContext *ctx1; + ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (ret < 0) { + js_std_dump_error(ctx); + break; + } else if (ret == 0) { + if (JS_IsUndefined(agent->broadcast_func)) { + break; + } else { + JSValue args[2]; + + pthread_mutex_lock(&agent_mutex); + while (!agent->broadcast_pending) { + pthread_cond_wait(&agent_cond, &agent_mutex); + } + + agent->broadcast_pending = FALSE; + pthread_cond_signal(&agent_cond); + + pthread_mutex_unlock(&agent_mutex); + + args[0] = JS_NewArrayBuffer(ctx, agent->broadcast_sab_buf, + agent->broadcast_sab_size, + NULL, NULL, TRUE); + args[1] = JS_NewInt32(ctx, agent->broadcast_val); + ret_val = JS_Call(ctx, agent->broadcast_func, JS_UNDEFINED, + 2, (JSValueConst *)args); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + if (JS_IsException(ret_val)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, ret_val); + JS_FreeValue(ctx, agent->broadcast_func); + agent->broadcast_func = JS_UNDEFINED; + } + } + } + JS_FreeValue(ctx, agent->broadcast_func); + + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return NULL; +} + +static JSValue js_agent_start(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *script; + Test262Agent *agent; + + if (JS_GetContextOpaque(ctx) != NULL) + return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); + + script = JS_ToCString(ctx, argv[0]); + if (!script) + return JS_EXCEPTION; + agent = malloc(sizeof(*agent)); + memset(agent, 0, sizeof(*agent)); + agent->broadcast_func = JS_UNDEFINED; + agent->broadcast_sab = JS_UNDEFINED; + agent->script = strdup(script); + JS_FreeCString(ctx, script); + list_add_tail(&agent->link, &agent_list); + pthread_create(&agent->tid, NULL, agent_start, agent); + return JS_UNDEFINED; +} + +static void js_agent_free(JSContext *ctx) +{ + struct list_head *el, *el1; + Test262Agent *agent; + + list_for_each_safe(el, el1, &agent_list) { + agent = list_entry(el, Test262Agent, link); + pthread_join(agent->tid, NULL); + JS_FreeValue(ctx, agent->broadcast_sab); + list_del(&agent->link); + free(agent); + } +} + +static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + Test262Agent *agent = JS_GetContextOpaque(ctx); + if (!agent) + return JS_ThrowTypeError(ctx, "must be called inside an agent"); + /* nothing to do */ + return JS_UNDEFINED; +} + +static BOOL is_broadcast_pending(void) +{ + struct list_head *el; + Test262Agent *agent; + list_for_each(el, &agent_list) { + agent = list_entry(el, Test262Agent, link); + if (agent->broadcast_pending) + return TRUE; + } + return FALSE; +} + +static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValueConst sab = argv[0]; + struct list_head *el; + Test262Agent *agent; + uint8_t *buf; + size_t buf_size; + int32_t val; + + if (JS_GetContextOpaque(ctx) != NULL) + return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); + + buf = JS_GetArrayBuffer(ctx, &buf_size, sab); + if (!buf) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &val, argv[1])) + return JS_EXCEPTION; + + /* broadcast the values and wait until all agents have started + calling their callbacks */ + pthread_mutex_lock(&agent_mutex); + list_for_each(el, &agent_list) { + agent = list_entry(el, Test262Agent, link); + agent->broadcast_pending = TRUE; + /* the shared array buffer is used by the thread, so increment + its refcount */ + agent->broadcast_sab = JS_DupValue(ctx, sab); + agent->broadcast_sab_buf = buf; + agent->broadcast_sab_size = buf_size; + agent->broadcast_val = val; + } + pthread_cond_broadcast(&agent_cond); + + while (is_broadcast_pending()) { + pthread_cond_wait(&agent_cond, &agent_mutex); + } + pthread_mutex_unlock(&agent_mutex); + return JS_UNDEFINED; +} + +static JSValue js_agent_receiveBroadcast(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + Test262Agent *agent = JS_GetContextOpaque(ctx); + if (!agent) + return JS_ThrowTypeError(ctx, "must be called inside an agent"); + if (!JS_IsFunction(ctx, argv[0])) + return JS_ThrowTypeError(ctx, "expecting function"); + JS_FreeValue(ctx, agent->broadcast_func); + agent->broadcast_func = JS_DupValue(ctx, argv[0]); + return JS_UNDEFINED; +} + +static JSValue js_agent_sleep(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint32_t duration; + if (JS_ToUint32(ctx, &duration, argv[0])) + return JS_EXCEPTION; + usleep(duration * 1000); + return JS_UNDEFINED; +} + +static int64_t get_clock_ms(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000); +} + +static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_NewInt64(ctx, get_clock_ms()); +} + +static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + AgentReport *rep; + JSValue ret; + + pthread_mutex_lock(&report_mutex); + if (list_empty(&report_list)) { + rep = NULL; + } else { + rep = list_entry(report_list.next, AgentReport, link); + list_del(&rep->link); + } + pthread_mutex_unlock(&report_mutex); + if (rep) { + ret = JS_NewString(ctx, rep->str); + free(rep->str); + free(rep); + } else { + ret = JS_NULL; + } + return ret; +} + +static JSValue js_agent_report(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *str; + AgentReport *rep; + + str = JS_ToCString(ctx, argv[0]); + if (!str) + return JS_EXCEPTION; + rep = malloc(sizeof(*rep)); + rep->str = strdup(str); + JS_FreeCString(ctx, str); + + pthread_mutex_lock(&report_mutex); + list_add_tail(&rep->link, &report_list); + pthread_mutex_unlock(&report_mutex); + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_agent_funcs[] = { + /* only in main */ + JS_CFUNC_DEF("start", 1, js_agent_start ), + JS_CFUNC_DEF("getReport", 0, js_agent_getReport ), + JS_CFUNC_DEF("broadcast", 2, js_agent_broadcast ), + /* only in agent */ + JS_CFUNC_DEF("report", 1, js_agent_report ), + JS_CFUNC_DEF("leaving", 0, js_agent_leaving ), + JS_CFUNC_DEF("receiveBroadcast", 1, js_agent_receiveBroadcast ), + /* in both */ + JS_CFUNC_DEF("sleep", 1, js_agent_sleep ), + JS_CFUNC_DEF("monotonicNow", 0, js_agent_monotonicNow ), +}; + +static JSValue js_new_agent(JSContext *ctx) +{ + JSValue agent; + agent = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, agent, js_agent_funcs, + countof(js_agent_funcs)); + return agent; +} +#endif + +static JSValue js_createRealm(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSContext *ctx1; + JSValue ret; + + ctx1 = JS_NewContext(JS_GetRuntime(ctx)); + if (!ctx1) + return JS_ThrowOutOfMemory(ctx); + 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; +} + +static JSValue add_helpers1(JSContext *ctx) +{ + JSValue global_obj; + JSValue obj262, obj; + + global_obj = JS_GetGlobalObject(ctx); + + JS_SetPropertyStr(ctx, global_obj, "print", + JS_NewCFunction(ctx, js_print, "print", 1)); + + /* $262 special object used by the tests */ + obj262 = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, obj262, "detachArrayBuffer", + JS_NewCFunction(ctx, js_detachArrayBuffer, + "detachArrayBuffer", 1)); + JS_SetPropertyStr(ctx, obj262, "evalScript", + JS_NewCFunction(ctx, js_evalScript, + "evalScript", 1)); + JS_SetPropertyStr(ctx, obj262, "codePointRange", + JS_NewCFunction(ctx, js_string_codePointRange, + "codePointRange", 2)); +#ifdef CONFIG_AGENT + JS_SetPropertyStr(ctx, obj262, "agent", js_new_agent(ctx)); +#endif + + JS_SetPropertyStr(ctx, obj262, "global", + JS_DupValue(ctx, global_obj)); + JS_SetPropertyStr(ctx, obj262, "createRealm", + JS_NewCFunction(ctx, js_createRealm, + "createRealm", 0)); + 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)); + + JS_FreeValue(ctx, global_obj); + return obj262; +} + +static void add_helpers(JSContext *ctx) +{ + JS_FreeValue(ctx, add_helpers1(ctx)); +} + +static char *load_file(const char *filename, size_t *lenp) +{ + char *buf; + size_t buf_len; + buf = (char *)js_load_file(NULL, &buf_len, filename); + if (!buf) + perror_exit(1, filename); + if (lenp) + *lenp = buf_len; + return buf; +} + +static JSModuleDef *js_module_loader_test(JSContext *ctx, + const char *module_name, void *opaque) +{ + size_t buf_len; + uint8_t *buf; + JSModuleDef *m; + JSValue func_val; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + return m; +} + +int is_line_sep(char c) +{ + return (c == '\0' || c == '\n' || c == '\r'); +} + +char *find_line(const char *str, const char *line) +{ + if (str) { + const char *p; + int len = strlen(line); + for (p = str; (p = strstr(p, line)) != NULL; p += len + 1) { + if ((p == str || is_line_sep(p[-1])) && is_line_sep(p[len])) + return (char *)p; + } + } + return NULL; +} + +int is_word_sep(char c) +{ + return (c == '\0' || isspace((unsigned char)c) || c == ','); +} + +char *find_word(const char *str, const char *word) +{ + const char *p; + int len = strlen(word); + if (str && len) { + for (p = str; (p = strstr(p, word)) != NULL; p += len) { + if ((p == str || is_word_sep(p[-1])) && is_word_sep(p[len])) + return (char *)p; + } + } + return NULL; +} + +/* handle exclude directories */ +void update_exclude_dirs(void) +{ + namelist_t *lp = &test_list; + namelist_t *ep = &exclude_list; + namelist_t *dp = &exclude_dir_list; + char *name; + int i, j, count; + + /* split directpries from exclude_list */ + for (count = i = 0; i < ep->count; i++) { + name = ep->array[i]; + if (has_suffix(name, "/")) { + namelist_add(dp, NULL, name); + free(name); + } else { + ep->array[count++] = name; + } + } + ep->count = count; + + namelist_sort(dp); + + /* filter out excluded directories */ + for (count = i = 0; i < lp->count; i++) { + name = lp->array[i]; + for (j = 0; j < dp->count; j++) { + if (has_prefix(name, dp->array[j])) { + test_excluded++; + free(name); + name = NULL; + break; + } + } + if (name) { + lp->array[count++] = name; + } + } + lp->count = count; +} + +void load_config(const char *filename) +{ + char buf[1024]; + FILE *f; + char *base_name; + enum { + SECTION_NONE = 0, + SECTION_CONFIG, + SECTION_EXCLUDE, + SECTION_FEATURES, + SECTION_TESTS, + } section = SECTION_NONE; + int lineno = 0; + + f = fopen(filename, "rb"); + if (!f) { + perror_exit(1, filename); + } + base_name = get_basename(filename); + + while (fgets(buf, sizeof(buf), f) != NULL) { + char *p, *q; + lineno++; + p = str_strip(buf); + if (*p == '#' || *p == ';' || *p == '\0') + continue; /* line comment */ + + if (*p == "[]"[0]) { + /* new section */ + p++; + p[strcspn(p, "]")] = '\0'; + if (str_equal(p, "config")) + section = SECTION_CONFIG; + else if (str_equal(p, "exclude")) + section = SECTION_EXCLUDE; + else if (str_equal(p, "features")) + section = SECTION_FEATURES; + else if (str_equal(p, "tests")) + section = SECTION_TESTS; + else + section = SECTION_NONE; + continue; + } + q = strchr(p, '='); + if (q) { + /* setting: name=value */ + *q++ = '\0'; + q = str_strip(q); + } + switch (section) { + case SECTION_CONFIG: + if (!q) { + printf("%s:%d: syntax error\n", filename, lineno); + continue; + } + if (str_equal(p, "style")) { + new_style = str_equal(q, "new"); + continue; + } + if (str_equal(p, "testdir")) { + char *testdir = compose_path(base_name, q); + enumerate_tests(testdir); + free(testdir); + continue; + } + if (str_equal(p, "harnessdir")) { + harness_dir = compose_path(base_name, q); + continue; + } + if (str_equal(p, "harnessexclude")) { + str_append(&harness_exclude, " ", q); + continue; + } + if (str_equal(p, "features")) { + str_append(&harness_features, " ", q); + continue; + } + if (str_equal(p, "skip-features")) { + str_append(&harness_skip_features, " ", q); + continue; + } + if (str_equal(p, "mode")) { + if (str_equal(q, "default") || str_equal(q, "default-nostrict")) + test_mode = TEST_DEFAULT_NOSTRICT; + else if (str_equal(q, "default-strict")) + test_mode = TEST_DEFAULT_STRICT; + else if (str_equal(q, "nostrict")) + test_mode = TEST_NOSTRICT; + else if (str_equal(q, "strict")) + test_mode = TEST_STRICT; + else if (str_equal(q, "all") || str_equal(q, "both")) + test_mode = TEST_ALL; + else + fatal(2, "unknown test mode: %s", q); + continue; + } + if (str_equal(p, "strict")) { + if (str_equal(q, "skip") || str_equal(q, "no")) + test_mode = TEST_NOSTRICT; + continue; + } + if (str_equal(p, "nostrict")) { + if (str_equal(q, "skip") || str_equal(q, "no")) + test_mode = TEST_STRICT; + continue; + } + if (str_equal(p, "async")) { + skip_async = !str_equal(q, "yes"); + continue; + } + if (str_equal(p, "module")) { + skip_module = !str_equal(q, "yes"); + continue; + } + if (str_equal(p, "verbose")) { + verbose = str_equal(q, "yes"); + continue; + } + if (str_equal(p, "errorfile")) { + error_filename = compose_path(base_name, q); + continue; + } + if (str_equal(p, "excludefile")) { + char *path = compose_path(base_name, q); + namelist_load(&exclude_list, path); + free(path); + continue; + } + if (str_equal(p, "reportfile")) { + report_filename = compose_path(base_name, q); + continue; + } + case SECTION_EXCLUDE: + namelist_add(&exclude_list, base_name, p); + break; + case SECTION_FEATURES: + if (!q || str_equal(q, "yes")) + str_append(&harness_features, " ", p); + else + str_append(&harness_skip_features, " ", p); + break; + case SECTION_TESTS: + namelist_add(&test_list, base_name, p); + break; + default: + /* ignore settings in other sections */ + break; + } + } + fclose(f); + free(base_name); +} + +char *find_error(const char *filename, int *pline, int is_strict) +{ + if (error_file) { + size_t len = strlen(filename); + const char *p, *q, *r; + int line; + + for (p = error_file; (p = strstr(p, filename)) != NULL; p += len) { + if ((p == error_file || p[-1] == '\n' || p[-1] == '(') && p[len] == ':') { + q = p + len; + line = 1; + if (*q == ':') { + line = strtol(q + 1, (char**)&q, 10); + if (*q == ':') + q++; + } + while (*q == ' ') { + q++; + } + /* check strict mode indicator */ + if (!strstart(q, "strict mode: ", &q) != !is_strict) + continue; + r = q = skip_prefix(q, "unexpected error: "); + r += strcspn(r, "\n"); + while (r[0] == '\n' && r[1] && strncmp(r + 1, filename, 8)) { + r++; + r += strcspn(r, "\n"); + } + if (pline) + *pline = line; + return strdup_len(q, r - q); + } + } + } + return NULL; +} + +int skip_comments(const char *str, int line, int *pline) +{ + const char *p; + int c; + + p = str; + while ((c = (unsigned char)*p++) != '\0') { + if (isspace(c)) { + if (c == '\n') + line++; + continue; + } + if (c == '/' && *p == '/') { + while (*++p && *p != '\n') + continue; + continue; + } + if (c == '/' && *p == '*') { + for (p += 1; *p; p++) { + if (*p == '\n') { + line++; + continue; + } + if (*p == '*' && p[1] == '/') { + p += 2; + break; + } + } + continue; + } + break; + } + if (pline) + *pline = line; + + return p - str; +} + +int longest_match(const char *str, const char *find, int pos, int *ppos, int line, int *pline) +{ + int len, maxlen; + + maxlen = 0; + + if (*find) { + const char *p; + for (p = str + pos; *p; p++) { + if (*p == *find) { + for (len = 1; p[len] && p[len] == find[len]; len++) + continue; + if (len > maxlen) { + maxlen = len; + if (ppos) + *ppos = p - str; + if (pline) + *pline = line; + if (!find[len]) + break; + } + } + if (*p == '\n') + line++; + } + } + return maxlen; +} + +static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename, int is_test, int is_negative, + const char *error_type, FILE *outfile, int eval_flags, + int is_async) +{ + JSValue res_val, exception_val; + int ret, error_line, pos, pos_line; + BOOL is_error, has_error_line; + const char *error_name; + + pos = skip_comments(buf, 1, &pos_line); + error_line = pos_line; + has_error_line = FALSE; + exception_val = JS_UNDEFINED; + error_name = NULL; + + async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */ + + res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + + if (is_async && !JS_IsException(res_val)) { + JS_FreeValue(ctx, res_val); + for(;;) { + JSContext *ctx1; + ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (ret < 0) { + res_val = JS_EXCEPTION; + break; + } else if (ret == 0) { + /* test if the test called $DONE() once */ + if (async_done != 1) { + res_val = JS_ThrowTypeError(ctx, "$DONE() not called"); + } else { + res_val = JS_UNDEFINED; + } + break; + } + } + } + + if (JS_IsException(res_val)) { + exception_val = JS_GetException(ctx); + is_error = JS_IsError(ctx, exception_val); + /* XXX: should get the filename and line number */ + if (outfile) { + if (!is_error) + fprintf(outfile, "%sThrow: ", (eval_flags & JS_EVAL_FLAG_STRICT) ? + "strict mode: " : ""); + js_print(ctx, JS_NULL, 1, &exception_val); + } + if (is_error) { + JSValue name, stack; + const char *stack_str; + + name = JS_GetPropertyStr(ctx, exception_val, "name"); + error_name = JS_ToCString(ctx, name); + stack = JS_GetPropertyStr(ctx, exception_val, "stack"); + if (!JS_IsUndefined(stack)) { + stack_str = JS_ToCString(ctx, stack); + if (stack_str) { + const char *p; + int len; + + if (outfile) + fprintf(outfile, "%s", stack_str); + + len = strlen(filename); + p = strstr(stack_str, filename); + if (p != NULL && p[len] == ':') { + error_line = atoi(p + len + 1); + has_error_line = TRUE; + } + JS_FreeCString(ctx, stack_str); + } + } + JS_FreeValue(ctx, stack); + JS_FreeValue(ctx, name); + } + if (is_negative) { + ret = 0; + if (error_type) { + char *error_class; + const char *msg; + + msg = JS_ToCString(ctx, exception_val); + error_class = strdup_len(msg, strcspn(msg, ":")); + if (!str_equal(error_class, error_type)) + ret = -1; + free(error_class); + JS_FreeCString(ctx, msg); + } + } else { + ret = -1; + } + } else { + if (is_negative) + ret = -1; + else + ret = 0; + } + + if (verbose && is_test) { + JSValue msg_val = JS_UNDEFINED; + const char *msg = NULL; + int s_line; + char *s = find_error(filename, &s_line, eval_flags & JS_EVAL_FLAG_STRICT); + const char *strict_mode = (eval_flags & JS_EVAL_FLAG_STRICT) ? "strict mode: " : ""; + + if (!JS_IsUndefined(exception_val)) { + msg_val = JS_ToString(ctx, exception_val); + msg = JS_ToCString(ctx, msg_val); + } + if (is_negative) { // expect error + if (ret == 0) { + if (msg && s && + (str_equal(s, "expected error") || + strstart(s, "unexpected error type:", NULL) || + str_equal(s, msg))) { // did not have error yet + if (!has_error_line) { + longest_match(buf, msg, pos, &pos, pos_line, &error_line); + } + printf("%s:%d: %sOK, now has error %s\n", + filename, error_line, strict_mode, msg); + fixed_errors++; + } + } else { + if (!s) { // not yet reported + if (msg) { + fprintf(error_out, "%s:%d: %sunexpected error type: %s\n", + filename, error_line, strict_mode, msg); + } else { + fprintf(error_out, "%s:%d: %sexpected error\n", + filename, error_line, strict_mode); + } + new_errors++; + } + } + } else { // should not have error + if (msg) { + if (!s || !str_equal(s, msg)) { + if (!has_error_line) { + char *p = skip_prefix(msg, "Test262 Error: "); + if (strstr(p, "Test case returned non-true value!")) { + longest_match(buf, "runTestCase", pos, &pos, pos_line, &error_line); + } else { + longest_match(buf, p, pos, &pos, pos_line, &error_line); + } + } + fprintf(error_out, "%s:%d: %s%s%s\n", filename, error_line, strict_mode, + error_file ? "unexpected error: " : "", msg); + + if (s && (!str_equal(s, msg) || error_line != s_line)) { + printf("%s:%d: %sprevious error: %s\n", filename, s_line, strict_mode, s); + changed_errors++; + } else { + new_errors++; + } + } + } else { + if (s) { + printf("%s:%d: %sOK, fixed error: %s\n", filename, s_line, strict_mode, s); + fixed_errors++; + } + } + } + JS_FreeValue(ctx, msg_val); + JS_FreeCString(ctx, msg); + free(s); + } + JS_FreeCString(ctx, error_name); + JS_FreeValue(ctx, exception_val); + JS_FreeValue(ctx, res_val); + return ret; +} + +static int eval_file(JSContext *ctx, const char *base, const char *p, + int eval_flags) +{ + char *buf; + size_t buf_len; + char *filename = compose_path(base, p); + + buf = load_file(filename, &buf_len); + if (!buf) { + warning("cannot load %s", filename); + goto fail; + } + if (eval_buf(ctx, buf, buf_len, filename, FALSE, FALSE, NULL, stderr, + eval_flags, FALSE)) { + warning("error evaluating %s", filename); + goto fail; + } + free(buf); + free(filename); + return 0; + +fail: + free(buf); + free(filename); + return 1; +} + +char *extract_desc(const char *buf, char style) +{ + const char *p, *desc_start; + char *desc; + int len; + + p = buf; + while (*p != '\0') { + if (p[0] == '/' && p[1] == '*' && p[2] == style && p[3] != '/') { + p += 3; + desc_start = p; + while (*p != '\0' && (p[0] != '*' || p[1] != '/')) + p++; + if (*p == '\0') { + warning("Expecting end of desc comment"); + return NULL; + } + len = p - desc_start; + desc = malloc(len + 1); + memcpy(desc, desc_start, len); + desc[len] = '\0'; + return desc; + } else { + p++; + } + } + return NULL; +} + +static char *find_tag(char *desc, const char *tag, int *state) +{ + char *p; + p = strstr(desc, tag); + if (p) { + p += strlen(tag); + *state = 0; + } + return p; +} + +static char *get_option(char **pp, int *state) +{ + char *p, *p0, *option = NULL; + if (*pp) { + for (p = *pp;; p++) { + switch (*p) { + case '[': + *state += 1; + continue; + case ']': + *state -= 1; + if (*state > 0) + continue; + p = NULL; + break; + case ' ': + case '\t': + case '\r': + case ',': + case '-': + continue; + case '\n': + if (*state > 0 || p[1] == ' ') + continue; + p = NULL; + break; + case '\0': + p = NULL; + break; + default: + p0 = p; + p += strcspn(p0, " \t\r\n,]"); + option = strdup_len(p0, p - p0); + break; + } + break; + } + *pp = p; + } + return option; +} + +void update_stats(JSRuntime *rt, const char *filename) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + if (stats_count++ == 0) { + stats_avg = stats_all = stats_min = stats_max = stats; + stats_min_filename = strdup(filename); + stats_max_filename = strdup(filename); + } else { + if (stats_max.malloc_size < stats.malloc_size) { + stats_max = stats; + free(stats_max_filename); + stats_max_filename = strdup(filename); + } + if (stats_min.malloc_size > stats.malloc_size) { + stats_min = stats; + free(stats_min_filename); + stats_min_filename = strdup(filename); + } + +#define update(f) stats_avg.f = (stats_all.f += stats.f) / stats_count + update(malloc_count); + update(malloc_size); + update(memory_used_count); + update(memory_used_size); + update(atom_count); + update(atom_size); + update(str_count); + update(str_size); + update(obj_count); + update(obj_size); + update(prop_count); + update(prop_size); + update(shape_count); + update(shape_size); + update(js_func_count); + update(js_func_size); + update(js_func_code_size); + update(js_func_pc2line_count); + update(js_func_pc2line_size); + update(c_func_count); + update(array_count); + update(fast_array_count); + update(fast_array_elements); + } +#undef update +} + +int run_test_buf(const char *filename, char *harness, namelist_t *ip, + char *buf, size_t buf_len, const char* error_type, + int eval_flags, BOOL is_negative, BOOL is_async, + BOOL can_block) +{ + JSRuntime *rt; + JSContext *ctx; + int i, ret; + + rt = JS_NewRuntime(); + if (rt == NULL) { + fatal(1, "JS_NewRuntime failure"); + } + ctx = JS_NewContext(rt); + if (ctx == NULL) { + JS_FreeRuntime(rt); + fatal(1, "JS_NewContext failure"); + } + JS_SetRuntimeInfo(rt, filename); + + JS_SetCanBlock(rt, can_block); + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL); + + add_helpers(ctx); + + for (i = 0; i < ip->count; i++) { + if (eval_file(ctx, harness, ip->array[i], + JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRIP)) { + fatal(1, "error including %s for %s", ip->array[i], filename); + } + } + + ret = eval_buf(ctx, buf, buf_len, filename, TRUE, is_negative, + error_type, outfile, eval_flags, is_async); + ret = (ret != 0); + + if (dump_memory) { + update_stats(rt, filename); + } +#ifdef CONFIG_AGENT + js_agent_free(ctx); +#endif + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + + test_count++; + if (ret) { + test_failed++; + if (outfile) { + /* do not output a failure number to minimize diff */ + fprintf(outfile, " FAILED\n"); + } + } + return ret; +} + +int run_test(const char *filename, int index) +{ + char harnessbuf[1024]; + char *harness; + char *buf; + size_t buf_len; + char *desc, *p; + char *error_type; + int ret, eval_flags, use_strict, use_nostrict; + BOOL is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip; + BOOL can_block; + namelist_t include_list = { 0 }, *ip = &include_list; + + is_nostrict = is_onlystrict = is_negative = is_async = is_module = skip = FALSE; + can_block = TRUE; + error_type = NULL; + buf = load_file(filename, &buf_len); + + harness = harness_dir; + + if (new_style) { + if (!harness) { + p = strstr(filename, "test/"); + if (p) { + snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s", + (int)(p - filename), filename, "harness"); + } + harness = harnessbuf; + } + namelist_add(ip, NULL, "sta.js"); + namelist_add(ip, NULL, "assert.js"); + /* extract the YAML frontmatter */ + desc = extract_desc(buf, '-'); + if (desc) { + char *ifile, *option; + int state; + p = find_tag(desc, "includes:", &state); + if (p) { + while ((ifile = get_option(&p, &state)) != NULL) { + // skip unsupported harness files + if (find_word(harness_exclude, ifile)) { + skip |= 1; + } else { + namelist_add(ip, NULL, ifile); + } + free(ifile); + } + } + p = find_tag(desc, "flags:", &state); + if (p) { + while ((option = get_option(&p, &state)) != NULL) { + if (str_equal(option, "noStrict") || + str_equal(option, "raw")) { + is_nostrict = TRUE; + skip |= (test_mode == TEST_STRICT); + } + else if (str_equal(option, "onlyStrict")) { + is_onlystrict = TRUE; + skip |= (test_mode == TEST_NOSTRICT); + } + else if (str_equal(option, "async")) { + is_async = TRUE; + skip |= skip_async; + } + else if (str_equal(option, "module")) { + is_module = TRUE; + skip |= skip_module; + } + else if (str_equal(option, "CanBlockIsFalse")) { + can_block = FALSE; + } + free(option); + } + } + p = find_tag(desc, "negative:", &state); + if (p) { + /* XXX: should extract the phase */ + char *q = find_tag(p, "type:", &state); + if (q) { + while (isspace(*q)) + q++; + error_type = strdup_len(q, strcspn(q, " \n")); + } + is_negative = TRUE; + } + p = find_tag(desc, "features:", &state); + if (p) { + while ((option = get_option(&p, &state)) != NULL) { + if (find_word(harness_features, option)) { + /* feature is enabled */ + } else if (find_word(harness_skip_features, option)) { + /* skip disabled feature */ + skip |= 1; + } else { + /* feature is not listed: skip and warn */ + printf("%s:%d: unknown feature: %s\n", filename, 1, option); + skip |= 1; + } + free(option); + } + } + free(desc); + } + if (is_async) + namelist_add(ip, NULL, "doneprintHandle.js"); + } else { + char *ifile; + + if (!harness) { + p = strstr(filename, "test/"); + if (p) { + snprintf(harnessbuf, sizeof(harnessbuf), "%.*s%s", + (int)(p - filename), filename, "test/harness"); + } + harness = harnessbuf; + } + + namelist_add(ip, NULL, "sta.js"); + + /* include extra harness files */ + for (p = buf; (p = strstr(p, "$INCLUDE(\"")) != NULL; p++) { + p += 10; + ifile = strdup_len(p, strcspn(p, "\"")); + // skip unsupported harness files + if (find_word(harness_exclude, ifile)) { + skip |= 1; + } else { + namelist_add(ip, NULL, ifile); + } + free(ifile); + } + + /* locate the old style configuration comment */ + desc = extract_desc(buf, '*'); + if (desc) { + if (strstr(desc, "@noStrict")) { + is_nostrict = TRUE; + skip |= (test_mode == TEST_STRICT); + } + if (strstr(desc, "@onlyStrict")) { + is_onlystrict = TRUE; + skip |= (test_mode == TEST_NOSTRICT); + } + if (strstr(desc, "@negative")) { + /* XXX: should extract the regex to check error type */ + is_negative = TRUE; + } + free(desc); + } + } + + if (outfile && index >= 0) { + fprintf(outfile, "%d: %s%s%s%s%s%s%s\n", index, filename, + is_nostrict ? " @noStrict" : "", + is_onlystrict ? " @onlyStrict" : "", + is_async ? " async" : "", + is_module ? " module" : "", + is_negative ? " @negative" : "", + skip ? " SKIPPED" : ""); + fflush(outfile); + } + + use_strict = use_nostrict = 0; + /* XXX: should remove 'test_mode' or simplify it just to force + strict or non strict mode for single file tests */ + switch (test_mode) { + case TEST_DEFAULT_NOSTRICT: + if (is_onlystrict) + use_strict = 1; + else + use_nostrict = 1; + break; + case TEST_DEFAULT_STRICT: + if (is_nostrict) + use_nostrict = 1; + else + use_strict = 1; + break; + case TEST_NOSTRICT: + if (!is_onlystrict) + use_nostrict = 1; + break; + case TEST_STRICT: + if (!is_nostrict) + use_strict = 1; + break; + case TEST_ALL: + if (is_module) { + use_nostrict = 1; + } else { + if (!is_nostrict) + use_strict = 1; + if (!is_onlystrict) + use_nostrict = 1; + } + break; + } + + if (skip || use_strict + use_nostrict == 0) { + test_skipped++; + ret = -2; + } else { + clock_t clocks; + + if (is_module) { + eval_flags = JS_EVAL_TYPE_MODULE; + } else { + eval_flags = JS_EVAL_TYPE_GLOBAL; + } + clocks = clock(); + ret = 0; + if (use_nostrict) { + ret = run_test_buf(filename, harness, ip, buf, buf_len, + error_type, eval_flags, is_negative, is_async, + can_block); + } + if (use_strict) { + ret |= run_test_buf(filename, harness, ip, buf, buf_len, + error_type, eval_flags | JS_EVAL_FLAG_STRICT, + is_negative, is_async, can_block); + } + clocks = clock() - clocks; + if (outfile && index >= 0 && clocks >= CLOCKS_PER_SEC / 10) { + /* output timings for tests that take more than 100 ms */ + fprintf(outfile, " time: %d ms\n", (int)(clocks * 1000LL / CLOCKS_PER_SEC)); + } + } + namelist_free(&include_list); + free(error_type); + free(buf); + + return ret; +} + +/* run a test when called by test262-harness+eshost */ +int run_test262_harness_test(const char *filename, BOOL is_module) +{ + JSRuntime *rt; + JSContext *ctx; + char *buf; + size_t buf_len; + int eval_flags, ret_code, ret; + JSValue res_val; + BOOL can_block; + + outfile = stdout; /* for js_print */ + + rt = JS_NewRuntime(); + if (rt == NULL) { + fatal(1, "JS_NewRuntime failure"); + } + ctx = JS_NewContext(rt); + if (ctx == NULL) { + JS_FreeRuntime(rt); + fatal(1, "JS_NewContext failure"); + } + JS_SetRuntimeInfo(rt, filename); + + can_block = TRUE; + JS_SetCanBlock(rt, can_block); + + /* loader for ES6 modules */ + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL); + + add_helpers(ctx); + + buf = load_file(filename, &buf_len); + + if (is_module) { + eval_flags = JS_EVAL_TYPE_MODULE; + } else { + eval_flags = JS_EVAL_TYPE_GLOBAL; + } + res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); + ret_code = 0; + if (JS_IsException(res_val)) { + js_std_dump_error(ctx); + ret_code = 1; + } else { + JS_FreeValue(ctx, res_val); + for(;;) { + JSContext *ctx1; + ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (ret < 0) { + js_std_dump_error(ctx1); + ret_code = 1; + } else if (ret == 0) { + break; + } + } + } + free(buf); +#ifdef CONFIG_AGENT + js_agent_free(ctx); +#endif + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return ret_code; +} + +clock_t last_clock; + +void show_progress(int force) { + clock_t t = clock(); + if (force || !last_clock || (t - last_clock) > CLOCKS_PER_SEC / 20) { + last_clock = t; + /* output progress indicator: erase end of line and return to col 0 */ + fprintf(stderr, "%d/%d/%d\033[K\r", + test_failed, test_count, test_skipped); + fflush(stderr); + } +} + +static int slow_test_threshold; + +void run_test_dir_list(namelist_t *lp, int start_index, int stop_index) +{ + int i; + + namelist_sort(lp); + for (i = 0; i < lp->count; i++) { + const char *p = lp->array[i]; + if (namelist_find(&exclude_list, p) >= 0) { + test_excluded++; + } else if (test_index < start_index) { + test_skipped++; + } else if (stop_index >= 0 && test_index > stop_index) { + test_skipped++; + } else { + int ti; + if (slow_test_threshold != 0) { + ti = get_clock_ms(); + } else { + ti = 0; + } + run_test(p, test_index); + if (slow_test_threshold != 0) { + ti = get_clock_ms() - ti; + if (ti >= slow_test_threshold) + fprintf(stderr, "\n%s (%d ms)\n", p, ti); + } + show_progress(FALSE); + } + test_index++; + } + show_progress(TRUE); +} + +void help(void) +{ + printf("run-test262 version " CONFIG_VERSION "\n" + "usage: run-test262 [options] {-f file ... | [dir_list] [index range]}\n" + "-h help\n" + "-a run tests in strict and nostrict modes\n" + "-m print memory usage summary\n" + "-n use new style harness\n" + "-N run test prepared by test262-harness+eshost\n" + "-s run tests in strict mode, skip @nostrict tests\n" + "-E only run tests from the error file\n" + "-u update error file\n" + "-v verbose: output error messages\n" + "-T duration display tests taking more than 'duration' ms\n" + "-c file read configuration from 'file'\n" + "-d dir run all test files in directory tree 'dir'\n" + "-e file load the known errors from 'file'\n" + "-f file execute single test from 'file'\n" + "-r file set the report file name (default=none)\n" + "-x file exclude tests listed in 'file'\n"); + exit(1); +} + +char *get_opt_arg(const char *option, char *arg) +{ + if (!arg) { + fatal(2, "missing argument for option %s", option); + } + return arg; +} + +int main(int argc, char **argv) +{ + int optind, start_index, stop_index; + BOOL is_dir_list; + BOOL only_check_errors = FALSE; + const char *filename; + BOOL is_test262_harness = FALSE; + BOOL is_module = FALSE; + +#if !defined(_WIN32) + /* Date tests assume California local time */ + setenv("TZ", "America/Los_Angeles", 1); +#endif + + /* cannot use getopt because we want to pass the command line to + the script */ + optind = 1; + is_dir_list = TRUE; + while (optind < argc) { + char *arg = argv[optind]; + if (*arg != '-') + break; + optind++; + if (str_equal(arg, "-h")) { + help(); + } else if (str_equal(arg, "-m")) { + dump_memory++; + } else if (str_equal(arg, "-n")) { + new_style++; + } else if (str_equal(arg, "-s")) { + test_mode = TEST_STRICT; + } else if (str_equal(arg, "-a")) { + test_mode = TEST_ALL; + } else if (str_equal(arg, "-u")) { + update_errors++; + } else if (str_equal(arg, "-v")) { + verbose++; + } else if (str_equal(arg, "-c")) { + load_config(get_opt_arg(arg, argv[optind++])); + } else if (str_equal(arg, "-d")) { + enumerate_tests(get_opt_arg(arg, argv[optind++])); + } else if (str_equal(arg, "-e")) { + error_filename = get_opt_arg(arg, argv[optind++]); + } else if (str_equal(arg, "-x")) { + namelist_load(&exclude_list, get_opt_arg(arg, argv[optind++])); + } else if (str_equal(arg, "-f")) { + is_dir_list = FALSE; + } else if (str_equal(arg, "-r")) { + report_filename = get_opt_arg(arg, argv[optind++]); + } else if (str_equal(arg, "-E")) { + only_check_errors = TRUE; + } else if (str_equal(arg, "-T")) { + slow_test_threshold = atoi(get_opt_arg(arg, argv[optind++])); + } else if (str_equal(arg, "-N")) { + is_test262_harness = TRUE; + } else if (str_equal(arg, "--module")) { + is_module = TRUE; + } else { + fatal(1, "unknown option: %s", arg); + break; + } + } + + if (optind >= argc && !test_list.count) + help(); + + if (is_test262_harness) { + return run_test262_harness_test(argv[optind], is_module); + } + + error_out = stdout; + if (error_filename) { + error_file = load_file(error_filename, NULL); + if (only_check_errors && error_file) { + namelist_free(&test_list); + namelist_add_from_error_file(&test_list, error_file); + } + if (update_errors) { + free(error_file); + error_file = NULL; + error_out = fopen(error_filename, "w"); + if (!error_out) { + perror_exit(1, error_filename); + } + } + } + + update_exclude_dirs(); + + if (is_dir_list) { + if (optind < argc && !isdigit(argv[optind][0])) { + filename = argv[optind++]; + namelist_load(&test_list, filename); + } + start_index = 0; + stop_index = -1; + if (optind < argc) { + start_index = atoi(argv[optind++]); + if (optind < argc) { + stop_index = atoi(argv[optind++]); + } + } + if (!report_filename || str_equal(report_filename, "none")) { + outfile = NULL; + } else if (str_equal(report_filename, "-")) { + outfile = stdout; + } else { + outfile = fopen(report_filename, "wb"); + if (!outfile) { + perror_exit(1, report_filename); + } + } + run_test_dir_list(&test_list, start_index, stop_index); + + if (outfile && outfile != stdout) { + fclose(outfile); + outfile = NULL; + } + } else { + outfile = stdout; + while (optind < argc) { + run_test(argv[optind++], -1); + } + } + + if (dump_memory) { + if (dump_memory > 1 && stats_count > 1) { + printf("\nMininum memory statistics for %s:\n\n", stats_min_filename); + JS_DumpMemoryUsage(stdout, &stats_min, NULL); + printf("\nMaximum memory statistics for %s:\n\n", stats_max_filename); + JS_DumpMemoryUsage(stdout, &stats_max, NULL); + } + printf("\nAverage memory statistics for %d tests:\n\n", stats_count); + JS_DumpMemoryUsage(stdout, &stats_avg, NULL); + printf("\n"); + } + + if (is_dir_list) { + fprintf(stderr, "Result: %d/%d error%s", + test_failed, test_count, test_count != 1 ? "s" : ""); + if (test_excluded) + fprintf(stderr, ", %d excluded", test_excluded); + if (test_skipped) + fprintf(stderr, ", %d skipped", test_skipped); + if (error_file) { + if (new_errors) + fprintf(stderr, ", %d new", new_errors); + if (changed_errors) + fprintf(stderr, ", %d changed", changed_errors); + if (fixed_errors) + fprintf(stderr, ", %d fixed", fixed_errors); + } + fprintf(stderr, "\n"); + } + + if (error_out && error_out != stdout) { + fclose(error_out); + error_out = NULL; + } + + namelist_free(&test_list); + namelist_free(&exclude_list); + namelist_free(&exclude_dir_list); + free(harness_dir); + free(harness_features); + free(harness_exclude); + free(error_file); + + return 0; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 000000000..49ae5306b --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,54 @@ +add_library(quickjs + libbf.c + cutils.c + libregexp.c + libunicode.c + core/string.c + core/function.c + core/memory.c + core/bytecode.c + core/object.c + core/exception.c + core/gc.c + core/malloc.c + core/shape.c + core/parser.c + core/convertion.c + core/runtime.c + core/module.c + core/builtins/js-array.c + core/builtins/js-async-function.c + core/builtins/js-async-generator.c + core/builtins/js-atomics.c + core/builtins/js-big-num.c + core/builtins/js-boolean.c + core/builtins/js-date.c + core/builtins/js-function.c + core/builtins/js-generator.c + core/builtins/js-json.c + core/builtins/js-map.c + core/builtins/js-math.c + core/builtins/js-number.c + core/builtins/js-object.c + core/builtins/js-closures.c + core/builtins/js-operator.c + core/builtins/js-promise.c + core/builtins/js-proxy.c + core/builtins/js-reflect.c + core/builtins/js-regexp.c + core/builtins/js-string.c + core/builtins/js-symbol.c + core/builtins/js-typed-array.c +) + +execute_process( + COMMAND cat ../VERSION + OUTPUT_VARIABLE QUICKJS_VERSION +) + +target_compile_options(quickjs PUBLIC + -g + -D_GNU_SOURCE + -DCONFIG_BIGNUM + -DCONFIG_VERSION=${\"QUICKJS_VERSION\"} +) \ No newline at end of file diff --git a/src/core/base.h b/src/core/base.h index f02747fc8..c8207d74c 100644 --- a/src/core/base.h +++ b/src/core/base.h @@ -1,119 +1,119 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -*/ - -#ifndef QUICKJS_BASE_H -#define QUICKJS_BASE_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -#include -#elif defined(__linux__) -#include -#elif defined(__FreeBSD__) -#include -#endif - -#ifdef CONFIG_BIGNUM -#include "quickjs/libbf.h" -#endif - -#define OPTIMIZE 1 -#define SHORT_OPCODES 1 -#if defined(EMSCRIPTEN) -#define DIRECT_DISPATCH 0 -#else -#define DIRECT_DISPATCH 1 -#endif - -#if defined(__APPLE__) -#define MALLOC_OVERHEAD 0 -#else -#define MALLOC_OVERHEAD 8 -#endif - -#if !defined(_WIN32) -/* define it if printf uses the RNDN rounding mode instead of RNDNA */ -#define CONFIG_PRINTF_RNDN -#endif - -/* define to include Atomics.* operations which depend on the OS - threads */ -#if !defined(EMSCRIPTEN) -#define CONFIG_ATOMICS -#endif - -#if !defined(EMSCRIPTEN) -/* enable stack limitation */ -#define CONFIG_STACK_CHECK -#endif - - -/* dump object free */ -//#define DUMP_FREE -//#define DUMP_CLOSURE -/* dump the bytecode of the compiled functions: combination of bits - 1: dump pass 3 final byte code - 2: dump pass 2 code - 4: dump pass 1 code - 8: dump stdlib functions - 16: dump bytecode in hex - 32: dump line number table - */ -//#define DUMP_BYTECODE (1) -/* dump the occurence of the automatic GC */ -//#define DUMP_GC -/* dump objects freed by the garbage collector */ -//#define DUMP_GC_FREE -/* dump objects leaking when freeing the runtime */ -//#define DUMP_LEAKS 1 -/* dump memory usage before running the garbage collector */ -//#define DUMP_MEM -//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */ -//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */ -//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */ -//#define DUMP_MODULE_RESOLVE -//#define DUMP_PROMISE -//#define DUMP_READ_OBJECT - -/* test the GC by forcing it before each object allocation */ -//#define FORCE_GC_AT_MALLOC - -#ifdef CONFIG_ATOMICS -#include -#include -#include -#endif - - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + +#ifndef QUICKJS_BASE_H +#define QUICKJS_BASE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#elif defined(__linux__) +#include +#elif defined(__FreeBSD__) +#include +#endif + +#ifdef CONFIG_BIGNUM +#include "quickjs/libbf.h" +#endif + +#define OPTIMIZE 1 +#define SHORT_OPCODES 1 +#if defined(EMSCRIPTEN) +#define DIRECT_DISPATCH 0 +#else +#define DIRECT_DISPATCH 1 +#endif + +#if defined(__APPLE__) +#define MALLOC_OVERHEAD 0 +#else +#define MALLOC_OVERHEAD 8 +#endif + +#if !defined(_WIN32) +/* define it if printf uses the RNDN rounding mode instead of RNDNA */ +#define CONFIG_PRINTF_RNDN +#endif + +/* define to include Atomics.* operations which depend on the OS + threads */ +#if !defined(EMSCRIPTEN) +#define CONFIG_ATOMICS +#endif + +#if !defined(EMSCRIPTEN) +/* enable stack limitation */ +#define CONFIG_STACK_CHECK +#endif + + +/* dump object free */ +//#define DUMP_FREE +//#define DUMP_CLOSURE +/* dump the bytecode of the compiled functions: combination of bits + 1: dump pass 3 final byte code + 2: dump pass 2 code + 4: dump pass 1 code + 8: dump stdlib functions + 16: dump bytecode in hex + 32: dump line number table + */ +//#define DUMP_BYTECODE (1) +/* dump the occurence of the automatic GC */ +//#define DUMP_GC +/* dump objects freed by the garbage collector */ +//#define DUMP_GC_FREE +/* dump objects leaking when freeing the runtime */ +//#define DUMP_LEAKS 1 +/* dump memory usage before running the garbage collector */ +//#define DUMP_MEM +//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */ +//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */ +//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */ +//#define DUMP_MODULE_RESOLVE +//#define DUMP_PROMISE +//#define DUMP_READ_OBJECT + +/* test the GC by forcing it before each object allocation */ +//#define FORCE_GC_AT_MALLOC + +#ifdef CONFIG_ATOMICS +#include +#include +#include +#endif + + #endif \ No newline at end of file diff --git a/src/core/builtins/js-array.c b/src/core/builtins/js-array.c index 87304d034..c79dde393 100644 --- a/src/core/builtins/js-array.c +++ b/src/core/builtins/js-array.c @@ -1,1753 +1,1753 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-array.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "js-function.h" -#include "js-object.h" -#include "js-operator.h" -#include "js-typed-array.h" - -void js_array_finalizer(JSRuntime* rt, JSValue val) { - JSObject* p = JS_VALUE_GET_OBJ(val); - int i; - - for (i = 0; i < p->u.array.count; i++) { - JS_FreeValueRT(rt, p->u.array.u.values[i]); - } - js_free_rt(rt, p->u.array.u.values); -} - -void js_array_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - JSObject* p = JS_VALUE_GET_OBJ(val); - int i; - - for (i = 0; i < p->u.array.count; i++) { - JS_MarkValue(rt, p->u.array.u.values[i], mark_func); - } -} - -JSValue js_create_iterator_result(JSContext* ctx, JSValue val, BOOL done) { - JSValue obj; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, val); - return obj; - } - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, val, JS_PROP_C_W_E) < 0) { - goto fail; - } - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) { - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - return obj; -} - -BOOL js_is_fast_array(JSContext* ctx, JSValueConst obj) { - /* Try and handle fast arrays explicitly */ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { - return TRUE; - } - } - return FALSE; -} - -/* Access an Array's internal JSValue array if available */ -BOOL js_get_fast_array(JSContext* ctx, JSValueConst obj, JSValue** arrpp, uint32_t* countp) { - /* Try and handle fast arrays explicitly */ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { - *countp = p->u.array.count; - *arrpp = p->u.array.u.values; - return TRUE; - } - } - return FALSE; -} - - -/* return -1 if exception */ -int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) -{ - uint32_t new_size; - size_t slack; - JSValue *new_array_prop; - /* XXX: potential arithmetic overflow */ - new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); - new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); - if (!new_array_prop) - return -1; - new_size += slack / sizeof(*new_array_prop); - p->u.array.u.values = new_array_prop; - p->u.array.u1.size = new_size; - return 0; -} - -__exception int js_append_enumerate(JSContext* ctx, JSValue* sp) { - JSValue iterator, enumobj, method, value; - int is_array_iterator; - JSValue* arrp; - uint32_t i, count32, pos; - - if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { - JS_ThrowInternalError(ctx, "invalid index for append"); - return -1; - } - - pos = JS_VALUE_GET_INT(sp[-2]); - - /* XXX: further optimisations: - - use ctx->array_proto_values? - - check if array_iterator_prototype next method is built-in and - avoid constructing actual iterator object? - - build this into js_for_of_start and use in all `for (x of o)` loops - */ - iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); - if (JS_IsException(iterator)) - return -1; - is_array_iterator = JS_IsCFunction(ctx, iterator, (JSCFunction*)js_create_array_iterator, JS_ITERATOR_KIND_VALUE); - JS_FreeValue(ctx, iterator); - - enumobj = JS_GetIterator(ctx, sp[-1], FALSE); - if (JS_IsException(enumobj)) - return -1; - method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); - if (JS_IsException(method)) { - JS_FreeValue(ctx, enumobj); - return -1; - } - if (is_array_iterator && JS_IsCFunction(ctx, method, (JSCFunction*)js_array_iterator_next, 0) && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { - uint32_t len; - if (js_get_length32(ctx, &len, sp[-1])) - goto exception; - /* if len > count32, the elements >= count32 might be read in - the prototypes and might have side effects */ - if (len != count32) - goto general_case; - /* Handle fast arrays explicitly */ - for (i = 0; i < count32; i++) { - if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) - goto exception; - } - } else { - general_case: - for (;;) { - BOOL done; - value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); - if (JS_IsException(value)) - goto exception; - if (done) { - /* value is JS_UNDEFINED */ - break; - } - if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) - goto exception; - } - } - /* Note: could raise an error if too many elements */ - sp[-2] = JS_NewInt32(ctx, pos); - JS_FreeValue(ctx, enumobj); - JS_FreeValue(ctx, method); - return 0; - -exception: - JS_IteratorClose(ctx, enumobj, TRUE); - JS_FreeValue(ctx, enumobj); - JS_FreeValue(ctx, method); - return -1; -} - -/* Array */ - -int JS_CopySubArray(JSContext* ctx, JSValueConst obj, int64_t to_pos, int64_t from_pos, int64_t count, int dir) { - int64_t i, from, to; - JSValue val; - int fromPresent; - - /* XXX: should special case fast arrays */ - for (i = 0; i < count; i++) { - if (dir < 0) { - from = from_pos + count - i - 1; - to = to_pos + count - i - 1; - } else { - from = from_pos + i; - to = to_pos + i; - } - fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); - if (fromPresent < 0) - goto exception; - - if (fromPresent) { - if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) - goto exception; - } else { - if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) - goto exception; - } - } - return 0; - -exception: - return -1; -} - -JSValue js_array_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { - JSValue obj; - int i; - - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY); - if (JS_IsException(obj)) - return obj; - if (argc == 1 && JS_IsNumber(argv[0])) { - uint32_t len; - if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) - goto fail; - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) - goto fail; - } else { - for (i = 0; i < argc; i++) { - if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) - goto fail; - } - } - return obj; -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_from(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // from(items, mapfn = void 0, this_arg = void 0) - JSValueConst items = argv[0], mapfn, this_arg; - JSValueConst args[2]; - JSValue stack[2]; - JSValue iter, r, v, v2, arrayLike; - int64_t k, len; - int done, mapping; - - mapping = FALSE; - mapfn = JS_UNDEFINED; - this_arg = JS_UNDEFINED; - r = JS_UNDEFINED; - arrayLike = JS_UNDEFINED; - stack[0] = JS_UNDEFINED; - stack[1] = JS_UNDEFINED; - - if (argc > 1) { - mapfn = argv[1]; - if (!JS_IsUndefined(mapfn)) { - if (check_function(ctx, mapfn)) - goto exception; - mapping = 1; - if (argc > 2) - this_arg = argv[2]; - } - } - iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); - if (JS_IsException(iter)) - goto exception; - if (!JS_IsUndefined(iter)) { - JS_FreeValue(ctx, iter); - if (JS_IsConstructor(ctx, this_val)) - r = JS_CallConstructor(ctx, this_val, 0, NULL); - else - r = JS_NewArray(ctx); - if (JS_IsException(r)) - goto exception; - stack[0] = JS_DupValue(ctx, items); - if (js_for_of_start(ctx, &stack[1], FALSE)) - goto exception; - for (k = 0;; k++) { - v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); - if (JS_IsException(v)) - goto exception_close; - if (done) - break; - if (mapping) { - args[0] = v; - args[1] = JS_NewInt32(ctx, k); - v2 = JS_Call(ctx, mapfn, this_arg, 2, args); - JS_FreeValue(ctx, v); - v = v2; - if (JS_IsException(v)) - goto exception_close; - } - if (JS_DefinePropertyValueInt64(ctx, r, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception_close; - } - } else { - arrayLike = JS_ToObject(ctx, items); - if (JS_IsException(arrayLike)) - goto exception; - if (js_get_length64(ctx, &len, arrayLike) < 0) - goto exception; - v = JS_NewInt64(ctx, len); - args[0] = v; - if (JS_IsConstructor(ctx, this_val)) { - r = JS_CallConstructor(ctx, this_val, 1, args); - } else { - r = js_array_constructor(ctx, JS_UNDEFINED, 1, args); - } - JS_FreeValue(ctx, v); - if (JS_IsException(r)) - goto exception; - for (k = 0; k < len; k++) { - v = JS_GetPropertyInt64(ctx, arrayLike, k); - if (JS_IsException(v)) - goto exception; - if (mapping) { - args[0] = v; - args[1] = JS_NewInt32(ctx, k); - v2 = JS_Call(ctx, mapfn, this_arg, 2, args); - JS_FreeValue(ctx, v); - v = v2; - if (JS_IsException(v)) - goto exception; - } - if (JS_DefinePropertyValueInt64(ctx, r, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - } - if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0) - goto exception; - goto done; - -exception_close: - if (!JS_IsUndefined(stack[0])) - JS_IteratorClose(ctx, stack[0], TRUE); -exception: - JS_FreeValue(ctx, r); - r = JS_EXCEPTION; -done: - JS_FreeValue(ctx, arrayLike); - JS_FreeValue(ctx, stack[0]); - JS_FreeValue(ctx, stack[1]); - return r; -} - -JSValue js_array_of(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, args[1]; - int i; - - if (JS_IsConstructor(ctx, this_val)) { - args[0] = JS_NewInt32(ctx, argc); - obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst*)args); - } else { - obj = JS_NewArray(ctx); - } - if (JS_IsException(obj)) - return JS_EXCEPTION; - for (i = 0; i < argc; i++) { - if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]), JS_PROP_THROW) < 0) { - goto fail; - } - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) { - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - return obj; -} - -JSValue js_array_isArray(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - int ret; - ret = JS_IsArray(ctx, argv[0]); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_get_this(JSContext* ctx, JSValueConst this_val) { - return JS_DupValue(ctx, this_val); -} - -JSValue JS_ArraySpeciesCreate(JSContext* ctx, JSValueConst obj, JSValueConst len_val) { - JSValue ctor, ret, species; - int res; - JSContext* realm; - - res = JS_IsArray(ctx, obj); - if (res < 0) - return JS_EXCEPTION; - if (!res) - return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); - ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); - if (JS_IsException(ctor)) - return ctor; - if (JS_IsConstructor(ctx, ctor)) { - /* legacy web compatibility */ - realm = JS_GetFunctionRealm(ctx, ctor); - if (!realm) { - JS_FreeValue(ctx, ctor); - return JS_EXCEPTION; - } - if (realm != ctx && js_same_value(ctx, ctor, realm->array_ctor)) { - JS_FreeValue(ctx, ctor); - ctor = JS_UNDEFINED; - } - } - if (JS_IsObject(ctor)) { - species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); - JS_FreeValue(ctx, ctor); - if (JS_IsException(species)) - return species; - ctor = species; - if (JS_IsNull(ctor)) - ctor = JS_UNDEFINED; - } - if (JS_IsUndefined(ctor)) { - return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); - } else { - ret = JS_CallConstructor(ctx, ctor, 1, &len_val); - JS_FreeValue(ctx, ctor); - return ret; - } -} - -int JS_isConcatSpreadable(JSContext* ctx, JSValueConst obj) { - JSValue val; - - if (!JS_IsObject(obj)) - return FALSE; - val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable); - if (JS_IsException(val)) - return -1; - if (!JS_IsUndefined(val)) - return JS_ToBoolFree(ctx, val); - return JS_IsArray(ctx, obj); -} - -JSValue js_array_concat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, arr, val; - JSValueConst e; - int64_t len, k, n; - int i, res; - - arr = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - goto exception; - - arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); - if (JS_IsException(arr)) - goto exception; - n = 0; - for (i = -1; i < argc; i++) { - if (i < 0) - e = obj; - else - e = argv[i]; - - res = JS_isConcatSpreadable(ctx, e); - if (res < 0) - goto exception; - if (res) { - if (js_get_length64(ctx, &len, e)) - goto exception; - if (n + len > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - for (k = 0; k < len; k++, n++) { - res = JS_TryGetPropertyInt64(ctx, e, k, &val); - if (res < 0) - goto exception; - if (res) { - if (JS_DefinePropertyValueInt64(ctx, arr, n, val, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - } - } else { - if (n >= MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e), JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - n++; - } - } - if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) - goto exception; - - JS_FreeValue(ctx, obj); - return arr; - -exception: - JS_FreeValue(ctx, arr); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -int js_typed_array_get_length_internal(JSContext* ctx, JSValueConst obj); - -JSValue js_typed_array___speciesCreate(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - -JSValue js_array_every(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special) { - JSValue obj, val, index_val, res, ret; - JSValueConst args[3]; - JSValueConst func, this_arg; - int64_t len, k, n; - int present; - - ret = JS_UNDEFINED; - val = JS_UNDEFINED; - if (special & special_TA) { - obj = JS_DupValue(ctx, this_val); - len = js_typed_array_get_length_internal(ctx, obj); - if (len < 0) - goto exception; - } else { - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - } - func = argv[0]; - this_arg = JS_UNDEFINED; - if (argc > 1) - this_arg = argv[1]; - - if (check_function(ctx, func)) - goto exception; - - switch (special) { - case special_every: - case special_every | special_TA: - ret = JS_TRUE; - break; - case special_some: - case special_some | special_TA: - ret = JS_FALSE; - break; - case special_map: - /* XXX: JS_ArraySpeciesCreate should take int64_t */ - ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len)); - if (JS_IsException(ret)) - goto exception; - break; - case special_filter: - ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); - if (JS_IsException(ret)) - goto exception; - break; - case special_map | special_TA: - args[0] = obj; - args[1] = JS_NewInt32(ctx, len); - ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); - if (JS_IsException(ret)) - goto exception; - break; - case special_filter | special_TA: - ret = JS_NewArray(ctx); - if (JS_IsException(ret)) - goto exception; - break; - } - n = 0; - - for (k = 0; k < len; k++) { - if (special & special_TA) { - val = JS_GetPropertyInt64(ctx, obj, k); - if (JS_IsException(val)) - goto exception; - present = TRUE; - } else { - present = JS_TryGetPropertyInt64(ctx, obj, k, &val); - if (present < 0) - goto exception; - } - if (present) { - index_val = JS_NewInt64(ctx, k); - if (JS_IsException(index_val)) - goto exception; - args[0] = val; - args[1] = index_val; - args[2] = obj; - res = JS_Call(ctx, func, this_arg, 3, args); - JS_FreeValue(ctx, index_val); - if (JS_IsException(res)) - goto exception; - switch (special) { - case special_every: - case special_every | special_TA: - if (!JS_ToBoolFree(ctx, res)) { - ret = JS_FALSE; - goto done; - } - break; - case special_some: - case special_some | special_TA: - if (JS_ToBoolFree(ctx, res)) { - ret = JS_TRUE; - goto done; - } - break; - case special_map: - if (JS_DefinePropertyValueInt64(ctx, ret, k, res, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - break; - case special_map | special_TA: - if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0) - goto exception; - break; - case special_filter: - case special_filter | special_TA: - if (JS_ToBoolFree(ctx, res)) { - if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val), JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - break; - default: - JS_FreeValue(ctx, res); - break; - } - JS_FreeValue(ctx, val); - val = JS_UNDEFINED; - } - } -done: - if (special == (special_filter | special_TA)) { - JSValue arr; - args[0] = obj; - args[1] = JS_NewInt32(ctx, n); - arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); - if (JS_IsException(arr)) - goto exception; - args[0] = ret; - res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); - if (check_exception_free(ctx, res)) - goto exception; - JS_FreeValue(ctx, ret); - ret = arr; - } - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return ret; - -exception: - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_reduce(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special) { - JSValue obj, val, index_val, acc, acc1; - JSValueConst args[4]; - JSValueConst func; - int64_t len, k, k1; - int present; - - acc = JS_UNDEFINED; - val = JS_UNDEFINED; - if (special & special_TA) { - obj = JS_DupValue(ctx, this_val); - len = js_typed_array_get_length_internal(ctx, obj); - if (len < 0) - goto exception; - } else { - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - } - func = argv[0]; - - if (check_function(ctx, func)) - goto exception; - - k = 0; - if (argc > 1) { - acc = JS_DupValue(ctx, argv[1]); - } else { - for (;;) { - if (k >= len) { - JS_ThrowTypeError(ctx, "empty array"); - goto exception; - } - k1 = (special & special_reduceRight) ? len - k - 1 : k; - k++; - if (special & special_TA) { - acc = JS_GetPropertyInt64(ctx, obj, k1); - if (JS_IsException(acc)) - goto exception; - break; - } else { - present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); - if (present < 0) - goto exception; - if (present) - break; - } - } - } - for (; k < len; k++) { - k1 = (special & special_reduceRight) ? len - k - 1 : k; - if (special & special_TA) { - val = JS_GetPropertyInt64(ctx, obj, k1); - if (JS_IsException(val)) - goto exception; - present = TRUE; - } else { - present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); - if (present < 0) - goto exception; - } - if (present) { - index_val = JS_NewInt64(ctx, k1); - if (JS_IsException(index_val)) - goto exception; - args[0] = acc; - args[1] = val; - args[2] = index_val; - args[3] = obj; - acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args); - JS_FreeValue(ctx, index_val); - JS_FreeValue(ctx, val); - val = JS_UNDEFINED; - if (JS_IsException(acc1)) - goto exception; - JS_FreeValue(ctx, acc); - acc = acc1; - } - } - JS_FreeValue(ctx, obj); - return acc; - -exception: - JS_FreeValue(ctx, acc); - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_fill(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj; - int64_t len, start, end; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - start = 0; - if (argc > 1 && !JS_IsUndefined(argv[1])) { - if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len)) - goto exception; - } - - end = len; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len)) - goto exception; - } - - /* XXX: should special case fast arrays */ - while (start < end) { - if (JS_SetPropertyInt64(ctx, obj, start, JS_DupValue(ctx, argv[0])) < 0) - goto exception; - start++; - } - return obj; - -exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_includes(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, val; - int64_t len, n, res; - JSValue* arrp; - uint32_t count; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - res = FALSE; - if (len > 0) { - n = 0; - if (argc > 1) { - if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) - goto exception; - } - if (js_get_fast_array(ctx, obj, &arrp, &count)) { - for (; n < count; n++) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), JS_DupValue(ctx, arrp[n]), JS_EQ_SAME_VALUE_ZERO)) { - res = TRUE; - goto done; - } - } - } - for (; n < len; n++) { - val = JS_GetPropertyInt64(ctx, obj, n); - if (JS_IsException(val)) - goto exception; - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_SAME_VALUE_ZERO)) { - res = TRUE; - break; - } - } - } -done: - JS_FreeValue(ctx, obj); - return JS_NewBool(ctx, res); - -exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, val; - int64_t len, n, res; - JSValue* arrp; - uint32_t count; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - res = -1; - if (len > 0) { - n = 0; - if (argc > 1) { - if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) - goto exception; - } - if (js_get_fast_array(ctx, obj, &arrp, &count)) { - for (; n < count; n++) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) { - res = n; - goto done; - } - } - } - for (; n < len; n++) { - int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); - if (present < 0) - goto exception; - if (present) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { - res = n; - break; - } - } - } - } -done: - JS_FreeValue(ctx, obj); - return JS_NewInt64(ctx, res); - -exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_lastIndexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, val; - int64_t len, n, res; - int present; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - res = -1; - if (len > 0) { - n = len - 1; - if (argc > 1) { - if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len)) - goto exception; - } - /* XXX: should special case fast arrays */ - for (; n >= 0; n--) { - present = JS_TryGetPropertyInt64(ctx, obj, n, &val); - if (present < 0) - goto exception; - if (present) { - if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { - res = n; - break; - } - } - } - } - JS_FreeValue(ctx, obj); - return JS_NewInt64(ctx, res); - -exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_find(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int findIndex) { - JSValueConst func, this_arg; - JSValueConst args[3]; - JSValue obj, val, index_val, res; - int64_t len, k; - - index_val = JS_UNDEFINED; - val = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - func = argv[0]; - if (check_function(ctx, func)) - goto exception; - - this_arg = JS_UNDEFINED; - if (argc > 1) - this_arg = argv[1]; - - for (k = 0; k < len; k++) { - index_val = JS_NewInt64(ctx, k); - if (JS_IsException(index_val)) - goto exception; - val = JS_GetPropertyValue(ctx, obj, index_val); - if (JS_IsException(val)) - goto exception; - args[0] = val; - args[1] = index_val; - args[2] = this_val; - res = JS_Call(ctx, func, this_arg, 3, args); - if (JS_IsException(res)) - goto exception; - if (JS_ToBoolFree(ctx, res)) { - if (findIndex) { - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return index_val; - } else { - JS_FreeValue(ctx, index_val); - JS_FreeValue(ctx, obj); - return val; - } - } - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, index_val); - } - JS_FreeValue(ctx, obj); - if (findIndex) - return JS_NewInt32(ctx, -1); - else - return JS_UNDEFINED; - -exception: - JS_FreeValue(ctx, index_val); - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, method, ret; - - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - return JS_EXCEPTION; - method = JS_GetProperty(ctx, obj, JS_ATOM_join); - if (JS_IsException(method)) { - ret = JS_EXCEPTION; - } else if (!JS_IsFunction(ctx, method)) { - /* Use intrinsic Object.prototype.toString */ - JS_FreeValue(ctx, method); - ret = js_object_toString(ctx, obj, 0, NULL); - } else { - ret = JS_CallFree(ctx, method, obj, 0, NULL); - } - JS_FreeValue(ctx, obj); - return ret; -} - -JSValue js_array_join(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int toLocaleString) { - JSValue obj, sep = JS_UNDEFINED, el; - StringBuffer b_s, *b = &b_s; - JSString* p = NULL; - int64_t i, n; - int c; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &n, obj)) - goto exception; - - c = ','; /* default separator */ - if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { - sep = JS_ToString(ctx, argv[0]); - if (JS_IsException(sep)) - goto exception; - p = JS_VALUE_GET_STRING(sep); - if (p->len == 1 && !p->is_wide_char) - c = p->u.str8[0]; - else - c = -1; - } - string_buffer_init(ctx, b, 0); - - for (i = 0; i < n; i++) { - if (i > 0) { - if (c >= 0) { - string_buffer_putc8(b, c); - } else { - string_buffer_concat(b, p, 0, p->len); - } - } - el = JS_GetPropertyUint32(ctx, obj, i); - if (JS_IsException(el)) - goto fail; - if (!JS_IsNull(el) && !JS_IsUndefined(el)) { - if (toLocaleString) { - el = JS_ToLocaleStringFree(ctx, el); - } - if (string_buffer_concat_value_free(b, el)) - goto fail; - } - } - JS_FreeValue(ctx, sep); - JS_FreeValue(ctx, obj); - return string_buffer_end(b); - -fail: - string_buffer_free(b); - JS_FreeValue(ctx, sep); -exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_pop(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int shift) { - JSValue obj, res = JS_UNDEFINED; - int64_t len, newLen; - JSValue* arrp; - uint32_t count32; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - newLen = 0; - if (len > 0) { - newLen = len - 1; - /* Special case fast arrays */ - if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - if (shift) { - res = arrp[0]; - memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); - p->u.array.count--; - } else { - res = arrp[count32 - 1]; - p->u.array.count--; - } - } else { - if (shift) { - res = JS_GetPropertyInt64(ctx, obj, 0); - if (JS_IsException(res)) - goto exception; - if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) - goto exception; - } else { - res = JS_GetPropertyInt64(ctx, obj, newLen); - if (JS_IsException(res)) - goto exception; - } - if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) - goto exception; - } - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) - goto exception; - - JS_FreeValue(ctx, obj); - return res; - -exception: - JS_FreeValue(ctx, res); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_push(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int unshift) { - JSValue obj; - int i; - int64_t len, from, newLen; - - obj = JS_ToObject(ctx, this_val); - - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ARRAY || !p->fast_array || !p->extensible) - goto generic_case; - /* length must be writable */ - if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) - goto generic_case; - /* check the length */ - if (unlikely(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT)) - goto generic_case; - len = JS_VALUE_GET_INT(p->prop[0].u.value); - /* we don't support holes */ - if (unlikely(len != p->u.array.count)) - goto generic_case; - newLen = len + argc; - if (unlikely(newLen > INT32_MAX)) - goto generic_case; - if (newLen > p->u.array.u1.size) { - if (expand_fast_array(ctx, p, newLen)) - goto exception; - } - if (unshift && argc > 0) { - memmove(p->u.array.u.values + argc, p->u.array.u.values, len * sizeof(p->u.array.u.values[0])); - from = 0; - } else { - from = len; - } - for (i = 0; i < argc; i++) { - p->u.array.u.values[from + i] = JS_DupValue(ctx, argv[i]); - } - p->u.array.count = newLen; - p->prop[0].u.value = JS_NewInt32(ctx, newLen); - } else { - generic_case: - if (js_get_length64(ctx, &len, obj)) - goto exception; - newLen = len + argc; - if (newLen > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - from = len; - if (unshift && argc > 0) { - if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) - goto exception; - from = 0; - } - for (i = 0; i < argc; i++) { - if (JS_SetPropertyInt64(ctx, obj, from + i, JS_DupValue(ctx, argv[i])) < 0) - goto exception; - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) - goto exception; - } - JS_FreeValue(ctx, obj); - return JS_NewInt64(ctx, newLen); - -exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_reverse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, lval, hval; - JSValue* arrp; - int64_t len, l, h; - int l_present, h_present; - uint32_t count32; - - lval = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - /* Special case fast arrays */ - if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { - uint32_t ll, hh; - - if (count32 > 1) { - for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) { - lval = arrp[ll]; - arrp[ll] = arrp[hh]; - arrp[hh] = lval; - } - } - return obj; - } - - for (l = 0, h = len - 1; l < h; l++, h--) { - l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval); - if (l_present < 0) - goto exception; - h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval); - if (h_present < 0) - goto exception; - if (h_present) { - if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0) - goto exception; - - if (l_present) { - if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { - lval = JS_UNDEFINED; - goto exception; - } - lval = JS_UNDEFINED; - } else { - if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0) - goto exception; - } - } else { - if (l_present) { - if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0) - goto exception; - if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { - lval = JS_UNDEFINED; - goto exception; - } - lval = JS_UNDEFINED; - } - } - } - return obj; - -exception: - JS_FreeValue(ctx, lval); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_array_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int splice) { - JSValue obj, arr, val, len_val; - int64_t len, start, k, final, n, count, del_count, new_len; - int kPresent; - JSValue* arrp; - uint32_t count32, i, item_count; - - arr = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) - goto exception; - - if (splice) { - if (argc == 0) { - item_count = 0; - del_count = 0; - } else if (argc == 1) { - item_count = 0; - del_count = len - start; - } else { - item_count = argc - 2; - if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) - goto exception; - } - if (len + item_count - del_count > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - count = del_count; - } else { - item_count = 0; /* avoid warning */ - final = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) - goto exception; - } - count = max_int64(final - start, 0); - } - len_val = JS_NewInt64(ctx, count); - arr = JS_ArraySpeciesCreate(ctx, obj, len_val); - JS_FreeValue(ctx, len_val); - if (JS_IsException(arr)) - goto exception; - - k = start; - final = start + count; - n = 0; - /* The fast array test on arr ensures that - JS_CreateDataPropertyUint32() won't modify obj in case arr is - an exotic object */ - /* Special case fast arrays */ - if (js_get_fast_array(ctx, obj, &arrp, &count32) && js_is_fast_array(ctx, arr)) { - /* XXX: should share code with fast array constructor */ - for (; k < final && k < count32; k++, n++) { - if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0) - goto exception; - } - } - /* Copy the remaining elements if any (handle case of inherited properties) */ - for (; k < final; k++, n++) { - kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); - if (kPresent < 0) - goto exception; - if (kPresent) { - if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) - goto exception; - } - } - if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) - goto exception; - - if (splice) { - new_len = len + item_count - del_count; - if (item_count != del_count) { - if (JS_CopySubArray(ctx, obj, start + item_count, start + del_count, len - (start + del_count), item_count <= del_count ? +1 : -1) < 0) - goto exception; - - for (k = len; k-- > new_len;) { - if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0) - goto exception; - } - } - for (i = 0; i < item_count; i++) { - if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0) - goto exception; - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) - goto exception; - } - JS_FreeValue(ctx, obj); - return arr; - -exception: - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -JSValue js_array_copyWithin(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj; - int64_t len, from, to, final, count; - - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len)) - goto exception; - - if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len)) - goto exception; - - final = len; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len)) - goto exception; - } - - count = min_int64(final - from, len - to); - - if (JS_CopySubArray(ctx, obj, to, from, count, (from < to && to < from + count) ? -1 : +1)) - goto exception; - - return obj; - -exception: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -int64_t JS_FlattenIntoArray(JSContext* ctx, JSValueConst target, JSValueConst source, int64_t sourceLen, int64_t targetIndex, int depth, JSValueConst mapperFunction, JSValueConst thisArg) { - JSValue element; - int64_t sourceIndex, elementLen; - int present, is_array; - - if (js_check_stack_overflow(ctx->rt, 0)) { - JS_ThrowStackOverflow(ctx); - return -1; - } - - for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { - present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); - if (present < 0) - return -1; - if (!present) - continue; - if (!JS_IsUndefined(mapperFunction)) { - JSValueConst args[3] = {element, JS_NewInt64(ctx, sourceIndex), source}; - element = JS_Call(ctx, mapperFunction, thisArg, 3, args); - JS_FreeValue(ctx, (JSValue)args[0]); - JS_FreeValue(ctx, (JSValue)args[1]); - if (JS_IsException(element)) - return -1; - } - if (depth > 0) { - is_array = JS_IsArray(ctx, element); - if (is_array < 0) - goto fail; - if (is_array) { - if (js_get_length64(ctx, &elementLen, element) < 0) - goto fail; - targetIndex = JS_FlattenIntoArray(ctx, target, element, elementLen, targetIndex, depth - 1, JS_UNDEFINED, JS_UNDEFINED); - if (targetIndex < 0) - goto fail; - JS_FreeValue(ctx, element); - continue; - } - } - if (targetIndex >= MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array too long"); - goto fail; - } - if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - return -1; - targetIndex++; - } - return targetIndex; - -fail: - JS_FreeValue(ctx, element); - return -1; -} - -JSValue js_array_flatten(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int map) { - JSValue obj, arr; - JSValueConst mapperFunction, thisArg; - int64_t sourceLen; - int depthNum; - - arr = JS_UNDEFINED; - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &sourceLen, obj)) - goto exception; - - depthNum = 1; - mapperFunction = JS_UNDEFINED; - thisArg = JS_UNDEFINED; - if (map) { - mapperFunction = argv[0]; - if (argc > 1) { - thisArg = argv[1]; - } - if (check_function(ctx, mapperFunction)) - goto exception; - } else { - if (argc > 0 && !JS_IsUndefined(argv[0])) { - if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0) - goto exception; - } - } - arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); - if (JS_IsException(arr)) - goto exception; - if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, mapperFunction, thisArg) < 0) - goto exception; - JS_FreeValue(ctx, obj); - return arr; - -exception: - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -/* Array sort */ -typedef struct ValueSlot { - JSValue val; - JSString* str; - int64_t pos; -} ValueSlot; - -struct array_sort_context { - JSContext* ctx; - int exception; - int has_method; - JSValueConst method; -}; - -int js_array_cmp_generic(const void* a, const void* b, void* opaque) { - struct array_sort_context* psc = opaque; - JSContext* ctx = psc->ctx; - JSValueConst argv[2]; - JSValue res; - ValueSlot* ap = (ValueSlot*)(void*)a; - ValueSlot* bp = (ValueSlot*)(void*)b; - int cmp; - - if (psc->exception) - return 0; - - if (psc->has_method) { - /* custom sort function is specified as returning 0 for identical - * objects: avoid method call overhead. - */ - if (!memcmp(&ap->val, &bp->val, sizeof(ap->val))) - goto cmp_same; - argv[0] = ap->val; - argv[1] = bp->val; - res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv); - if (JS_IsException(res)) - goto exception; - if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { - int val = JS_VALUE_GET_INT(res); - cmp = (val > 0) - (val < 0); - } else { - double val; - if (JS_ToFloat64Free(ctx, &val, res) < 0) - goto exception; - cmp = (val > 0) - (val < 0); - } - } else { - /* Not supposed to bypass ToString even for identical objects as - * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js - */ - if (!ap->str) { - JSValue str = JS_ToString(ctx, ap->val); - if (JS_IsException(str)) - goto exception; - ap->str = JS_VALUE_GET_STRING(str); - } - if (!bp->str) { - JSValue str = JS_ToString(ctx, bp->val); - if (JS_IsException(str)) - goto exception; - bp->str = JS_VALUE_GET_STRING(str); - } - cmp = js_string_compare(ctx, ap->str, bp->str); - } - if (cmp != 0) - return cmp; -cmp_same: - /* make sort stable: compare array offsets */ - return (ap->pos > bp->pos) - (ap->pos < bp->pos); - -exception: - psc->exception = 1; - return 0; -} - -JSValue js_array_sort(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - struct array_sort_context asc = {ctx, 0, 0, argv[0]}; - JSValue obj = JS_UNDEFINED; - ValueSlot* array = NULL; - size_t array_size = 0, pos = 0, n = 0; - int64_t i, len, undefined_count = 0; - int present; - - if (!JS_IsUndefined(asc.method)) { - if (check_function(ctx, asc.method)) - goto exception; - asc.has_method = 1; - } - obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - - /* XXX: should special case fast arrays */ - for (i = 0; i < len; i++) { - if (pos >= array_size) { - size_t new_size, slack; - ValueSlot* new_array; - new_size = (array_size + (array_size >> 1) + 31) & ~15; - new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack); - if (new_array == NULL) - goto exception; - new_size += slack / sizeof(*new_array); - array = new_array; - array_size = new_size; - } - present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val); - if (present < 0) - goto exception; - if (present == 0) - continue; - if (JS_IsUndefined(array[pos].val)) { - undefined_count++; - continue; - } - array[pos].str = NULL; - array[pos].pos = i; - pos++; - } - rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc); - if (asc.exception) - goto exception; - - /* XXX: should special case fast arrays */ - while (n < pos) { - if (array[n].str) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); - if (array[n].pos == n) { - JS_FreeValue(ctx, array[n].val); - } else { - if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { - n++; - goto exception; - } - } - n++; - } - js_free(ctx, array); - for (i = n; undefined_count-- > 0; i++) { - if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0) - goto fail; - } - for (; i < len; i++) { - if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0) - goto fail; - } - return obj; - -exception: - for (; n < pos; n++) { - JS_FreeValue(ctx, array[n].val); - if (array[n].str) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); - } - js_free(ctx, array); -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -void js_array_iterator_finalizer(JSRuntime* rt, JSValue val) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSArrayIteratorData* it = p->u.array_iterator_data; - if (it) { - JS_FreeValueRT(rt, it->obj); - js_free_rt(rt, it); - } -} - -void js_array_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSArrayIteratorData* it = p->u.array_iterator_data; - if (it) { - JS_MarkValue(rt, it->obj, mark_func); - } -} - -JSValue js_create_array(JSContext* ctx, int len, JSValueConst* tab) { - JSValue obj; - int i; - - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - for (i = 0; i < len; i++) { - if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } - return obj; -} - -JSValue js_create_array_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - JSValue enum_obj, arr; - JSArrayIteratorData* it; - JSIteratorKindEnum kind; - int class_id; - - kind = magic & 3; - if (magic & 4) { - /* string iterator case */ - arr = JS_ToStringCheckObject(ctx, this_val); - class_id = JS_CLASS_STRING_ITERATOR; - } else { - arr = JS_ToObject(ctx, this_val); - class_id = JS_CLASS_ARRAY_ITERATOR; - } - if (JS_IsException(arr)) - goto fail; - enum_obj = JS_NewObjectClass(ctx, class_id); - if (JS_IsException(enum_obj)) - goto fail; - it = js_malloc(ctx, sizeof(*it)); - if (!it) - goto fail1; - it->obj = arr; - it->kind = kind; - it->idx = 0; - JS_SetOpaque(enum_obj, it); - return enum_obj; -fail1: - JS_FreeValue(ctx, enum_obj); -fail: - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -JSValue js_array_iterator_next(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, BOOL* pdone, int magic) { - JSArrayIteratorData* it; - uint32_t len, idx; - JSValue val, obj; - JSObject* p; - - it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR); - if (!it) - goto fail1; - if (JS_IsUndefined(it->obj)) - goto done; - p = JS_VALUE_GET_OBJ(it->obj); - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail1; - } - len = p->u.array.count; - } else { - if (js_get_length32(ctx, &len, it->obj)) { - fail1: - *pdone = FALSE; - return JS_EXCEPTION; - } - } - idx = it->idx; - if (idx >= len) { - JS_FreeValue(ctx, it->obj); - it->obj = JS_UNDEFINED; - done: - *pdone = TRUE; - return JS_UNDEFINED; - } - it->idx = idx + 1; - *pdone = FALSE; - if (it->kind == JS_ITERATOR_KIND_KEY) { - return JS_NewUint32(ctx, idx); - } else { - val = JS_GetPropertyUint32(ctx, it->obj, idx); - if (JS_IsException(val)) - return JS_EXCEPTION; - if (it->kind == JS_ITERATOR_KIND_VALUE) { - return val; - } else { - JSValueConst args[2]; - JSValue num; - num = JS_NewUint32(ctx, idx); - args[0] = num; - args[1] = val; - obj = js_create_array(ctx, 2, args); - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, num); - return obj; - } - } -} - -JSValue js_iterator_proto_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_DupValue(ctx, this_val); -} - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-array.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "js-function.h" +#include "js-object.h" +#include "js-operator.h" +#include "js-typed-array.h" + +void js_array_finalizer(JSRuntime* rt, JSValue val) { + JSObject* p = JS_VALUE_GET_OBJ(val); + int i; + + for (i = 0; i < p->u.array.count; i++) { + JS_FreeValueRT(rt, p->u.array.u.values[i]); + } + js_free_rt(rt, p->u.array.u.values); +} + +void js_array_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + JSObject* p = JS_VALUE_GET_OBJ(val); + int i; + + for (i = 0; i < p->u.array.count; i++) { + JS_MarkValue(rt, p->u.array.u.values[i], mark_func); + } +} + +JSValue js_create_iterator_result(JSContext* ctx, JSValue val, BOOL done) { + JSValue obj; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, val); + return obj; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, val, JS_PROP_C_W_E) < 0) { + goto fail; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +BOOL js_is_fast_array(JSContext* ctx, JSValueConst obj) { + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + return TRUE; + } + } + return FALSE; +} + +/* Access an Array's internal JSValue array if available */ +BOOL js_get_fast_array(JSContext* ctx, JSValueConst obj, JSValue** arrpp, uint32_t* countp) { + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + *countp = p->u.array.count; + *arrpp = p->u.array.u.values; + return TRUE; + } + } + return FALSE; +} + + +/* return -1 if exception */ +int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) +{ + uint32_t new_size; + size_t slack; + JSValue *new_array_prop; + /* XXX: potential arithmetic overflow */ + new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); + new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); + if (!new_array_prop) + return -1; + new_size += slack / sizeof(*new_array_prop); + p->u.array.u.values = new_array_prop; + p->u.array.u1.size = new_size; + return 0; +} + +__exception int js_append_enumerate(JSContext* ctx, JSValue* sp) { + JSValue iterator, enumobj, method, value; + int is_array_iterator; + JSValue* arrp; + uint32_t i, count32, pos; + + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { + JS_ThrowInternalError(ctx, "invalid index for append"); + return -1; + } + + pos = JS_VALUE_GET_INT(sp[-2]); + + /* XXX: further optimisations: + - use ctx->array_proto_values? + - check if array_iterator_prototype next method is built-in and + avoid constructing actual iterator object? + - build this into js_for_of_start and use in all `for (x of o)` loops + */ + iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); + if (JS_IsException(iterator)) + return -1; + is_array_iterator = JS_IsCFunction(ctx, iterator, (JSCFunction*)js_create_array_iterator, JS_ITERATOR_KIND_VALUE); + JS_FreeValue(ctx, iterator); + + enumobj = JS_GetIterator(ctx, sp[-1], FALSE); + if (JS_IsException(enumobj)) + return -1; + method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); + if (JS_IsException(method)) { + JS_FreeValue(ctx, enumobj); + return -1; + } + if (is_array_iterator && JS_IsCFunction(ctx, method, (JSCFunction*)js_array_iterator_next, 0) && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { + uint32_t len; + if (js_get_length32(ctx, &len, sp[-1])) + goto exception; + /* if len > count32, the elements >= count32 might be read in + the prototypes and might have side effects */ + if (len != count32) + goto general_case; + /* Handle fast arrays explicitly */ + for (i = 0; i < count32; i++) { + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) + goto exception; + } + } else { + general_case: + for (;;) { + BOOL done; + value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); + if (JS_IsException(value)) + goto exception; + if (done) { + /* value is JS_UNDEFINED */ + break; + } + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) + goto exception; + } + } + /* Note: could raise an error if too many elements */ + sp[-2] = JS_NewInt32(ctx, pos); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return 0; + +exception: + JS_IteratorClose(ctx, enumobj, TRUE); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return -1; +} + +/* Array */ + +int JS_CopySubArray(JSContext* ctx, JSValueConst obj, int64_t to_pos, int64_t from_pos, int64_t count, int dir) { + int64_t i, from, to; + JSValue val; + int fromPresent; + + /* XXX: should special case fast arrays */ + for (i = 0; i < count; i++) { + if (dir < 0) { + from = from_pos + count - i - 1; + to = to_pos + count - i - 1; + } else { + from = from_pos + i; + to = to_pos + i; + } + fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); + if (fromPresent < 0) + goto exception; + + if (fromPresent) { + if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) + goto exception; + } else { + if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) + goto exception; + } + } + return 0; + +exception: + return -1; +} + +JSValue js_array_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { + JSValue obj; + int i; + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY); + if (JS_IsException(obj)) + return obj; + if (argc == 1 && JS_IsNumber(argv[0])) { + uint32_t len; + if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) + goto fail; + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) + goto fail; + } else { + for (i = 0; i < argc; i++) { + if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) + goto fail; + } + } + return obj; +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_from(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // from(items, mapfn = void 0, this_arg = void 0) + JSValueConst items = argv[0], mapfn, this_arg; + JSValueConst args[2]; + JSValue stack[2]; + JSValue iter, r, v, v2, arrayLike; + int64_t k, len; + int done, mapping; + + mapping = FALSE; + mapfn = JS_UNDEFINED; + this_arg = JS_UNDEFINED; + r = JS_UNDEFINED; + arrayLike = JS_UNDEFINED; + stack[0] = JS_UNDEFINED; + stack[1] = JS_UNDEFINED; + + if (argc > 1) { + mapfn = argv[1]; + if (!JS_IsUndefined(mapfn)) { + if (check_function(ctx, mapfn)) + goto exception; + mapping = 1; + if (argc > 2) + this_arg = argv[2]; + } + } + iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto exception; + if (!JS_IsUndefined(iter)) { + JS_FreeValue(ctx, iter); + if (JS_IsConstructor(ctx, this_val)) + r = JS_CallConstructor(ctx, this_val, 0, NULL); + else + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + stack[0] = JS_DupValue(ctx, items); + if (js_for_of_start(ctx, &stack[1], FALSE)) + goto exception; + for (k = 0;; k++) { + v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); + if (JS_IsException(v)) + goto exception_close; + if (done) + break; + if (mapping) { + args[0] = v; + args[1] = JS_NewInt32(ctx, k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception_close; + } + if (JS_DefinePropertyValueInt64(ctx, r, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception_close; + } + } else { + arrayLike = JS_ToObject(ctx, items); + if (JS_IsException(arrayLike)) + goto exception; + if (js_get_length64(ctx, &len, arrayLike) < 0) + goto exception; + v = JS_NewInt64(ctx, len); + args[0] = v; + if (JS_IsConstructor(ctx, this_val)) { + r = JS_CallConstructor(ctx, this_val, 1, args); + } else { + r = js_array_constructor(ctx, JS_UNDEFINED, 1, args); + } + JS_FreeValue(ctx, v); + if (JS_IsException(r)) + goto exception; + for (k = 0; k < len; k++) { + v = JS_GetPropertyInt64(ctx, arrayLike, k); + if (JS_IsException(v)) + goto exception; + if (mapping) { + args[0] = v; + args[1] = JS_NewInt32(ctx, k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, r, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0) + goto exception; + goto done; + +exception_close: + if (!JS_IsUndefined(stack[0])) + JS_IteratorClose(ctx, stack[0], TRUE); +exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; +done: + JS_FreeValue(ctx, arrayLike); + JS_FreeValue(ctx, stack[0]); + JS_FreeValue(ctx, stack[1]); + return r; +} + +JSValue js_array_of(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, args[1]; + int i; + + if (JS_IsConstructor(ctx, this_val)) { + args[0] = JS_NewInt32(ctx, argc); + obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst*)args); + } else { + obj = JS_NewArray(ctx); + } + if (JS_IsException(obj)) + return JS_EXCEPTION; + for (i = 0; i < argc; i++) { + if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]), JS_PROP_THROW) < 0) { + goto fail; + } + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +JSValue js_array_isArray(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + int ret; + ret = JS_IsArray(ctx, argv[0]); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_get_this(JSContext* ctx, JSValueConst this_val) { + return JS_DupValue(ctx, this_val); +} + +JSValue JS_ArraySpeciesCreate(JSContext* ctx, JSValueConst obj, JSValueConst len_val) { + JSValue ctor, ret, species; + int res; + JSContext* realm; + + res = JS_IsArray(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + if (!res) + return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsConstructor(ctx, ctor)) { + /* legacy web compatibility */ + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) { + JS_FreeValue(ctx, ctor); + return JS_EXCEPTION; + } + if (realm != ctx && js_same_value(ctx, ctor, realm->array_ctor)) { + JS_FreeValue(ctx, ctor); + ctor = JS_UNDEFINED; + } + } + if (JS_IsObject(ctor)) { + species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); + JS_FreeValue(ctx, ctor); + if (JS_IsException(species)) + return species; + ctor = species; + if (JS_IsNull(ctor)) + ctor = JS_UNDEFINED; + } + if (JS_IsUndefined(ctor)) { + return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); + } else { + ret = JS_CallConstructor(ctx, ctor, 1, &len_val); + JS_FreeValue(ctx, ctor); + return ret; + } +} + +int JS_isConcatSpreadable(JSContext* ctx, JSValueConst obj) { + JSValue val; + + if (!JS_IsObject(obj)) + return FALSE; + val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) + return JS_ToBoolFree(ctx, val); + return JS_IsArray(ctx, obj); +} + +JSValue js_array_concat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, arr, val; + JSValueConst e; + int64_t len, k, n; + int i, res; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + + arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(arr)) + goto exception; + n = 0; + for (i = -1; i < argc; i++) { + if (i < 0) + e = obj; + else + e = argv[i]; + + res = JS_isConcatSpreadable(ctx, e); + if (res < 0) + goto exception; + if (res) { + if (js_get_length64(ctx, &len, e)) + goto exception; + if (n + len > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + for (k = 0; k < len; k++, n++) { + res = JS_TryGetPropertyInt64(ctx, e, k, &val); + if (res < 0) + goto exception; + if (res) { + if (JS_DefinePropertyValueInt64(ctx, arr, n, val, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + } else { + if (n >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e), JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + n++; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + goto exception; + + JS_FreeValue(ctx, obj); + return arr; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +int js_typed_array_get_length_internal(JSContext* ctx, JSValueConst obj); + +JSValue js_typed_array___speciesCreate(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + +JSValue js_array_every(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special) { + JSValue obj, val, index_val, res, ret; + JSValueConst args[3]; + JSValueConst func, this_arg; + int64_t len, k, n; + int present; + + ret = JS_UNDEFINED; + val = JS_UNDEFINED; + if (special & special_TA) { + obj = JS_DupValue(ctx, this_val); + len = js_typed_array_get_length_internal(ctx, obj); + if (len < 0) + goto exception; + } else { + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + } + func = argv[0]; + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + if (check_function(ctx, func)) + goto exception; + + switch (special) { + case special_every: + case special_every | special_TA: + ret = JS_TRUE; + break; + case special_some: + case special_some | special_TA: + ret = JS_FALSE; + break; + case special_map: + /* XXX: JS_ArraySpeciesCreate should take int64_t */ + ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len)); + if (JS_IsException(ret)) + goto exception; + break; + case special_filter: + ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(ret)) + goto exception; + break; + case special_map | special_TA: + args[0] = obj; + args[1] = JS_NewInt32(ctx, len); + ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(ret)) + goto exception; + break; + case special_filter | special_TA: + ret = JS_NewArray(ctx); + if (JS_IsException(ret)) + goto exception; + break; + } + n = 0; + + for (k = 0; k < len; k++) { + if (special & special_TA) { + val = JS_GetPropertyInt64(ctx, obj, k); + if (JS_IsException(val)) + goto exception; + present = TRUE; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (present < 0) + goto exception; + } + if (present) { + index_val = JS_NewInt64(ctx, k); + if (JS_IsException(index_val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = obj; + res = JS_Call(ctx, func, this_arg, 3, args); + JS_FreeValue(ctx, index_val); + if (JS_IsException(res)) + goto exception; + switch (special) { + case special_every: + case special_every | special_TA: + if (!JS_ToBoolFree(ctx, res)) { + ret = JS_FALSE; + goto done; + } + break; + case special_some: + case special_some | special_TA: + if (JS_ToBoolFree(ctx, res)) { + ret = JS_TRUE; + goto done; + } + break; + case special_map: + if (JS_DefinePropertyValueInt64(ctx, ret, k, res, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + break; + case special_map | special_TA: + if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0) + goto exception; + break; + case special_filter: + case special_filter | special_TA: + if (JS_ToBoolFree(ctx, res)) { + if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val), JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + break; + default: + JS_FreeValue(ctx, res); + break; + } + JS_FreeValue(ctx, val); + val = JS_UNDEFINED; + } + } +done: + if (special == (special_filter | special_TA)) { + JSValue arr; + args[0] = obj; + args[1] = JS_NewInt32(ctx, n); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(arr)) + goto exception; + args[0] = ret; + res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); + if (check_exception_free(ctx, res)) + goto exception; + JS_FreeValue(ctx, ret); + ret = arr; + } + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return ret; + +exception: + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_reduce(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special) { + JSValue obj, val, index_val, acc, acc1; + JSValueConst args[4]; + JSValueConst func; + int64_t len, k, k1; + int present; + + acc = JS_UNDEFINED; + val = JS_UNDEFINED; + if (special & special_TA) { + obj = JS_DupValue(ctx, this_val); + len = js_typed_array_get_length_internal(ctx, obj); + if (len < 0) + goto exception; + } else { + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + } + func = argv[0]; + + if (check_function(ctx, func)) + goto exception; + + k = 0; + if (argc > 1) { + acc = JS_DupValue(ctx, argv[1]); + } else { + for (;;) { + if (k >= len) { + JS_ThrowTypeError(ctx, "empty array"); + goto exception; + } + k1 = (special & special_reduceRight) ? len - k - 1 : k; + k++; + if (special & special_TA) { + acc = JS_GetPropertyInt64(ctx, obj, k1); + if (JS_IsException(acc)) + goto exception; + break; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); + if (present < 0) + goto exception; + if (present) + break; + } + } + } + for (; k < len; k++) { + k1 = (special & special_reduceRight) ? len - k - 1 : k; + if (special & special_TA) { + val = JS_GetPropertyInt64(ctx, obj, k1); + if (JS_IsException(val)) + goto exception; + present = TRUE; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); + if (present < 0) + goto exception; + } + if (present) { + index_val = JS_NewInt64(ctx, k1); + if (JS_IsException(index_val)) + goto exception; + args[0] = acc; + args[1] = val; + args[2] = index_val; + args[3] = obj; + acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args); + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, val); + val = JS_UNDEFINED; + if (JS_IsException(acc1)) + goto exception; + JS_FreeValue(ctx, acc); + acc = acc1; + } + } + JS_FreeValue(ctx, obj); + return acc; + +exception: + JS_FreeValue(ctx, acc); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_fill(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj; + int64_t len, start, end; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + start = 0; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len)) + goto exception; + } + + end = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len)) + goto exception; + } + + /* XXX: should special case fast arrays */ + while (start < end) { + if (JS_SetPropertyInt64(ctx, obj, start, JS_DupValue(ctx, argv[0])) < 0) + goto exception; + start++; + } + return obj; + +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_includes(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, val; + int64_t len, n, res; + JSValue* arrp; + uint32_t count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + res = FALSE; + if (len > 0) { + n = 0; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count)) { + for (; n < count; n++) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), JS_DupValue(ctx, arrp[n]), JS_EQ_SAME_VALUE_ZERO)) { + res = TRUE; + goto done; + } + } + } + for (; n < len; n++) { + val = JS_GetPropertyInt64(ctx, obj, n); + if (JS_IsException(val)) + goto exception; + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_SAME_VALUE_ZERO)) { + res = TRUE; + break; + } + } + } +done: + JS_FreeValue(ctx, obj); + return JS_NewBool(ctx, res); + +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, val; + int64_t len, n, res; + JSValue* arrp; + uint32_t count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + res = -1; + if (len > 0) { + n = 0; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) + goto exception; + } + if (js_get_fast_array(ctx, obj, &arrp, &count)) { + for (; n < count; n++) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) { + res = n; + goto done; + } + } + } + for (; n < len; n++) { + int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); + if (present < 0) + goto exception; + if (present) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { + res = n; + break; + } + } + } + } +done: + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, res); + +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_lastIndexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, val; + int64_t len, n, res; + int present; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + res = -1; + if (len > 0) { + n = len - 1; + if (argc > 1) { + if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len)) + goto exception; + } + /* XXX: should special case fast arrays */ + for (; n >= 0; n--) { + present = JS_TryGetPropertyInt64(ctx, obj, n, &val); + if (present < 0) + goto exception; + if (present) { + if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { + res = n; + break; + } + } + } + } + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, res); + +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_find(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int findIndex) { + JSValueConst func, this_arg; + JSValueConst args[3]; + JSValue obj, val, index_val, res; + int64_t len, k; + + index_val = JS_UNDEFINED; + val = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + func = argv[0]; + if (check_function(ctx, func)) + goto exception; + + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + for (k = 0; k < len; k++) { + index_val = JS_NewInt64(ctx, k); + if (JS_IsException(index_val)) + goto exception; + val = JS_GetPropertyValue(ctx, obj, index_val); + if (JS_IsException(val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = this_val; + res = JS_Call(ctx, func, this_arg, 3, args); + if (JS_IsException(res)) + goto exception; + if (JS_ToBoolFree(ctx, res)) { + if (findIndex) { + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return index_val; + } else { + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, obj); + return val; + } + } + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, index_val); + } + JS_FreeValue(ctx, obj); + if (findIndex) + return JS_NewInt32(ctx, -1); + else + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, index_val); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, method, ret; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + method = JS_GetProperty(ctx, obj, JS_ATOM_join); + if (JS_IsException(method)) { + ret = JS_EXCEPTION; + } else if (!JS_IsFunction(ctx, method)) { + /* Use intrinsic Object.prototype.toString */ + JS_FreeValue(ctx, method); + ret = js_object_toString(ctx, obj, 0, NULL); + } else { + ret = JS_CallFree(ctx, method, obj, 0, NULL); + } + JS_FreeValue(ctx, obj); + return ret; +} + +JSValue js_array_join(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int toLocaleString) { + JSValue obj, sep = JS_UNDEFINED, el; + StringBuffer b_s, *b = &b_s; + JSString* p = NULL; + int64_t i, n; + int c; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &n, obj)) + goto exception; + + c = ','; /* default separator */ + if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { + sep = JS_ToString(ctx, argv[0]); + if (JS_IsException(sep)) + goto exception; + p = JS_VALUE_GET_STRING(sep); + if (p->len == 1 && !p->is_wide_char) + c = p->u.str8[0]; + else + c = -1; + } + string_buffer_init(ctx, b, 0); + + for (i = 0; i < n; i++) { + if (i > 0) { + if (c >= 0) { + string_buffer_putc8(b, c); + } else { + string_buffer_concat(b, p, 0, p->len); + } + } + el = JS_GetPropertyUint32(ctx, obj, i); + if (JS_IsException(el)) + goto fail; + if (!JS_IsNull(el) && !JS_IsUndefined(el)) { + if (toLocaleString) { + el = JS_ToLocaleStringFree(ctx, el); + } + if (string_buffer_concat_value_free(b, el)) + goto fail; + } + } + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, obj); + return string_buffer_end(b); + +fail: + string_buffer_free(b); + JS_FreeValue(ctx, sep); +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_pop(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int shift) { + JSValue obj, res = JS_UNDEFINED; + int64_t len, newLen; + JSValue* arrp; + uint32_t count32; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = 0; + if (len > 0) { + newLen = len - 1; + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + if (shift) { + res = arrp[0]; + memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); + p->u.array.count--; + } else { + res = arrp[count32 - 1]; + p->u.array.count--; + } + } else { + if (shift) { + res = JS_GetPropertyInt64(ctx, obj, 0); + if (JS_IsException(res)) + goto exception; + if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) + goto exception; + } else { + res = JS_GetPropertyInt64(ctx, obj, newLen); + if (JS_IsException(res)) + goto exception; + } + if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) + goto exception; + + JS_FreeValue(ctx, obj); + return res; + +exception: + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_push(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int unshift) { + JSValue obj; + int i; + int64_t len, from, newLen; + + obj = JS_ToObject(ctx, this_val); + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ARRAY || !p->fast_array || !p->extensible) + goto generic_case; + /* length must be writable */ + if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) + goto generic_case; + /* check the length */ + if (unlikely(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT)) + goto generic_case; + len = JS_VALUE_GET_INT(p->prop[0].u.value); + /* we don't support holes */ + if (unlikely(len != p->u.array.count)) + goto generic_case; + newLen = len + argc; + if (unlikely(newLen > INT32_MAX)) + goto generic_case; + if (newLen > p->u.array.u1.size) { + if (expand_fast_array(ctx, p, newLen)) + goto exception; + } + if (unshift && argc > 0) { + memmove(p->u.array.u.values + argc, p->u.array.u.values, len * sizeof(p->u.array.u.values[0])); + from = 0; + } else { + from = len; + } + for (i = 0; i < argc; i++) { + p->u.array.u.values[from + i] = JS_DupValue(ctx, argv[i]); + } + p->u.array.count = newLen; + p->prop[0].u.value = JS_NewInt32(ctx, newLen); + } else { + generic_case: + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = len + argc; + if (newLen > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + from = len; + if (unshift && argc > 0) { + if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) + goto exception; + from = 0; + } + for (i = 0; i < argc; i++) { + if (JS_SetPropertyInt64(ctx, obj, from + i, JS_DupValue(ctx, argv[i])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) + goto exception; + } + JS_FreeValue(ctx, obj); + return JS_NewInt64(ctx, newLen); + +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_reverse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, lval, hval; + JSValue* arrp; + int64_t len, l, h; + int l_present, h_present; + uint32_t count32; + + lval = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + uint32_t ll, hh; + + if (count32 > 1) { + for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) { + lval = arrp[ll]; + arrp[ll] = arrp[hh]; + arrp[hh] = lval; + } + } + return obj; + } + + for (l = 0, h = len - 1; l < h; l++, h--) { + l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval); + if (l_present < 0) + goto exception; + h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval); + if (h_present < 0) + goto exception; + if (h_present) { + if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0) + goto exception; + + if (l_present) { + if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { + lval = JS_UNDEFINED; + goto exception; + } + lval = JS_UNDEFINED; + } else { + if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0) + goto exception; + } + } else { + if (l_present) { + if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0) + goto exception; + if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { + lval = JS_UNDEFINED; + goto exception; + } + lval = JS_UNDEFINED; + } + } + } + return obj; + +exception: + JS_FreeValue(ctx, lval); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_array_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int splice) { + JSValue obj, arr, val, len_val; + int64_t len, start, k, final, n, count, del_count, new_len; + int kPresent; + JSValue* arrp; + uint32_t count32, i, item_count; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + if (splice) { + if (argc == 0) { + item_count = 0; + del_count = 0; + } else if (argc == 1) { + item_count = 0; + del_count = len - start; + } else { + item_count = argc - 2; + if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) + goto exception; + } + if (len + item_count - del_count > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + count = del_count; + } else { + item_count = 0; /* avoid warning */ + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int64(final - start, 0); + } + len_val = JS_NewInt64(ctx, count); + arr = JS_ArraySpeciesCreate(ctx, obj, len_val); + JS_FreeValue(ctx, len_val); + if (JS_IsException(arr)) + goto exception; + + k = start; + final = start + count; + n = 0; + /* The fast array test on arr ensures that + JS_CreateDataPropertyUint32() won't modify obj in case arr is + an exotic object */ + /* Special case fast arrays */ + if (js_get_fast_array(ctx, obj, &arrp, &count32) && js_is_fast_array(ctx, arr)) { + /* XXX: should share code with fast array constructor */ + for (; k < final && k < count32; k++, n++) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0) + goto exception; + } + } + /* Copy the remaining elements if any (handle case of inherited properties) */ + for (; k < final; k++, n++) { + kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (kPresent < 0) + goto exception; + if (kPresent) { + if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) + goto exception; + } + } + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) + goto exception; + + if (splice) { + new_len = len + item_count - del_count; + if (item_count != del_count) { + if (JS_CopySubArray(ctx, obj, start + item_count, start + del_count, len - (start + del_count), item_count <= del_count ? +1 : -1) < 0) + goto exception; + + for (k = len; k-- > new_len;) { + if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0) + goto exception; + } + } + for (i = 0; i < item_count; i++) { + if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) + goto exception; + } + JS_FreeValue(ctx, obj); + return arr; + +exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +JSValue js_array_copyWithin(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj; + int64_t len, from, to, final, count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len)) + goto exception; + + if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len)) + goto exception; + + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len)) + goto exception; + } + + count = min_int64(final - from, len - to); + + if (JS_CopySubArray(ctx, obj, to, from, count, (from < to && to < from + count) ? -1 : +1)) + goto exception; + + return obj; + +exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +int64_t JS_FlattenIntoArray(JSContext* ctx, JSValueConst target, JSValueConst source, int64_t sourceLen, int64_t targetIndex, int depth, JSValueConst mapperFunction, JSValueConst thisArg) { + JSValue element; + int64_t sourceIndex, elementLen; + int present, is_array; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + + for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { + present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); + if (present < 0) + return -1; + if (!present) + continue; + if (!JS_IsUndefined(mapperFunction)) { + JSValueConst args[3] = {element, JS_NewInt64(ctx, sourceIndex), source}; + element = JS_Call(ctx, mapperFunction, thisArg, 3, args); + JS_FreeValue(ctx, (JSValue)args[0]); + JS_FreeValue(ctx, (JSValue)args[1]); + if (JS_IsException(element)) + return -1; + } + if (depth > 0) { + is_array = JS_IsArray(ctx, element); + if (is_array < 0) + goto fail; + if (is_array) { + if (js_get_length64(ctx, &elementLen, element) < 0) + goto fail; + targetIndex = JS_FlattenIntoArray(ctx, target, element, elementLen, targetIndex, depth - 1, JS_UNDEFINED, JS_UNDEFINED); + if (targetIndex < 0) + goto fail; + JS_FreeValue(ctx, element); + continue; + } + } + if (targetIndex >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array too long"); + goto fail; + } + if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + return -1; + targetIndex++; + } + return targetIndex; + +fail: + JS_FreeValue(ctx, element); + return -1; +} + +JSValue js_array_flatten(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int map) { + JSValue obj, arr; + JSValueConst mapperFunction, thisArg; + int64_t sourceLen; + int depthNum; + + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &sourceLen, obj)) + goto exception; + + depthNum = 1; + mapperFunction = JS_UNDEFINED; + thisArg = JS_UNDEFINED; + if (map) { + mapperFunction = argv[0]; + if (argc > 1) { + thisArg = argv[1]; + } + if (check_function(ctx, mapperFunction)) + goto exception; + } else { + if (argc > 0 && !JS_IsUndefined(argv[0])) { + if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0) + goto exception; + } + } + arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); + if (JS_IsException(arr)) + goto exception; + if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, mapperFunction, thisArg) < 0) + goto exception; + JS_FreeValue(ctx, obj); + return arr; + +exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +/* Array sort */ +typedef struct ValueSlot { + JSValue val; + JSString* str; + int64_t pos; +} ValueSlot; + +struct array_sort_context { + JSContext* ctx; + int exception; + int has_method; + JSValueConst method; +}; + +int js_array_cmp_generic(const void* a, const void* b, void* opaque) { + struct array_sort_context* psc = opaque; + JSContext* ctx = psc->ctx; + JSValueConst argv[2]; + JSValue res; + ValueSlot* ap = (ValueSlot*)(void*)a; + ValueSlot* bp = (ValueSlot*)(void*)b; + int cmp; + + if (psc->exception) + return 0; + + if (psc->has_method) { + /* custom sort function is specified as returning 0 for identical + * objects: avoid method call overhead. + */ + if (!memcmp(&ap->val, &bp->val, sizeof(ap->val))) + goto cmp_same; + argv[0] = ap->val; + argv[1] = bp->val; + res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv); + if (JS_IsException(res)) + goto exception; + if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { + int val = JS_VALUE_GET_INT(res); + cmp = (val > 0) - (val < 0); + } else { + double val; + if (JS_ToFloat64Free(ctx, &val, res) < 0) + goto exception; + cmp = (val > 0) - (val < 0); + } + } else { + /* Not supposed to bypass ToString even for identical objects as + * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js + */ + if (!ap->str) { + JSValue str = JS_ToString(ctx, ap->val); + if (JS_IsException(str)) + goto exception; + ap->str = JS_VALUE_GET_STRING(str); + } + if (!bp->str) { + JSValue str = JS_ToString(ctx, bp->val); + if (JS_IsException(str)) + goto exception; + bp->str = JS_VALUE_GET_STRING(str); + } + cmp = js_string_compare(ctx, ap->str, bp->str); + } + if (cmp != 0) + return cmp; +cmp_same: + /* make sort stable: compare array offsets */ + return (ap->pos > bp->pos) - (ap->pos < bp->pos); + +exception: + psc->exception = 1; + return 0; +} + +JSValue js_array_sort(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + struct array_sort_context asc = {ctx, 0, 0, argv[0]}; + JSValue obj = JS_UNDEFINED; + ValueSlot* array = NULL; + size_t array_size = 0, pos = 0, n = 0; + int64_t i, len, undefined_count = 0; + int present; + + if (!JS_IsUndefined(asc.method)) { + if (check_function(ctx, asc.method)) + goto exception; + asc.has_method = 1; + } + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + /* XXX: should special case fast arrays */ + for (i = 0; i < len; i++) { + if (pos >= array_size) { + size_t new_size, slack; + ValueSlot* new_array; + new_size = (array_size + (array_size >> 1) + 31) & ~15; + new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack); + if (new_array == NULL) + goto exception; + new_size += slack / sizeof(*new_array); + array = new_array; + array_size = new_size; + } + present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val); + if (present < 0) + goto exception; + if (present == 0) + continue; + if (JS_IsUndefined(array[pos].val)) { + undefined_count++; + continue; + } + array[pos].str = NULL; + array[pos].pos = i; + pos++; + } + rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc); + if (asc.exception) + goto exception; + + /* XXX: should special case fast arrays */ + while (n < pos) { + if (array[n].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); + if (array[n].pos == n) { + JS_FreeValue(ctx, array[n].val); + } else { + if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { + n++; + goto exception; + } + } + n++; + } + js_free(ctx, array); + for (i = n; undefined_count-- > 0; i++) { + if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0) + goto fail; + } + for (; i < len; i++) { + if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0) + goto fail; + } + return obj; + +exception: + for (; n < pos; n++) { + JS_FreeValue(ctx, array[n].val); + if (array[n].str) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); + } + js_free(ctx, array); +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +void js_array_iterator_finalizer(JSRuntime* rt, JSValue val) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSArrayIteratorData* it = p->u.array_iterator_data; + if (it) { + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); + } +} + +void js_array_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSArrayIteratorData* it = p->u.array_iterator_data; + if (it) { + JS_MarkValue(rt, it->obj, mark_func); + } +} + +JSValue js_create_array(JSContext* ctx, int len, JSValueConst* tab) { + JSValue obj; + int i; + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + for (i = 0; i < len; i++) { + if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +JSValue js_create_array_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + JSValue enum_obj, arr; + JSArrayIteratorData* it; + JSIteratorKindEnum kind; + int class_id; + + kind = magic & 3; + if (magic & 4) { + /* string iterator case */ + arr = JS_ToStringCheckObject(ctx, this_val); + class_id = JS_CLASS_STRING_ITERATOR; + } else { + arr = JS_ToObject(ctx, this_val); + class_id = JS_CLASS_ARRAY_ITERATOR; + } + if (JS_IsException(arr)) + goto fail; + enum_obj = JS_NewObjectClass(ctx, class_id); + if (JS_IsException(enum_obj)) + goto fail; + it = js_malloc(ctx, sizeof(*it)); + if (!it) + goto fail1; + it->obj = arr; + it->kind = kind; + it->idx = 0; + JS_SetOpaque(enum_obj, it); + return enum_obj; +fail1: + JS_FreeValue(ctx, enum_obj); +fail: + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +JSValue js_array_iterator_next(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, BOOL* pdone, int magic) { + JSArrayIteratorData* it; + uint32_t len, idx; + JSValue val, obj; + JSObject* p; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR); + if (!it) + goto fail1; + if (JS_IsUndefined(it->obj)) + goto done; + p = JS_VALUE_GET_OBJ(it->obj); + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail1; + } + len = p->u.array.count; + } else { + if (js_get_length32(ctx, &len, it->obj)) { + fail1: + *pdone = FALSE; + return JS_EXCEPTION; + } + } + idx = it->idx; + if (idx >= len) { + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + *pdone = TRUE; + return JS_UNDEFINED; + } + it->idx = idx + 1; + *pdone = FALSE; + if (it->kind == JS_ITERATOR_KIND_KEY) { + return JS_NewUint32(ctx, idx); + } else { + val = JS_GetPropertyUint32(ctx, it->obj, idx); + if (JS_IsException(val)) + return JS_EXCEPTION; + if (it->kind == JS_ITERATOR_KIND_VALUE) { + return val; + } else { + JSValueConst args[2]; + JSValue num; + num = JS_NewUint32(ctx, idx); + args[0] = num; + args[1] = val; + obj = js_create_array(ctx, 2, args); + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, num); + return obj; + } + } +} + +JSValue js_iterator_proto_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_DupValue(ctx, this_val); +} + diff --git a/src/core/builtins/js-array.h b/src/core/builtins/js-array.h index a986ac5fe..e766b457b 100644 --- a/src/core/builtins/js-array.h +++ b/src/core/builtins/js-array.h @@ -1,100 +1,100 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_ARRAY_H -#define QUICKJS_JS_ARRAY_H - -#include "../types.h" -#include "quickjs/cutils.h" -#include "quickjs/quickjs.h" - -#define special_every 0 -#define special_some 1 -#define special_forEach 2 -#define special_map 3 -#define special_filter 4 -#define special_TA 8 - -#define special_reduce 0 -#define special_reduceRight 1 - -typedef struct JSArrayIteratorData { - JSValue obj; - JSIteratorKindEnum kind; - uint32_t idx; -} JSArrayIteratorData; - -JSValue js_create_iterator_result(JSContext* ctx, JSValue val, BOOL done); -JSValue js_array_iterator_next(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, BOOL* pdone, int magic); - -JSValue js_create_array_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -BOOL js_is_fast_array(JSContext* ctx, JSValueConst obj); -/* Access an Array's internal JSValue array if available */ -BOOL js_get_fast_array(JSContext* ctx, JSValueConst obj, JSValue** arrpp, uint32_t* countp); - -int expand_fast_array(JSContext* ctx, JSObject* p, uint32_t new_len); - -int JS_CopySubArray(JSContext* ctx, JSValueConst obj, int64_t to_pos, int64_t from_pos, int64_t count, int dir); -JSValue js_array_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); -JSValue js_array_from(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_of(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_isArray(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_get_this(JSContext* ctx, JSValueConst this_val); -JSValue JS_ArraySpeciesCreate(JSContext* ctx, JSValueConst obj, JSValueConst len_val); -int JS_isConcatSpreadable(JSContext* ctx, JSValueConst obj); -JSValue js_array_concat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -int js_typed_array_get_length_internal(JSContext* ctx, JSValueConst obj); -JSValue js_typed_array___speciesCreate(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_every(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special); -JSValue js_array_reduce(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special); -JSValue js_array_fill(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_includes(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_lastIndexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_find(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int findIndex); -JSValue js_array_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_join(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int toLocaleString); -JSValue js_array_pop(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int shift); -JSValue js_array_push(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int unshift); -JSValue js_array_reverse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_array_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int splice); -JSValue js_array_copyWithin(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -int64_t JS_FlattenIntoArray(JSContext* ctx, JSValueConst target, JSValueConst source, int64_t sourceLen, int64_t targetIndex, int depth, JSValueConst mapperFunction, JSValueConst thisArg); -JSValue js_array_flatten(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int map); -int js_array_cmp_generic(const void* a, const void* b, void* opaque); -JSValue js_array_sort(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - -void js_array_iterator_finalizer(JSRuntime* rt, JSValue val); -void js_array_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -JSValue js_create_array(JSContext* ctx, int len, JSValueConst* tab); - -__exception int js_append_enumerate(JSContext* ctx, JSValue* sp); - -void js_array_finalizer(JSRuntime* rt, JSValue val); -void js_array_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - -JSValue js_iterator_proto_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_ARRAY_H +#define QUICKJS_JS_ARRAY_H + +#include "../types.h" +#include "quickjs/cutils.h" +#include "quickjs/quickjs.h" + +#define special_every 0 +#define special_some 1 +#define special_forEach 2 +#define special_map 3 +#define special_filter 4 +#define special_TA 8 + +#define special_reduce 0 +#define special_reduceRight 1 + +typedef struct JSArrayIteratorData { + JSValue obj; + JSIteratorKindEnum kind; + uint32_t idx; +} JSArrayIteratorData; + +JSValue js_create_iterator_result(JSContext* ctx, JSValue val, BOOL done); +JSValue js_array_iterator_next(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, BOOL* pdone, int magic); + +JSValue js_create_array_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +BOOL js_is_fast_array(JSContext* ctx, JSValueConst obj); +/* Access an Array's internal JSValue array if available */ +BOOL js_get_fast_array(JSContext* ctx, JSValueConst obj, JSValue** arrpp, uint32_t* countp); + +int expand_fast_array(JSContext* ctx, JSObject* p, uint32_t new_len); + +int JS_CopySubArray(JSContext* ctx, JSValueConst obj, int64_t to_pos, int64_t from_pos, int64_t count, int dir); +JSValue js_array_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); +JSValue js_array_from(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_of(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_isArray(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_get_this(JSContext* ctx, JSValueConst this_val); +JSValue JS_ArraySpeciesCreate(JSContext* ctx, JSValueConst obj, JSValueConst len_val); +int JS_isConcatSpreadable(JSContext* ctx, JSValueConst obj); +JSValue js_array_concat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +int js_typed_array_get_length_internal(JSContext* ctx, JSValueConst obj); +JSValue js_typed_array___speciesCreate(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_every(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special); +JSValue js_array_reduce(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special); +JSValue js_array_fill(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_includes(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_lastIndexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_find(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int findIndex); +JSValue js_array_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_join(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int toLocaleString); +JSValue js_array_pop(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int shift); +JSValue js_array_push(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int unshift); +JSValue js_array_reverse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_array_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int splice); +JSValue js_array_copyWithin(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +int64_t JS_FlattenIntoArray(JSContext* ctx, JSValueConst target, JSValueConst source, int64_t sourceLen, int64_t targetIndex, int depth, JSValueConst mapperFunction, JSValueConst thisArg); +JSValue js_array_flatten(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int map); +int js_array_cmp_generic(const void* a, const void* b, void* opaque); +JSValue js_array_sort(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + +void js_array_iterator_finalizer(JSRuntime* rt, JSValue val); +void js_array_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +JSValue js_create_array(JSContext* ctx, int len, JSValueConst* tab); + +__exception int js_append_enumerate(JSContext* ctx, JSValue* sp); + +void js_array_finalizer(JSRuntime* rt, JSValue val); +void js_array_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + +JSValue js_iterator_proto_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-async-function.c b/src/core/builtins/js-async-function.c index f3d49287b..c26cfe740 100644 --- a/src/core/builtins/js-async-function.c +++ b/src/core/builtins/js-async-function.c @@ -1,310 +1,310 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-async-function.h" -#include "../exception.h" -#include "../function.h" -#include "../gc.h" -#include "js-closures.h" -#include "js-promise.h" -#include "quickjs/cutils.h" -#include "quickjs/list.h" - -/* JSAsyncFunctionState (used by generator and async functions) */ -__exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, - JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - JSObject *p; - JSFunctionBytecode *b; - JSStackFrame *sf; - int local_count, i, arg_buf_len, n; - - sf = &s->frame; - init_list_head(&sf->var_ref_list); - p = JS_VALUE_GET_OBJ(func_obj); - b = p->u.func.function_bytecode; - sf->js_mode = b->js_mode; - sf->cur_pc = b->byte_code_buf; - arg_buf_len = max_int(b->arg_count, argc); - local_count = arg_buf_len + b->var_count + b->stack_size; - sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); - if (!sf->arg_buf) - return -1; - sf->cur_func = JS_DupValue(ctx, func_obj); - s->this_val = JS_DupValue(ctx, this_obj); - s->argc = argc; - sf->arg_count = arg_buf_len; - sf->var_buf = sf->arg_buf + arg_buf_len; - sf->cur_sp = sf->var_buf + b->var_count; - for(i = 0; i < argc; i++) - sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); - n = arg_buf_len + b->var_count; - for(i = argc; i < n; i++) - sf->arg_buf[i] = JS_UNDEFINED; - return 0; -} - -void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, - JS_MarkFunc *mark_func) -{ - JSStackFrame *sf; - JSValue *sp; - - sf = &s->frame; - JS_MarkValue(rt, sf->cur_func, mark_func); - JS_MarkValue(rt, s->this_val, mark_func); - if (sf->cur_sp) { - /* if the function is running, cur_sp is not known so we - cannot mark the stack. Marking the variables is not needed - because a running function cannot be part of a removable - cycle */ - for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) - JS_MarkValue(rt, *sp, mark_func); - } -} - -void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) -{ - JSStackFrame *sf; - JSValue *sp; - - sf = &s->frame; - - /* close the closure variables. */ - close_var_refs(rt, sf); - - if (sf->arg_buf) { - /* cannot free the function if it is running */ - assert(sf->cur_sp != NULL); - for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { - JS_FreeValueRT(rt, *sp); - } - js_free_rt(rt, sf->arg_buf); - } - JS_FreeValueRT(rt, sf->cur_func); - JS_FreeValueRT(rt, s->this_val); -} - -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, JS_CALL_FLAG_GENERATOR); -} - - - -/* AsyncFunction */ - -void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) -{ - if (s->is_active) { - async_func_free(rt, &s->func_state); - s->is_active = FALSE; - } -} - -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); -} - -void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) -{ - if (--s->header.ref_count == 0) { - js_async_function_free0(rt, s); - } -} - -void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionData *s = p->u.async_function_data; - if (s) { - js_async_function_free(rt, s); - } -} - -void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSAsyncFunctionData *s = p->u.async_function_data; - if (s) { - mark_func(rt, &s->header); - } -} - -int js_async_function_resolve_create(JSContext *ctx, - JSAsyncFunctionData *s, - JSValue *resolving_funcs) -{ - int i; - JSObject *p; - - for(i = 0; i < 2; i++) { - resolving_funcs[i] = - JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); - if (JS_IsException(resolving_funcs[i])) { - if (i == 1) - JS_FreeValue(ctx, resolving_funcs[0]); - return -1; - } - p = JS_VALUE_GET_OBJ(resolving_funcs[i]); - s->header.ref_count++; - p->u.async_function_data = s; - } - return 0; -} - -void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) -{ - JSValue func_ret, ret2; - - func_ret = async_func_resume(ctx, &s->func_state); - if (JS_IsException(func_ret)) { - JSValue error; - fail: - error = JS_GetException(ctx); - ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - js_async_function_terminate(ctx->rt, s); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - } else { - JSValue value; - value = s->func_state.frame.cur_sp[-1]; - s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; - if (JS_IsUndefined(func_ret)) { - /* function returned */ - ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, - 1, (JSValueConst *)&value); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, value); - js_async_function_terminate(ctx->rt, s); - } else { - JSValue promise, resolving_funcs[2], resolving_funcs1[2]; - int i, res; - - /* await */ - JS_FreeValue(ctx, func_ret); /* not used */ - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&value, 0); - JS_FreeValue(ctx, value); - if (JS_IsException(promise)) - goto fail; - if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { - JS_FreeValue(ctx, promise); - goto fail; - } - - /* Note: no need to create 'thrownawayCapability' as in - the spec */ - for(i = 0; i < 2; i++) - resolving_funcs1[i] = JS_UNDEFINED; - res = perform_promise_then(ctx, promise, - (JSValueConst *)resolving_funcs, - (JSValueConst *)resolving_funcs1); - JS_FreeValue(ctx, promise); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (res) - goto fail; - } - } -} - -JSValue js_async_function_resolve_call(JSContext *ctx, - JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags) -{ - JSObject *p = JS_VALUE_GET_OBJ(func_obj); - JSAsyncFunctionData *s = p->u.async_function_data; - BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; - JSValueConst arg; - - if (argc > 0) - arg = argv[0]; - else - arg = JS_UNDEFINED; - s->func_state.throw_flag = is_reject; - if (is_reject) { - JS_Throw(ctx, JS_DupValue(ctx, arg)); - } else { - /* return value of await */ - s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg); - } - js_async_function_resume(ctx, s); - return JS_UNDEFINED; -} - -JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags) -{ - JSValue promise; - JSAsyncFunctionData *s; - - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - return JS_EXCEPTION; - s->header.ref_count = 1; - add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); - s->is_active = FALSE; - s->resolving_funcs[0] = JS_UNDEFINED; - s->resolving_funcs[1] = JS_UNDEFINED; - - promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); - if (JS_IsException(promise)) - goto fail; - - if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { - fail: - JS_FreeValue(ctx, promise); - js_async_function_free(ctx->rt, s); - return JS_EXCEPTION; - } - s->is_active = TRUE; - - js_async_function_resume(ctx, s); - - js_async_function_free(ctx->rt, s); - - return promise; -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-async-function.h" +#include "../exception.h" +#include "../function.h" +#include "../gc.h" +#include "js-closures.h" +#include "js-promise.h" +#include "quickjs/cutils.h" +#include "quickjs/list.h" + +/* JSAsyncFunctionState (used by generator and async functions) */ +__exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, + JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame *sf; + int local_count, i, arg_buf_len, n; + + sf = &s->frame; + init_list_head(&sf->var_ref_list); + p = JS_VALUE_GET_OBJ(func_obj); + b = p->u.func.function_bytecode; + sf->js_mode = b->js_mode; + sf->cur_pc = b->byte_code_buf; + arg_buf_len = max_int(b->arg_count, argc); + local_count = arg_buf_len + b->var_count + b->stack_size; + sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); + if (!sf->arg_buf) + return -1; + sf->cur_func = JS_DupValue(ctx, func_obj); + s->this_val = JS_DupValue(ctx, this_obj); + s->argc = argc; + sf->arg_count = arg_buf_len; + sf->var_buf = sf->arg_buf + arg_buf_len; + sf->cur_sp = sf->var_buf + b->var_count; + for(i = 0; i < argc; i++) + sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); + n = arg_buf_len + b->var_count; + for(i = argc; i < n; i++) + sf->arg_buf[i] = JS_UNDEFINED; + return 0; +} + +void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + if (sf->cur_sp) { + /* if the function is running, cur_sp is not known so we + cannot mark the stack. Marking the variables is not needed + because a running function cannot be part of a removable + cycle */ + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) + JS_MarkValue(rt, *sp, mark_func); + } +} + +void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + + /* close the closure variables. */ + close_var_refs(rt, sf); + + if (sf->arg_buf) { + /* cannot free the function if it is running */ + assert(sf->cur_sp != NULL); + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { + JS_FreeValueRT(rt, *sp); + } + js_free_rt(rt, sf->arg_buf); + } + JS_FreeValueRT(rt, sf->cur_func); + JS_FreeValueRT(rt, s->this_val); +} + +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, JS_CALL_FLAG_GENERATOR); +} + + + +/* AsyncFunction */ + +void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (s->is_active) { + async_func_free(rt, &s->func_state); + s->is_active = FALSE; + } +} + +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); +} + +void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (--s->header.ref_count == 0) { + js_async_function_free0(rt, s); + } +} + +void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + js_async_function_free(rt, s); + } +} + +void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + mark_func(rt, &s->header); + } +} + +int js_async_function_resolve_create(JSContext *ctx, + JSAsyncFunctionData *s, + JSValue *resolving_funcs) +{ + int i; + JSObject *p; + + for(i = 0; i < 2; i++) { + resolving_funcs[i] = + JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); + if (JS_IsException(resolving_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + p = JS_VALUE_GET_OBJ(resolving_funcs[i]); + s->header.ref_count++; + p->u.async_function_data = s; + } + return 0; +} + +void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +{ + JSValue func_ret, ret2; + + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + JSValue error; + fail: + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + js_async_function_terminate(ctx->rt, s); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + } else { + JSValue value; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + if (JS_IsUndefined(func_ret)) { + /* function returned */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, value); + js_async_function_terminate(ctx->rt, s); + } else { + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + JS_FreeValue(ctx, value); + if (JS_IsException(promise)) + goto fail; + if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + (JSValueConst *)resolving_funcs, + (JSValueConst *)resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + } + } +} + +JSValue js_async_function_resolve_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSAsyncFunctionData *s = p->u.async_function_data; + BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; + JSValueConst arg; + + if (argc > 0) + arg = argv[0]; + else + arg = JS_UNDEFINED; + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, JS_DupValue(ctx, arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg); + } + js_async_function_resume(ctx, s); + return JS_UNDEFINED; +} + +JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags) +{ + JSValue promise; + JSAsyncFunctionData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + s->is_active = FALSE; + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + + promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); + if (JS_IsException(promise)) + goto fail; + + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + fail: + JS_FreeValue(ctx, promise); + js_async_function_free(ctx->rt, s); + return JS_EXCEPTION; + } + s->is_active = TRUE; + + js_async_function_resume(ctx, s); + + js_async_function_free(ctx->rt, s); + + return promise; +} diff --git a/src/core/builtins/js-async-function.h b/src/core/builtins/js-async-function.h index 33efca855..ff9131ea6 100644 --- a/src/core/builtins/js-async-function.h +++ b/src/core/builtins/js-async-function.h @@ -1,59 +1,59 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_ASYNC_FUNCTION_H -#define QUICKJS_JS_ASYNC_FUNCTION_H - -#include "quickjs/quickjs.h" -#include "../types.h" - -JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s); -__exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, - JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv); -void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); -void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, - JS_MarkFunc *mark_func); - -void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s); -void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); -void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s); -void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); -void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); -int js_async_function_resolve_create(JSContext *ctx, - JSAsyncFunctionData *s, - JSValue *resolving_funcs); -void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s); -JSValue js_async_function_resolve_call(JSContext *ctx, - JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags); -JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_ASYNC_FUNCTION_H +#define QUICKJS_JS_ASYNC_FUNCTION_H + +#include "quickjs/quickjs.h" +#include "../types.h" + +JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s); +__exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, + JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv); +void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); +void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func); + +void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s); +void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); +void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s); +void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); +void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); +int js_async_function_resolve_create(JSContext *ctx, + JSAsyncFunctionData *s, + JSValue *resolving_funcs); +void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s); +JSValue js_async_function_resolve_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags); +JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-async-generator.c b/src/core/builtins/js-async-generator.c index d5b75d9b0..46dac367d 100644 --- a/src/core/builtins/js-async-generator.c +++ b/src/core/builtins/js-async-generator.c @@ -1,440 +1,440 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-async-generator.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "js-array.h" -#include "js-async-function.h" -#include "js-generator.h" -#include "js-promise.h" -#include "quickjs/cutils.h" -#include "quickjs/list.h" - -/* AsyncGenerator */ - -void js_async_generator_free(JSRuntime *rt, - JSAsyncGeneratorData *s) -{ - struct list_head *el, *el1; - JSAsyncGeneratorRequest *req; - - list_for_each_safe(el, el1, &s->queue) { - req = list_entry(el, JSAsyncGeneratorRequest, link); - JS_FreeValueRT(rt, req->result); - JS_FreeValueRT(rt, req->promise); - JS_FreeValueRT(rt, req->resolving_funcs[0]); - JS_FreeValueRT(rt, req->resolving_funcs[1]); - js_free_rt(rt, req); - } - if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && - s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { - async_func_free(rt, &s->func_state); - } - js_free_rt(rt, s); -} - -void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); - - if (s) { - js_async_generator_free(rt, s); - } -} - -void js_async_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); - struct list_head *el; - JSAsyncGeneratorRequest *req; - if (s) { - list_for_each(el, &s->queue) { - req = list_entry(el, JSAsyncGeneratorRequest, link); - JS_MarkValue(rt, req->result, mark_func); - JS_MarkValue(rt, req->promise, mark_func); - JS_MarkValue(rt, req->resolving_funcs[0], mark_func); - JS_MarkValue(rt, req->resolving_funcs[1], mark_func); - } - if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && - s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { - async_func_mark(rt, &s->func_state, mark_func); - } - } -} - -JSValue js_async_generator_resolve_function(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int magic, JSValue *func_data); - -int js_async_generator_resolve_function_create(JSContext *ctx, - JSValueConst generator, - JSValue *resolving_funcs, - BOOL is_resume_next) -{ - int i; - JSValue func; - - for(i = 0; i < 2; i++) { - func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, - i + is_resume_next * 2, 1, &generator); - if (JS_IsException(func)) { - if (i == 1) - JS_FreeValue(ctx, resolving_funcs[0]); - return -1; - } - resolving_funcs[i] = func; - } - return 0; -} - -int js_async_generator_await(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value) -{ - JSValue promise, resolving_funcs[2], resolving_funcs1[2]; - int i, res; - - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, &value, 0); - if (JS_IsException(promise)) - goto fail; - - if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), - resolving_funcs, FALSE)) { - JS_FreeValue(ctx, promise); - goto fail; - } - - /* Note: no need to create 'thrownawayCapability' as in - the spec */ - for(i = 0; i < 2; i++) - resolving_funcs1[i] = JS_UNDEFINED; - res = perform_promise_then(ctx, promise, - (JSValueConst *)resolving_funcs, - (JSValueConst *)resolving_funcs1); - JS_FreeValue(ctx, promise); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (res) - goto fail; - return 0; -fail: - return -1; -} - -void js_async_generator_resolve_or_reject(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst result, - int is_reject) -{ - JSAsyncGeneratorRequest *next; - JSValue ret; - - next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); - list_del(&next->link); - ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, - &result); - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, next->result); - JS_FreeValue(ctx, next->promise); - JS_FreeValue(ctx, next->resolving_funcs[0]); - JS_FreeValue(ctx, next->resolving_funcs[1]); - js_free(ctx, next); -} - -void js_async_generator_resolve(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value, - BOOL done) -{ - JSValue result; - result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done); - /* XXX: better exception handling ? */ - js_async_generator_resolve_or_reject(ctx, s, result, 0); - JS_FreeValue(ctx, result); -} - -void js_async_generator_reject(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst exception) -{ - js_async_generator_resolve_or_reject(ctx, s, exception, 1); -} - -void js_async_generator_complete(JSContext *ctx, - JSAsyncGeneratorData *s) -{ - if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { - s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; - async_func_free(ctx->rt, &s->func_state); - } -} - -int js_async_generator_completed_return(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value) -{ - JSValue promise, resolving_funcs[2], resolving_funcs1[2]; - int res; - - promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&value, 0); - if (JS_IsException(promise)) - return -1; - if (js_async_generator_resolve_function_create(ctx, - JS_MKPTR(JS_TAG_OBJECT, s->generator), - resolving_funcs1, - TRUE)) { - JS_FreeValue(ctx, promise); - return -1; - } - resolving_funcs[0] = JS_UNDEFINED; - resolving_funcs[1] = JS_UNDEFINED; - res = perform_promise_then(ctx, promise, - (JSValueConst *)resolving_funcs1, - (JSValueConst *)resolving_funcs); - JS_FreeValue(ctx, resolving_funcs1[0]); - JS_FreeValue(ctx, resolving_funcs1[1]); - JS_FreeValue(ctx, promise); - return res; -} - -void js_async_generator_resume_next(JSContext *ctx, - JSAsyncGeneratorData *s) -{ - JSAsyncGeneratorRequest *next; - JSValue func_ret, value; - - for(;;) { - if (list_empty(&s->queue)) - break; - next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); - switch(s->state) { - case JS_ASYNC_GENERATOR_STATE_EXECUTING: - /* only happens when restarting execution after await() */ - goto resume_exec; - case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: - goto done; - case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: - if (next->completion_type == GEN_MAGIC_NEXT) { - goto exec_no_arg; - } else { - js_async_generator_complete(ctx, s); - } - break; - case JS_ASYNC_GENERATOR_STATE_COMPLETED: - if (next->completion_type == GEN_MAGIC_NEXT) { - js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE); - } else if (next->completion_type == GEN_MAGIC_RETURN) { - s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; - js_async_generator_completed_return(ctx, s, next->result); - goto done; - } else { - js_async_generator_reject(ctx, s, next->result); - } - goto done; - case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: - case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: - value = JS_DupValue(ctx, next->result); - if (next->completion_type == GEN_MAGIC_THROW && - s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { - JS_Throw(ctx, value); - s->func_state.throw_flag = TRUE; - } else { - /* 'yield' returns a value. 'yield *' also returns a value - in case the 'throw' method is called */ - s->func_state.frame.cur_sp[-1] = value; - s->func_state.frame.cur_sp[0] = - JS_NewInt32(ctx, next->completion_type); - s->func_state.frame.cur_sp++; - exec_no_arg: - s->func_state.throw_flag = FALSE; - } - s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; - resume_exec: - func_ret = async_func_resume(ctx, &s->func_state); - if (JS_IsException(func_ret)) { - value = JS_GetException(ctx); - js_async_generator_complete(ctx, s); - js_async_generator_reject(ctx, s, value); - JS_FreeValue(ctx, value); - } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { - int func_ret_code; - value = s->func_state.frame.cur_sp[-1]; - s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; - func_ret_code = JS_VALUE_GET_INT(func_ret); - switch(func_ret_code) { - case FUNC_RET_YIELD: - case FUNC_RET_YIELD_STAR: - if (func_ret_code == FUNC_RET_YIELD_STAR) - s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; - else - s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; - js_async_generator_resolve(ctx, s, value, FALSE); - JS_FreeValue(ctx, value); - break; - case FUNC_RET_AWAIT: - js_async_generator_await(ctx, s, value); - JS_FreeValue(ctx, value); - goto done; - default: - abort(); - } - } else { - assert(JS_IsUndefined(func_ret)); - /* end of function */ - value = s->func_state.frame.cur_sp[-1]; - s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; - js_async_generator_complete(ctx, s); - js_async_generator_resolve(ctx, s, value, TRUE); - JS_FreeValue(ctx, value); - } - break; - default: - abort(); - } - } -done: ; -} - -JSValue js_async_generator_resolve_function(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - BOOL is_reject = magic & 1; - JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); - JSValueConst arg = argv[0]; - - /* XXX: what if s == NULL */ - - if (magic >= 2) { - /* resume next case in AWAITING_RETURN state */ - assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || - s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); - s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; - if (is_reject) { - js_async_generator_reject(ctx, s, arg); - } else { - js_async_generator_resolve(ctx, s, arg, TRUE); - } - } else { - /* restart function execution after await() */ - assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); - s->func_state.throw_flag = is_reject; - if (is_reject) { - JS_Throw(ctx, JS_DupValue(ctx, arg)); - } else { - /* return value of await */ - s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg); - } - js_async_generator_resume_next(ctx, s); - } - return JS_UNDEFINED; -} - -/* magic = GEN_MAGIC_x */ -JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); - JSValue promise, resolving_funcs[2]; - JSAsyncGeneratorRequest *req; - - promise = JS_NewPromiseCapability(ctx, resolving_funcs); - if (JS_IsException(promise)) - return JS_EXCEPTION; - if (!s) { - JSValue err, res2; - JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); - err = JS_GetException(ctx); - res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, (JSValueConst *)&err); - JS_FreeValue(ctx, err); - JS_FreeValue(ctx, res2); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; - } - req = js_mallocz(ctx, sizeof(*req)); - if (!req) - goto fail; - req->completion_type = magic; - req->result = JS_DupValue(ctx, argv[0]); - req->promise = JS_DupValue(ctx, promise); - req->resolving_funcs[0] = resolving_funcs[0]; - req->resolving_funcs[1] = resolving_funcs[1]; - list_add_tail(&req->link, &s->queue); - if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { - js_async_generator_resume_next(ctx, s); - } - return promise; -fail: - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - JS_FreeValue(ctx, promise); - return JS_EXCEPTION; -} - -JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags) -{ - JSValue obj, func_ret; - JSAsyncGeneratorData *s; - - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - return JS_EXCEPTION; - s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; - init_list_head(&s->queue); - if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { - s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; - goto fail; - } - - /* execute the function up to 'OP_initial_yield' (no yield nor - await are possible) */ - func_ret = async_func_resume(ctx, &s->func_state); - if (JS_IsException(func_ret)) - goto fail; - JS_FreeValue(ctx, func_ret); - - obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); - if (JS_IsException(obj)) - goto fail; - s->generator = JS_VALUE_GET_OBJ(obj); - JS_SetOpaque(obj, s); - return obj; -fail: - js_async_generator_free(ctx->rt, s); - return JS_EXCEPTION; -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-async-generator.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "js-array.h" +#include "js-async-function.h" +#include "js-generator.h" +#include "js-promise.h" +#include "quickjs/cutils.h" +#include "quickjs/list.h" + +/* AsyncGenerator */ + +void js_async_generator_free(JSRuntime *rt, + JSAsyncGeneratorData *s) +{ + struct list_head *el, *el1; + JSAsyncGeneratorRequest *req; + + list_for_each_safe(el, el1, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_FreeValueRT(rt, req->result); + JS_FreeValueRT(rt, req->promise); + JS_FreeValueRT(rt, req->resolving_funcs[0]); + JS_FreeValueRT(rt, req->resolving_funcs[1]); + js_free_rt(rt, req); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_free(rt, &s->func_state); + } + js_free_rt(rt, s); +} + +void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); + + if (s) { + js_async_generator_free(rt, s); + } +} + +void js_async_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); + struct list_head *el; + JSAsyncGeneratorRequest *req; + if (s) { + list_for_each(el, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_MarkValue(rt, req->result, mark_func); + JS_MarkValue(rt, req->promise, mark_func); + JS_MarkValue(rt, req->resolving_funcs[0], mark_func); + JS_MarkValue(rt, req->resolving_funcs[1], mark_func); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_mark(rt, &s->func_state, mark_func); + } + } +} + +JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValue *func_data); + +int js_async_generator_resolve_function_create(JSContext *ctx, + JSValueConst generator, + JSValue *resolving_funcs, + BOOL is_resume_next) +{ + int i; + JSValue func; + + for(i = 0; i < 2; i++) { + func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, + i + is_resume_next * 2, 1, &generator); + if (JS_IsException(func)) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + resolving_funcs[i] = func; + } + return 0; +} + +int js_async_generator_await(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, &value, 0); + if (JS_IsException(promise)) + goto fail; + + if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs, FALSE)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + (JSValueConst *)resolving_funcs, + (JSValueConst *)resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + return 0; +fail: + return -1; +} + +void js_async_generator_resolve_or_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst result, + int is_reject) +{ + JSAsyncGeneratorRequest *next; + JSValue ret; + + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + list_del(&next->link); + ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, + &result); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, next->result); + JS_FreeValue(ctx, next->promise); + JS_FreeValue(ctx, next->resolving_funcs[0]); + JS_FreeValue(ctx, next->resolving_funcs[1]); + js_free(ctx, next); +} + +void js_async_generator_resolve(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value, + BOOL done) +{ + JSValue result; + result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done); + /* XXX: better exception handling ? */ + js_async_generator_resolve_or_reject(ctx, s, result, 0); + JS_FreeValue(ctx, result); +} + +void js_async_generator_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst exception) +{ + js_async_generator_resolve_or_reject(ctx, s, exception, 1); +} + +void js_async_generator_complete(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + async_func_free(ctx->rt, &s->func_state); + } +} + +int js_async_generator_completed_return(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int res; + + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + if (JS_IsException(promise)) + return -1; + if (js_async_generator_resolve_function_create(ctx, + JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs1, + TRUE)) { + JS_FreeValue(ctx, promise); + return -1; + } + resolving_funcs[0] = JS_UNDEFINED; + resolving_funcs[1] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + (JSValueConst *)resolving_funcs1, + (JSValueConst *)resolving_funcs); + JS_FreeValue(ctx, resolving_funcs1[0]); + JS_FreeValue(ctx, resolving_funcs1[1]); + JS_FreeValue(ctx, promise); + return res; +} + +void js_async_generator_resume_next(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + JSAsyncGeneratorRequest *next; + JSValue func_ret, value; + + for(;;) { + if (list_empty(&s->queue)) + break; + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + switch(s->state) { + case JS_ASYNC_GENERATOR_STATE_EXECUTING: + /* only happens when restarting execution after await() */ + goto resume_exec; + case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: + if (next->completion_type == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + js_async_generator_complete(ctx, s); + } + break; + case JS_ASYNC_GENERATOR_STATE_COMPLETED: + if (next->completion_type == GEN_MAGIC_NEXT) { + js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE); + } else if (next->completion_type == GEN_MAGIC_RETURN) { + s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; + js_async_generator_completed_return(ctx, s, next->result); + goto done; + } else { + js_async_generator_reject(ctx, s, next->result); + } + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + value = JS_DupValue(ctx, next->result); + if (next->completion_type == GEN_MAGIC_THROW && + s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, value); + s->func_state.throw_flag = TRUE; + } else { + /* 'yield' returns a value. 'yield *' also returns a value + in case the 'throw' method is called */ + s->func_state.frame.cur_sp[-1] = value; + s->func_state.frame.cur_sp[0] = + JS_NewInt32(ctx, next->completion_type); + s->func_state.frame.cur_sp++; + exec_no_arg: + s->func_state.throw_flag = FALSE; + } + s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; + resume_exec: + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + value = JS_GetException(ctx); + js_async_generator_complete(ctx, s); + js_async_generator_reject(ctx, s, value); + JS_FreeValue(ctx, value); + } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + int func_ret_code; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + func_ret_code = JS_VALUE_GET_INT(func_ret); + switch(func_ret_code) { + case FUNC_RET_YIELD: + case FUNC_RET_YIELD_STAR: + if (func_ret_code == FUNC_RET_YIELD_STAR) + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + else + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; + js_async_generator_resolve(ctx, s, value, FALSE); + JS_FreeValue(ctx, value); + break; + case FUNC_RET_AWAIT: + js_async_generator_await(ctx, s, value); + JS_FreeValue(ctx, value); + goto done; + default: + abort(); + } + } else { + assert(JS_IsUndefined(func_ret)); + /* end of function */ + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + js_async_generator_complete(ctx, s); + js_async_generator_resolve(ctx, s, value, TRUE); + JS_FreeValue(ctx, value); + } + break; + default: + abort(); + } + } +done: ; +} + +JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + BOOL is_reject = magic & 1; + JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); + JSValueConst arg = argv[0]; + + /* XXX: what if s == NULL */ + + if (magic >= 2) { + /* resume next case in AWAITING_RETURN state */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || + s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + if (is_reject) { + js_async_generator_reject(ctx, s, arg); + } else { + js_async_generator_resolve(ctx, s, arg, TRUE); + } + } else { + /* restart function execution after await() */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, JS_DupValue(ctx, arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg); + } + js_async_generator_resume_next(ctx, s); + } + return JS_UNDEFINED; +} + +/* magic = GEN_MAGIC_x */ +JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); + JSValue promise, resolving_funcs[2]; + JSAsyncGeneratorRequest *req; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + if (!s) { + JSValue err, res2; + JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); + err = JS_GetException(ctx); + res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + req = js_mallocz(ctx, sizeof(*req)); + if (!req) + goto fail; + req->completion_type = magic; + req->result = JS_DupValue(ctx, argv[0]); + req->promise = JS_DupValue(ctx, promise); + req->resolving_funcs[0] = resolving_funcs[0]; + req->resolving_funcs[1] = resolving_funcs[1]; + list_add_tail(&req->link, &s->queue); + if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { + js_async_generator_resume_next(ctx, s); + } + return promise; +fail: + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSAsyncGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; + init_list_head(&s->queue); + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' (no yield nor + await are possible) */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); + if (JS_IsException(obj)) + goto fail; + s->generator = JS_VALUE_GET_OBJ(obj); + JS_SetOpaque(obj, s); + return obj; +fail: + js_async_generator_free(ctx->rt, s); + return JS_EXCEPTION; +} diff --git a/src/core/builtins/js-async-generator.h b/src/core/builtins/js-async-generator.h index a10edcfc9..d83e5dd6a 100644 --- a/src/core/builtins/js-async-generator.h +++ b/src/core/builtins/js-async-generator.h @@ -1,107 +1,107 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_ASYNC_GENERATOR_H -#define QUICKJS_JS_ASYNC_GENERATOR_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "../types.h" - - -typedef enum JSAsyncGeneratorStateEnum { - JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, - JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, - JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, - JS_ASYNC_GENERATOR_STATE_EXECUTING, - JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, - JS_ASYNC_GENERATOR_STATE_COMPLETED, -} JSAsyncGeneratorStateEnum; - -typedef struct JSAsyncGeneratorRequest { - struct list_head link; - /* completion */ - int completion_type; /* GEN_MAGIC_x */ - JSValue result; - /* promise capability */ - JSValue promise; - JSValue resolving_funcs[2]; -} JSAsyncGeneratorRequest; - -typedef struct JSAsyncGeneratorData { - JSObject *generator; /* back pointer to the object (const) */ - JSAsyncGeneratorStateEnum state; - JSAsyncFunctionState func_state; - struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ -} JSAsyncGeneratorData; - -void js_async_generator_free(JSRuntime *rt, - JSAsyncGeneratorData *s); -void js_async_generator_finalizer(JSRuntime *rt, JSValue obj); -void js_async_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); - -int js_async_generator_resolve_function_create(JSContext *ctx, - JSValueConst generator, - JSValue *resolving_funcs, - BOOL is_resume_next); -int js_async_generator_await(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value); -void js_async_generator_resolve_or_reject(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst result, - int is_reject); - -void js_async_generator_resolve(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value, - BOOL done); - -void js_async_generator_reject(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst exception); - -void js_async_generator_complete(JSContext *ctx, - JSAsyncGeneratorData *s); - -int js_async_generator_completed_return(JSContext *ctx, - JSAsyncGeneratorData *s, - JSValueConst value); - -void js_async_generator_resume_next(JSContext *ctx, - JSAsyncGeneratorData *s); -/* magic = GEN_MAGIC_x */ -JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic); - -JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags); - - -#endif +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_ASYNC_GENERATOR_H +#define QUICKJS_JS_ASYNC_GENERATOR_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "../types.h" + + +typedef enum JSAsyncGeneratorStateEnum { + JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_ASYNC_GENERATOR_STATE_EXECUTING, + JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, + JS_ASYNC_GENERATOR_STATE_COMPLETED, +} JSAsyncGeneratorStateEnum; + +typedef struct JSAsyncGeneratorRequest { + struct list_head link; + /* completion */ + int completion_type; /* GEN_MAGIC_x */ + JSValue result; + /* promise capability */ + JSValue promise; + JSValue resolving_funcs[2]; +} JSAsyncGeneratorRequest; + +typedef struct JSAsyncGeneratorData { + JSObject *generator; /* back pointer to the object (const) */ + JSAsyncGeneratorStateEnum state; + JSAsyncFunctionState func_state; + struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ +} JSAsyncGeneratorData; + +void js_async_generator_free(JSRuntime *rt, + JSAsyncGeneratorData *s); +void js_async_generator_finalizer(JSRuntime *rt, JSValue obj); +void js_async_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); + +int js_async_generator_resolve_function_create(JSContext *ctx, + JSValueConst generator, + JSValue *resolving_funcs, + BOOL is_resume_next); +int js_async_generator_await(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value); +void js_async_generator_resolve_or_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst result, + int is_reject); + +void js_async_generator_resolve(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value, + BOOL done); + +void js_async_generator_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst exception); + +void js_async_generator_complete(JSContext *ctx, + JSAsyncGeneratorData *s); + +int js_async_generator_completed_return(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValueConst value); + +void js_async_generator_resume_next(JSContext *ctx, + JSAsyncGeneratorData *s); +/* magic = GEN_MAGIC_x */ +JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic); + +JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags); + + +#endif diff --git a/src/core/builtins/js-atomics.c b/src/core/builtins/js-atomics.c index d024346bf..36423df69 100644 --- a/src/core/builtins/js-atomics.c +++ b/src/core/builtins/js-atomics.c @@ -1,527 +1,527 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-atomics.h" -#include "../convertion.h" -#include "../exception.h" -#include "js-typed-array.h" - -#if CONFIG_BIGNUM -#include "js-big-num.h" -#endif - -/* Atomics */ -#ifdef CONFIG_ATOMICS - -typedef enum AtomicsOpEnum { - ATOMICS_OP_ADD, - ATOMICS_OP_AND, - ATOMICS_OP_OR, - ATOMICS_OP_SUB, - ATOMICS_OP_XOR, - ATOMICS_OP_EXCHANGE, - ATOMICS_OP_COMPARE_EXCHANGE, - ATOMICS_OP_LOAD, -} AtomicsOpEnum; - -void *js_atomics_get_ptr(JSContext *ctx, - JSArrayBuffer **pabuf, - int *psize_log2, JSClassID *pclass_id, - JSValueConst obj, JSValueConst idx_val, - int is_waitable) -{ - JSObject *p; - JSTypedArray *ta; - JSArrayBuffer *abuf; - void *ptr; - uint64_t idx; - BOOL err; - int size_log2; - - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - goto fail; - p = JS_VALUE_GET_OBJ(obj); -#ifdef CONFIG_BIGNUM - if (is_waitable) - err = (p->class_id != JS_CLASS_INT32_ARRAY && - p->class_id != JS_CLASS_BIG_INT64_ARRAY); - else - err = !(p->class_id >= JS_CLASS_INT8_ARRAY && - p->class_id <= JS_CLASS_BIG_UINT64_ARRAY); -#else - if (is_waitable) - err = (p->class_id != JS_CLASS_INT32_ARRAY); - else - err = !(p->class_id >= JS_CLASS_INT8_ARRAY && - p->class_id <= JS_CLASS_UINT32_ARRAY); -#endif - if (err) { - fail: - JS_ThrowTypeError(ctx, "integer TypedArray expected"); - return NULL; - } - ta = p->u.typed_array; - abuf = ta->buffer->u.array_buffer; - if (!abuf->shared) { - if (is_waitable == 2) { - JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); - return NULL; - } - if (abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return NULL; - } - } - if (JS_ToIndex(ctx, &idx, idx_val)) { - return NULL; - } - /* if the array buffer is detached, p->u.array.count = 0 */ - if (idx >= p->u.array.count) { - JS_ThrowRangeError(ctx, "out-of-bound access"); - return NULL; - } - size_log2 = typed_array_size_log2(p->class_id); - ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); - if (pabuf) - *pabuf = abuf; - if (psize_log2) - *psize_log2 = size_log2; - if (pclass_id) - *pclass_id = p->class_id; - return ptr; -} - -JSValue js_atomics_op(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv, int op) -{ - int size_log2; -#ifdef CONFIG_BIGNUM - uint64_t v, a, rep_val; -#else - uint32_t v, a, rep_val; -#endif - void *ptr; - JSValue ret; - JSClassID class_id; - JSArrayBuffer *abuf; - - ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id, - argv[0], argv[1], 0); - if (!ptr) - return JS_EXCEPTION; - rep_val = 0; - if (op == ATOMICS_OP_LOAD) { - v = 0; - } else { -#ifdef CONFIG_BIGNUM - if (size_log2 == 3) { - int64_t v64; - if (JS_ToBigInt64(ctx, &v64, argv[2])) - return JS_EXCEPTION; - v = v64; - if (op == ATOMICS_OP_COMPARE_EXCHANGE) { - if (JS_ToBigInt64(ctx, &v64, argv[3])) - return JS_EXCEPTION; - rep_val = v64; - } - } else -#endif - { - uint32_t v32; - if (JS_ToUint32(ctx, &v32, argv[2])) - return JS_EXCEPTION; - v = v32; - if (op == ATOMICS_OP_COMPARE_EXCHANGE) { - if (JS_ToUint32(ctx, &v32, argv[3])) - return JS_EXCEPTION; - rep_val = v32; - } - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - } - - switch(op | (size_log2 << 3)) { - -#ifdef CONFIG_BIGNUM -#define OP(op_name, func_name) \ - case ATOMICS_OP_ ## op_name | (0 << 3): \ - a = func_name((_Atomic(uint8_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (1 << 3): \ - a = func_name((_Atomic(uint16_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (2 << 3): \ - a = func_name((_Atomic(uint32_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (3 << 3): \ - a = func_name((_Atomic(uint64_t) *)ptr, v); \ - break; -#else -#define OP(op_name, func_name) \ - case ATOMICS_OP_ ## op_name | (0 << 3): \ - a = func_name((_Atomic(uint8_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (1 << 3): \ - a = func_name((_Atomic(uint16_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (2 << 3): \ - a = func_name((_Atomic(uint32_t) *)ptr, v); \ - break; -#endif - OP(ADD, atomic_fetch_add) - OP(AND, atomic_fetch_and) - OP(OR, atomic_fetch_or) - OP(SUB, atomic_fetch_sub) - OP(XOR, atomic_fetch_xor) - OP(EXCHANGE, atomic_exchange) -#undef OP - - case ATOMICS_OP_LOAD | (0 << 3): - a = atomic_load((_Atomic(uint8_t) *)ptr); - break; - case ATOMICS_OP_LOAD | (1 << 3): - a = atomic_load((_Atomic(uint16_t) *)ptr); - break; - case ATOMICS_OP_LOAD | (2 << 3): - a = atomic_load((_Atomic(uint32_t) *)ptr); - break; -#ifdef CONFIG_BIGNUM - case ATOMICS_OP_LOAD | (3 << 3): - a = atomic_load((_Atomic(uint64_t) *)ptr); - break; -#endif - - case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3): - { - uint8_t v1 = v; - atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val); - a = v1; - } - break; - case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3): - { - uint16_t v1 = v; - atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val); - a = v1; - } - break; - case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3): - { - uint32_t v1 = v; - atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val); - a = v1; - } - break; -#ifdef CONFIG_BIGNUM - case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3): - { - uint64_t v1 = v; - atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val); - a = v1; - } - break; -#endif - default: - abort(); - } - - switch(class_id) { - case JS_CLASS_INT8_ARRAY: - a = (int8_t)a; - goto done; - case JS_CLASS_UINT8_ARRAY: - a = (uint8_t)a; - goto done; - case JS_CLASS_INT16_ARRAY: - a = (int16_t)a; - goto done; - case JS_CLASS_UINT16_ARRAY: - a = (uint16_t)a; - goto done; - case JS_CLASS_INT32_ARRAY: - done: - ret = JS_NewInt32(ctx, a); - break; - case JS_CLASS_UINT32_ARRAY: - ret = JS_NewUint32(ctx, a); - break; -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT64_ARRAY: - ret = JS_NewBigInt64(ctx, a); - break; - case JS_CLASS_BIG_UINT64_ARRAY: - ret = JS_NewBigUint64(ctx, a); - break; -#endif - default: - abort(); - } - return ret; -} - -JSValue js_atomics_store(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - int size_log2; - void *ptr; - JSValue ret; - JSArrayBuffer *abuf; - - ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL, - argv[0], argv[1], 0); - if (!ptr) - return JS_EXCEPTION; -#ifdef CONFIG_BIGNUM - if (size_log2 == 3) { - int64_t v64; - ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2])); - if (JS_IsException(ret)) - return ret; - if (JS_ToBigInt64(ctx, &v64, ret)) { - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - atomic_store((_Atomic(uint64_t) *)ptr, v64); - } else -#endif - { - uint32_t v; - /* XXX: spec, would be simpler to return the written value */ - ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2])); - if (JS_IsException(ret)) - return ret; - if (JS_ToUint32(ctx, &v, ret)) { - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - switch(size_log2) { - case 0: - atomic_store((_Atomic(uint8_t) *)ptr, v); - break; - case 1: - atomic_store((_Atomic(uint16_t) *)ptr, v); - break; - case 2: - atomic_store((_Atomic(uint32_t) *)ptr, v); - break; - default: - abort(); - } - } - return ret; -} - -JSValue js_atomics_isLockFree(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - int v, ret; - if (JS_ToInt32Sat(ctx, &v, argv[0])) - return JS_EXCEPTION; - ret = (v == 1 || v == 2 || v == 4 -#ifdef CONFIG_BIGNUM - || v == 8 -#endif - ); - return JS_NewBool(ctx, ret); -} - -typedef struct JSAtomicsWaiter { - struct list_head link; - BOOL linked; - pthread_cond_t cond; - int32_t *ptr; -} JSAtomicsWaiter; - -pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER; -struct list_head js_atomics_waiter_list = - LIST_HEAD_INIT(js_atomics_waiter_list); - -JSValue js_atomics_wait(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - int64_t v; - int32_t v32; - void *ptr; - int64_t timeout; - struct timespec ts; - JSAtomicsWaiter waiter_s, *waiter; - int ret, size_log2, res; - double d; - - ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL, - argv[0], argv[1], 2); - if (!ptr) - return JS_EXCEPTION; -#ifdef CONFIG_BIGNUM - if (size_log2 == 3) { - if (JS_ToBigInt64(ctx, &v, argv[2])) - return JS_EXCEPTION; - } else -#endif - { - if (JS_ToInt32(ctx, &v32, argv[2])) - return JS_EXCEPTION; - v = v32; - } - if (JS_ToFloat64(ctx, &d, argv[3])) - return JS_EXCEPTION; - if (isnan(d) || d > INT64_MAX) - timeout = INT64_MAX; - else if (d < 0) - timeout = 0; - else - timeout = (int64_t)d; - if (!ctx->rt->can_block) - return JS_ThrowTypeError(ctx, "cannot block in this thread"); - - /* XXX: inefficient if large number of waiters, should hash on - 'ptr' value */ - /* XXX: use Linux futexes when available ? */ - pthread_mutex_lock(&js_atomics_mutex); - if (size_log2 == 3) { - res = *(int64_t *)ptr != v; - } else { - res = *(int32_t *)ptr != v; - } - if (res) { - pthread_mutex_unlock(&js_atomics_mutex); - return JS_AtomToString(ctx, JS_ATOM_not_equal); - } - - waiter = &waiter_s; - waiter->ptr = ptr; - pthread_cond_init(&waiter->cond, NULL); - waiter->linked = TRUE; - list_add_tail(&waiter->link, &js_atomics_waiter_list); - - if (timeout == INT64_MAX) { - pthread_cond_wait(&waiter->cond, &js_atomics_mutex); - ret = 0; - } else { - /* XXX: use clock monotonic */ - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += timeout / 1000; - ts.tv_nsec += (timeout % 1000) * 1000000; - if (ts.tv_nsec >= 1000000000) { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex, - &ts); - } - if (waiter->linked) - list_del(&waiter->link); - pthread_mutex_unlock(&js_atomics_mutex); - pthread_cond_destroy(&waiter->cond); - if (ret == ETIMEDOUT) { - return JS_AtomToString(ctx, JS_ATOM_timed_out); - } else { - return JS_AtomToString(ctx, JS_ATOM_ok); - } -} - -JSValue js_atomics_notify(JSContext *ctx, - JSValueConst this_obj, - int argc, JSValueConst *argv) -{ - struct list_head *el, *el1, waiter_list; - int32_t count, n; - void *ptr; - JSAtomicsWaiter *waiter; - JSArrayBuffer *abuf; - - ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1); - if (!ptr) - return JS_EXCEPTION; - - if (JS_IsUndefined(argv[2])) { - count = INT32_MAX; - } else { - if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0)) - return JS_EXCEPTION; - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - - n = 0; - if (abuf->shared && count > 0) { - pthread_mutex_lock(&js_atomics_mutex); - init_list_head(&waiter_list); - list_for_each_safe(el, el1, &js_atomics_waiter_list) { - waiter = list_entry(el, JSAtomicsWaiter, link); - if (waiter->ptr == ptr) { - list_del(&waiter->link); - waiter->linked = FALSE; - list_add_tail(&waiter->link, &waiter_list); - n++; - if (n >= count) - break; - } - } - list_for_each(el, &waiter_list) { - waiter = list_entry(el, JSAtomicsWaiter, link); - pthread_cond_signal(&waiter->cond); - } - pthread_mutex_unlock(&js_atomics_mutex); - } - return JS_NewInt32(ctx, n); -} - -const JSCFunctionListEntry js_atomics_funcs[] = { - JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ), - JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ), - JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ), - JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ), - JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ), - JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ), - JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ), - JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ), - JS_CFUNC_DEF("store", 3, js_atomics_store ), - JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ), - JS_CFUNC_DEF("wait", 4, js_atomics_wait ), - JS_CFUNC_DEF("notify", 3, js_atomics_notify ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ), -}; - -const JSCFunctionListEntry js_atomics_obj[] = { - JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), -}; - -void JS_AddIntrinsicAtomics(JSContext *ctx) -{ - /* add Atomics as autoinit object */ - JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj)); -} - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-atomics.h" +#include "../convertion.h" +#include "../exception.h" +#include "js-typed-array.h" + +#if CONFIG_BIGNUM +#include "js-big-num.h" +#endif + +/* Atomics */ +#ifdef CONFIG_ATOMICS + +typedef enum AtomicsOpEnum { + ATOMICS_OP_ADD, + ATOMICS_OP_AND, + ATOMICS_OP_OR, + ATOMICS_OP_SUB, + ATOMICS_OP_XOR, + ATOMICS_OP_EXCHANGE, + ATOMICS_OP_COMPARE_EXCHANGE, + ATOMICS_OP_LOAD, +} AtomicsOpEnum; + +void *js_atomics_get_ptr(JSContext *ctx, + JSArrayBuffer **pabuf, + int *psize_log2, JSClassID *pclass_id, + JSValueConst obj, JSValueConst idx_val, + int is_waitable) +{ + JSObject *p; + JSTypedArray *ta; + JSArrayBuffer *abuf; + void *ptr; + uint64_t idx; + BOOL err; + int size_log2; + + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(obj); +#ifdef CONFIG_BIGNUM + if (is_waitable) + err = (p->class_id != JS_CLASS_INT32_ARRAY && + p->class_id != JS_CLASS_BIG_INT64_ARRAY); + else + err = !(p->class_id >= JS_CLASS_INT8_ARRAY && + p->class_id <= JS_CLASS_BIG_UINT64_ARRAY); +#else + if (is_waitable) + err = (p->class_id != JS_CLASS_INT32_ARRAY); + else + err = !(p->class_id >= JS_CLASS_INT8_ARRAY && + p->class_id <= JS_CLASS_UINT32_ARRAY); +#endif + if (err) { + fail: + JS_ThrowTypeError(ctx, "integer TypedArray expected"); + return NULL; + } + ta = p->u.typed_array; + abuf = ta->buffer->u.array_buffer; + if (!abuf->shared) { + if (is_waitable == 2) { + JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); + return NULL; + } + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return NULL; + } + } + if (JS_ToIndex(ctx, &idx, idx_val)) { + return NULL; + } + /* if the array buffer is detached, p->u.array.count = 0 */ + if (idx >= p->u.array.count) { + JS_ThrowRangeError(ctx, "out-of-bound access"); + return NULL; + } + size_log2 = typed_array_size_log2(p->class_id); + ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); + if (pabuf) + *pabuf = abuf; + if (psize_log2) + *psize_log2 = size_log2; + if (pclass_id) + *pclass_id = p->class_id; + return ptr; +} + +JSValue js_atomics_op(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv, int op) +{ + int size_log2; +#ifdef CONFIG_BIGNUM + uint64_t v, a, rep_val; +#else + uint32_t v, a, rep_val; +#endif + void *ptr; + JSValue ret; + JSClassID class_id; + JSArrayBuffer *abuf; + + ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id, + argv[0], argv[1], 0); + if (!ptr) + return JS_EXCEPTION; + rep_val = 0; + if (op == ATOMICS_OP_LOAD) { + v = 0; + } else { +#ifdef CONFIG_BIGNUM + if (size_log2 == 3) { + int64_t v64; + if (JS_ToBigInt64(ctx, &v64, argv[2])) + return JS_EXCEPTION; + v = v64; + if (op == ATOMICS_OP_COMPARE_EXCHANGE) { + if (JS_ToBigInt64(ctx, &v64, argv[3])) + return JS_EXCEPTION; + rep_val = v64; + } + } else +#endif + { + uint32_t v32; + if (JS_ToUint32(ctx, &v32, argv[2])) + return JS_EXCEPTION; + v = v32; + if (op == ATOMICS_OP_COMPARE_EXCHANGE) { + if (JS_ToUint32(ctx, &v32, argv[3])) + return JS_EXCEPTION; + rep_val = v32; + } + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + } + + switch(op | (size_log2 << 3)) { + +#ifdef CONFIG_BIGNUM +#define OP(op_name, func_name) \ + case ATOMICS_OP_ ## op_name | (0 << 3): \ + a = func_name((_Atomic(uint8_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (1 << 3): \ + a = func_name((_Atomic(uint16_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (2 << 3): \ + a = func_name((_Atomic(uint32_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (3 << 3): \ + a = func_name((_Atomic(uint64_t) *)ptr, v); \ + break; +#else +#define OP(op_name, func_name) \ + case ATOMICS_OP_ ## op_name | (0 << 3): \ + a = func_name((_Atomic(uint8_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (1 << 3): \ + a = func_name((_Atomic(uint16_t) *)ptr, v); \ + break; \ + case ATOMICS_OP_ ## op_name | (2 << 3): \ + a = func_name((_Atomic(uint32_t) *)ptr, v); \ + break; +#endif + OP(ADD, atomic_fetch_add) + OP(AND, atomic_fetch_and) + OP(OR, atomic_fetch_or) + OP(SUB, atomic_fetch_sub) + OP(XOR, atomic_fetch_xor) + OP(EXCHANGE, atomic_exchange) +#undef OP + + case ATOMICS_OP_LOAD | (0 << 3): + a = atomic_load((_Atomic(uint8_t) *)ptr); + break; + case ATOMICS_OP_LOAD | (1 << 3): + a = atomic_load((_Atomic(uint16_t) *)ptr); + break; + case ATOMICS_OP_LOAD | (2 << 3): + a = atomic_load((_Atomic(uint32_t) *)ptr); + break; +#ifdef CONFIG_BIGNUM + case ATOMICS_OP_LOAD | (3 << 3): + a = atomic_load((_Atomic(uint64_t) *)ptr); + break; +#endif + + case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3): + { + uint8_t v1 = v; + atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val); + a = v1; + } + break; + case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3): + { + uint16_t v1 = v; + atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val); + a = v1; + } + break; + case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3): + { + uint32_t v1 = v; + atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val); + a = v1; + } + break; +#ifdef CONFIG_BIGNUM + case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3): + { + uint64_t v1 = v; + atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val); + a = v1; + } + break; +#endif + default: + abort(); + } + + switch(class_id) { + case JS_CLASS_INT8_ARRAY: + a = (int8_t)a; + goto done; + case JS_CLASS_UINT8_ARRAY: + a = (uint8_t)a; + goto done; + case JS_CLASS_INT16_ARRAY: + a = (int16_t)a; + goto done; + case JS_CLASS_UINT16_ARRAY: + a = (uint16_t)a; + goto done; + case JS_CLASS_INT32_ARRAY: + done: + ret = JS_NewInt32(ctx, a); + break; + case JS_CLASS_UINT32_ARRAY: + ret = JS_NewUint32(ctx, a); + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + ret = JS_NewBigInt64(ctx, a); + break; + case JS_CLASS_BIG_UINT64_ARRAY: + ret = JS_NewBigUint64(ctx, a); + break; +#endif + default: + abort(); + } + return ret; +} + +JSValue js_atomics_store(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + int size_log2; + void *ptr; + JSValue ret; + JSArrayBuffer *abuf; + + ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL, + argv[0], argv[1], 0); + if (!ptr) + return JS_EXCEPTION; +#ifdef CONFIG_BIGNUM + if (size_log2 == 3) { + int64_t v64; + ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2])); + if (JS_IsException(ret)) + return ret; + if (JS_ToBigInt64(ctx, &v64, ret)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + atomic_store((_Atomic(uint64_t) *)ptr, v64); + } else +#endif + { + uint32_t v; + /* XXX: spec, would be simpler to return the written value */ + ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2])); + if (JS_IsException(ret)) + return ret; + if (JS_ToUint32(ctx, &v, ret)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + switch(size_log2) { + case 0: + atomic_store((_Atomic(uint8_t) *)ptr, v); + break; + case 1: + atomic_store((_Atomic(uint16_t) *)ptr, v); + break; + case 2: + atomic_store((_Atomic(uint32_t) *)ptr, v); + break; + default: + abort(); + } + } + return ret; +} + +JSValue js_atomics_isLockFree(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + int v, ret; + if (JS_ToInt32Sat(ctx, &v, argv[0])) + return JS_EXCEPTION; + ret = (v == 1 || v == 2 || v == 4 +#ifdef CONFIG_BIGNUM + || v == 8 +#endif + ); + return JS_NewBool(ctx, ret); +} + +typedef struct JSAtomicsWaiter { + struct list_head link; + BOOL linked; + pthread_cond_t cond; + int32_t *ptr; +} JSAtomicsWaiter; + +pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER; +struct list_head js_atomics_waiter_list = + LIST_HEAD_INIT(js_atomics_waiter_list); + +JSValue js_atomics_wait(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + int64_t v; + int32_t v32; + void *ptr; + int64_t timeout; + struct timespec ts; + JSAtomicsWaiter waiter_s, *waiter; + int ret, size_log2, res; + double d; + + ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL, + argv[0], argv[1], 2); + if (!ptr) + return JS_EXCEPTION; +#ifdef CONFIG_BIGNUM + if (size_log2 == 3) { + if (JS_ToBigInt64(ctx, &v, argv[2])) + return JS_EXCEPTION; + } else +#endif + { + if (JS_ToInt32(ctx, &v32, argv[2])) + return JS_EXCEPTION; + v = v32; + } + if (JS_ToFloat64(ctx, &d, argv[3])) + return JS_EXCEPTION; + if (isnan(d) || d > INT64_MAX) + timeout = INT64_MAX; + else if (d < 0) + timeout = 0; + else + timeout = (int64_t)d; + if (!ctx->rt->can_block) + return JS_ThrowTypeError(ctx, "cannot block in this thread"); + + /* XXX: inefficient if large number of waiters, should hash on + 'ptr' value */ + /* XXX: use Linux futexes when available ? */ + pthread_mutex_lock(&js_atomics_mutex); + if (size_log2 == 3) { + res = *(int64_t *)ptr != v; + } else { + res = *(int32_t *)ptr != v; + } + if (res) { + pthread_mutex_unlock(&js_atomics_mutex); + return JS_AtomToString(ctx, JS_ATOM_not_equal); + } + + waiter = &waiter_s; + waiter->ptr = ptr; + pthread_cond_init(&waiter->cond, NULL); + waiter->linked = TRUE; + list_add_tail(&waiter->link, &js_atomics_waiter_list); + + if (timeout == INT64_MAX) { + pthread_cond_wait(&waiter->cond, &js_atomics_mutex); + ret = 0; + } else { + /* XXX: use clock monotonic */ + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += timeout / 1000; + ts.tv_nsec += (timeout % 1000) * 1000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex, + &ts); + } + if (waiter->linked) + list_del(&waiter->link); + pthread_mutex_unlock(&js_atomics_mutex); + pthread_cond_destroy(&waiter->cond); + if (ret == ETIMEDOUT) { + return JS_AtomToString(ctx, JS_ATOM_timed_out); + } else { + return JS_AtomToString(ctx, JS_ATOM_ok); + } +} + +JSValue js_atomics_notify(JSContext *ctx, + JSValueConst this_obj, + int argc, JSValueConst *argv) +{ + struct list_head *el, *el1, waiter_list; + int32_t count, n; + void *ptr; + JSAtomicsWaiter *waiter; + JSArrayBuffer *abuf; + + ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1); + if (!ptr) + return JS_EXCEPTION; + + if (JS_IsUndefined(argv[2])) { + count = INT32_MAX; + } else { + if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0)) + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + + n = 0; + if (abuf->shared && count > 0) { + pthread_mutex_lock(&js_atomics_mutex); + init_list_head(&waiter_list); + list_for_each_safe(el, el1, &js_atomics_waiter_list) { + waiter = list_entry(el, JSAtomicsWaiter, link); + if (waiter->ptr == ptr) { + list_del(&waiter->link); + waiter->linked = FALSE; + list_add_tail(&waiter->link, &waiter_list); + n++; + if (n >= count) + break; + } + } + list_for_each(el, &waiter_list) { + waiter = list_entry(el, JSAtomicsWaiter, link); + pthread_cond_signal(&waiter->cond); + } + pthread_mutex_unlock(&js_atomics_mutex); + } + return JS_NewInt32(ctx, n); +} + +const JSCFunctionListEntry js_atomics_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ), + JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ), + JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ), + JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ), + JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ), + JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ), + JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ), + JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ), + JS_CFUNC_DEF("store", 3, js_atomics_store ), + JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ), + JS_CFUNC_DEF("wait", 4, js_atomics_wait ), + JS_CFUNC_DEF("notify", 3, js_atomics_notify ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ), +}; + +const JSCFunctionListEntry js_atomics_obj[] = { + JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicAtomics(JSContext *ctx) +{ + /* add Atomics as autoinit object */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj)); +} + #endif /* CONFIG_ATOMICS */ \ No newline at end of file diff --git a/src/core/builtins/js-atomics.h b/src/core/builtins/js-atomics.h index cb5c3e265..2aa10eaa5 100644 --- a/src/core/builtins/js-atomics.h +++ b/src/core/builtins/js-atomics.h @@ -1,36 +1,36 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_ATOMICS_H -#define QUICKJS_JS_ATOMICS_H - -#include "quickjs/quickjs.h" - -#ifdef CONFIG_ATOMICS -void JS_AddIntrinsicAtomics(JSContext *ctx); -#endif - - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_ATOMICS_H +#define QUICKJS_JS_ATOMICS_H + +#include "quickjs/quickjs.h" + +#ifdef CONFIG_ATOMICS +void JS_AddIntrinsicAtomics(JSContext *ctx); +#endif + + #endif \ No newline at end of file diff --git a/src/core/builtins/js-big-num.c b/src/core/builtins/js-big-num.c index 1f96b6bc4..41c2c6d4d 100644 --- a/src/core/builtins/js-big-num.c +++ b/src/core/builtins/js-big-num.c @@ -1,4637 +1,4637 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-big-num.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "js-number.h" -#include "js-operator.h" - -#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) -{ - if (is_math_mode(ctx) && - v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { - return JS_NewInt64(ctx, v); - } else { - return JS_NewBigInt64_1(ctx, v); - } -} - -JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) -{ - JSValue val; - if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { - val = JS_NewInt64(ctx, v); - } else { - bf_t *a; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - if (bf_set_ui(a, v)) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - } - return val; -} - -/* if the returned bigfloat is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return - NULL in case of error. */ -bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val) -{ - uint32_t tag; - bf_t *r; - JSBigFloat *p; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_si(r, JS_VALUE_GET_INT(val))) - goto fail; - break; - case JS_TAG_FLOAT64: - r = buf; - bf_init(ctx->bf_ctx, r); - if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { - fail: - bf_delete(r); - return NULL; - } - break; - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; - break; - case JS_TAG_UNDEFINED: - default: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_nan(r); - break; - } - return r; -} - -/* return NULL if invalid type */ -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 */ -JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) -{ - const char *str, *p; - size_t len; - int flags; - - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - val = JS_NewBigInt64(ctx, 0); - } else { - flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; - 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; -} - -JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) -{ - val = JS_StringToBigInt(ctx, val); - if (JS_VALUE_IS_NAN(val)) - return JS_ThrowSyntaxError(ctx, "invalid bigint literal"); - return val; -} - -/* if the returned bigfloat is allocated it is equal to - 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */ -bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) -{ - uint32_t tag; - bf_t *r; - JSBigFloat *p; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - if (!is_math_mode(ctx)) - goto fail; - /* fall tru */ - case JS_TAG_BOOL: - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set_si(r, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_FLOAT64: - { - double d = JS_VALUE_GET_FLOAT64(val); - if (!is_math_mode(ctx)) - goto fail; - if (!isfinite(d)) - goto fail; - r = buf; - bf_init(ctx->bf_ctx, r); - d = trunc(d); - bf_set_float64(r, d); - } - break; - case JS_TAG_BIG_INT: - p = JS_VALUE_GET_PTR(val); - r = &p->num; - break; - case JS_TAG_BIG_FLOAT: - if (!is_math_mode(ctx)) - goto fail; - p = JS_VALUE_GET_PTR(val); - if (!bf_is_finite(&p->num)) - goto fail; - r = buf; - bf_init(ctx->bf_ctx, r); - bf_set(r, &p->num); - bf_rint(r, BF_RNDZ); - JS_FreeValue(ctx, val); - break; - case JS_TAG_STRING: - val = JS_StringToBigIntErr(ctx, val); - if (JS_IsException(val)) - return NULL; - goto redo; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - return NULL; - goto redo; - default: - fail: - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "cannot convert to bigint"); - return NULL; - } - return r; -} - -bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val) -{ - return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val)); -} - -__maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) -{ - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { - return val; - } else { - bf_t a_s, *a, *r; - int ret; - JSValue res; - - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - return JS_EXCEPTION; - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - r = JS_GetBigInt(res); - ret = bf_set(r, a); - JS_FreeBigInt(ctx, a, &a_s); - if (ret) { - JS_FreeValue(ctx, res); - return JS_ThrowOutOfMemory(ctx); - } - return JS_CompactBigInt(ctx, res); - } -} - -/* free the bf_t allocated by JS_ToBigInt */ -void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) -{ - if (a == buf) { - bf_delete(a); - } else { - JSBigFloat *p = (JSBigFloat *)((uint8_t *)a - - offsetof(JSBigFloat, num)); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p)); - } -} - -/* XXX: merge with JS_ToInt64Free with a specific flag */ -int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) -{ - bf_t a_s, *a; - - a = JS_ToBigIntFree(ctx, &a_s, val); - if (!a) { - *pres = 0; - return -1; - } - bf_get_int64(pres, a, BF_GET_INT_MOD); - JS_FreeBigInt(ctx, a, &a_s); - return 0; -} - -int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) -{ - return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val)); -} - -JSBigFloat *js_new_bf(JSContext *ctx) -{ - JSBigFloat *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return NULL; - p->header.ref_count = 1; - bf_init(ctx->bf_ctx, &p->num); - return p; -} - -JSValue JS_NewBigFloat(JSContext *ctx) -{ - JSBigFloat *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return JS_EXCEPTION; - p->header.ref_count = 1; - bf_init(ctx->bf_ctx, &p->num); - return JS_MKPTR(JS_TAG_BIG_FLOAT, p); -} - -JSValue JS_NewBigDecimal(JSContext *ctx) -{ - JSBigDecimal *p; - p = js_malloc(ctx, sizeof(*p)); - if (!p) - return JS_EXCEPTION; - p->header.ref_count = 1; - bfdec_init(ctx->bf_ctx, &p->num); - return JS_MKPTR(JS_TAG_BIG_DECIMAL, p); -} - -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); -} - -JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, - BOOL convert_to_safe_integer) -{ - int64_t v; - bf_t *a; - - if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) - return val; /* fail safe */ - a = JS_GetBigInt(val); - if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 && - v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { - JS_FreeValue(ctx, val); - return JS_NewInt64(ctx, v); - } else if (a->expn == BF_EXP_ZERO && a->sign) { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - assert(p->header.ref_count == 1); - a->sign = 0; - } - return val; -} - -/* Convert the big int to a safe integer if in math mode. normalize - the zero representation. Could also be used to convert the bigint - to a short bigint value. The reference count of the value must be - 1. Cannot fail */ -JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) -{ - return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); -} - -/* must be kept in sync with JSOverloadableOperatorEnum */ -/* XXX: use atoms ? */ -const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = { - "+", - "-", - "*", - "/", - "%", - "**", - "|", - "&", - "^", - "<<", - ">>", - ">>>", - "==", - "<", - "pos", - "neg", - "++", - "--", - "~", -}; - -int get_ovop_from_opcode(OPCodeEnum op) -{ - switch(op) { - case OP_add: - return JS_OVOP_ADD; - case OP_sub: - return JS_OVOP_SUB; - case OP_mul: - return JS_OVOP_MUL; - case OP_div: - return JS_OVOP_DIV; - case OP_mod: - case OP_math_mod: - return JS_OVOP_MOD; - case OP_pow: - return JS_OVOP_POW; - case OP_or: - return JS_OVOP_OR; - case OP_and: - return JS_OVOP_AND; - case OP_xor: - return JS_OVOP_XOR; - case OP_shl: - return JS_OVOP_SHL; - case OP_sar: - return JS_OVOP_SAR; - case OP_shr: - return JS_OVOP_SHR; - case OP_eq: - case OP_neq: - return JS_OVOP_EQ; - case OP_lt: - case OP_lte: - case OP_gt: - case OP_gte: - return JS_OVOP_LESS; - case OP_plus: - return JS_OVOP_POS; - case OP_neg: - return JS_OVOP_NEG; - case OP_inc: - return JS_OVOP_INC; - case OP_dec: - return JS_OVOP_DEC; - default: - abort(); - } -} - -/* return NULL if not present */ -JSObject *find_binary_op(JSBinaryOperatorDef *def, - uint32_t operator_index, - JSOverloadableOperatorEnum op) -{ - JSBinaryOperatorDefEntry *ent; - int i; - for(i = 0; i < def->count; i++) { - ent = &def->tab[i]; - if (ent->operator_index == operator_index) - return ent->ops[op]; - } - return NULL; -} - -/* return -1 if exception, 0 if no operator overloading, 1 if - overloaded operator called */ -__exception int js_call_binary_op_fallback(JSContext *ctx, - JSValue *pret, - JSValueConst op1, - JSValueConst op2, - OPCodeEnum op, - BOOL is_numeric, - int hint) -{ - JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2; - JSOperatorSetData *opset1, *opset2; - JSOverloadableOperatorEnum ovop; - JSObject *p; - JSValueConst args[2]; - - if (!ctx->allow_operator_overloading) - return 0; - - opset2_obj = JS_UNDEFINED; - opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); - if (JS_IsException(opset1_obj)) - goto exception; - if (JS_IsUndefined(opset1_obj)) - return 0; - opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); - if (!opset1) - goto exception; - - opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet); - if (JS_IsException(opset2_obj)) - goto exception; - if (JS_IsUndefined(opset2_obj)) { - JS_FreeValue(ctx, opset1_obj); - return 0; - } - opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET); - if (!opset2) - goto exception; - - if (opset1->is_primitive && opset2->is_primitive) { - JS_FreeValue(ctx, opset1_obj); - JS_FreeValue(ctx, opset2_obj); - return 0; - } - - ovop = get_ovop_from_opcode(op); - - if (opset1->operator_counter == opset2->operator_counter) { - p = opset1->self_ops[ovop]; - } else if (opset1->operator_counter > opset2->operator_counter) { - p = find_binary_op(&opset1->left, opset2->operator_counter, ovop); - } else { - p = find_binary_op(&opset2->right, opset1->operator_counter, ovop); - } - if (!p) { - JS_ThrowTypeError(ctx, "operator %s: no function defined", - js_overloadable_operator_names[ovop]); - goto exception; - } - - if (opset1->is_primitive) { - if (is_numeric) { - new_op1 = JS_ToNumeric(ctx, op1); - } else { - new_op1 = JS_ToPrimitive(ctx, op1, hint); - } - if (JS_IsException(new_op1)) - goto exception; - } else { - new_op1 = JS_DupValue(ctx, op1); - } - - if (opset2->is_primitive) { - if (is_numeric) { - new_op2 = JS_ToNumeric(ctx, op2); - } else { - new_op2 = JS_ToPrimitive(ctx, op2, hint); - } - if (JS_IsException(new_op2)) { - JS_FreeValue(ctx, new_op1); - goto exception; - } - } else { - new_op2 = JS_DupValue(ctx, op2); - } - - /* XXX: could apply JS_ToPrimitive() if primitive type so that the - operator function does not get a value object */ - - method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) { - args[0] = new_op2; - args[1] = new_op1; - } else { - args[0] = new_op1; - args[1] = new_op2; - } - ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); - JS_FreeValue(ctx, new_op1); - JS_FreeValue(ctx, new_op2); - if (JS_IsException(ret)) - goto exception; - if (ovop == JS_OVOP_EQ) { - BOOL res = JS_ToBoolFree(ctx, ret); - if (op == OP_neq) - res ^= 1; - ret = JS_NewBool(ctx, res); - } else if (ovop == JS_OVOP_LESS) { - if (JS_IsUndefined(ret)) { - ret = JS_FALSE; - } else { - BOOL res = JS_ToBoolFree(ctx, ret); - if (op == OP_lte || op == OP_gte) - res ^= 1; - ret = JS_NewBool(ctx, res); - } - } - JS_FreeValue(ctx, opset1_obj); - JS_FreeValue(ctx, opset2_obj); - *pret = ret; - return 1; -exception: - JS_FreeValue(ctx, opset1_obj); - JS_FreeValue(ctx, opset2_obj); - *pret = JS_UNDEFINED; - return -1; -} - -/* try to call the operation on the operatorSet field of 'obj'. Only - used for "/" and "**" on the BigInt prototype in math mode */ -__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 */ -__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; -} - -JSValue throw_bf_exception(JSContext *ctx, int status) -{ - const char *str; - if (status & BF_ST_MEM_ERROR) - return JS_ThrowOutOfMemory(ctx); - if (status & BF_ST_DIVIDE_ZERO) { - str = "division by zero"; - } else if (status & BF_ST_INVALID_OP) { - str = "invalid operation"; - } else { - str = "integer overflow"; - } - return JS_ThrowRangeError(ctx, "%s", str); -} - -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; -} - -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; -} - -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; -} - -no_inline __exception int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, val; - int v, ret; - uint32_t tag; - - op1 = sp[-1]; - /* fast path for float64 */ - if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) - goto handle_float64; - if (JS_IsObject(op1)) { - 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; - } - } - - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) - goto exception; - tag = JS_VALUE_GET_TAG(op1); - switch(tag) { - case JS_TAG_INT: - { - int64_t v64; - v64 = JS_VALUE_GET_INT(op1); - switch(op) { - case OP_inc: - case OP_dec: - v = 2 * (op - OP_dec) - 1; - v64 += v; - break; - case OP_plus: - break; - case OP_neg: - if (v64 == 0) { - sp[-1] = __JS_NewFloat64(ctx, -0.0); - return 0; - } else { - v64 = -v64; - } - break; - default: - abort(); - } - sp[-1] = JS_NewInt64(ctx, v64); - } - break; - case JS_TAG_BIG_INT: - handle_bigint: - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) - goto exception; - break; - 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; - d += v; - break; - case OP_plus: - break; - case OP_neg: - d = -d; - break; - default: - abort(); - } - sp[-1] = __JS_NewFloat64(ctx, d); - } - break; - } - return 0; -exception: - sp[-1] = JS_UNDEFINED; - return -1; -} - -__exception int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op) -{ - JSValue op1; - - /* XXX: allow custom operators */ - op1 = sp[-1]; - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - sp[-1] = JS_UNDEFINED; - return -1; - } - sp[-1] = op1; - sp[0] = JS_DupValue(ctx, op1); - return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); -} - -no_inline int js_not_slow(JSContext *ctx, JSValue *sp) -{ - JSValue op1, val; - int ret; - - op1 = sp[-1]; - if (JS_IsObject(op1)) { - ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); - if (ret < 0) - return -1; - if (ret) { - JS_FreeValue(ctx, op1); - sp[-1] = val; - return 0; - } - } - - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) - goto exception; - if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { - if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) - goto exception; - } else { - int32_t v1; - if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) - goto exception; - sp[-1] = JS_NewInt32(ctx, ~v1); - } - return 0; -exception: - sp[-1] = JS_UNDEFINED; - return -1; -} - -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; -} - -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 */ -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); -} - -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; -} - -no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, op2, res; - uint32_t tag1, tag2; - int ret; - double d1, d2; - - op1 = sp[-2]; - op2 = sp[-1]; - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - /* fast path for float operations */ - if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { - d1 = JS_VALUE_GET_FLOAT64(op1); - d2 = JS_VALUE_GET_FLOAT64(op2); - goto handle_float64; - } - - /* try to call an overloaded operator */ - if ((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); - if (ret != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (ret < 0) { - goto exception; - } else { - sp[-2] = res; - return 0; - } - } - } - - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { - int32_t v1, v2; - int64_t v; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - switch(op) { - case OP_sub: - v = (int64_t)v1 - (int64_t)v2; - break; - case OP_mul: - v = (int64_t)v1 * (int64_t)v2; - if (is_math_mode(ctx) && - (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) - goto handle_bigint; - if (v == 0 && (v1 | v2) < 0) { - sp[-2] = __JS_NewFloat64(ctx, -0.0); - return 0; - } - break; - case OP_div: - if (is_math_mode(ctx)) - goto handle_bigint; - sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); - return 0; - case OP_math_mod: - if (unlikely(v2 == 0)) { - throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); - goto exception; - } - v = (int64_t)v1 % (int64_t)v2; - if (v < 0) { - if (v2 < 0) - v -= v2; - else - v += v2; - } - break; - case OP_mod: - if (v1 < 0 || v2 <= 0) { - sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); - return 0; - } else { - v = (int64_t)v1 % (int64_t)v2; - } - break; - case OP_pow: - if (!is_math_mode(ctx)) { - sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); - return 0; - } else { - goto handle_bigint; - } - break; - default: - abort(); - } - sp[-2] = JS_NewInt64(ctx, v); - } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { - if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { - if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - handle_bigint: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - } else { - double dr; - /* float64 result */ - if (JS_ToFloat64Free(ctx, &d1, op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (JS_ToFloat64Free(ctx, &d2, op2)) - goto exception; - handle_float64: - if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) - goto handle_bigint; - switch(op) { - case OP_sub: - dr = d1 - d2; - break; - case OP_mul: - dr = d1 * d2; - break; - case OP_div: - dr = d1 / d2; - break; - case OP_mod: - dr = fmod(d1, d2); - break; - case OP_math_mod: - d2 = fabs(d2); - dr = fmod(d1, d2); - /* XXX: loss of accuracy if dr < 0 */ - if (dr < 0) - dr += d2; - break; - case OP_pow: - dr = js_pow(d1, d2); - break; - default: - abort(); - } - sp[-2] = __JS_NewFloat64(ctx, dr); - } - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2, res; - uint32_t tag1, tag2; - int ret; - - op1 = sp[-2]; - op2 = sp[-1]; - - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - /* fast path for float64 */ - if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { - double d1, d2; - d1 = JS_VALUE_GET_FLOAT64(op1); - d2 = JS_VALUE_GET_FLOAT64(op2); - sp[-2] = __JS_NewFloat64(ctx, d1 + d2); - return 0; - } - - if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { - /* try to call an overloaded operator */ - if ((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED && - tag2 != JS_TAG_STRING)) || - (tag2 == JS_TAG_OBJECT && - (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); - if (ret < 0) { - goto exception; - } else { - sp[-2] = res; - return 0; - } - } - } - - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - } - - if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { - sp[-2] = JS_ConcatString(ctx, op1, op2); - if (JS_IsException(sp[-2])) - goto exception; - return 0; - } - - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { - int32_t v1, v2; - int64_t v; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - v = (int64_t)v1 + (int64_t)v2; - sp[-2] = JS_NewInt64(ctx, v); - } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { - if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) - goto exception; - } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { - if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) - goto exception; - } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - handle_bigint: - if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) - goto exception; - } else { - double d1, d2; - /* float64 result */ - if (JS_ToFloat64Free(ctx, &d1, op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (JS_ToFloat64Free(ctx, &d2, op2)) - goto exception; - if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) - goto handle_bigint; - sp[-2] = __JS_NewFloat64(ctx, d1 + d2); - } - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -no_inline __exception int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, op2, res; - int ret; - uint32_t tag1, tag2; - uint32_t v1, v2, r; - - op1 = sp[-2]; - op2 = sp[-1]; - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - - /* try to call an overloaded operator */ - if ((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); - if (ret != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (ret < 0) { - goto exception; - } else { - sp[-2] = res; - return 0; - } - } - } - - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - - if (is_math_mode(ctx)) - goto bigint_op; - - tag1 = JS_VALUE_GET_TAG(op1); - tag2 = JS_VALUE_GET_TAG(op2); - if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { - if (tag1 != tag2) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - JS_ThrowTypeError(ctx, "both operands must be bigint"); - goto exception; - } else { - bigint_op: - if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) - goto exception; - } - } else { - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) - goto exception; - switch(op) { - case OP_shl: - r = v1 << (v2 & 0x1f); - break; - case OP_sar: - r = (int)v1 >> (v2 & 0x1f); - break; - case OP_and: - r = v1 & v2; - break; - case OP_or: - r = v1 | v2; - break; - case OP_xor: - r = v1 ^ v2; - break; - default: - abort(); - } - sp[-2] = JS_NewInt32(ctx, r); - } - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -/* Note: also used for bigint */ -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; -} - -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; -} - -no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, op2, ret; - int res; - uint32_t tag1, tag2; - - op1 = sp[-2]; - op2 = sp[-1]; - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - /* try to call an overloaded operator */ - if ((tag1 == JS_TAG_OBJECT && - (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || - (tag2 == JS_TAG_OBJECT && - (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op, - FALSE, HINT_NUMBER); - if (res != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (res < 0) { - goto exception; - } else { - sp[-2] = ret; - return 0; - } - } - } - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - - if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { - JSString *p1, *p2; - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = js_string_compare(ctx, p1, p2); - switch(op) { - case OP_lt: - res = (res < 0); - break; - case OP_lte: - res = (res <= 0); - break; - case OP_gt: - res = (res > 0); - break; - default: - case OP_gte: - res = (res >= 0); - break; - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && - (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { - /* fast path for float64/int */ - goto float64_compare; - } else { - if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || - (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && - !is_math_mode(ctx)) { - if (tag1 == JS_TAG_STRING) { - op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) - goto invalid_bigint_string; - } - if (tag2 == JS_TAG_STRING) { - op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { - invalid_bigint_string: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - res = FALSE; - goto done; - } - } - } else { - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - } - - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - - 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; - } - } - } -done: - sp[-2] = JS_NewBool(ctx, res); - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -BOOL tag_is_number(uint32_t tag) -{ - return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || - tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT || - tag == JS_TAG_BIG_DECIMAL); -} - -no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq) -{ - JSValue op1, op2, ret; - int res; - uint32_t tag1, tag2; - - op1 = sp[-2]; - op2 = sp[-1]; -redo: - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag_is_number(tag1) && tag_is_number(tag2)) { - if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { - res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); - } else if ((tag1 == JS_TAG_FLOAT64 && - (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || - (tag2 == JS_TAG_FLOAT64 && - (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { - double d1, d2; - if (tag1 == JS_TAG_FLOAT64) { - d1 = JS_VALUE_GET_FLOAT64(op1); - } else { - d1 = JS_VALUE_GET_INT(op1); - } - if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - } else { - d2 = JS_VALUE_GET_INT(op2); - } - res = (d1 == d2); - } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { - res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2); - if (res < 0) - goto exception; - } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { - res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2); - if (res < 0) - goto exception; - } else { - res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2); - if (res < 0) - goto exception; - } - } else if (tag1 == tag2) { - if (tag1 == JS_TAG_OBJECT) { - /* try the fallback operator */ - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq, - FALSE, HINT_NONE); - if (res != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (res < 0) { - goto exception; - } else { - sp[-2] = ret; - return 0; - } - } - } - res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); - } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || - (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { - res = TRUE; - } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || - (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { - - if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && - !is_math_mode(ctx)) { - if (tag1 == JS_TAG_STRING) { - op1 = JS_StringToBigInt(ctx, op1); - if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) - goto invalid_bigint_string; - } - if (tag2 == JS_TAG_STRING) { - op2 = JS_StringToBigInt(ctx, op2); - if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { - invalid_bigint_string: - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - res = FALSE; - goto done; - } - } - } else { - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - } - res = js_strict_eq(ctx, op1, op2); - } else if (tag1 == JS_TAG_BOOL) { - op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); - goto redo; - } else if (tag2 == JS_TAG_BOOL) { - op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); - goto redo; - } else if ((tag1 == JS_TAG_OBJECT && - (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || - (tag2 == JS_TAG_OBJECT && - (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { - - /* try the fallback operator */ - res = js_call_binary_op_fallback(ctx, &ret, op1, op2, - is_neq ? OP_neq : OP_eq, - FALSE, HINT_NONE); - if (res != 0) { - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - if (res < 0) { - goto exception; - } else { - sp[-2] = ret; - return 0; - } - } - - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - goto redo; - } else { - /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ - if ((JS_IsHTMLDDA(ctx, op1) && - (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || - (JS_IsHTMLDDA(ctx, op2) && - (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { - res = TRUE; - } else { - res = FALSE; - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - } -done: - sp[-2] = JS_NewBool(ctx, res ^ is_neq); - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - uint32_t v1, v2, r; - - op1 = sp[-2]; - op2 = sp[-1]; - op1 = JS_ToNumericFree(ctx, op1); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToNumericFree(ctx, op2); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - /* XXX: could forbid >>> in bignum mode */ - if (!is_math_mode(ctx) && - (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || - JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { - JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - goto exception; - } - /* cannot give an exception */ - JS_ToUint32Free(ctx, &v1, op1); - JS_ToUint32Free(ctx, &v2, op2); - r = v1 >> (v2 & 0x1f); - sp[-2] = JS_NewUint32(ctx, r); - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, - int64_t exponent) -{ - bf_t r_s, *r = &r_s; - double d; - int ret; - - /* always convert to Float64 */ - bf_init(ctx->bf_ctx, r); - ret = bf_mul_pow_radix(r, a, 10, exponent, - 53, bf_set_exp_bits(11) | BF_RNDN | - BF_FLAG_SUBNORMAL); - bf_get_float64(r, &d, BF_RNDN); - bf_delete(r); - if (ret & BF_ST_MEM_ERROR) - return JS_ThrowOutOfMemory(ctx); - else - return __JS_NewFloat64(ctx, d); -} - -no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) -{ - bf_t a_s, *a, *r; - JSValue op1, op2, res; - int64_t e; - int ret; - - res = JS_NewBigFloat(ctx); - if (JS_IsException(res)) - return -1; - r = JS_GetBigFloat(res); - op1 = sp[-2]; - op2 = sp[-1]; - a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) - return -1; - if (JS_IsBigInt(ctx, op2)) { - ret = JS_ToBigInt64(ctx, &e, op2); - } else { - ret = JS_ToInt64(ctx, &e, op2); - } - if (ret) { - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, res); - return -1; - } - - bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags); - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = res; - return 0; -} - - -#else /* !CONFIG_BIGNUM */ - -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; -} - -no_inline __exception int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) -{ - JSValue op1; - double d; - - op1 = sp[-1]; - if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) { - sp[-1] = JS_UNDEFINED; - return -1; - } - switch(op) { - case OP_inc: - d++; - break; - case OP_dec: - d--; - break; - case OP_plus: - break; - case OP_neg: - d = -d; - break; - default: - abort(); - } - sp[-1] = JS_NewFloat64(ctx, d); - return 0; -} - -/* specific case necessary for correct return value semantics */ -__exception int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op) -{ - JSValue op1; - double d, r; - - op1 = sp[-1]; - if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) { - sp[-1] = JS_UNDEFINED; - return -1; - } - r = d + 2 * (op - OP_post_dec) - 1; - sp[0] = JS_NewFloat64(ctx, r); - sp[-1] = JS_NewFloat64(ctx, d); - return 0; -} - -no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, op2; - double d1, d2, r; - - op1 = sp[-2]; - op2 = sp[-1]; - if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) { - goto exception; - } - switch(op) { - case OP_sub: - r = d1 - d2; - break; - case OP_mul: - r = d1 * d2; - break; - case OP_div: - r = d1 / d2; - break; - case OP_mod: - r = fmod(d1, d2); - break; - case OP_pow: - r = js_pow(d1, d2); - break; - default: - abort(); - } - sp[-2] = JS_NewFloat64(ctx, r); - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - uint32_t tag1, tag2; - - op1 = sp[-2]; - op2 = sp[-1]; - tag1 = JS_VALUE_GET_TAG(op1); - tag2 = JS_VALUE_GET_TAG(op2); - if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) && - (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) { - goto add_numbers; - } else { - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - tag1 = JS_VALUE_GET_TAG(op1); - tag2 = JS_VALUE_GET_TAG(op2); - if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { - sp[-2] = JS_ConcatString(ctx, op1, op2); - if (JS_IsException(sp[-2])) - goto exception; - } else { - double d1, d2; - add_numbers: - if (JS_ToFloat64Free(ctx, &d1, op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (JS_ToFloat64Free(ctx, &d2, op2)) - goto exception; - sp[-2] = JS_NewFloat64(ctx, d1 + d2); - } - } - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -no_inline __exception int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, op2; - uint32_t v1, v2, r; - - op1 = sp[-2]; - op2 = sp[-1]; - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) - goto exception; - switch(op) { - case OP_shl: - r = v1 << (v2 & 0x1f); - break; - case OP_sar: - r = (int)v1 >> (v2 & 0x1f); - break; - case OP_and: - r = v1 & v2; - break; - case OP_or: - r = v1 | v2; - break; - case OP_xor: - r = v1 ^ v2; - break; - default: - abort(); - } - sp[-2] = JS_NewInt32(ctx, r); - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -no_inline int js_not_slow(JSContext *ctx, JSValue *sp) -{ - int32_t v1; - - if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) { - sp[-1] = JS_UNDEFINED; - return -1; - } - sp[-1] = JS_NewInt32(ctx, ~v1); - return 0; -} - -no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) -{ - JSValue op1, op2; - int res; - - op1 = sp[-2]; - op2 = sp[-1]; - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING && - JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { - JSString *p1, *p2; - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = js_string_compare(ctx, p1, p2); - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - switch(op) { - case OP_lt: - res = (res < 0); - break; - case OP_lte: - res = (res <= 0); - break; - case OP_gt: - res = (res > 0); - break; - default: - case OP_gte: - res = (res >= 0); - break; - } - } else { - double d1, d2; - if (JS_ToFloat64Free(ctx, &d1, op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (JS_ToFloat64Free(ctx, &d2, op2)) - goto exception; - switch(op) { - case OP_lt: - res = (d1 < d2); /* if NaN return false */ - break; - case OP_lte: - res = (d1 <= d2); /* if NaN return false */ - break; - case OP_gt: - res = (d1 > d2); /* if NaN return false */ - break; - default: - case OP_gte: - res = (d1 >= d2); /* if NaN return false */ - break; - } - } - sp[-2] = JS_NewBool(ctx, res); - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq) -{ - JSValue op1, op2; - int tag1, tag2; - BOOL res; - - op1 = sp[-2]; - op2 = sp[-1]; -redo: - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - if (tag1 == tag2 || - (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) || - (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) { - res = js_strict_eq(ctx, op1, op2); - } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || - (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { - res = TRUE; - } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT || - tag2 == JS_TAG_FLOAT64)) || - (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT || - tag1 == JS_TAG_FLOAT64))) { - double d1; - double d2; - if (JS_ToFloat64Free(ctx, &d1, op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (JS_ToFloat64Free(ctx, &d2, op2)) - goto exception; - res = (d1 == d2); - } else if (tag1 == JS_TAG_BOOL) { - op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); - goto redo; - } else if (tag2 == JS_TAG_BOOL) { - op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); - goto redo; - } else if (tag1 == JS_TAG_OBJECT && - (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) { - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) { - JS_FreeValue(ctx, op2); - goto exception; - } - goto redo; - } else if (tag2 == JS_TAG_OBJECT && - (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) { - op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); - if (JS_IsException(op2)) { - JS_FreeValue(ctx, op1); - goto exception; - } - goto redo; - } else { - /* 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); - } - sp[-2] = JS_NewBool(ctx, res ^ is_neq); - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) -{ - JSValue op1, op2; - uint32_t v1, v2, r; - - op1 = sp[-2]; - op2 = sp[-1]; - if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) { - JS_FreeValue(ctx, op2); - goto exception; - } - if (unlikely(JS_ToUint32Free(ctx, &v2, op2))) - goto exception; - r = v1 >> (v2 & 0x1f); - sp[-2] = JS_NewUint32(ctx, r); - return 0; -exception: - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -} - -#endif /* !CONFIG_BIGNUM */ -#ifdef CONFIG_BIGNUM - -/* Operators */ - -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); - } -} - -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 */ -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; -} - -JSValue js_operators_create(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_operators_create_internal(ctx, argc, argv, FALSE); -} - -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; -} - -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; -} - -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); -} - -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; -} - -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 */ - -JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) -{ - uint32_t tag; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch(tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_BIG_INT: - break; - case JS_TAG_FLOAT64: - case JS_TAG_BIG_FLOAT: - { - 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 { - JSValue val1 = JS_NewBigInt(ctx); - bf_t *r; - int ret; - if (JS_IsException(val1)) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - r = JS_GetBigInt(val1); - ret = bf_set(r, a); - ret |= bf_rint(r, BF_RNDZ); - JS_FreeValue(ctx, val); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val1); - val = JS_ThrowOutOfMemory(ctx); - } else if (ret & BF_ST_INEXACT) { - JS_FreeValue(ctx, val1); - val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer"); - } else { - val = JS_CompactBigInt(ctx, val1); - } - } - if (a == &a_s) - bf_delete(a); - } - break; - 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, HINT_NUMBER); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - default: - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert to bigint"); - } - return val; -} - -JSValue js_bigint_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - if (!JS_IsUndefined(new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); -} - -JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsBigInt(ctx, this_val)) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BIG_INT) { - if (JS_IsBigInt(ctx, p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a bigint"); -} - -JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int base; - JSValue ret; - - val = js_thisBigIntValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (argc == 0 || JS_IsUndefined(argv[0])) { - base = 10; - } else { - base = js_get_radix(ctx, argv[0]); - if (base < 0) - goto fail; - } - ret = js_bigint_to_string1(ctx, val, base); - JS_FreeValue(ctx, val); - return ret; -fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigIntValue(ctx, this_val); -} - -JSValue js_bigint_div(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, b_s, *a, *b, *r, *q; - int status; - JSValue q_val, r_val; - - q_val = JS_NewBigInt(ctx); - if (JS_IsException(q_val)) - return JS_EXCEPTION; - r_val = JS_NewBigInt(ctx); - if (JS_IsException(r_val)) - goto fail; - b = NULL; - a = JS_ToBigInt(ctx, &a_s, argv[0]); - if (!a) - goto fail; - b = JS_ToBigInt(ctx, &b_s, argv[1]); - if (!b) { - JS_FreeBigInt(ctx, a, &a_s); - goto fail; - } - q = JS_GetBigInt(q_val); - r = JS_GetBigInt(r_val); - status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf); - JS_FreeBigInt(ctx, a, &a_s); - JS_FreeBigInt(ctx, b, &b_s); - if (unlikely(status)) { - throw_bf_exception(ctx, status); - goto fail; - } - q_val = JS_CompactBigInt(ctx, q_val); - if (magic & 0x10) { - JSValue ret; - ret = JS_NewArray(ctx); - if (JS_IsException(ret)) - goto fail; - JS_SetPropertyUint32(ctx, ret, 0, q_val); - JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val)); - return ret; - } else { - JS_FreeValue(ctx, r_val); - return q_val; - } -fail: - JS_FreeValue(ctx, q_val); - JS_FreeValue(ctx, r_val); - return JS_EXCEPTION; -} - -JSValue js_bigint_sqrt(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - bf_t a_s, *a, *r, *rem; - int status; - JSValue r_val, rem_val; - - r_val = JS_NewBigInt(ctx); - if (JS_IsException(r_val)) - return JS_EXCEPTION; - rem_val = JS_NewBigInt(ctx); - if (JS_IsException(rem_val)) - return JS_EXCEPTION; - r = JS_GetBigInt(r_val); - rem = JS_GetBigInt(rem_val); - - a = JS_ToBigInt(ctx, &a_s, argv[0]); - if (!a) - goto fail; - status = bf_sqrtrem(r, rem, a); - JS_FreeBigInt(ctx, a, &a_s); - if (unlikely(status & ~BF_ST_INEXACT)) { - throw_bf_exception(ctx, status); - goto fail; - } - r_val = JS_CompactBigInt(ctx, r_val); - if (magic) { - JSValue ret; - ret = JS_NewArray(ctx); - if (JS_IsException(ret)) - goto fail; - JS_SetPropertyUint32(ctx, ret, 0, r_val); - JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val)); - return ret; - } else { - JS_FreeValue(ctx, rem_val); - return r_val; - } -fail: - JS_FreeValue(ctx, r_val); - JS_FreeValue(ctx, rem_val); - return JS_EXCEPTION; -} - -JSValue js_bigint_op1(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - bf_t a_s, *a; - int64_t res; - - a = JS_ToBigInt(ctx, &a_s, argv[0]); - if (!a) - return JS_EXCEPTION; - switch(magic) { - case 0: /* floorLog2 */ - if (a->sign || a->expn <= 0) { - res = -1; - } else { - res = a->expn - 1; - } - break; - case 1: /* ctz */ - if (bf_is_zero(a)) { - res = -1; - } else { - res = bf_get_exp_min(a); - } - break; - default: - abort(); - } - JS_FreeBigInt(ctx, a, &a_s); - return JS_NewBigInt64(ctx, res); -} - -JSValue js_bigint_asUintN(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, int asIntN) -{ - uint64_t bits; - bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; - JSValue res; - - if (JS_ToIndex(ctx, &bits, argv[0])) - return JS_EXCEPTION; - res = JS_NewBigInt(ctx); - if (JS_IsException(res)) - return JS_EXCEPTION; - r = JS_GetBigInt(res); - a = JS_ToBigInt(ctx, &a_s, argv[1]); - if (!a) { - JS_FreeValue(ctx, res); - return JS_EXCEPTION; - } - /* XXX: optimize */ - r = JS_GetBigInt(res); - bf_init(ctx->bf_ctx, mask); - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); - bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ); - bf_logic_and(r, a, mask); - if (asIntN && bits != 0) { - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ); - if (bf_cmpu(r, mask) >= 0) { - bf_set_ui(mask, 1); - bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); - bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ); - } - } - bf_delete(mask); - JS_FreeBigInt(ctx, a, &a_s); - return JS_CompactBigInt(ctx, res); -} - -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 ), -}; - -const JSCFunctionListEntry js_bigint_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_bigint_toString ), - JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), -}; - -void JS_AddIntrinsicBigInt(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValueConst obj1; - - rt->bigint_ops.to_string = js_bigint_to_string; - rt->bigint_ops.from_string = js_string_to_bigint; - rt->bigint_ops.unary_arith = js_unary_arith_bigint; - rt->bigint_ops.binary_arith = js_binary_arith_bigint; - rt->bigint_ops.compare = js_compare_bigfloat; - - ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], - js_bigint_proto_funcs, - countof(js_bigint_proto_funcs)); - obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1, - ctx->class_proto[JS_CLASS_BIG_INT]); - JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, - countof(js_bigint_funcs)); -} - -/* BigFloat */ - -JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsBigFloat(this_val)) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BIG_FLOAT) { - if (JS_IsBigFloat(p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a bigfloat"); -} - -JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int base; - JSValue ret; - - val = js_thisBigFloatValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (argc == 0 || JS_IsUndefined(argv[0])) { - base = 10; - } else { - base = js_get_radix(ctx, argv[0]); - if (base < 0) - goto fail; - } - ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); - JS_FreeValue(ctx, val); - return ret; -fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigFloatValue(ctx, this_val); -} - -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; -} - -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; -} - -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; -} - -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; -} - -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; -} - -const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ), - JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ), - JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ), - JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ), - JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ), -}; - -JSValue js_bigfloat_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue val; - if (!JS_IsUndefined(new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (argc == 0) { - bf_t *r; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - return val; - r = JS_GetBigFloat(val); - bf_set_zero(r, 0); - } else { - val = JS_DupValue(ctx, argv[0]); - redo: - switch(JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_BIG_FLOAT: - break; - case JS_TAG_FLOAT64: - { - bf_t *r; - double d = JS_VALUE_GET_FLOAT64(val); - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigFloat(val); - if (bf_set_float64(r, d)) - goto fail; - } - break; - case JS_TAG_INT: - { - bf_t *r; - int32_t v = JS_VALUE_GET_INT(val); - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigFloat(val); - if (bf_set_si(r, v)) - goto fail; - } - break; - case JS_TAG_BIG_INT: - /* We keep the full precision of the integer */ - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - val = JS_MKPTR(JS_TAG_BIG_FLOAT, p); - } - break; - case JS_TAG_BIG_DECIMAL: - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_STRING: - { - const char *str, *p; - size_t len; - int err; - - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - bf_t *r; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - break; - r = JS_GetBigFloat(val); - bf_set_zero(r, 0); - err = 0; - } else { - val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | - ATOD_TYPE_BIG_FLOAT | - ATOD_ACCEPT_PREFIX_AFTER_SIGN); - if (JS_IsException(val)) { - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - p += skip_spaces(p); - err = ((p - str) != len); - } - JS_FreeCString(ctx, str); - if (err) { - JS_FreeValue(ctx, val); - return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal"); - } - } - break; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - break; - goto redo; - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - default: - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert to bigfloat"); - } - } - return val; -fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -JSValue js_bigfloat_get_const(JSContext *ctx, - JSValueConst this_val, int magic) -{ - bf_t *r; - JSValue val; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - return val; - r = JS_GetBigFloat(val); - switch(magic) { - case 0: /* PI */ - bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case 1: /* LN2 */ - bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags); - break; - case 2: /* MIN_VALUE */ - case 3: /* MAX_VALUE */ - { - slimb_t e_range, e; - e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1); - bf_set_ui(r, 1); - if (magic == 2) { - e = -e_range + 2; - if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL) - e -= ctx->fp_env.prec - 1; - bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags); - } else { - bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec, - ctx->fp_env.flags); - bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags); - bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec, - ctx->fp_env.flags); - } - } - break; - case 4: /* EPSILON */ - bf_set_ui(r, 1); - bf_mul_2exp(r, 1 - ctx->fp_env.prec, - ctx->fp_env.prec, ctx->fp_env.flags); - break; - default: - abort(); - } - return val; -} - -JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - bf_t *a; - const char *str; - JSValue ret; - int radix; - JSFloatEnv *fe; - - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &radix, argv[1])) { - fail: - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - if (radix != 0 && (radix < 2 || radix > 36)) { - JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); - goto fail; - } - fe = &ctx->fp_env; - if (argc > 2) { - fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); - if (!fe) - goto fail; - } - ret = JS_NewBigFloat(ctx); - if (JS_IsException(ret)) - goto done; - a = JS_GetBigFloat(ret); - /* XXX: use js_atof() */ - bf_atof(a, str, NULL, radix, fe->prec, fe->flags); -done: - JS_FreeCString(ctx, str); - return ret; -} - -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)); -} - -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, -}; - -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; -} - -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; -} - -const JSCFunctionListEntry js_bigfloat_funcs[] = { - JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ), - JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ), - JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ), - JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ), - JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ), - JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ), - JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ), - JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ), - JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ), - JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ), - JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ), - JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ), - JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ), - JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ), - JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ), - JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ), - JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ), - JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ), - JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ), - JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ), - JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ), - JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ), - JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ), - JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ), - JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ), - JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ), - JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ), - JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ), - JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ), - JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ), - JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ), -}; - -/* FloatEnv */ - -JSValue js_float_env_constructor(JSContext *ctx, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue obj; - JSFloatEnv *fe; - int64_t prec; - int flags, rndmode; - - prec = ctx->fp_env.prec; - flags = ctx->fp_env.flags; - if (!JS_IsUndefined(argv[0])) { - if (JS_ToInt64Sat(ctx, &prec, argv[0])) - return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */ - if (argc > 1 && !JS_IsUndefined(argv[1])) { - if (JS_ToInt32Sat(ctx, &rndmode, argv[1])) - return JS_EXCEPTION; - if (rndmode < BF_RNDN || rndmode > BF_RNDF) - return JS_ThrowRangeError(ctx, "invalid rounding mode"); - flags = rndmode; - } - } - - obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV); - if (JS_IsException(obj)) - return JS_EXCEPTION; - fe = js_malloc(ctx, sizeof(*fe)); - if (!fe) - return JS_EXCEPTION; - fe->prec = prec; - fe->flags = flags; - fe->status = 0; - JS_SetOpaque(obj, fe); - return obj; -} - -void js_float_env_finalizer(JSRuntime *rt, JSValue val) -{ - JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV); - js_free_rt(rt, fe); -} - -JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val) -{ - return JS_NewInt64(ctx, ctx->fp_env.prec); -} - -JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) -{ - return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); -} - -JSValue js_float_env_setPrec(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst func; - int exp_bits, flags, saved_flags; - JSValue ret; - limb_t saved_prec; - int64_t prec; - - func = argv[0]; - if (JS_ToInt64Sat(ctx, &prec, argv[1])) - return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - exp_bits = BF_EXP_BITS_MAX; - - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) - return JS_EXCEPTION; - if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) - return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); - } - - flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); - - saved_prec = ctx->fp_env.prec; - saved_flags = ctx->fp_env.flags; - - ctx->fp_env.prec = prec; - ctx->fp_env.flags = flags; - - ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); - /* always restore the floating point precision */ - ctx->fp_env.prec = saved_prec; - ctx->fp_env.flags = saved_flags; - return ret; -} - -#define FE_PREC (-1) -#define FE_EXP (-2) -#define FE_RNDMODE (-3) -#define FE_SUBNORMAL (-4) - -JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic) -{ - JSFloatEnv *fe; - fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - switch(magic) { - case FE_PREC: - return JS_NewInt64(ctx, fe->prec); - case FE_EXP: - return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags)); - case FE_RNDMODE: - return JS_NewInt32(ctx, fe->flags & BF_RND_MASK); - case FE_SUBNORMAL: - return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0); - default: - return JS_NewBool(ctx, (fe->status & magic) != 0); - } -} - -JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) -{ - JSFloatEnv *fe; - int b; - int64_t prec; - - fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - switch(magic) { - case FE_PREC: - if (JS_ToInt64Sat(ctx, &prec, val)) - return JS_EXCEPTION; - if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) - return JS_ThrowRangeError(ctx, "invalid precision"); - fe->prec = prec; - break; - case FE_EXP: - if (JS_ToInt32Sat(ctx, &b, val)) - return JS_EXCEPTION; - if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) - return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); - fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | - bf_set_exp_bits(b); - break; - case FE_RNDMODE: - b = bigfloat_get_rnd_mode(ctx, val); - if (b < 0) - return JS_EXCEPTION; - fe->flags = (fe->flags & ~BF_RND_MASK) | b; - break; - case FE_SUBNORMAL: - b = JS_ToBool(ctx, val); - fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); - break; - default: - b = JS_ToBool(ctx, val); - fe->status = (fe->status & ~magic) & ((-b) & magic); - break; - } - return JS_UNDEFINED; -} - -JSValue js_float_env_clearStatus(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); - if (!fe) - return JS_EXCEPTION; - fe->status = 0; - return JS_UNDEFINED; -} - -const JSCFunctionListEntry js_float_env_funcs[] = { - JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ), - JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ), - JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ), - JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ), - JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ), - JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ), - JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ), - JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ), - JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ), - JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), - JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), - JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), - JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), - JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), -}; - -const JSCFunctionListEntry js_float_env_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_PREC ), - JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_EXP ), - JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_RNDMODE ), - JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status, - js_float_env_proto_set_status, FE_SUBNORMAL ), - JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_INVALID_OP ), - JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ), - JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_OVERFLOW ), - JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_UNDERFLOW ), - JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status, - js_float_env_proto_set_status, BF_ST_INEXACT ), - JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ), -}; - -void JS_AddIntrinsicBigFloat(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValueConst obj1; - - rt->bigfloat_ops.to_string = js_bigfloat_to_string; - rt->bigfloat_ops.from_string = js_string_to_bigfloat; - rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat; - rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat; - rt->bigfloat_ops.compare = js_compare_bigfloat; - rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64; - rt->bigfloat_ops.mul_pow10 = js_mul_pow10; - - ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT], - js_bigfloat_proto_funcs, - countof(js_bigfloat_proto_funcs)); - obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1, - ctx->class_proto[JS_CLASS_BIG_FLOAT]); - JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs, - countof(js_bigfloat_funcs)); - - ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV], - js_float_env_proto_funcs, - countof(js_float_env_proto_funcs)); - obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", - js_float_env_constructor, 1, - ctx->class_proto[JS_CLASS_FLOAT_ENV]); - JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, - countof(js_float_env_funcs)); -} - -/* BigDecimal */ - -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; -} - -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; -} - -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"); -} - -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); -} - -JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisBigDecimalValue(ctx, this_val); -} - -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; - -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; -} - - -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; - } -} - -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; -} - -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; -} - -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; -} - -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 ), -}; - -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; -} - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-big-num.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "js-number.h" +#include "js-operator.h" + +#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) +{ + if (is_math_mode(ctx) && + v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { + return JS_NewInt64(ctx, v); + } else { + return JS_NewBigInt64_1(ctx, v); + } +} + +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) +{ + JSValue val; + if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { + val = JS_NewInt64(ctx, v); + } else { + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_ui(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + } + return val; +} + +/* if the returned bigfloat is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return + NULL in case of error. */ +bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val) +{ + uint32_t tag; + bf_t *r; + JSBigFloat *p; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_si(r, JS_VALUE_GET_INT(val))) + goto fail; + break; + case JS_TAG_FLOAT64: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { + fail: + bf_delete(r); + return NULL; + } + break; + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_UNDEFINED: + default: + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set_nan(r); + break; + } + return r; +} + +/* return NULL if invalid type */ +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 */ +JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) +{ + const char *str, *p; + size_t len; + int flags; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + val = JS_NewBigInt64(ctx, 0); + } else { + flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; + 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; +} + +JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) +{ + val = JS_StringToBigInt(ctx, val); + if (JS_VALUE_IS_NAN(val)) + return JS_ThrowSyntaxError(ctx, "invalid bigint literal"); + return val; +} + +/* if the returned bigfloat is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */ +bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) +{ + uint32_t tag; + bf_t *r; + JSBigFloat *p; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + if (!is_math_mode(ctx)) + goto fail; + /* fall tru */ + case JS_TAG_BOOL: + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set_si(r, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (!is_math_mode(ctx)) + goto fail; + if (!isfinite(d)) + goto fail; + r = buf; + bf_init(ctx->bf_ctx, r); + d = trunc(d); + bf_set_float64(r, d); + } + break; + case JS_TAG_BIG_INT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_BIG_FLOAT: + if (!is_math_mode(ctx)) + goto fail; + p = JS_VALUE_GET_PTR(val); + if (!bf_is_finite(&p->num)) + goto fail; + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set(r, &p->num); + bf_rint(r, BF_RNDZ); + JS_FreeValue(ctx, val); + break; + case JS_TAG_STRING: + val = JS_StringToBigIntErr(ctx, val); + if (JS_IsException(val)) + return NULL; + goto redo; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return NULL; + goto redo; + default: + fail: + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "cannot convert to bigint"); + return NULL; + } + return r; +} + +bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val) +{ + return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val)); +} + +__maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { + return val; + } else { + bf_t a_s, *a, *r; + int ret; + JSValue res; + + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) { + JS_FreeValue(ctx, res); + return JS_EXCEPTION; + } + r = JS_GetBigInt(res); + ret = bf_set(r, a); + JS_FreeBigInt(ctx, a, &a_s); + if (ret) { + JS_FreeValue(ctx, res); + return JS_ThrowOutOfMemory(ctx); + } + return JS_CompactBigInt(ctx, res); + } +} + +/* free the bf_t allocated by JS_ToBigInt */ +void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) +{ + if (a == buf) { + bf_delete(a); + } else { + JSBigFloat *p = (JSBigFloat *)((uint8_t *)a - + offsetof(JSBigFloat, num)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p)); + } +} + +/* XXX: merge with JS_ToInt64Free with a specific flag */ +int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + bf_t a_s, *a; + + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) { + *pres = 0; + return -1; + } + bf_get_int64(pres, a, BF_GET_INT_MOD); + JS_FreeBigInt(ctx, a, &a_s); + return 0; +} + +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) +{ + return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val)); +} + +JSBigFloat *js_new_bf(JSContext *ctx) +{ + JSBigFloat *p; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return NULL; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return p; +} + +JSValue JS_NewBigFloat(JSContext *ctx) +{ + JSBigFloat *p; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_FLOAT, p); +} + +JSValue JS_NewBigDecimal(JSContext *ctx) +{ + JSBigDecimal *p; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bfdec_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_DECIMAL, p); +} + +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); +} + +JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, + BOOL convert_to_safe_integer) +{ + int64_t v; + bf_t *a; + + if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) + return val; /* fail safe */ + a = JS_GetBigInt(val); + if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 && + v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { + JS_FreeValue(ctx, val); + return JS_NewInt64(ctx, v); + } else if (a->expn == BF_EXP_ZERO && a->sign) { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + assert(p->header.ref_count == 1); + a->sign = 0; + } + return val; +} + +/* Convert the big int to a safe integer if in math mode. normalize + the zero representation. Could also be used to convert the bigint + to a short bigint value. The reference count of the value must be + 1. Cannot fail */ +JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) +{ + return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); +} + +/* must be kept in sync with JSOverloadableOperatorEnum */ +/* XXX: use atoms ? */ +const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = { + "+", + "-", + "*", + "/", + "%", + "**", + "|", + "&", + "^", + "<<", + ">>", + ">>>", + "==", + "<", + "pos", + "neg", + "++", + "--", + "~", +}; + +int get_ovop_from_opcode(OPCodeEnum op) +{ + switch(op) { + case OP_add: + return JS_OVOP_ADD; + case OP_sub: + return JS_OVOP_SUB; + case OP_mul: + return JS_OVOP_MUL; + case OP_div: + return JS_OVOP_DIV; + case OP_mod: + case OP_math_mod: + return JS_OVOP_MOD; + case OP_pow: + return JS_OVOP_POW; + case OP_or: + return JS_OVOP_OR; + case OP_and: + return JS_OVOP_AND; + case OP_xor: + return JS_OVOP_XOR; + case OP_shl: + return JS_OVOP_SHL; + case OP_sar: + return JS_OVOP_SAR; + case OP_shr: + return JS_OVOP_SHR; + case OP_eq: + case OP_neq: + return JS_OVOP_EQ; + case OP_lt: + case OP_lte: + case OP_gt: + case OP_gte: + return JS_OVOP_LESS; + case OP_plus: + return JS_OVOP_POS; + case OP_neg: + return JS_OVOP_NEG; + case OP_inc: + return JS_OVOP_INC; + case OP_dec: + return JS_OVOP_DEC; + default: + abort(); + } +} + +/* return NULL if not present */ +JSObject *find_binary_op(JSBinaryOperatorDef *def, + uint32_t operator_index, + JSOverloadableOperatorEnum op) +{ + JSBinaryOperatorDefEntry *ent; + int i; + for(i = 0; i < def->count; i++) { + ent = &def->tab[i]; + if (ent->operator_index == operator_index) + return ent->ops[op]; + } + return NULL; +} + +/* return -1 if exception, 0 if no operator overloading, 1 if + overloaded operator called */ +__exception int js_call_binary_op_fallback(JSContext *ctx, + JSValue *pret, + JSValueConst op1, + JSValueConst op2, + OPCodeEnum op, + BOOL is_numeric, + int hint) +{ + JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2; + JSOperatorSetData *opset1, *opset2; + JSOverloadableOperatorEnum ovop; + JSObject *p; + JSValueConst args[2]; + + if (!ctx->allow_operator_overloading) + return 0; + + opset2_obj = JS_UNDEFINED; + opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset1_obj)) + goto exception; + if (JS_IsUndefined(opset1_obj)) + return 0; + opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); + if (!opset1) + goto exception; + + opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet); + if (JS_IsException(opset2_obj)) + goto exception; + if (JS_IsUndefined(opset2_obj)) { + JS_FreeValue(ctx, opset1_obj); + return 0; + } + opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET); + if (!opset2) + goto exception; + + if (opset1->is_primitive && opset2->is_primitive) { + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + return 0; + } + + ovop = get_ovop_from_opcode(op); + + if (opset1->operator_counter == opset2->operator_counter) { + p = opset1->self_ops[ovop]; + } else if (opset1->operator_counter > opset2->operator_counter) { + p = find_binary_op(&opset1->left, opset2->operator_counter, ovop); + } else { + p = find_binary_op(&opset2->right, opset1->operator_counter, ovop); + } + if (!p) { + JS_ThrowTypeError(ctx, "operator %s: no function defined", + js_overloadable_operator_names[ovop]); + goto exception; + } + + if (opset1->is_primitive) { + if (is_numeric) { + new_op1 = JS_ToNumeric(ctx, op1); + } else { + new_op1 = JS_ToPrimitive(ctx, op1, hint); + } + if (JS_IsException(new_op1)) + goto exception; + } else { + new_op1 = JS_DupValue(ctx, op1); + } + + if (opset2->is_primitive) { + if (is_numeric) { + new_op2 = JS_ToNumeric(ctx, op2); + } else { + new_op2 = JS_ToPrimitive(ctx, op2, hint); + } + if (JS_IsException(new_op2)) { + JS_FreeValue(ctx, new_op1); + goto exception; + } + } else { + new_op2 = JS_DupValue(ctx, op2); + } + + /* XXX: could apply JS_ToPrimitive() if primitive type so that the + operator function does not get a value object */ + + method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) { + args[0] = new_op2; + args[1] = new_op1; + } else { + args[0] = new_op1; + args[1] = new_op2; + } + ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); + JS_FreeValue(ctx, new_op1); + JS_FreeValue(ctx, new_op2); + if (JS_IsException(ret)) + goto exception; + if (ovop == JS_OVOP_EQ) { + BOOL res = JS_ToBoolFree(ctx, ret); + if (op == OP_neq) + res ^= 1; + ret = JS_NewBool(ctx, res); + } else if (ovop == JS_OVOP_LESS) { + if (JS_IsUndefined(ret)) { + ret = JS_FALSE; + } else { + BOOL res = JS_ToBoolFree(ctx, ret); + if (op == OP_lte || op == OP_gte) + res ^= 1; + ret = JS_NewBool(ctx, res); + } + } + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + *pret = ret; + return 1; +exception: + JS_FreeValue(ctx, opset1_obj); + JS_FreeValue(ctx, opset2_obj); + *pret = JS_UNDEFINED; + return -1; +} + +/* try to call the operation on the operatorSet field of 'obj'. Only + used for "/" and "**" on the BigInt prototype in math mode */ +__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 */ +__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; +} + +JSValue throw_bf_exception(JSContext *ctx, int status) +{ + const char *str; + if (status & BF_ST_MEM_ERROR) + return JS_ThrowOutOfMemory(ctx); + if (status & BF_ST_DIVIDE_ZERO) { + str = "division by zero"; + } else if (status & BF_ST_INVALID_OP) { + str = "invalid operation"; + } else { + str = "integer overflow"; + } + return JS_ThrowRangeError(ctx, "%s", str); +} + +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; +} + +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; +} + +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; +} + +no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, val; + int v, ret; + uint32_t tag; + + op1 = sp[-1]; + /* fast path for float64 */ + if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) + goto handle_float64; + if (JS_IsObject(op1)) { + 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; + } + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); + switch(tag) { + case JS_TAG_INT: + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = __JS_NewFloat64(ctx, -0.0); + return 0; + } else { + v64 = -v64; + } + break; + default: + abort(); + } + sp[-1] = JS_NewInt64(ctx, v64); + } + break; + case JS_TAG_BIG_INT: + handle_bigint: + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) + goto exception; + break; + 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; + d += v; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); + } + sp[-1] = __JS_NewFloat64(ctx, d); + } + break; + } + return 0; +exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +__exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op) +{ + JSValue op1; + + /* XXX: allow custom operators */ + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + sp[-1] = JS_UNDEFINED; + return -1; + } + sp[-1] = op1; + sp[0] = JS_DupValue(ctx, op1); + return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); +} + +no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, val; + int ret; + + op1 = sp[-1]; + if (JS_IsObject(op1)) { + ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); + if (ret < 0) + return -1; + if (ret) { + JS_FreeValue(ctx, op1); + sp[-1] = val; + return 0; + } + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) + goto exception; + } else { + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = JS_NewInt32(ctx, ~v1); + } + return 0; +exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +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; +} + +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 */ +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); +} + +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; +} + +no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2, res; + uint32_t tag1, tag2; + int ret; + double d1, d2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float operations */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + goto handle_float64; + } + + /* try to call an overloaded operator */ + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); + if (ret != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + goto exception; + } else { + sp[-2] = res; + return 0; + } + } + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + switch(op) { + case OP_sub: + v = (int64_t)v1 - (int64_t)v2; + break; + case OP_mul: + v = (int64_t)v1 * (int64_t)v2; + if (is_math_mode(ctx) && + (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) + goto handle_bigint; + if (v == 0 && (v1 | v2) < 0) { + sp[-2] = __JS_NewFloat64(ctx, -0.0); + return 0; + } + break; + case OP_div: + if (is_math_mode(ctx)) + goto handle_bigint; + sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); + return 0; + case OP_math_mod: + if (unlikely(v2 == 0)) { + throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); + goto exception; + } + v = (int64_t)v1 % (int64_t)v2; + if (v < 0) { + if (v2 < 0) + v -= v2; + else + v += v2; + } + break; + case OP_mod: + if (v1 < 0 || v2 <= 0) { + sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); + return 0; + } else { + v = (int64_t)v1 % (int64_t)v2; + } + break; + case OP_pow: + if (!is_math_mode(ctx)) { + sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); + return 0; + } else { + goto handle_bigint; + } + break; + default: + abort(); + } + sp[-2] = JS_NewInt64(ctx, v); + } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { + if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + handle_bigint: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } else { + double dr; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + handle_float64: + if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) + goto handle_bigint; + switch(op) { + case OP_sub: + dr = d1 - d2; + break; + case OP_mul: + dr = d1 * d2; + break; + case OP_div: + dr = d1 / d2; + break; + case OP_mod: + dr = fmod(d1, d2); + break; + case OP_math_mod: + d2 = fabs(d2); + dr = fmod(d1, d2); + /* XXX: loss of accuracy if dr < 0 */ + if (dr < 0) + dr += d2; + break; + case OP_pow: + dr = js_pow(d1, d2); + break; + default: + abort(); + } + sp[-2] = __JS_NewFloat64(ctx, dr); + } + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2, res; + uint32_t tag1, tag2; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float64 */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + double d1, d2; + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + sp[-2] = __JS_NewFloat64(ctx, d1 + d2); + return 0; + } + + if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { + /* try to call an overloaded operator */ + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED && + tag2 != JS_TAG_STRING)) || + (tag2 == JS_TAG_OBJECT && + (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); + if (ret < 0) { + goto exception; + } else { + sp[-2] = res; + return 0; + } + } + } + + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + } + + if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + if (JS_IsException(sp[-2])) + goto exception; + return 0; + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + v = (int64_t)v1 + (int64_t)v2; + sp[-2] = JS_NewInt64(ctx, v); + } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { + if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + handle_bigint: + if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } else { + double d1, d2; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) + goto handle_bigint; + sp[-2] = __JS_NewFloat64(ctx, d1 + d2); + } + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2, res; + int ret; + uint32_t tag1, tag2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + /* try to call an overloaded operator */ + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); + if (ret != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (ret < 0) { + goto exception; + } else { + sp[-2] = res; + return 0; + } + } + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + if (is_math_mode(ctx)) + goto bigint_op; + + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (tag1 != tag2) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + JS_ThrowTypeError(ctx, "both operands must be bigint"); + goto exception; + } else { + bigint_op: + if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) + goto exception; + } + } else { + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = JS_NewInt32(ctx, r); + } + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +/* Note: also used for bigint */ +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; +} + +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; +} + +no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2, ret; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* try to call an overloaded operator */ + if ((tag1 == JS_TAG_OBJECT && + (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || + (tag2 == JS_TAG_OBJECT && + (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op, + FALSE, HINT_NUMBER); + if (res != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (res < 0) { + goto exception; + } else { + sp[-2] = ret; + return 0; + } + } + } + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + JSString *p1, *p2; + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = js_string_compare(ctx, p1, p2); + switch(op) { + case OP_lt: + res = (res < 0); + break; + case OP_lte: + res = (res <= 0); + break; + case OP_gt: + res = (res > 0); + break; + default: + case OP_gte: + res = (res >= 0); + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && + (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { + /* fast path for float64/int */ + goto float64_compare; + } else { + if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || + (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && + !is_math_mode(ctx)) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + 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; + } + } + } +done: + sp[-2] = JS_NewBool(ctx, res); + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +BOOL tag_is_number(uint32_t tag) +{ + return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || + tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT || + tag == JS_TAG_BIG_DECIMAL); +} + +no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + JSValue op1, op2, ret; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; +redo: + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag_is_number(tag1) && tag_is_number(tag2)) { + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + } else if ((tag1 == JS_TAG_FLOAT64 && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || + (tag2 == JS_TAG_FLOAT64 && + (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { + double d1, d2; + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + res = (d1 == d2); + } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { + res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } else { + res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } + } else if (tag1 == tag2) { + if (tag1 == JS_TAG_OBJECT) { + /* try the fallback operator */ + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, + is_neq ? OP_neq : OP_eq, + FALSE, HINT_NONE); + if (res != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (res < 0) { + goto exception; + } else { + sp[-2] = ret; + return 0; + } + } + } + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || + (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { + res = TRUE; + } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || + (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { + + if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && + !is_math_mode(ctx)) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + res = js_strict_eq(ctx, op1, op2); + } else if (tag1 == JS_TAG_BOOL) { + op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); + goto redo; + } else if ((tag1 == JS_TAG_OBJECT && + (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || + (tag2 == JS_TAG_OBJECT && + (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { + + /* try the fallback operator */ + res = js_call_binary_op_fallback(ctx, &ret, op1, op2, + is_neq ? OP_neq : OP_eq, + FALSE, HINT_NONE); + if (res != 0) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + if (res < 0) { + goto exception; + } else { + sp[-2] = ret; + return 0; + } + } + + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + goto redo; + } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = TRUE; + } else { + res = FALSE; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } +done: + sp[-2] = JS_NewBool(ctx, res ^ is_neq); + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + /* XXX: could forbid >>> in bignum mode */ + if (!is_math_mode(ctx) && + (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { + JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + goto exception; + } + /* cannot give an exception */ + JS_ToUint32Free(ctx, &v1, op1); + JS_ToUint32Free(ctx, &v2, op2); + r = v1 >> (v2 & 0x1f); + sp[-2] = JS_NewUint32(ctx, r); + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, + int64_t exponent) +{ + bf_t r_s, *r = &r_s; + double d; + int ret; + + /* always convert to Float64 */ + bf_init(ctx->bf_ctx, r); + ret = bf_mul_pow_radix(r, a, 10, exponent, + 53, bf_set_exp_bits(11) | BF_RNDN | + BF_FLAG_SUBNORMAL); + bf_get_float64(r, &d, BF_RNDN); + bf_delete(r); + if (ret & BF_ST_MEM_ERROR) + return JS_ThrowOutOfMemory(ctx); + else + return __JS_NewFloat64(ctx, d); +} + +no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) +{ + bf_t a_s, *a, *r; + JSValue op1, op2, res; + int64_t e; + int ret; + + res = JS_NewBigFloat(ctx); + if (JS_IsException(res)) + return -1; + r = JS_GetBigFloat(res); + op1 = sp[-2]; + op2 = sp[-1]; + a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) + return -1; + if (JS_IsBigInt(ctx, op2)) { + ret = JS_ToBigInt64(ctx, &e, op2); + } else { + ret = JS_ToInt64(ctx, &e, op2); + } + if (ret) { + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, res); + return -1; + } + + bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags); + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = res; + return 0; +} + + +#else /* !CONFIG_BIGNUM */ + +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; +} + +no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1; + double d; + + op1 = sp[-1]; + if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) { + sp[-1] = JS_UNDEFINED; + return -1; + } + switch(op) { + case OP_inc: + d++; + break; + case OP_dec: + d--; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); + } + sp[-1] = JS_NewFloat64(ctx, d); + return 0; +} + +/* specific case necessary for correct return value semantics */ +__exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op) +{ + JSValue op1; + double d, r; + + op1 = sp[-1]; + if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) { + sp[-1] = JS_UNDEFINED; + return -1; + } + r = d + 2 * (op - OP_post_dec) - 1; + sp[0] = JS_NewFloat64(ctx, r); + sp[-1] = JS_NewFloat64(ctx, d); + return 0; +} + +no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + double d1, d2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) { + goto exception; + } + switch(op) { + case OP_sub: + r = d1 - d2; + break; + case OP_mul: + r = d1 * d2; + break; + case OP_div: + r = d1 / d2; + break; + case OP_mod: + r = fmod(d1, d2); + break; + case OP_pow: + r = js_pow(d1, d2); + break; + default: + abort(); + } + sp[-2] = JS_NewFloat64(ctx, r); + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) && + (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) { + goto add_numbers; + } else { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + if (JS_IsException(sp[-2])) + goto exception; + } else { + double d1, d2; + add_numbers: + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + sp[-2] = JS_NewFloat64(ctx, d1 + d2); + } + } + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = JS_NewInt32(ctx, r); + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +{ + int32_t v1; + + if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) { + sp[-1] = JS_UNDEFINED; + return -1; + } + sp[-1] = JS_NewInt32(ctx, ~v1); + return 0; +} + +no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + int res; + + op1 = sp[-2]; + op2 = sp[-1]; + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING && + JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { + JSString *p1, *p2; + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = js_string_compare(ctx, p1, p2); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + switch(op) { + case OP_lt: + res = (res < 0); + break; + case OP_lte: + res = (res <= 0); + break; + case OP_gt: + res = (res > 0); + break; + default: + case OP_gte: + res = (res >= 0); + break; + } + } else { + double d1, d2; + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } + } + sp[-2] = JS_NewBool(ctx, res); + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + JSValue op1, op2; + int tag1, tag2; + BOOL res; + + op1 = sp[-2]; + op2 = sp[-1]; +redo: + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag1 == tag2 || + (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) || + (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) { + res = js_strict_eq(ctx, op1, op2); + } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || + (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { + res = TRUE; + } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT || + tag2 == JS_TAG_FLOAT64)) || + (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT || + tag1 == JS_TAG_FLOAT64))) { + double d1; + double d2; + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + res = (d1 == d2); + } else if (tag1 == JS_TAG_BOOL) { + op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); + goto redo; + } else if (tag1 == JS_TAG_OBJECT && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + goto redo; + } else if (tag2 == JS_TAG_OBJECT && + (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) { + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + goto redo; + } else { + /* 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); + } + sp[-2] = JS_NewBool(ctx, res ^ is_neq); + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToUint32Free(ctx, &v2, op2))) + goto exception; + r = v1 >> (v2 & 0x1f); + sp[-2] = JS_NewUint32(ctx, r); + return 0; +exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +#endif /* !CONFIG_BIGNUM */ +#ifdef CONFIG_BIGNUM + +/* Operators */ + +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); + } +} + +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 */ +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; +} + +JSValue js_operators_create(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_operators_create_internal(ctx, argc, argv, FALSE); +} + +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; +} + +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; +} + +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); +} + +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; +} + +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 */ + +JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BIG_INT: + break; + case JS_TAG_FLOAT64: + case JS_TAG_BIG_FLOAT: + { + 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 { + JSValue val1 = JS_NewBigInt(ctx); + bf_t *r; + int ret; + if (JS_IsException(val1)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + r = JS_GetBigInt(val1); + ret = bf_set(r, a); + ret |= bf_rint(r, BF_RNDZ); + JS_FreeValue(ctx, val); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val1); + val = JS_ThrowOutOfMemory(ctx); + } else if (ret & BF_ST_INEXACT) { + JS_FreeValue(ctx, val1); + val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer"); + } else { + val = JS_CompactBigInt(ctx, val1); + } + } + if (a == &a_s) + bf_delete(a); + } + break; + 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, HINT_NUMBER); + if (JS_IsException(val)) + break; + goto redo; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert to bigint"); + } + return val; +} + +JSValue js_bigint_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); +} + +JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_IsBigInt(ctx, this_val)) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_BIG_INT) { + if (JS_IsBigInt(ctx, p->u.object_data)) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a bigint"); +} + +JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int base; + JSValue ret; + + val = js_thisBigIntValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (argc == 0 || JS_IsUndefined(argv[0])) { + base = 10; + } else { + base = js_get_radix(ctx, argv[0]); + if (base < 0) + goto fail; + } + ret = js_bigint_to_string1(ctx, val, base); + JS_FreeValue(ctx, val); + return ret; +fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisBigIntValue(ctx, this_val); +} + +JSValue js_bigint_div(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, b_s, *a, *b, *r, *q; + int status; + JSValue q_val, r_val; + + q_val = JS_NewBigInt(ctx); + if (JS_IsException(q_val)) + return JS_EXCEPTION; + r_val = JS_NewBigInt(ctx); + if (JS_IsException(r_val)) + goto fail; + b = NULL; + a = JS_ToBigInt(ctx, &a_s, argv[0]); + if (!a) + goto fail; + b = JS_ToBigInt(ctx, &b_s, argv[1]); + if (!b) { + JS_FreeBigInt(ctx, a, &a_s); + goto fail; + } + q = JS_GetBigInt(q_val); + r = JS_GetBigInt(r_val); + status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf); + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + if (unlikely(status)) { + throw_bf_exception(ctx, status); + goto fail; + } + q_val = JS_CompactBigInt(ctx, q_val); + if (magic & 0x10) { + JSValue ret; + ret = JS_NewArray(ctx); + if (JS_IsException(ret)) + goto fail; + JS_SetPropertyUint32(ctx, ret, 0, q_val); + JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val)); + return ret; + } else { + JS_FreeValue(ctx, r_val); + return q_val; + } +fail: + JS_FreeValue(ctx, q_val); + JS_FreeValue(ctx, r_val); + return JS_EXCEPTION; +} + +JSValue js_bigint_sqrt(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + bf_t a_s, *a, *r, *rem; + int status; + JSValue r_val, rem_val; + + r_val = JS_NewBigInt(ctx); + if (JS_IsException(r_val)) + return JS_EXCEPTION; + rem_val = JS_NewBigInt(ctx); + if (JS_IsException(rem_val)) + return JS_EXCEPTION; + r = JS_GetBigInt(r_val); + rem = JS_GetBigInt(rem_val); + + a = JS_ToBigInt(ctx, &a_s, argv[0]); + if (!a) + goto fail; + status = bf_sqrtrem(r, rem, a); + JS_FreeBigInt(ctx, a, &a_s); + if (unlikely(status & ~BF_ST_INEXACT)) { + throw_bf_exception(ctx, status); + goto fail; + } + r_val = JS_CompactBigInt(ctx, r_val); + if (magic) { + JSValue ret; + ret = JS_NewArray(ctx); + if (JS_IsException(ret)) + goto fail; + JS_SetPropertyUint32(ctx, ret, 0, r_val); + JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val)); + return ret; + } else { + JS_FreeValue(ctx, rem_val); + return r_val; + } +fail: + JS_FreeValue(ctx, r_val); + JS_FreeValue(ctx, rem_val); + return JS_EXCEPTION; +} + +JSValue js_bigint_op1(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + bf_t a_s, *a; + int64_t res; + + a = JS_ToBigInt(ctx, &a_s, argv[0]); + if (!a) + return JS_EXCEPTION; + switch(magic) { + case 0: /* floorLog2 */ + if (a->sign || a->expn <= 0) { + res = -1; + } else { + res = a->expn - 1; + } + break; + case 1: /* ctz */ + if (bf_is_zero(a)) { + res = -1; + } else { + res = bf_get_exp_min(a); + } + break; + default: + abort(); + } + JS_FreeBigInt(ctx, a, &a_s); + return JS_NewBigInt64(ctx, res); +} + +JSValue js_bigint_asUintN(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, int asIntN) +{ + uint64_t bits; + bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; + JSValue res; + + if (JS_ToIndex(ctx, &bits, argv[0])) + return JS_EXCEPTION; + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; + r = JS_GetBigInt(res); + a = JS_ToBigInt(ctx, &a_s, argv[1]); + if (!a) { + JS_FreeValue(ctx, res); + return JS_EXCEPTION; + } + /* XXX: optimize */ + r = JS_GetBigInt(res); + bf_init(ctx->bf_ctx, mask); + bf_set_ui(mask, 1); + bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); + bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ); + bf_logic_and(r, a, mask); + if (asIntN && bits != 0) { + bf_set_ui(mask, 1); + bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ); + if (bf_cmpu(r, mask) >= 0) { + bf_set_ui(mask, 1); + bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); + bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ); + } + } + bf_delete(mask); + JS_FreeBigInt(ctx, a, &a_s); + return JS_CompactBigInt(ctx, res); +} + +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 ), +}; + +const JSCFunctionListEntry js_bigint_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigint_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicBigInt(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValueConst obj1; + + rt->bigint_ops.to_string = js_bigint_to_string; + rt->bigint_ops.from_string = js_string_to_bigint; + rt->bigint_ops.unary_arith = js_unary_arith_bigint; + rt->bigint_ops.binary_arith = js_binary_arith_bigint; + rt->bigint_ops.compare = js_compare_bigfloat; + + ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], + js_bigint_proto_funcs, + countof(js_bigint_proto_funcs)); + obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_INT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, + countof(js_bigint_funcs)); +} + +/* BigFloat */ + +JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_IsBigFloat(this_val)) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_BIG_FLOAT) { + if (JS_IsBigFloat(p->u.object_data)) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a bigfloat"); +} + +JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int base; + JSValue ret; + + val = js_thisBigFloatValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (argc == 0 || JS_IsUndefined(argv[0])) { + base = 10; + } else { + base = js_get_radix(ctx, argv[0]); + if (base < 0) + goto fail; + } + ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); + JS_FreeValue(ctx, val); + return ret; +fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisBigFloatValue(ctx, this_val); +} + +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; +} + +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; +} + +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; +} + +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; +} + +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; +} + +const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { + JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ), + JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ), + JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ), + JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ), + JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ), +}; + +JSValue js_bigfloat_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue val; + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (argc == 0) { + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); + bf_set_zero(r, 0); + } else { + val = JS_DupValue(ctx, argv[0]); + redo: + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_BIG_FLOAT: + break; + case JS_TAG_FLOAT64: + { + bf_t *r; + double d = JS_VALUE_GET_FLOAT64(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_float64(r, d)) + goto fail; + } + break; + case JS_TAG_INT: + { + bf_t *r; + int32_t v = JS_VALUE_GET_INT(val); + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + if (bf_set_si(r, v)) + goto fail; + } + break; + case JS_TAG_BIG_INT: + /* We keep the full precision of the integer */ + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + val = JS_MKPTR(JS_TAG_BIG_FLOAT, p); + } + break; + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + break; + goto redo; + case JS_TAG_STRING: + { + const char *str, *p; + size_t len; + int err; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + bf_t *r; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + break; + r = JS_GetBigFloat(val); + bf_set_zero(r, 0); + err = 0; + } else { + val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | + ATOD_TYPE_BIG_FLOAT | + ATOD_ACCEPT_PREFIX_AFTER_SIGN); + if (JS_IsException(val)) { + JS_FreeCString(ctx, str); + return JS_EXCEPTION; + } + p += skip_spaces(p); + err = ((p - str) != len); + } + JS_FreeCString(ctx, str); + if (err) { + JS_FreeValue(ctx, val); + return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal"); + } + } + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + break; + goto redo; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert to bigfloat"); + } + } + return val; +fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +JSValue js_bigfloat_get_const(JSContext *ctx, + JSValueConst this_val, int magic) +{ + bf_t *r; + JSValue val; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + return val; + r = JS_GetBigFloat(val); + switch(magic) { + case 0: /* PI */ + bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case 1: /* LN2 */ + bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags); + break; + case 2: /* MIN_VALUE */ + case 3: /* MAX_VALUE */ + { + slimb_t e_range, e; + e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1); + bf_set_ui(r, 1); + if (magic == 2) { + e = -e_range + 2; + if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL) + e -= ctx->fp_env.prec - 1; + bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags); + } else { + bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec, + ctx->fp_env.flags); + bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags); + bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec, + ctx->fp_env.flags); + } + } + break; + case 4: /* EPSILON */ + bf_set_ui(r, 1); + bf_mul_2exp(r, 1 - ctx->fp_env.prec, + ctx->fp_env.prec, ctx->fp_env.flags); + break; + default: + abort(); + } + return val; +} + +JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + bf_t *a; + const char *str; + JSValue ret; + int radix; + JSFloatEnv *fe; + + str = JS_ToCString(ctx, argv[0]); + if (!str) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &radix, argv[1])) { + fail: + JS_FreeCString(ctx, str); + return JS_EXCEPTION; + } + if (radix != 0 && (radix < 2 || radix > 36)) { + JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + goto fail; + } + fe = &ctx->fp_env; + if (argc > 2) { + fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); + if (!fe) + goto fail; + } + ret = JS_NewBigFloat(ctx); + if (JS_IsException(ret)) + goto done; + a = JS_GetBigFloat(ret); + /* XXX: use js_atof() */ + bf_atof(a, str, NULL, radix, fe->prec, fe->flags); +done: + JS_FreeCString(ctx, str); + return ret; +} + +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)); +} + +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, +}; + +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; +} + +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; +} + +const JSCFunctionListEntry js_bigfloat_funcs[] = { + JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ), + JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ), + JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ), + JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ), + JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ), + JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ), + JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ), + JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ), + JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ), + JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ), + JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ), + JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ), + JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ), + JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ), + JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ), + JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ), + JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ), + JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ), + JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ), + JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ), + JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ), + JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ), + JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ), + JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ), + JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ), + JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ), + JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ), + JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ), + JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ), + JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ), + JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ), + JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ), +}; + +/* FloatEnv */ + +JSValue js_float_env_constructor(JSContext *ctx, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue obj; + JSFloatEnv *fe; + int64_t prec; + int flags, rndmode; + + prec = ctx->fp_env.prec; + flags = ctx->fp_env.flags; + if (!JS_IsUndefined(argv[0])) { + if (JS_ToInt64Sat(ctx, &prec, argv[0])) + return JS_EXCEPTION; + if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) + return JS_ThrowRangeError(ctx, "invalid precision"); + flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */ + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt32Sat(ctx, &rndmode, argv[1])) + return JS_EXCEPTION; + if (rndmode < BF_RNDN || rndmode > BF_RNDF) + return JS_ThrowRangeError(ctx, "invalid rounding mode"); + flags = rndmode; + } + } + + obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV); + if (JS_IsException(obj)) + return JS_EXCEPTION; + fe = js_malloc(ctx, sizeof(*fe)); + if (!fe) + return JS_EXCEPTION; + fe->prec = prec; + fe->flags = flags; + fe->status = 0; + JS_SetOpaque(obj, fe); + return obj; +} + +void js_float_env_finalizer(JSRuntime *rt, JSValue val) +{ + JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV); + js_free_rt(rt, fe); +} + +JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewInt64(ctx, ctx->fp_env.prec); +} + +JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) +{ + return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); +} + +JSValue js_float_env_setPrec(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst func; + int exp_bits, flags, saved_flags; + JSValue ret; + limb_t saved_prec; + int64_t prec; + + func = argv[0]; + if (JS_ToInt64Sat(ctx, &prec, argv[1])) + return JS_EXCEPTION; + if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) + return JS_ThrowRangeError(ctx, "invalid precision"); + exp_bits = BF_EXP_BITS_MAX; + + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) + return JS_EXCEPTION; + if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) + return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); + } + + flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); + + saved_prec = ctx->fp_env.prec; + saved_flags = ctx->fp_env.flags; + + ctx->fp_env.prec = prec; + ctx->fp_env.flags = flags; + + ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); + /* always restore the floating point precision */ + ctx->fp_env.prec = saved_prec; + ctx->fp_env.flags = saved_flags; + return ret; +} + +#define FE_PREC (-1) +#define FE_EXP (-2) +#define FE_RNDMODE (-3) +#define FE_SUBNORMAL (-4) + +JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic) +{ + JSFloatEnv *fe; + fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); + if (!fe) + return JS_EXCEPTION; + switch(magic) { + case FE_PREC: + return JS_NewInt64(ctx, fe->prec); + case FE_EXP: + return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags)); + case FE_RNDMODE: + return JS_NewInt32(ctx, fe->flags & BF_RND_MASK); + case FE_SUBNORMAL: + return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0); + default: + return JS_NewBool(ctx, (fe->status & magic) != 0); + } +} + +JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) +{ + JSFloatEnv *fe; + int b; + int64_t prec; + + fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); + if (!fe) + return JS_EXCEPTION; + switch(magic) { + case FE_PREC: + if (JS_ToInt64Sat(ctx, &prec, val)) + return JS_EXCEPTION; + if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) + return JS_ThrowRangeError(ctx, "invalid precision"); + fe->prec = prec; + break; + case FE_EXP: + if (JS_ToInt32Sat(ctx, &b, val)) + return JS_EXCEPTION; + if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) + return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); + fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | + bf_set_exp_bits(b); + break; + case FE_RNDMODE: + b = bigfloat_get_rnd_mode(ctx, val); + if (b < 0) + return JS_EXCEPTION; + fe->flags = (fe->flags & ~BF_RND_MASK) | b; + break; + case FE_SUBNORMAL: + b = JS_ToBool(ctx, val); + fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); + break; + default: + b = JS_ToBool(ctx, val); + fe->status = (fe->status & ~magic) & ((-b) & magic); + break; + } + return JS_UNDEFINED; +} + +JSValue js_float_env_clearStatus(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); + if (!fe) + return JS_EXCEPTION; + fe->status = 0; + return JS_UNDEFINED; +} + +const JSCFunctionListEntry js_float_env_funcs[] = { + JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ), + JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ), + JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ), + JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ), + JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ), + JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ), + JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ), + JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ), + JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ), + JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), + JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), + JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), + JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), + JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), +}; + +const JSCFunctionListEntry js_float_env_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_PREC ), + JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_EXP ), + JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_RNDMODE ), + JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status, + js_float_env_proto_set_status, FE_SUBNORMAL ), + JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_INVALID_OP ), + JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ), + JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_OVERFLOW ), + JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_UNDERFLOW ), + JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status, + js_float_env_proto_set_status, BF_ST_INEXACT ), + JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ), +}; + +void JS_AddIntrinsicBigFloat(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValueConst obj1; + + rt->bigfloat_ops.to_string = js_bigfloat_to_string; + rt->bigfloat_ops.from_string = js_string_to_bigfloat; + rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat; + rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat; + rt->bigfloat_ops.compare = js_compare_bigfloat; + rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64; + rt->bigfloat_ops.mul_pow10 = js_mul_pow10; + + ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT], + js_bigfloat_proto_funcs, + countof(js_bigfloat_proto_funcs)); + obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1, + ctx->class_proto[JS_CLASS_BIG_FLOAT]); + JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs, + countof(js_bigfloat_funcs)); + + ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV], + js_float_env_proto_funcs, + countof(js_float_env_proto_funcs)); + obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", + js_float_env_constructor, 1, + ctx->class_proto[JS_CLASS_FLOAT_ENV]); + JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, + countof(js_float_env_funcs)); +} + +/* BigDecimal */ + +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; +} + +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; +} + +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"); +} + +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); +} + +JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisBigDecimalValue(ctx, this_val); +} + +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; + +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; +} + + +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; + } +} + +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; +} + +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; +} + +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; +} + +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 ), +}; + +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 */ \ No newline at end of file diff --git a/src/core/builtins/js-big-num.h b/src/core/builtins/js-big-num.h index 5270949a1..f5bee5d7a 100644 --- a/src/core/builtins/js-big-num.h +++ b/src/core/builtins/js-big-num.h @@ -1,124 +1,124 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_BIG_NUM_H -#define QUICKJS_JS_BIG_NUM_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "../types.h" - -#if CONFIG_BIGNUM -#include "quickjs/libbf.h" - -JSValue JS_NewBigInt(JSContext *ctx); -JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v); - -no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op); -no_inline __exception int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op); -__exception int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op); -no_inline int js_not_slow(JSContext *ctx, JSValue *sp); -int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2); -int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2); -int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b); -int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, - JSValue *pres, JSValue op1, JSValue op2); -no_inline __exception int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op); -/* Note: also used for bigint */ -int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2); -int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, - JSValue op1, JSValue op2); -no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op); -no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq); -no_inline int js_shr_slow(JSContext *ctx, JSValue *sp); -JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, - int64_t exponent); -no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp); -JSBigFloat *js_new_bf(JSContext *ctx); -void js_float_env_finalizer(JSRuntime *rt, JSValue val); -JSValue JS_NewBigFloat(JSContext *ctx); -static inline bf_t *JS_GetBigFloat(JSValueConst val) -{ - JSBigFloat *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -JSValue JS_NewBigDecimal(JSContext *ctx); -static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) -{ - JSBigDecimal *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -JSValue JS_NewBigInt(JSContext *ctx); -static inline bf_t *JS_GetBigInt(JSValueConst val) { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - return &p->num; -} -JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, - BOOL convert_to_safe_integer); -no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp); -JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); -int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); -bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); -__maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val); -void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); -bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val); -JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, - BOOL allow_null_or_undefined); -bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); - -#else - -no_inline __exception int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op); -__exception int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op); -no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp); -no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op); -no_inline __exception int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op); -no_inline int js_not_slow(JSContext *ctx, JSValue *sp); -no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op); -no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq); -no_inline int js_shr_slow(JSContext *ctx, JSValue *sp); -#endif - - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_BIG_NUM_H +#define QUICKJS_JS_BIG_NUM_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "../types.h" + +#if CONFIG_BIGNUM +#include "quickjs/libbf.h" + +JSValue JS_NewBigInt(JSContext *ctx); +JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v); + +no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op); +no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op); +__exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op); +no_inline int js_not_slow(JSContext *ctx, JSValue *sp); +int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2); +int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2); +int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b); +int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2); +no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op); +/* Note: also used for bigint */ +int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2); +int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2); +no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op); +no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq); +no_inline int js_shr_slow(JSContext *ctx, JSValue *sp); +JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, + int64_t exponent); +no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp); +JSBigFloat *js_new_bf(JSContext *ctx); +void js_float_env_finalizer(JSRuntime *rt, JSValue val); +JSValue JS_NewBigFloat(JSContext *ctx); +static inline bf_t *JS_GetBigFloat(JSValueConst val) +{ + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +JSValue JS_NewBigDecimal(JSContext *ctx); +static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) +{ + JSBigDecimal *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +JSValue JS_NewBigInt(JSContext *ctx); +static inline bf_t *JS_GetBigInt(JSValueConst val) { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, + BOOL convert_to_safe_integer); +no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp); +JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); +int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); +bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); +__maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val); +void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); +bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val); +JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, + BOOL allow_null_or_undefined); +bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); + +#else + +no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op); +__exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op); +no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp); +no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op); +no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op); +no_inline int js_not_slow(JSContext *ctx, JSValue *sp); +no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op); +no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq); +no_inline int js_shr_slow(JSContext *ctx, JSValue *sp); +#endif + + #endif \ No newline at end of file diff --git a/src/core/builtins/js-boolean.c b/src/core/builtins/js-boolean.c index 3343d48ea..bcb2fa2f6 100644 --- a/src/core/builtins/js-boolean.c +++ b/src/core/builtins/js-boolean.c @@ -1,67 +1,67 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-boolean.h" -#include "../exception.h" -#include "../object.h" - -/* Boolean */ -JSValue js_boolean_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { - JSValue val, obj; - val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0])); - if (!JS_IsUndefined(new_target)) { - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN); - if (!JS_IsException(obj)) - JS_SetObjectData(ctx, obj, val); - return obj; - } else { - return val; - } -} - -JSValue js_thisBooleanValue(JSContext* ctx, JSValueConst this_val) { - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject* p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_BOOLEAN) { - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL) - return p->u.object_data; - } - } - return JS_ThrowTypeError(ctx, "not a boolean"); -} - -JSValue js_boolean_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue val = js_thisBooleanValue(ctx, this_val); - if (JS_IsException(val)) - return val; - return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? JS_ATOM_true : JS_ATOM_false); -} - -JSValue js_boolean_valueOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return js_thisBooleanValue(ctx, this_val); -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-boolean.h" +#include "../exception.h" +#include "../object.h" + +/* Boolean */ +JSValue js_boolean_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { + JSValue val, obj; + val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0])); + if (!JS_IsUndefined(new_target)) { + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN); + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, val); + return obj; + } else { + return val; + } +} + +JSValue js_thisBooleanValue(JSContext* ctx, JSValueConst this_val) { + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject* p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_BOOLEAN) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL) + return p->u.object_data; + } + } + return JS_ThrowTypeError(ctx, "not a boolean"); +} + +JSValue js_boolean_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue val = js_thisBooleanValue(ctx, this_val); + if (JS_IsException(val)) + return val; + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? JS_ATOM_true : JS_ATOM_false); +} + +JSValue js_boolean_valueOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return js_thisBooleanValue(ctx, this_val); +} diff --git a/src/core/builtins/js-boolean.h b/src/core/builtins/js-boolean.h index a3bce2d63..aff2dc736 100644 --- a/src/core/builtins/js-boolean.h +++ b/src/core/builtins/js-boolean.h @@ -1,36 +1,36 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_BOOLEAN_H -#define QUICKJS_JS_BOOLEAN_H - -#include "quickjs/quickjs.h" - -JSValue js_boolean_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); -JSValue js_thisBooleanValue(JSContext* ctx, JSValueConst this_val); -JSValue js_boolean_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_boolean_valueOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_BOOLEAN_H +#define QUICKJS_JS_BOOLEAN_H + +#include "quickjs/quickjs.h" + +JSValue js_boolean_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); +JSValue js_thisBooleanValue(JSContext* ctx, JSValueConst this_val); +JSValue js_boolean_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_boolean_valueOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-closures.c b/src/core/builtins/js-closures.c index 9f844e5b8..5426d73a6 100644 --- a/src/core/builtins/js-closures.c +++ b/src/core/builtins/js-closures.c @@ -1,195 +1,195 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-closures.h" -#include "../gc.h" -#include "../object.h" -#include "js-function.h" -#include "quickjs/list.h" - -JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, - int var_idx, BOOL is_arg) -{ - JSVarRef *var_ref; - struct list_head *el; - - list_for_each(el, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { - var_ref->header.ref_count++; - return var_ref; - } - } - /* create a new one */ - var_ref = js_malloc(ctx, sizeof(JSVarRef)); - if (!var_ref) - return NULL; - var_ref->header.ref_count = 1; - var_ref->is_detached = FALSE; - var_ref->is_arg = is_arg; - var_ref->var_idx = var_idx; - list_add_tail(&var_ref->header.link, &sf->var_ref_list); - if (is_arg) - var_ref->pvalue = &sf->arg_buf[var_idx]; - else - var_ref->pvalue = &sf->var_buf[var_idx]; - var_ref->value = JS_UNDEFINED; - return var_ref; -} - -JSValue js_closure2(JSContext *ctx, JSValue func_obj, - JSFunctionBytecode *b, - JSVarRef **cur_var_refs, - JSStackFrame *sf) -{ - JSObject *p; - JSVarRef **var_refs; - int i; - - p = JS_VALUE_GET_OBJ(func_obj); - p->u.func.function_bytecode = b; - p->u.func.home_object = NULL; - p->u.func.var_refs = NULL; - if (b->closure_var_count) { - var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); - if (!var_refs) - goto fail; - p->u.func.var_refs = var_refs; - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - JSVarRef *var_ref; - if (cv->is_local) { - /* reuse the existing variable reference if it already exists */ - var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); - if (!var_ref) - goto fail; - } else { - var_ref = cur_var_refs[cv->var_idx]; - var_ref->header.ref_count++; - } - var_refs[i] = var_ref; - } - } - return func_obj; -fail: - /* bfunc is freed when func_obj is freed */ - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - - -JSValue js_closure(JSContext *ctx, JSValue bfunc, - JSVarRef **cur_var_refs, - JSStackFrame *sf) -{ - JSFunctionBytecode *b; - JSValue func_obj; - JSAtom name_atom; - - b = JS_VALUE_GET_PTR(bfunc); - func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); - if (JS_IsException(func_obj)) { - JS_FreeValue(ctx, bfunc); - return JS_EXCEPTION; - } - func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); - if (JS_IsException(func_obj)) { - /* bfunc has been freed */ - goto fail; - } - name_atom = b->func_name; - if (name_atom == JS_ATOM_NULL) - name_atom = JS_ATOM_empty_string; - js_function_set_properties(ctx, func_obj, name_atom, - b->defined_arg_count); - - if (b->func_kind & JS_FUNC_GENERATOR) { - JSValue proto; - int proto_class_id; - /* generators have a prototype field which is used as - prototype for the generator object */ - if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) - proto_class_id = JS_CLASS_ASYNC_GENERATOR; - else - proto_class_id = JS_CLASS_GENERATOR; - proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); - if (JS_IsException(proto)) - goto fail; - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, - JS_PROP_WRITABLE); - } else if (b->has_prototype) { - /* add the 'prototype' property: delay instantiation to avoid - creating cycles for every javascript function. The prototype - object is created on the fly when first accessed */ - JS_SetConstructorBit(ctx, func_obj, TRUE); - JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, - JS_AUTOINIT_ID_PROTOTYPE, NULL, - JS_PROP_WRITABLE); - } - return func_obj; -fail: - /* bfunc is freed when func_obj is freed */ - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - -void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) -{ - struct list_head *el, *el1; - JSVarRef *var_ref; - int var_idx = idx; - - list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { - var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]); - var_ref->pvalue = &var_ref->value; - list_del(&var_ref->header.link); - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; - add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); - } - } -} - - -void close_var_refs(JSRuntime* rt, JSStackFrame* sf) { - struct list_head *el, *el1; - JSVarRef* var_ref; - int var_idx; - - list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); - var_idx = var_ref->var_idx; - if (var_ref->is_arg) - var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); - else - var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); - var_ref->pvalue = &var_ref->value; - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; - add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); - } +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-closures.h" +#include "../gc.h" +#include "../object.h" +#include "js-function.h" +#include "quickjs/list.h" + +JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, + int var_idx, BOOL is_arg) +{ + JSVarRef *var_ref; + struct list_head *el; + + list_for_each(el, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { + var_ref->header.ref_count++; + return var_ref; + } + } + /* create a new one */ + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + var_ref->is_detached = FALSE; + var_ref->is_arg = is_arg; + var_ref->var_idx = var_idx; + list_add_tail(&var_ref->header.link, &sf->var_ref_list); + if (is_arg) + var_ref->pvalue = &sf->arg_buf[var_idx]; + else + var_ref->pvalue = &sf->var_buf[var_idx]; + var_ref->value = JS_UNDEFINED; + return var_ref; +} + +JSValue js_closure2(JSContext *ctx, JSValue func_obj, + JSFunctionBytecode *b, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSObject *p; + JSVarRef **var_refs; + int i; + + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + if (cv->is_local) { + /* reuse the existing variable reference if it already exists */ + var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); + if (!var_ref) + goto fail; + } else { + var_ref = cur_var_refs[cv->var_idx]; + var_ref->header.ref_count++; + } + var_refs[i] = var_ref; + } + } + return func_obj; +fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + + +JSValue js_closure(JSContext *ctx, JSValue bfunc, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSFunctionBytecode *b; + JSValue func_obj; + JSAtom name_atom; + + b = JS_VALUE_GET_PTR(bfunc); + func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); + if (JS_IsException(func_obj)) { + JS_FreeValue(ctx, bfunc); + return JS_EXCEPTION; + } + func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); + if (JS_IsException(func_obj)) { + /* bfunc has been freed */ + goto fail; + } + name_atom = b->func_name; + if (name_atom == JS_ATOM_NULL) + name_atom = JS_ATOM_empty_string; + js_function_set_properties(ctx, func_obj, name_atom, + b->defined_arg_count); + + if (b->func_kind & JS_FUNC_GENERATOR) { + JSValue proto; + int proto_class_id; + /* generators have a prototype field which is used as + prototype for the generator object */ + if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) + proto_class_id = JS_CLASS_ASYNC_GENERATOR; + else + proto_class_id = JS_CLASS_GENERATOR; + proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); + if (JS_IsException(proto)) + goto fail; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, + JS_PROP_WRITABLE); + } else if (b->has_prototype) { + /* add the 'prototype' property: delay instantiation to avoid + creating cycles for every javascript function. The prototype + object is created on the fly when first accessed */ + JS_SetConstructorBit(ctx, func_obj, TRUE); + JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, + JS_AUTOINIT_ID_PROTOTYPE, NULL, + JS_PROP_WRITABLE); + } + return func_obj; +fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx = idx; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { + var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + list_del(&var_ref->header.link); + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } + } +} + + +void close_var_refs(JSRuntime* rt, JSStackFrame* sf) { + struct list_head *el, *el1; + JSVarRef* var_ref; + int var_idx; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + var_idx = var_ref->var_idx; + if (var_ref->is_arg) + var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); + else + var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } } \ No newline at end of file diff --git a/src/core/builtins/js-closures.h b/src/core/builtins/js-closures.h index ff4d24d1b..f3e753476 100644 --- a/src/core/builtins/js-closures.h +++ b/src/core/builtins/js-closures.h @@ -1,49 +1,49 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_CLOSURES_H -#define QUICKJS_JS_CLOSURES_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "../types.h" - -void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg); - -JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, - int var_idx, BOOL is_arg); - -JSValue js_closure2(JSContext *ctx, JSValue func_obj, - JSFunctionBytecode *b, - JSVarRef **cur_var_refs, - JSStackFrame *sf); - -JSValue js_closure(JSContext *ctx, JSValue bfunc, - JSVarRef **cur_var_refs, - JSStackFrame *sf); - -void close_var_refs(JSRuntime* rt, JSStackFrame* sf); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_CLOSURES_H +#define QUICKJS_JS_CLOSURES_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "../types.h" + +void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg); + +JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, + int var_idx, BOOL is_arg); + +JSValue js_closure2(JSContext *ctx, JSValue func_obj, + JSFunctionBytecode *b, + JSVarRef **cur_var_refs, + JSStackFrame *sf); + +JSValue js_closure(JSContext *ctx, JSValue bfunc, + JSVarRef **cur_var_refs, + JSStackFrame *sf); + +void close_var_refs(JSRuntime* rt, JSStackFrame* sf); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-date.c b/src/core/builtins/js-date.c index 3c4792bfe..70d1f8886 100644 --- a/src/core/builtins/js-date.c +++ b/src/core/builtins/js-date.c @@ -1,970 +1,970 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-date.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "js-object.h" - -/* Date */ - -int64_t math_mod(int64_t a, int64_t b) { - /* return positive modulo */ - int64_t m = a % b; - return m + (m < 0) * b; -} - -int64_t floor_div(int64_t a, int64_t b) { - /* integer division rounding toward -Infinity */ - int64_t m = a % b; - return (a - (m + (m < 0) * b)) / b; -} - -#if 0 -/* OS dependent: return the UTC time in ms since 1970. */ -JSValue js___date_now(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t d; - struct timeval tv; - gettimeofday(&tv, NULL); - d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); - return JS_NewInt64(ctx, d); -} -#endif - -/* OS dependent. d = argv[0] is in ms from 1970. Return the difference - between UTC time and local time 'd' in minutes */ -int getTimezoneOffset(int64_t time) { -#if defined(_WIN32) - /* XXX: TODO */ - return 0; -#else - time_t ti; - struct tm tm; - - time /= 1000; /* convert to seconds */ - if (sizeof(time_t) == 4) { - /* on 32-bit systems, we need to clamp the time value to the - range of `time_t`. This is better than truncating values to - 32 bits and hopefully provides the same result as 64-bit - implementation of localtime_r. - */ - if ((time_t)-1 < 0) { - if (time < INT32_MIN) { - time = INT32_MIN; - } else if (time > INT32_MAX) { - time = INT32_MAX; - } - } else { - if (time < 0) { - time = 0; - } else if (time > UINT32_MAX) { - time = UINT32_MAX; - } - } - } - ti = time; - localtime_r(&ti, &tm); - return -tm.tm_gmtoff / 60; -#endif -} - -/* OS dependent: return the UTC time in microseconds since 1970. */ -JSValue js___date_clock(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - int64_t d; - struct timeval tv; - gettimeofday(&tv, NULL); - d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; - return JS_NewInt64(ctx, d); -} - -__exception int JS_ThisTimeValue(JSContext* ctx, double* valp, JSValueConst this_val) { - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject* p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) - return JS_ToFloat64(ctx, valp, p->u.object_data); - } - JS_ThrowTypeError(ctx, "not a Date object"); - return -1; -} - -JSValue JS_SetThisTimeValue(JSContext* ctx, JSValueConst this_val, double v) { - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject* p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_DATE) { - JS_FreeValue(ctx, p->u.object_data); - p->u.object_data = JS_NewFloat64(ctx, v); - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a Date object"); -} - -int64_t days_from_year(int64_t y) { - return 365 * (y - 1970) + floor_div(y - 1969, 4) - floor_div(y - 1901, 100) + floor_div(y - 1601, 400); -} - -int64_t days_in_year(int64_t y) { - return 365 + !(y % 4) - !(y % 100) + !(y % 400); -} - -/* return the year, update days */ -int64_t year_from_days(int64_t* days) { - int64_t y, d1, nd, d = *days; - y = floor_div(d * 10000, 3652425) + 1970; - /* the initial approximation is very good, so only a few - iterations are necessary */ - for (;;) { - d1 = d - days_from_year(y); - if (d1 < 0) { - y--; - d1 += days_in_year(y); - } else { - nd = days_in_year(y); - if (d1 < nd) - break; - d1 -= nd; - y++; - } - } - *days = d1; - return y; -} - -int const month_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; -char const day_names[] = "SunMonTueWedThuFriSat"; - -__exception int get_date_fields(JSContext* ctx, JSValueConst obj, double fields[9], int is_local, int force) { - double dval; - int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; - - if (JS_ThisTimeValue(ctx, &dval, obj)) - return -1; - - if (isnan(dval)) { - if (!force) - return FALSE; /* NaN */ - d = 0; /* initialize all fields to 0 */ - } else { - d = dval; - if (is_local) { - tz = -getTimezoneOffset(d); - d += tz * 60000; - } - } - - /* result is >= 0, we can use % */ - h = math_mod(d, 86400000); - days = (d - h) / 86400000; - ms = h % 1000; - h = (h - ms) / 1000; - s = h % 60; - h = (h - s) / 60; - m = h % 60; - h = (h - m) / 60; - wd = math_mod(days + 4, 7); /* week day */ - y = year_from_days(&days); - - for (i = 0; i < 11; i++) { - md = month_days[i]; - if (i == 1) - md += days_in_year(y) - 365; - if (days < md) - break; - days -= md; - } - fields[0] = y; - fields[1] = i; - fields[2] = days + 1; - fields[3] = h; - fields[4] = m; - fields[5] = s; - fields[6] = ms; - fields[7] = wd; - fields[8] = tz; - return TRUE; -} - -double time_clip(double t) { - if (t >= -8.64e15 && t <= 8.64e15) - return trunc(t) + 0.0; /* convert -0 to +0 */ - else - return NAN; -} - -/* The spec mandates the use of 'double' and it fixes the order - of the operations */ -double set_date_fields(double fields[], int is_local) { - int64_t y; - double days, d, h, m1; - int i, m, md; - - m1 = fields[1]; - m = fmod(m1, 12); - if (m < 0) - m += 12; - y = (int64_t)(fields[0] + floor(m1 / 12)); - days = days_from_year(y); - - for (i = 0; i < m; i++) { - md = month_days[i]; - if (i == 1) - md += days_in_year(y) - 365; - days += md; - } - days += fields[2] - 1; - h = fields[3] * 3600000 + fields[4] * 60000 + fields[5] * 1000 + fields[6]; - d = days * 86400000 + h; - if (is_local) - d += getTimezoneOffset(d) * 60000; - return time_clip(d); -} - -JSValue get_date_field(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - // get_date_field(obj, n, is_local) - double fields[9]; - int res, n, is_local; - - is_local = magic & 0x0F; - n = (magic >> 4) & 0x0F; - res = get_date_fields(ctx, this_val, fields, is_local, 0); - if (res < 0) - return JS_EXCEPTION; - if (!res) - return JS_NAN; - - if (magic & 0x100) { // getYear - fields[0] -= 1900; - } - return JS_NewFloat64(ctx, fields[n]); -} - -JSValue set_date_field(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - // _field(obj, first_field, end_field, args, is_local) - double fields[9]; - int res, first_field, end_field, is_local, i, n; - double d, a; - - d = NAN; - first_field = (magic >> 8) & 0x0F; - end_field = (magic >> 4) & 0x0F; - is_local = magic & 0x0F; - - res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); - if (res < 0) - return JS_EXCEPTION; - if (res && argc > 0) { - n = end_field - first_field; - if (argc < n) - n = argc; - for (i = 0; i < n; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isfinite(a)) - goto done; - fields[first_field + i] = trunc(a); - } - d = set_date_fields(fields, is_local); - } -done: - return JS_SetThisTimeValue(ctx, this_val, d); -} - -/* fmt: - 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" - 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" - 2: toISOString: "2018-01-02T23:02:56.927Z" - 3: toLocaleString: "1/2/2018, 11:40:40 PM" - part: 1=date, 2=time 3=all - XXX: should use a variant of strftime(). - */ -JSValue get_date_string(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - // _string(obj, fmt, part) - char buf[64]; - double fields[9]; - int res, fmt, part, pos; - int y, mon, d, h, m, s, ms, wd, tz; - - fmt = (magic >> 4) & 0x0F; - part = magic & 0x0F; - - res = get_date_fields(ctx, this_val, fields, fmt & 1, 0); - if (res < 0) - return JS_EXCEPTION; - if (!res) { - if (fmt == 2) - return JS_ThrowRangeError(ctx, "Date value is NaN"); - else - return JS_NewString(ctx, "Invalid Date"); - } - - y = fields[0]; - mon = fields[1]; - d = fields[2]; - h = fields[3]; - m = fields[4]; - s = fields[5]; - ms = fields[6]; - wd = fields[7]; - tz = fields[8]; - - pos = 0; - - if (part & 1) { /* date part */ - switch (fmt) { - case 0: - pos += snprintf(buf + pos, sizeof(buf) - pos, "%.3s, %02d %.3s %0*d ", day_names + wd * 3, d, month_names + mon * 3, 4 + (y < 0), y); - break; - case 1: - pos += snprintf(buf + pos, sizeof(buf) - pos, "%.3s %.3s %02d %0*d", day_names + wd * 3, month_names + mon * 3, d, 4 + (y < 0), y); - if (part == 3) { - buf[pos++] = ' '; - } - break; - case 2: - if (y >= 0 && y <= 9999) { - pos += snprintf(buf + pos, sizeof(buf) - pos, "%04d", y); - } else { - pos += snprintf(buf + pos, sizeof(buf) - pos, "%+07d", y); - } - pos += snprintf(buf + pos, sizeof(buf) - pos, "-%02d-%02dT", mon + 1, d); - break; - case 3: - pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y); - if (part == 3) { - buf[pos++] = ','; - buf[pos++] = ' '; - } - break; - } - } - if (part & 2) { /* time part */ - switch (fmt) { - case 0: - pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d:%02d:%02d GMT", h, m, s); - break; - case 1: - pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d:%02d:%02d GMT", h, m, s); - if (tz < 0) { - buf[pos++] = '-'; - tz = -tz; - } else { - buf[pos++] = '+'; - } - /* tz is >= 0, can use % */ - pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d%02d", tz / 60, tz % 60); - /* XXX: tack the time zone code? */ - break; - case 2: - pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d:%02d:%02d.%03dZ", h, m, s, ms); - break; - case 3: - pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s, (h < 12) ? 'A' : 'P'); - break; - } - } - return JS_NewStringLen(ctx, buf, pos); -} - -/* OS dependent: return the UTC time in ms since 1970. */ -int64_t date_now(void) { - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); -} - -JSValue js_date_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { - // Date(y, mon, d, h, m, s, ms) - JSValue rv; - int i, n; - double a, val; - - if (JS_IsUndefined(new_target)) { - /* invoked as function */ - argc = 0; - } - n = argc; - if (n == 0) { - val = date_now(); - } else if (n == 1) { - JSValue v, dv; - if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { - JSObject* p = JS_VALUE_GET_OBJ(argv[0]); - if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) { - if (JS_ToFloat64(ctx, &val, p->u.object_data)) - return JS_EXCEPTION; - val = time_clip(val); - goto has_val; - } - } - v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); - if (JS_IsString(v)) { - dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst*)&v); - JS_FreeValue(ctx, v); - if (JS_IsException(dv)) - return JS_EXCEPTION; - if (JS_ToFloat64Free(ctx, &val, dv)) - return JS_EXCEPTION; - } else { - if (JS_ToFloat64Free(ctx, &val, v)) - return JS_EXCEPTION; - } - val = time_clip(val); - } else { - double fields[] = {0, 0, 1, 0, 0, 0, 0}; - if (n > 7) - n = 7; - for (i = 0; i < n; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isfinite(a)) - break; - fields[i] = trunc(a); - if (i == 0 && fields[0] >= 0 && fields[0] < 100) - fields[0] += 1900; - } - val = (i == n) ? set_date_fields(fields, 1) : NAN; - } -has_val: -#if 0 - JSValueConst args[3]; - args[0] = new_target; - args[1] = ctx->class_proto[JS_CLASS_DATE]; - args[2] = JS_NewFloat64(ctx, val); - rv = js___date_create(ctx, JS_UNDEFINED, 3, args); -#else - rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); - if (!JS_IsException(rv)) - JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); -#endif - if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { - /* invoked as a function, return (new Date()).toString(); */ - JSValue s; - s = get_date_string(ctx, rv, 0, NULL, 0x13); - JS_FreeValue(ctx, rv); - rv = s; - } - return rv; -} - -JSValue js_Date_UTC(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // UTC(y, mon, d, h, m, s, ms) - double fields[] = {0, 0, 1, 0, 0, 0, 0}; - int i, n; - double a; - - n = argc; - if (n == 0) - return JS_NAN; - if (n > 7) - n = 7; - for (i = 0; i < n; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isfinite(a)) - return JS_NAN; - fields[i] = trunc(a); - if (i == 0 && fields[0] >= 0 && fields[0] < 100) - fields[0] += 1900; - } - return JS_NewFloat64(ctx, set_date_fields(fields, 0)); -} - -void string_skip_spaces(JSString* sp, int* pp) { - while (*pp < sp->len && string_get(sp, *pp) == ' ') - *pp += 1; -} - -void string_skip_non_spaces(JSString* sp, int* pp) { - while (*pp < sp->len && string_get(sp, *pp) != ' ') - *pp += 1; -} - -/* parse a numeric field with an optional sign if accept_sign is TRUE */ -int string_get_digits(JSString* sp, int* pp, int64_t* pval) { - int64_t v = 0; - int c, p = *pp, p_start; - - if (p >= sp->len) - return -1; - p_start = p; - while (p < sp->len) { - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) { - if (p == p_start) - return -1; - else - break; - } - v = v * 10 + c - '0'; - p++; - } - *pval = v; - *pp = p; - return 0; -} - -int string_get_signed_digits(JSString* sp, int* pp, int64_t* pval) { - int res, sgn, p = *pp; - - if (p >= sp->len) - return -1; - - sgn = string_get(sp, p); - if (sgn == '-' || sgn == '+') - p++; - - res = string_get_digits(sp, &p, pval); - if (res == 0 && sgn == '-') - *pval = -*pval; - *pp = p; - return res; -} - -/* parse a fixed width numeric field */ -int string_get_fixed_width_digits(JSString* sp, int* pp, int n, int64_t* pval) { - int64_t v = 0; - int i, c, p = *pp; - - for (i = 0; i < n; i++) { - if (p >= sp->len) - return -1; - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) - return -1; - v = v * 10 + c - '0'; - p++; - } - *pval = v; - *pp = p; - return 0; -} - -int string_get_milliseconds(JSString* sp, int* pp, int64_t* pval) { - /* parse milliseconds as a fractional part, round to nearest */ - /* XXX: the spec does not indicate which rounding should be used */ - int mul = 1000, ms = 0, p = *pp, c, p_start; - if (p >= sp->len) - return -1; - p_start = p; - while (p < sp->len) { - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) { - if (p == p_start) - return -1; - else - break; - } - if (mul == 1 && c >= '5') - ms += 1; - ms += (c - '0') * (mul /= 10); - p++; - } - *pval = ms; - *pp = p; - return 0; -} - -int find_abbrev(JSString* sp, int p, const char* list, int count) { - int n, i; - - if (p + 3 <= sp->len) { - for (n = 0; n < count; n++) { - for (i = 0; i < 3; i++) { - if (string_get(sp, p + i) != month_names[n * 3 + i]) - goto next; - } - return n; - next:; - } - } - return -1; -} - -int string_get_month(JSString* sp, int* pp, int64_t* pval) { - int n; - - string_skip_spaces(sp, pp); - n = find_abbrev(sp, *pp, month_names, 12); - if (n < 0) - return -1; - - *pval = n; - *pp += 3; - return 0; -} - -JSValue js_Date_parse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // parse(s) - JSValue s, rv; - int64_t fields[] = {0, 1, 1, 0, 0, 0, 0}; - double fields1[7]; - int64_t tz, hh, mm; - double d; - int p, i, c, sgn, l; - JSString* sp; - BOOL is_local; - - rv = JS_NAN; - - s = JS_ToString(ctx, argv[0]); - if (JS_IsException(s)) - return JS_EXCEPTION; - - sp = JS_VALUE_GET_STRING(s); - p = 0; - if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) { - /* ISO format */ - /* year field can be negative */ - if (string_get_signed_digits(sp, &p, &fields[0])) - goto done; - - for (i = 1; i < 7; i++) { - if (p >= sp->len) - break; - switch (i) { - case 1: - case 2: - c = '-'; - break; - case 3: - c = 'T'; - break; - case 4: - case 5: - c = ':'; - break; - case 6: - c = '.'; - break; - } - if (string_get(sp, p) != c) - break; - p++; - if (i == 6) { - if (string_get_milliseconds(sp, &p, &fields[i])) - goto done; - } else { - if (string_get_digits(sp, &p, &fields[i])) - goto done; - } - } - /* no time: UTC by default */ - is_local = (i > 3); - fields[1] -= 1; - - /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ - tz = 0; - if (p < sp->len) { - sgn = string_get(sp, p); - if (sgn == '+' || sgn == '-') { - p++; - l = sp->len - p; - if (l != 4 && l != 5) - goto done; - if (string_get_fixed_width_digits(sp, &p, 2, &hh)) - goto done; - if (l == 5) { - if (string_get(sp, p) != ':') - goto done; - p++; - } - if (string_get_fixed_width_digits(sp, &p, 2, &mm)) - goto done; - tz = hh * 60 + mm; - if (sgn == '-') - tz = -tz; - is_local = FALSE; - } else if (sgn == 'Z') { - p++; - is_local = FALSE; - } else { - goto done; - } - /* error if extraneous characters */ - if (p != sp->len) - goto done; - } - } else { - /* toString or toUTCString format */ - /* skip the day of the week */ - string_skip_non_spaces(sp, &p); - string_skip_spaces(sp, &p); - if (p >= sp->len) - goto done; - c = string_get(sp, p); - if (c >= '0' && c <= '9') { - /* day of month first */ - if (string_get_digits(sp, &p, &fields[2])) - goto done; - if (string_get_month(sp, &p, &fields[1])) - goto done; - } else { - /* month first */ - if (string_get_month(sp, &p, &fields[1])) - goto done; - string_skip_spaces(sp, &p); - if (string_get_digits(sp, &p, &fields[2])) - goto done; - } - /* year */ - string_skip_spaces(sp, &p); - if (string_get_signed_digits(sp, &p, &fields[0])) - goto done; - - /* hour, min, seconds */ - string_skip_spaces(sp, &p); - for (i = 0; i < 3; i++) { - if (i == 1 || i == 2) { - if (p >= sp->len) - goto done; - if (string_get(sp, p) != ':') - goto done; - p++; - } - if (string_get_digits(sp, &p, &fields[3 + i])) - goto done; - } - // XXX: parse optional milliseconds? - - /* parse the time zone offset if present: [+-]HHmm */ - is_local = FALSE; - tz = 0; - for (tz = 0; p < sp->len; p++) { - sgn = string_get(sp, p); - if (sgn == '+' || sgn == '-') { - p++; - if (string_get_fixed_width_digits(sp, &p, 2, &hh)) - goto done; - if (string_get_fixed_width_digits(sp, &p, 2, &mm)) - goto done; - tz = hh * 60 + mm; - if (sgn == '-') - tz = -tz; - break; - } - } - } - for (i = 0; i < 7; i++) - fields1[i] = fields[i]; - d = set_date_fields(fields1, is_local) - tz * 60000; - rv = JS_NewFloat64(ctx, d); - -done: - JS_FreeValue(ctx, s); - return rv; -} - -JSValue js_Date_now(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // now() - return JS_NewInt64(ctx, date_now()); -} - -JSValue js_date_Symbol_toPrimitive(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // Symbol_toPrimitive(hint) - JSValueConst obj = this_val; - JSAtom hint = JS_ATOM_NULL; - int hint_num; - - if (!JS_IsObject(obj)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - if (JS_IsString(argv[0])) { - hint = JS_ValueToAtom(ctx, argv[0]); - if (hint == JS_ATOM_NULL) - return JS_EXCEPTION; - JS_FreeAtom(ctx, hint); - } - switch (hint) { - case JS_ATOM_number: -#ifdef CONFIG_BIGNUM - case JS_ATOM_integer: -#endif - hint_num = HINT_NUMBER; - break; - case JS_ATOM_string: - case JS_ATOM_default: - hint_num = HINT_STRING; - break; - default: - return JS_ThrowTypeError(ctx, "invalid hint"); - } - return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); -} - -JSValue js_date_getTimezoneOffset(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // getTimezoneOffset() - double v; - - if (JS_ThisTimeValue(ctx, &v, this_val)) - return JS_EXCEPTION; - if (isnan(v)) - return JS_NAN; - else - return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); -} - -JSValue js_date_getTime(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // getTime() - double v; - - if (JS_ThisTimeValue(ctx, &v, this_val)) - return JS_EXCEPTION; - return JS_NewFloat64(ctx, v); -} - -JSValue js_date_setTime(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // setTime(v) - double v; - - if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) - return JS_EXCEPTION; - return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); -} - -JSValue js_date_setYear(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // setYear(y) - double y; - JSValueConst args[1]; - - if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) - return JS_EXCEPTION; - y = +y; - if (isfinite(y)) { - y = trunc(y); - if (y >= 0 && y < 100) - y += 1900; - } - args[0] = JS_NewFloat64(ctx, y); - return set_date_field(ctx, this_val, 1, args, 0x011); -} - -JSValue js_date_toJSON(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // toJSON(key) - JSValue obj, tv, method, rv; - double d; - - rv = JS_EXCEPTION; - tv = JS_UNDEFINED; - - obj = JS_ToObject(ctx, this_val); - tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); - if (JS_IsException(tv)) - goto exception; - if (JS_IsNumber(tv)) { - if (JS_ToFloat64(ctx, &d, tv) < 0) - goto exception; - if (!isfinite(d)) { - rv = JS_NULL; - goto done; - } - } - method = JS_GetPropertyStr(ctx, obj, "toISOString"); - if (JS_IsException(method)) - goto exception; - if (!JS_IsFunction(ctx, method)) { - JS_ThrowTypeError(ctx, "object needs toISOString method"); - JS_FreeValue(ctx, method); - goto exception; - } - rv = JS_CallFree(ctx, method, obj, 0, NULL); -exception: -done: - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, tv); - return rv; -} - -const JSCFunctionListEntry js_date_funcs[] = { - JS_CFUNC_DEF("now", 0, js_Date_now), - JS_CFUNC_DEF("parse", 1, js_Date_parse), - JS_CFUNC_DEF("UTC", 7, js_Date_UTC), -}; - -const JSCFunctionListEntry js_date_proto_funcs[] = { - JS_CFUNC_DEF("valueOf", 0, js_date_getTime), - JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13), - JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive), - JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03), - JS_ALIAS_DEF("toGMTString", "toUTCString"), - JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23), - JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11), - JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12), - JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33), - JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31), - JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32), - JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset), - JS_CFUNC_DEF("getTime", 0, js_date_getTime), - JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101), - JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01), - JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00), - JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11), - JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10), - JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21), - JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20), - JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31), - JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30), - JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41), - JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40), - JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51), - JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50), - JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61), - JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60), - JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71), - JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70), - JS_CFUNC_DEF("setTime", 1, js_date_setTime), - JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671), - JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670), - JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571), - JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570), - JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471), - JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470), - JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371), - JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370), - JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231), - JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230), - JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131), - JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130), - JS_CFUNC_DEF("setYear", 1, js_date_setYear), - JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031), - JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030), - JS_CFUNC_DEF("toJSON", 1, js_date_toJSON), -}; - -void JS_AddIntrinsicDate(JSContext* ctx) { - JSValueConst obj; - - /* Date */ - ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs, countof(js_date_proto_funcs)); - obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7, ctx->class_proto[JS_CLASS_DATE]); - JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs)); +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-date.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "js-object.h" + +/* Date */ + +int64_t math_mod(int64_t a, int64_t b) { + /* return positive modulo */ + int64_t m = a % b; + return m + (m < 0) * b; +} + +int64_t floor_div(int64_t a, int64_t b) { + /* integer division rounding toward -Infinity */ + int64_t m = a % b; + return (a - (m + (m < 0) * b)) / b; +} + +#if 0 +/* OS dependent: return the UTC time in ms since 1970. */ +JSValue js___date_now(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t d; + struct timeval tv; + gettimeofday(&tv, NULL); + d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); + return JS_NewInt64(ctx, d); +} +#endif + +/* OS dependent. d = argv[0] is in ms from 1970. Return the difference + between UTC time and local time 'd' in minutes */ +int getTimezoneOffset(int64_t time) { +#if defined(_WIN32) + /* XXX: TODO */ + return 0; +#else + time_t ti; + struct tm tm; + + time /= 1000; /* convert to seconds */ + if (sizeof(time_t) == 4) { + /* on 32-bit systems, we need to clamp the time value to the + range of `time_t`. This is better than truncating values to + 32 bits and hopefully provides the same result as 64-bit + implementation of localtime_r. + */ + if ((time_t)-1 < 0) { + if (time < INT32_MIN) { + time = INT32_MIN; + } else if (time > INT32_MAX) { + time = INT32_MAX; + } + } else { + if (time < 0) { + time = 0; + } else if (time > UINT32_MAX) { + time = UINT32_MAX; + } + } + } + ti = time; + localtime_r(&ti, &tm); + return -tm.tm_gmtoff / 60; +#endif +} + +/* OS dependent: return the UTC time in microseconds since 1970. */ +JSValue js___date_clock(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + int64_t d; + struct timeval tv; + gettimeofday(&tv, NULL); + d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; + return JS_NewInt64(ctx, d); +} + +__exception int JS_ThisTimeValue(JSContext* ctx, double* valp, JSValueConst this_val) { + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject* p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) + return JS_ToFloat64(ctx, valp, p->u.object_data); + } + JS_ThrowTypeError(ctx, "not a Date object"); + return -1; +} + +JSValue JS_SetThisTimeValue(JSContext* ctx, JSValueConst this_val, double v) { + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject* p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_DATE) { + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = JS_NewFloat64(ctx, v); + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a Date object"); +} + +int64_t days_from_year(int64_t y) { + return 365 * (y - 1970) + floor_div(y - 1969, 4) - floor_div(y - 1901, 100) + floor_div(y - 1601, 400); +} + +int64_t days_in_year(int64_t y) { + return 365 + !(y % 4) - !(y % 100) + !(y % 400); +} + +/* return the year, update days */ +int64_t year_from_days(int64_t* days) { + int64_t y, d1, nd, d = *days; + y = floor_div(d * 10000, 3652425) + 1970; + /* the initial approximation is very good, so only a few + iterations are necessary */ + for (;;) { + d1 = d - days_from_year(y); + if (d1 < 0) { + y--; + d1 += days_in_year(y); + } else { + nd = days_in_year(y); + if (d1 < nd) + break; + d1 -= nd; + y++; + } + } + *days = d1; + return y; +} + +int const month_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; +char const day_names[] = "SunMonTueWedThuFriSat"; + +__exception int get_date_fields(JSContext* ctx, JSValueConst obj, double fields[9], int is_local, int force) { + double dval; + int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; + + if (JS_ThisTimeValue(ctx, &dval, obj)) + return -1; + + if (isnan(dval)) { + if (!force) + return FALSE; /* NaN */ + d = 0; /* initialize all fields to 0 */ + } else { + d = dval; + if (is_local) { + tz = -getTimezoneOffset(d); + d += tz * 60000; + } + } + + /* result is >= 0, we can use % */ + h = math_mod(d, 86400000); + days = (d - h) / 86400000; + ms = h % 1000; + h = (h - ms) / 1000; + s = h % 60; + h = (h - s) / 60; + m = h % 60; + h = (h - m) / 60; + wd = math_mod(days + 4, 7); /* week day */ + y = year_from_days(&days); + + for (i = 0; i < 11; i++) { + md = month_days[i]; + if (i == 1) + md += days_in_year(y) - 365; + if (days < md) + break; + days -= md; + } + fields[0] = y; + fields[1] = i; + fields[2] = days + 1; + fields[3] = h; + fields[4] = m; + fields[5] = s; + fields[6] = ms; + fields[7] = wd; + fields[8] = tz; + return TRUE; +} + +double time_clip(double t) { + if (t >= -8.64e15 && t <= 8.64e15) + return trunc(t) + 0.0; /* convert -0 to +0 */ + else + return NAN; +} + +/* The spec mandates the use of 'double' and it fixes the order + of the operations */ +double set_date_fields(double fields[], int is_local) { + int64_t y; + double days, d, h, m1; + int i, m, md; + + m1 = fields[1]; + m = fmod(m1, 12); + if (m < 0) + m += 12; + y = (int64_t)(fields[0] + floor(m1 / 12)); + days = days_from_year(y); + + for (i = 0; i < m; i++) { + md = month_days[i]; + if (i == 1) + md += days_in_year(y) - 365; + days += md; + } + days += fields[2] - 1; + h = fields[3] * 3600000 + fields[4] * 60000 + fields[5] * 1000 + fields[6]; + d = days * 86400000 + h; + if (is_local) + d += getTimezoneOffset(d) * 60000; + return time_clip(d); +} + +JSValue get_date_field(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + // get_date_field(obj, n, is_local) + double fields[9]; + int res, n, is_local; + + is_local = magic & 0x0F; + n = (magic >> 4) & 0x0F; + res = get_date_fields(ctx, this_val, fields, is_local, 0); + if (res < 0) + return JS_EXCEPTION; + if (!res) + return JS_NAN; + + if (magic & 0x100) { // getYear + fields[0] -= 1900; + } + return JS_NewFloat64(ctx, fields[n]); +} + +JSValue set_date_field(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + // _field(obj, first_field, end_field, args, is_local) + double fields[9]; + int res, first_field, end_field, is_local, i, n; + double d, a; + + d = NAN; + first_field = (magic >> 8) & 0x0F; + end_field = (magic >> 4) & 0x0F; + is_local = magic & 0x0F; + + res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); + if (res < 0) + return JS_EXCEPTION; + if (res && argc > 0) { + n = end_field - first_field; + if (argc < n) + n = argc; + for (i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + goto done; + fields[first_field + i] = trunc(a); + } + d = set_date_fields(fields, is_local); + } +done: + return JS_SetThisTimeValue(ctx, this_val, d); +} + +/* fmt: + 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" + 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" + 2: toISOString: "2018-01-02T23:02:56.927Z" + 3: toLocaleString: "1/2/2018, 11:40:40 PM" + part: 1=date, 2=time 3=all + XXX: should use a variant of strftime(). + */ +JSValue get_date_string(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + // _string(obj, fmt, part) + char buf[64]; + double fields[9]; + int res, fmt, part, pos; + int y, mon, d, h, m, s, ms, wd, tz; + + fmt = (magic >> 4) & 0x0F; + part = magic & 0x0F; + + res = get_date_fields(ctx, this_val, fields, fmt & 1, 0); + if (res < 0) + return JS_EXCEPTION; + if (!res) { + if (fmt == 2) + return JS_ThrowRangeError(ctx, "Date value is NaN"); + else + return JS_NewString(ctx, "Invalid Date"); + } + + y = fields[0]; + mon = fields[1]; + d = fields[2]; + h = fields[3]; + m = fields[4]; + s = fields[5]; + ms = fields[6]; + wd = fields[7]; + tz = fields[8]; + + pos = 0; + + if (part & 1) { /* date part */ + switch (fmt) { + case 0: + pos += snprintf(buf + pos, sizeof(buf) - pos, "%.3s, %02d %.3s %0*d ", day_names + wd * 3, d, month_names + mon * 3, 4 + (y < 0), y); + break; + case 1: + pos += snprintf(buf + pos, sizeof(buf) - pos, "%.3s %.3s %02d %0*d", day_names + wd * 3, month_names + mon * 3, d, 4 + (y < 0), y); + if (part == 3) { + buf[pos++] = ' '; + } + break; + case 2: + if (y >= 0 && y <= 9999) { + pos += snprintf(buf + pos, sizeof(buf) - pos, "%04d", y); + } else { + pos += snprintf(buf + pos, sizeof(buf) - pos, "%+07d", y); + } + pos += snprintf(buf + pos, sizeof(buf) - pos, "-%02d-%02dT", mon + 1, d); + break; + case 3: + pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y); + if (part == 3) { + buf[pos++] = ','; + buf[pos++] = ' '; + } + break; + } + } + if (part & 2) { /* time part */ + switch (fmt) { + case 0: + pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d:%02d:%02d GMT", h, m, s); + break; + case 1: + pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d:%02d:%02d GMT", h, m, s); + if (tz < 0) { + buf[pos++] = '-'; + tz = -tz; + } else { + buf[pos++] = '+'; + } + /* tz is >= 0, can use % */ + pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d%02d", tz / 60, tz % 60); + /* XXX: tack the time zone code? */ + break; + case 2: + pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d:%02d:%02d.%03dZ", h, m, s, ms); + break; + case 3: + pos += snprintf(buf + pos, sizeof(buf) - pos, "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s, (h < 12) ? 'A' : 'P'); + break; + } + } + return JS_NewStringLen(ctx, buf, pos); +} + +/* OS dependent: return the UTC time in ms since 1970. */ +int64_t date_now(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); +} + +JSValue js_date_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { + // Date(y, mon, d, h, m, s, ms) + JSValue rv; + int i, n; + double a, val; + + if (JS_IsUndefined(new_target)) { + /* invoked as function */ + argc = 0; + } + n = argc; + if (n == 0) { + val = date_now(); + } else if (n == 1) { + JSValue v, dv; + if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { + JSObject* p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) { + if (JS_ToFloat64(ctx, &val, p->u.object_data)) + return JS_EXCEPTION; + val = time_clip(val); + goto has_val; + } + } + v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); + if (JS_IsString(v)) { + dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst*)&v); + JS_FreeValue(ctx, v); + if (JS_IsException(dv)) + return JS_EXCEPTION; + if (JS_ToFloat64Free(ctx, &val, dv)) + return JS_EXCEPTION; + } else { + if (JS_ToFloat64Free(ctx, &val, v)) + return JS_EXCEPTION; + } + val = time_clip(val); + } else { + double fields[] = {0, 0, 1, 0, 0, 0, 0}; + if (n > 7) + n = 7; + for (i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + break; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; + } + val = (i == n) ? set_date_fields(fields, 1) : NAN; + } +has_val: +#if 0 + JSValueConst args[3]; + args[0] = new_target; + args[1] = ctx->class_proto[JS_CLASS_DATE]; + args[2] = JS_NewFloat64(ctx, val); + rv = js___date_create(ctx, JS_UNDEFINED, 3, args); +#else + rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); + if (!JS_IsException(rv)) + JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); +#endif + if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { + /* invoked as a function, return (new Date()).toString(); */ + JSValue s; + s = get_date_string(ctx, rv, 0, NULL, 0x13); + JS_FreeValue(ctx, rv); + rv = s; + } + return rv; +} + +JSValue js_Date_UTC(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // UTC(y, mon, d, h, m, s, ms) + double fields[] = {0, 0, 1, 0, 0, 0, 0}; + int i, n; + double a; + + n = argc; + if (n == 0) + return JS_NAN; + if (n > 7) + n = 7; + for (i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + return JS_NAN; + fields[i] = trunc(a); + if (i == 0 && fields[0] >= 0 && fields[0] < 100) + fields[0] += 1900; + } + return JS_NewFloat64(ctx, set_date_fields(fields, 0)); +} + +void string_skip_spaces(JSString* sp, int* pp) { + while (*pp < sp->len && string_get(sp, *pp) == ' ') + *pp += 1; +} + +void string_skip_non_spaces(JSString* sp, int* pp) { + while (*pp < sp->len && string_get(sp, *pp) != ' ') + *pp += 1; +} + +/* parse a numeric field with an optional sign if accept_sign is TRUE */ +int string_get_digits(JSString* sp, int* pp, int64_t* pval) { + int64_t v = 0; + int c, p = *pp, p_start; + + if (p >= sp->len) + return -1; + p_start = p; + while (p < sp->len) { + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) { + if (p == p_start) + return -1; + else + break; + } + v = v * 10 + c - '0'; + p++; + } + *pval = v; + *pp = p; + return 0; +} + +int string_get_signed_digits(JSString* sp, int* pp, int64_t* pval) { + int res, sgn, p = *pp; + + if (p >= sp->len) + return -1; + + sgn = string_get(sp, p); + if (sgn == '-' || sgn == '+') + p++; + + res = string_get_digits(sp, &p, pval); + if (res == 0 && sgn == '-') + *pval = -*pval; + *pp = p; + return res; +} + +/* parse a fixed width numeric field */ +int string_get_fixed_width_digits(JSString* sp, int* pp, int n, int64_t* pval) { + int64_t v = 0; + int i, c, p = *pp; + + for (i = 0; i < n; i++) { + if (p >= sp->len) + return -1; + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) + return -1; + v = v * 10 + c - '0'; + p++; + } + *pval = v; + *pp = p; + return 0; +} + +int string_get_milliseconds(JSString* sp, int* pp, int64_t* pval) { + /* parse milliseconds as a fractional part, round to nearest */ + /* XXX: the spec does not indicate which rounding should be used */ + int mul = 1000, ms = 0, p = *pp, c, p_start; + if (p >= sp->len) + return -1; + p_start = p; + while (p < sp->len) { + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) { + if (p == p_start) + return -1; + else + break; + } + if (mul == 1 && c >= '5') + ms += 1; + ms += (c - '0') * (mul /= 10); + p++; + } + *pval = ms; + *pp = p; + return 0; +} + +int find_abbrev(JSString* sp, int p, const char* list, int count) { + int n, i; + + if (p + 3 <= sp->len) { + for (n = 0; n < count; n++) { + for (i = 0; i < 3; i++) { + if (string_get(sp, p + i) != month_names[n * 3 + i]) + goto next; + } + return n; + next:; + } + } + return -1; +} + +int string_get_month(JSString* sp, int* pp, int64_t* pval) { + int n; + + string_skip_spaces(sp, pp); + n = find_abbrev(sp, *pp, month_names, 12); + if (n < 0) + return -1; + + *pval = n; + *pp += 3; + return 0; +} + +JSValue js_Date_parse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // parse(s) + JSValue s, rv; + int64_t fields[] = {0, 1, 1, 0, 0, 0, 0}; + double fields1[7]; + int64_t tz, hh, mm; + double d; + int p, i, c, sgn, l; + JSString* sp; + BOOL is_local; + + rv = JS_NAN; + + s = JS_ToString(ctx, argv[0]); + if (JS_IsException(s)) + return JS_EXCEPTION; + + sp = JS_VALUE_GET_STRING(s); + p = 0; + if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) { + /* ISO format */ + /* year field can be negative */ + if (string_get_signed_digits(sp, &p, &fields[0])) + goto done; + + for (i = 1; i < 7; i++) { + if (p >= sp->len) + break; + switch (i) { + case 1: + case 2: + c = '-'; + break; + case 3: + c = 'T'; + break; + case 4: + case 5: + c = ':'; + break; + case 6: + c = '.'; + break; + } + if (string_get(sp, p) != c) + break; + p++; + if (i == 6) { + if (string_get_milliseconds(sp, &p, &fields[i])) + goto done; + } else { + if (string_get_digits(sp, &p, &fields[i])) + goto done; + } + } + /* no time: UTC by default */ + is_local = (i > 3); + fields[1] -= 1; + + /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ + tz = 0; + if (p < sp->len) { + sgn = string_get(sp, p); + if (sgn == '+' || sgn == '-') { + p++; + l = sp->len - p; + if (l != 4 && l != 5) + goto done; + if (string_get_fixed_width_digits(sp, &p, 2, &hh)) + goto done; + if (l == 5) { + if (string_get(sp, p) != ':') + goto done; + p++; + } + if (string_get_fixed_width_digits(sp, &p, 2, &mm)) + goto done; + tz = hh * 60 + mm; + if (sgn == '-') + tz = -tz; + is_local = FALSE; + } else if (sgn == 'Z') { + p++; + is_local = FALSE; + } else { + goto done; + } + /* error if extraneous characters */ + if (p != sp->len) + goto done; + } + } else { + /* toString or toUTCString format */ + /* skip the day of the week */ + string_skip_non_spaces(sp, &p); + string_skip_spaces(sp, &p); + if (p >= sp->len) + goto done; + c = string_get(sp, p); + if (c >= '0' && c <= '9') { + /* day of month first */ + if (string_get_digits(sp, &p, &fields[2])) + goto done; + if (string_get_month(sp, &p, &fields[1])) + goto done; + } else { + /* month first */ + if (string_get_month(sp, &p, &fields[1])) + goto done; + string_skip_spaces(sp, &p); + if (string_get_digits(sp, &p, &fields[2])) + goto done; + } + /* year */ + string_skip_spaces(sp, &p); + if (string_get_signed_digits(sp, &p, &fields[0])) + goto done; + + /* hour, min, seconds */ + string_skip_spaces(sp, &p); + for (i = 0; i < 3; i++) { + if (i == 1 || i == 2) { + if (p >= sp->len) + goto done; + if (string_get(sp, p) != ':') + goto done; + p++; + } + if (string_get_digits(sp, &p, &fields[3 + i])) + goto done; + } + // XXX: parse optional milliseconds? + + /* parse the time zone offset if present: [+-]HHmm */ + is_local = FALSE; + tz = 0; + for (tz = 0; p < sp->len; p++) { + sgn = string_get(sp, p); + if (sgn == '+' || sgn == '-') { + p++; + if (string_get_fixed_width_digits(sp, &p, 2, &hh)) + goto done; + if (string_get_fixed_width_digits(sp, &p, 2, &mm)) + goto done; + tz = hh * 60 + mm; + if (sgn == '-') + tz = -tz; + break; + } + } + } + for (i = 0; i < 7; i++) + fields1[i] = fields[i]; + d = set_date_fields(fields1, is_local) - tz * 60000; + rv = JS_NewFloat64(ctx, d); + +done: + JS_FreeValue(ctx, s); + return rv; +} + +JSValue js_Date_now(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // now() + return JS_NewInt64(ctx, date_now()); +} + +JSValue js_date_Symbol_toPrimitive(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // Symbol_toPrimitive(hint) + JSValueConst obj = this_val; + JSAtom hint = JS_ATOM_NULL; + int hint_num; + + if (!JS_IsObject(obj)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + if (JS_IsString(argv[0])) { + hint = JS_ValueToAtom(ctx, argv[0]); + if (hint == JS_ATOM_NULL) + return JS_EXCEPTION; + JS_FreeAtom(ctx, hint); + } + switch (hint) { + case JS_ATOM_number: +#ifdef CONFIG_BIGNUM + case JS_ATOM_integer: +#endif + hint_num = HINT_NUMBER; + break; + case JS_ATOM_string: + case JS_ATOM_default: + hint_num = HINT_STRING; + break; + default: + return JS_ThrowTypeError(ctx, "invalid hint"); + } + return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); +} + +JSValue js_date_getTimezoneOffset(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // getTimezoneOffset() + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + if (isnan(v)) + return JS_NAN; + else + return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); +} + +JSValue js_date_getTime(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // getTime() + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val)) + return JS_EXCEPTION; + return JS_NewFloat64(ctx, v); +} + +JSValue js_date_setTime(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // setTime(v) + double v; + + if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) + return JS_EXCEPTION; + return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); +} + +JSValue js_date_setYear(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // setYear(y) + double y; + JSValueConst args[1]; + + if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) + return JS_EXCEPTION; + y = +y; + if (isfinite(y)) { + y = trunc(y); + if (y >= 0 && y < 100) + y += 1900; + } + args[0] = JS_NewFloat64(ctx, y); + return set_date_field(ctx, this_val, 1, args, 0x011); +} + +JSValue js_date_toJSON(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // toJSON(key) + JSValue obj, tv, method, rv; + double d; + + rv = JS_EXCEPTION; + tv = JS_UNDEFINED; + + obj = JS_ToObject(ctx, this_val); + tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); + if (JS_IsException(tv)) + goto exception; + if (JS_IsNumber(tv)) { + if (JS_ToFloat64(ctx, &d, tv) < 0) + goto exception; + if (!isfinite(d)) { + rv = JS_NULL; + goto done; + } + } + method = JS_GetPropertyStr(ctx, obj, "toISOString"); + if (JS_IsException(method)) + goto exception; + if (!JS_IsFunction(ctx, method)) { + JS_ThrowTypeError(ctx, "object needs toISOString method"); + JS_FreeValue(ctx, method); + goto exception; + } + rv = JS_CallFree(ctx, method, obj, 0, NULL); +exception: +done: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, tv); + return rv; +} + +const JSCFunctionListEntry js_date_funcs[] = { + JS_CFUNC_DEF("now", 0, js_Date_now), + JS_CFUNC_DEF("parse", 1, js_Date_parse), + JS_CFUNC_DEF("UTC", 7, js_Date_UTC), +}; + +const JSCFunctionListEntry js_date_proto_funcs[] = { + JS_CFUNC_DEF("valueOf", 0, js_date_getTime), + JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13), + JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive), + JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03), + JS_ALIAS_DEF("toGMTString", "toUTCString"), + JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23), + JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11), + JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33), + JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31), + JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32), + JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset), + JS_CFUNC_DEF("getTime", 0, js_date_getTime), + JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101), + JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01), + JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00), + JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11), + JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10), + JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21), + JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20), + JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31), + JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30), + JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41), + JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40), + JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51), + JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50), + JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61), + JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60), + JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71), + JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70), + JS_CFUNC_DEF("setTime", 1, js_date_setTime), + JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671), + JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670), + JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571), + JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570), + JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471), + JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470), + JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371), + JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370), + JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231), + JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230), + JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131), + JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130), + JS_CFUNC_DEF("setYear", 1, js_date_setYear), + JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031), + JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030), + JS_CFUNC_DEF("toJSON", 1, js_date_toJSON), +}; + +void JS_AddIntrinsicDate(JSContext* ctx) { + JSValueConst obj; + + /* Date */ + ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs, countof(js_date_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7, ctx->class_proto[JS_CLASS_DATE]); + JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs)); } \ No newline at end of file diff --git a/src/core/builtins/js-date.h b/src/core/builtins/js-date.h index ad25753e4..5997d7848 100644 --- a/src/core/builtins/js-date.h +++ b/src/core/builtins/js-date.h @@ -1,81 +1,81 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_DATE_H -#define QUICKJS_JS_DATE_H - -#include "quickjs/quickjs.h" -#include "../types.h" - -JSValue js___date_clock(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -__exception int JS_ThisTimeValue(JSContext* ctx, double* valp, JSValueConst this_val); -JSValue JS_SetThisTimeValue(JSContext* ctx, JSValueConst this_val, double v); -int64_t days_from_year(int64_t y); -int64_t days_in_year(int64_t y); -/* return the year, update days */ -int64_t year_from_days(int64_t *days); -__exception int get_date_fields(JSContext *ctx, JSValueConst obj, - double fields[9], int is_local, int force); -double time_clip(double t); -/* The spec mandates the use of 'double' and it fixes the order - of the operations */ -double set_date_fields(double fields[], int is_local); -JSValue get_date_field(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -JSValue set_date_field(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -/* fmt: - 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" - 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" - 2: toISOString: "2018-01-02T23:02:56.927Z" - 3: toLocaleString: "1/2/2018, 11:40:40 PM" - part: 1=date, 2=time 3=all - XXX: should use a variant of strftime(). - */ -JSValue get_date_string(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -/* OS dependent: return the UTC time in ms since 1970. */ -int64_t date_now(void); -JSValue js_date_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); -JSValue js_Date_UTC(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -void string_skip_spaces(JSString* sp, int* pp); -void string_skip_non_spaces(JSString* sp, int* pp); -/* parse a numeric field with an optional sign if accept_sign is TRUE */ -int string_get_digits(JSString* sp, int* pp, int64_t* pval); -int string_get_signed_digits(JSString* sp, int* pp, int64_t* pval); -/* parse a fixed width numeric field */ -int string_get_fixed_width_digits(JSString* sp, int* pp, int n, int64_t* pval); -int string_get_milliseconds(JSString* sp, int* pp, int64_t* pval); -int find_abbrev(JSString* sp, int p, const char* list, int count); -int string_get_month(JSString* sp, int* pp, int64_t* pval); -JSValue js_Date_parse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_Date_now(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_date_Symbol_toPrimitive(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_date_getTimezoneOffset(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_date_getTime(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_date_setTime(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_date_setYear(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_date_toJSON(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_DATE_H +#define QUICKJS_JS_DATE_H + +#include "quickjs/quickjs.h" +#include "../types.h" + +JSValue js___date_clock(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +__exception int JS_ThisTimeValue(JSContext* ctx, double* valp, JSValueConst this_val); +JSValue JS_SetThisTimeValue(JSContext* ctx, JSValueConst this_val, double v); +int64_t days_from_year(int64_t y); +int64_t days_in_year(int64_t y); +/* return the year, update days */ +int64_t year_from_days(int64_t *days); +__exception int get_date_fields(JSContext *ctx, JSValueConst obj, + double fields[9], int is_local, int force); +double time_clip(double t); +/* The spec mandates the use of 'double' and it fixes the order + of the operations */ +double set_date_fields(double fields[], int is_local); +JSValue get_date_field(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +JSValue set_date_field(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +/* fmt: + 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" + 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" + 2: toISOString: "2018-01-02T23:02:56.927Z" + 3: toLocaleString: "1/2/2018, 11:40:40 PM" + part: 1=date, 2=time 3=all + XXX: should use a variant of strftime(). + */ +JSValue get_date_string(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +/* OS dependent: return the UTC time in ms since 1970. */ +int64_t date_now(void); +JSValue js_date_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); +JSValue js_Date_UTC(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +void string_skip_spaces(JSString* sp, int* pp); +void string_skip_non_spaces(JSString* sp, int* pp); +/* parse a numeric field with an optional sign if accept_sign is TRUE */ +int string_get_digits(JSString* sp, int* pp, int64_t* pval); +int string_get_signed_digits(JSString* sp, int* pp, int64_t* pval); +/* parse a fixed width numeric field */ +int string_get_fixed_width_digits(JSString* sp, int* pp, int n, int64_t* pval); +int string_get_milliseconds(JSString* sp, int* pp, int64_t* pval); +int find_abbrev(JSString* sp, int p, const char* list, int count); +int string_get_month(JSString* sp, int* pp, int64_t* pval); +JSValue js_Date_parse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_Date_now(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_date_Symbol_toPrimitive(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_date_getTimezoneOffset(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_date_getTime(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_date_setTime(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_date_setYear(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_date_toJSON(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-function.c b/src/core/builtins/js-function.c index 875979f46..f76a66eaf 100644 --- a/src/core/builtins/js-function.c +++ b/src/core/builtins/js-function.c @@ -1,644 +1,644 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-function.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../gc.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "../types.h" -#include "js-closures.h" -#include "js-operator.h" - -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); -} - -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); -} - -void js_bytecode_function_finalizer(JSRuntime* rt, JSValue val) { - JSObject *p1, *p = JS_VALUE_GET_OBJ(val); - JSFunctionBytecode* b; - JSVarRef** var_refs; - int i; - - p1 = p->u.func.home_object; - if (p1) { - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); - } - b = p->u.func.function_bytecode; - if (b) { - var_refs = p->u.func.var_refs; - if (var_refs) { - for (i = 0; i < b->closure_var_count; i++) - free_var_ref(rt, var_refs[i]); - js_free_rt(rt, var_refs); - } - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); - } -} - -void js_bytecode_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSVarRef** var_refs = p->u.func.var_refs; - JSFunctionBytecode* b = p->u.func.function_bytecode; - int i; - - if (p->u.func.home_object) { - JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), mark_func); - } - if (b) { - if (var_refs) { - for (i = 0; i < b->closure_var_count; i++) { - JSVarRef* var_ref = var_refs[i]; - if (var_ref && var_ref->is_detached) { - mark_func(rt, &var_ref->header); - } - } - } - /* must mark the function bytecode because template objects may be - part of a cycle */ - JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); - } -} - -void js_bound_function_finalizer(JSRuntime* rt, JSValue val) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSBoundFunction* bf = p->u.bound_function; - int i; - - JS_FreeValueRT(rt, bf->func_obj); - JS_FreeValueRT(rt, bf->this_val); - for (i = 0; i < bf->argc; i++) { - JS_FreeValueRT(rt, bf->argv[i]); - } - js_free_rt(rt, bf); -} - -void js_bound_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSBoundFunction* bf = p->u.bound_function; - int i; - - JS_MarkValue(rt, bf->func_obj, mark_func); - JS_MarkValue(rt, bf->this_val, mark_func); - for (i = 0; i < bf->argc; i++) - JS_MarkValue(rt, bf->argv[i], mark_func); -} - -void free_arg_list(JSContext* ctx, JSValue* tab, uint32_t len) { - uint32_t i; - for (i = 0; i < len; i++) { - JS_FreeValue(ctx, tab[i]); - } - js_free(ctx, tab); -} - -JSValue js_function_proto(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_UNDEFINED; -} - -/* XXX: add a specific eval mode so that Function("}), ({") is rejected */ -JSValue js_function_constructor(JSContext* ctx, - JSValueConst new_target, - int argc, - JSValueConst* argv, - int magic) { - JSFunctionKindEnum func_kind = magic; - int i, n, ret; - JSValue s, proto, obj = JS_UNDEFINED; - StringBuffer b_s, *b = &b_s; - - string_buffer_init(ctx, b, 0); - string_buffer_putc8(b, '('); - - if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) { - string_buffer_puts8(b, "async "); - } - string_buffer_puts8(b, "function"); - - if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) { - string_buffer_putc8(b, '*'); - } - string_buffer_puts8(b, " anonymous("); - - n = argc - 1; - for (i = 0; i < n; i++) { - if (i != 0) { - string_buffer_putc8(b, ','); - } - if (string_buffer_concat_value(b, argv[i])) - goto fail; - } - string_buffer_puts8(b, "\n) {\n"); - if (n >= 0) { - if (string_buffer_concat_value(b, argv[n])) - goto fail; - } - string_buffer_puts8(b, "\n})"); - s = string_buffer_end(b); - if (JS_IsException(s)) - goto fail1; - - obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1); - JS_FreeValue(ctx, s); - if (JS_IsException(obj)) - goto fail1; - if (!JS_IsUndefined(new_target)) { - /* set the prototype */ - proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); - if (JS_IsException(proto)) - goto fail1; - if (!JS_IsObject(proto)) { - JSContext* realm; - JS_FreeValue(ctx, proto); - realm = JS_GetFunctionRealm(ctx, new_target); - if (!realm) - goto fail1; - proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]); - } - ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); - JS_FreeValue(ctx, proto); - if (ret < 0) - goto fail1; - } - return obj; - -fail: - string_buffer_free(b); -fail1: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -__exception int js_get_length32(JSContext* ctx, uint32_t* pres, JSValueConst obj) { - JSValue len_val; - len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); - if (JS_IsException(len_val)) { - *pres = 0; - return -1; - } - return JS_ToUint32Free(ctx, pres, len_val); -} - -__exception int js_get_length64(JSContext* ctx, int64_t* pres, JSValueConst obj) { - JSValue len_val; - len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); - if (JS_IsException(len_val)) { - *pres = 0; - return -1; - } - return JS_ToLengthFree(ctx, pres, len_val); -} - -/* XXX: should use ValueArray */ -JSValue* build_arg_list(JSContext* ctx, uint32_t* plen, JSValueConst array_arg) { - uint32_t len, i; - JSValue *tab, ret; - JSObject* p; - - if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) { - JS_ThrowTypeError(ctx, "not a object"); - return NULL; - } - if (js_get_length32(ctx, &len, array_arg)) - return NULL; - if (len > JS_MAX_LOCAL_VARS) { - JS_ThrowInternalError(ctx, "too many arguments"); - return NULL; - } - /* avoid allocating 0 bytes */ - tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); - if (!tab) - return NULL; - p = JS_VALUE_GET_OBJ(array_arg); - if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) && p->fast_array && - len == p->u.array.count) { - for (i = 0; i < len; i++) { - tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]); - } - } else { - for (i = 0; i < len; i++) { - ret = JS_GetPropertyUint32(ctx, array_arg, i); - if (JS_IsException(ret)) { - free_arg_list(ctx, tab, i); - return NULL; - } - tab[i] = ret; - } - } - *plen = len; - return tab; -} - -void js_function_set_properties(JSContext* ctx, JSValueConst func_obj, JSAtom name, int len) { - /* ES6 feature non compatible with ES5.1: length is configurable */ - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len), JS_PROP_CONFIGURABLE); - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); -} - -/* magic value: 0 = normal apply, 1 = apply for constructor, 2 = - Reflect.apply */ -JSValue js_function_apply(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - JSValueConst this_arg, array_arg; - uint32_t len; - JSValue *tab, ret; - - if (check_function(ctx, this_val)) - return JS_EXCEPTION; - this_arg = argv[0]; - array_arg = argv[1]; - if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) { - return JS_Call(ctx, this_val, this_arg, 0, NULL); - } - tab = build_arg_list(ctx, &len, array_arg); - if (!tab) - return JS_EXCEPTION; - if (magic & 1) { - ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst*)tab); - } else { - ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst*)tab); - } - free_arg_list(ctx, tab, len); - return ret; -} - -JSValue js_function_call(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - if (argc <= 0) { - return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL); - } else { - return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1); - } -} - -JSValue js_function_bind(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSBoundFunction* bf; - JSValue func_obj, name1, len_val; - JSObject* p; - int arg_count, i, ret; - - if (check_function(ctx, this_val)) - return JS_EXCEPTION; - - func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, JS_CLASS_BOUND_FUNCTION); - if (JS_IsException(func_obj)) - return JS_EXCEPTION; - p = JS_VALUE_GET_OBJ(func_obj); - p->is_constructor = JS_IsConstructor(ctx, this_val); - arg_count = max_int(0, argc - 1); - bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue)); - if (!bf) - goto exception; - bf->func_obj = JS_DupValue(ctx, this_val); - bf->this_val = JS_DupValue(ctx, argv[0]); - bf->argc = arg_count; - for (i = 0; i < arg_count; i++) { - bf->argv[i] = JS_DupValue(ctx, argv[i + 1]); - } - p->u.bound_function = bf; - - /* XXX: the spec could be simpler by only using GetOwnProperty */ - ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length); - if (ret < 0) - goto exception; - if (!ret) { - len_val = JS_NewInt32(ctx, 0); - } else { - len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length); - if (JS_IsException(len_val)) - goto exception; - if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { - /* most common case */ - int len1 = JS_VALUE_GET_INT(len_val); - if (len1 <= arg_count) - len1 = 0; - else - len1 -= arg_count; - len_val = JS_NewInt32(ctx, len1); - } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) { - double d = JS_VALUE_GET_FLOAT64(len_val); - if (isnan(d)) { - d = 0.0; - } else { - d = trunc(d); - if (d <= (double)arg_count) - d = 0.0; - else - d -= (double)arg_count; /* also converts -0 to +0 */ - } - len_val = JS_NewFloat64(ctx, d); - } else { - JS_FreeValue(ctx, len_val); - len_val = JS_NewInt32(ctx, 0); - } - } - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, len_val, JS_PROP_CONFIGURABLE); - - name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); - if (JS_IsException(name1)) - goto exception; - if (!JS_IsString(name1)) { - JS_FreeValue(ctx, name1); - name1 = JS_AtomToString(ctx, JS_ATOM_empty_string); - } - name1 = JS_ConcatString3(ctx, "bound ", name1, ""); - if (JS_IsException(name1)) - goto exception; - JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, JS_PROP_CONFIGURABLE); - return func_obj; -exception: - JS_FreeValue(ctx, func_obj); - return JS_EXCEPTION; -} - -JSValue js_function_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSObject* p; - JSFunctionKindEnum func_kind = JS_FUNC_NORMAL; - - if (check_function(ctx, this_val)) - return JS_EXCEPTION; - - p = JS_VALUE_GET_OBJ(this_val); - if (js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode* b = p->u.func.function_bytecode; - if (b->has_debug && b->debug.source) { - return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len); - } - func_kind = b->func_kind; - } - { - JSValue name; - const char *pref, *suff; - - switch (func_kind) { - default: - case JS_FUNC_NORMAL: - pref = "function "; - break; - case JS_FUNC_GENERATOR: - pref = "function *"; - break; - case JS_FUNC_ASYNC: - pref = "async function "; - break; - case JS_FUNC_ASYNC_GENERATOR: - pref = "async function *"; - break; - } - suff = "() {\n [native code]\n}"; - name = JS_GetProperty(ctx, this_val, JS_ATOM_name); - if (JS_IsUndefined(name)) - name = JS_AtomToString(ctx, JS_ATOM_empty_string); - return JS_ConcatString3(ctx, pref, name, suff); - } -} - -JSValue js_function_hasInstance(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - int ret; - ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -/* XXX: not 100% compatible, but mozilla seems to use a similar - implementation to ensure that caller in non strict mode does not - throw (ES5 compatibility) */ -JSValue js_function_proto_caller(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSFunctionBytecode* b = JS_GetFunctionBytecode(this_val); - if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) { - return js_throw_type_error(ctx, this_val, 0, NULL); - } - return JS_UNDEFINED; -} - -JSValue js_function_proto_fileName(JSContext* ctx, JSValueConst this_val) { - JSFunctionBytecode* b = JS_GetFunctionBytecode(this_val); - if (b && b->has_debug) { - return JS_AtomToString(ctx, b->debug.filename); - } - return JS_UNDEFINED; -} - -JSValue js_function_proto_lineNumber(JSContext* ctx, JSValueConst this_val) { - JSFunctionBytecode* b = JS_GetFunctionBytecode(this_val); - if (b && b->has_debug) { - return JS_NewInt32(ctx, b->debug.line_num); - } - return JS_UNDEFINED; -} - -int js_arguments_define_own_property(JSContext* ctx, - JSValueConst this_obj, - JSAtom prop, - JSValueConst val, - JSValueConst getter, - JSValueConst setter, - int flags) { - JSObject* p; - uint32_t idx; - p = JS_VALUE_GET_OBJ(this_obj); - /* convert to normal array when redefining an existing numeric field */ - if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && idx < p->u.array.count) { - if (convert_fast_array_to_array(ctx, p)) - return -1; - } - /* run the default define own property */ - return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, flags | JS_PROP_NO_EXOTIC); -} - -JSValue js_build_arguments(JSContext* ctx, int argc, JSValueConst* argv) { - JSValue val, *tab; - JSProperty* pr; - JSObject* p; - int i; - - val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_ARGUMENTS); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_OBJ(val); - - /* add the length field (cannot fail) */ - pr = add_property(ctx, p, JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - pr->u.value = JS_NewInt32(ctx, argc); - - /* initialize the fast array part */ - tab = NULL; - if (argc > 0) { - tab = js_malloc(ctx, sizeof(tab[0]) * argc); - if (!tab) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - for (i = 0; i < argc; i++) { - tab[i] = JS_DupValue(ctx, argv[i]); - } - } - p->u.array.u.values = tab; - p->u.array.count = argc; - - JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, JS_DupValue(ctx, ctx->array_proto_values), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - /* add callee property to throw a TypeError in strict mode */ - JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, ctx->throw_type_error, ctx->throw_type_error, - JS_PROP_HAS_GET | JS_PROP_HAS_SET); - return val; -} - -/* legacy arguments object: add references to the function arguments */ -JSValue js_build_mapped_arguments(JSContext* ctx, - int argc, - JSValueConst* argv, - JSStackFrame* sf, - int arg_count) { - JSValue val; - JSProperty* pr; - JSObject* p; - int i; - - val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_MAPPED_ARGUMENTS); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_OBJ(val); - - /* add the length field (cannot fail) */ - pr = add_property(ctx, p, JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - pr->u.value = JS_NewInt32(ctx, argc); - - for (i = 0; i < arg_count; i++) { - JSVarRef* var_ref; - var_ref = get_var_ref(ctx, sf, i, TRUE); - if (!var_ref) - goto fail; - pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); - if (!pr) { - free_var_ref(ctx->rt, var_ref); - goto fail; - } - pr->u.var_ref = var_ref; - } - - /* the arguments not mapped to the arguments of the function can - be normal properties */ - for (i = arg_count; i < argc; i++) { - if (JS_DefinePropertyValueUint32(ctx, val, i, JS_DupValue(ctx, argv[i]), JS_PROP_C_W_E) < 0) - goto fail; - } - - JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, JS_DupValue(ctx, ctx->array_proto_values), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - /* callee returns this function in non strict mode */ - JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); - return val; -fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - - -/* return NULL without exception if not a function or no bytecode */ -JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return NULL; - p = JS_VALUE_GET_OBJ(val); - if (!js_class_has_bytecode(p->class_id)) - return NULL; - return p->u.func.function_bytecode; -} - -void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, - JSValueConst home_obj) -{ - JSObject *p, *p1; - JSFunctionBytecode *b; - - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) - return; - p = JS_VALUE_GET_OBJ(func_obj); - if (!js_class_has_bytecode(p->class_id)) - return; - b = p->u.func.function_bytecode; - if (b->need_home_object) { - p1 = p->u.func.home_object; - if (p1) { - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - } - if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) - p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj)); - else - p1 = NULL; - p->u.func.home_object = p1; - } -} - -JSValue js_get_function_name(JSContext *ctx, JSAtom name) -{ - JSValue name_str; - - name_str = JS_AtomToString(ctx, name); - if (JS_AtomSymbolHasDescription(ctx, name)) { - name_str = JS_ConcatString3(ctx, "[", name_str, "]"); - } - return name_str; -} - -/* Modify the name of a method according to the atom and - 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and - JS_PROP_HAS_SET. Also set the home object of the method. - Return < 0 if exception. */ -int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, - JSAtom name, int flags, JSValueConst home_obj) -{ - JSValue name_str; - - name_str = js_get_function_name(ctx, name); - if (flags & JS_PROP_HAS_GET) { - name_str = JS_ConcatString3(ctx, "get ", name_str, ""); - } else if (flags & JS_PROP_HAS_SET) { - name_str = JS_ConcatString3(ctx, "set ", name_str, ""); - } - if (JS_IsException(name_str)) - return -1; - if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, - JS_PROP_CONFIGURABLE) < 0) - return -1; - js_method_set_home_object(ctx, func_obj, home_obj); - return 0; -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-function.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../gc.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "../types.h" +#include "js-closures.h" +#include "js-operator.h" + +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); +} + +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); +} + +void js_bytecode_function_finalizer(JSRuntime* rt, JSValue val) { + JSObject *p1, *p = JS_VALUE_GET_OBJ(val); + JSFunctionBytecode* b; + JSVarRef** var_refs; + int i; + + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + b = p->u.func.function_bytecode; + if (b) { + var_refs = p->u.func.var_refs; + if (var_refs) { + for (i = 0; i < b->closure_var_count; i++) + free_var_ref(rt, var_refs[i]); + js_free_rt(rt, var_refs); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); + } +} + +void js_bytecode_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSVarRef** var_refs = p->u.func.var_refs; + JSFunctionBytecode* b = p->u.func.function_bytecode; + int i; + + if (p->u.func.home_object) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), mark_func); + } + if (b) { + if (var_refs) { + for (i = 0; i < b->closure_var_count; i++) { + JSVarRef* var_ref = var_refs[i]; + if (var_ref && var_ref->is_detached) { + mark_func(rt, &var_ref->header); + } + } + } + /* must mark the function bytecode because template objects may be + part of a cycle */ + JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); + } +} + +void js_bound_function_finalizer(JSRuntime* rt, JSValue val) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSBoundFunction* bf = p->u.bound_function; + int i; + + JS_FreeValueRT(rt, bf->func_obj); + JS_FreeValueRT(rt, bf->this_val); + for (i = 0; i < bf->argc; i++) { + JS_FreeValueRT(rt, bf->argv[i]); + } + js_free_rt(rt, bf); +} + +void js_bound_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSBoundFunction* bf = p->u.bound_function; + int i; + + JS_MarkValue(rt, bf->func_obj, mark_func); + JS_MarkValue(rt, bf->this_val, mark_func); + for (i = 0; i < bf->argc; i++) + JS_MarkValue(rt, bf->argv[i], mark_func); +} + +void free_arg_list(JSContext* ctx, JSValue* tab, uint32_t len) { + uint32_t i; + for (i = 0; i < len; i++) { + JS_FreeValue(ctx, tab[i]); + } + js_free(ctx, tab); +} + +JSValue js_function_proto(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_UNDEFINED; +} + +/* XXX: add a specific eval mode so that Function("}), ({") is rejected */ +JSValue js_function_constructor(JSContext* ctx, + JSValueConst new_target, + int argc, + JSValueConst* argv, + int magic) { + JSFunctionKindEnum func_kind = magic; + int i, n, ret; + JSValue s, proto, obj = JS_UNDEFINED; + StringBuffer b_s, *b = &b_s; + + string_buffer_init(ctx, b, 0); + string_buffer_putc8(b, '('); + + if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) { + string_buffer_puts8(b, "async "); + } + string_buffer_puts8(b, "function"); + + if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) { + string_buffer_putc8(b, '*'); + } + string_buffer_puts8(b, " anonymous("); + + n = argc - 1; + for (i = 0; i < n; i++) { + if (i != 0) { + string_buffer_putc8(b, ','); + } + if (string_buffer_concat_value(b, argv[i])) + goto fail; + } + string_buffer_puts8(b, "\n) {\n"); + if (n >= 0) { + if (string_buffer_concat_value(b, argv[n])) + goto fail; + } + string_buffer_puts8(b, "\n})"); + s = string_buffer_end(b); + if (JS_IsException(s)) + goto fail1; + + obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1); + JS_FreeValue(ctx, s); + if (JS_IsException(obj)) + goto fail1; + if (!JS_IsUndefined(new_target)) { + /* set the prototype */ + proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); + if (JS_IsException(proto)) + goto fail1; + if (!JS_IsObject(proto)) { + JSContext* realm; + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, new_target); + if (!realm) + goto fail1; + proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]); + } + ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); + JS_FreeValue(ctx, proto); + if (ret < 0) + goto fail1; + } + return obj; + +fail: + string_buffer_free(b); +fail1: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +__exception int js_get_length32(JSContext* ctx, uint32_t* pres, JSValueConst obj) { + JSValue len_val; + len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); + if (JS_IsException(len_val)) { + *pres = 0; + return -1; + } + return JS_ToUint32Free(ctx, pres, len_val); +} + +__exception int js_get_length64(JSContext* ctx, int64_t* pres, JSValueConst obj) { + JSValue len_val; + len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); + if (JS_IsException(len_val)) { + *pres = 0; + return -1; + } + return JS_ToLengthFree(ctx, pres, len_val); +} + +/* XXX: should use ValueArray */ +JSValue* build_arg_list(JSContext* ctx, uint32_t* plen, JSValueConst array_arg) { + uint32_t len, i; + JSValue *tab, ret; + JSObject* p; + + if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "not a object"); + return NULL; + } + if (js_get_length32(ctx, &len, array_arg)) + return NULL; + if (len > JS_MAX_LOCAL_VARS) { + JS_ThrowInternalError(ctx, "too many arguments"); + return NULL; + } + /* avoid allocating 0 bytes */ + tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); + if (!tab) + return NULL; + p = JS_VALUE_GET_OBJ(array_arg); + if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) && p->fast_array && + len == p->u.array.count) { + for (i = 0; i < len; i++) { + tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]); + } + } else { + for (i = 0; i < len; i++) { + ret = JS_GetPropertyUint32(ctx, array_arg, i); + if (JS_IsException(ret)) { + free_arg_list(ctx, tab, i); + return NULL; + } + tab[i] = ret; + } + } + *plen = len; + return tab; +} + +void js_function_set_properties(JSContext* ctx, JSValueConst func_obj, JSAtom name, int len) { + /* ES6 feature non compatible with ES5.1: length is configurable */ + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len), JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); +} + +/* magic value: 0 = normal apply, 1 = apply for constructor, 2 = + Reflect.apply */ +JSValue js_function_apply(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + JSValueConst this_arg, array_arg; + uint32_t len; + JSValue *tab, ret; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + this_arg = argv[0]; + array_arg = argv[1]; + if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) { + return JS_Call(ctx, this_val, this_arg, 0, NULL); + } + tab = build_arg_list(ctx, &len, array_arg); + if (!tab) + return JS_EXCEPTION; + if (magic & 1) { + ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst*)tab); + } else { + ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst*)tab); + } + free_arg_list(ctx, tab, len); + return ret; +} + +JSValue js_function_call(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + if (argc <= 0) { + return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL); + } else { + return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1); + } +} + +JSValue js_function_bind(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSBoundFunction* bf; + JSValue func_obj, name1, len_val; + JSObject* p; + int arg_count, i, ret; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, JS_CLASS_BOUND_FUNCTION); + if (JS_IsException(func_obj)) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(func_obj); + p->is_constructor = JS_IsConstructor(ctx, this_val); + arg_count = max_int(0, argc - 1); + bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue)); + if (!bf) + goto exception; + bf->func_obj = JS_DupValue(ctx, this_val); + bf->this_val = JS_DupValue(ctx, argv[0]); + bf->argc = arg_count; + for (i = 0; i < arg_count; i++) { + bf->argv[i] = JS_DupValue(ctx, argv[i + 1]); + } + p->u.bound_function = bf; + + /* XXX: the spec could be simpler by only using GetOwnProperty */ + ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length); + if (ret < 0) + goto exception; + if (!ret) { + len_val = JS_NewInt32(ctx, 0); + } else { + len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length); + if (JS_IsException(len_val)) + goto exception; + if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { + /* most common case */ + int len1 = JS_VALUE_GET_INT(len_val); + if (len1 <= arg_count) + len1 = 0; + else + len1 -= arg_count; + len_val = JS_NewInt32(ctx, len1); + } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) { + double d = JS_VALUE_GET_FLOAT64(len_val); + if (isnan(d)) { + d = 0.0; + } else { + d = trunc(d); + if (d <= (double)arg_count) + d = 0.0; + else + d -= (double)arg_count; /* also converts -0 to +0 */ + } + len_val = JS_NewFloat64(ctx, d); + } else { + JS_FreeValue(ctx, len_val); + len_val = JS_NewInt32(ctx, 0); + } + } + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, len_val, JS_PROP_CONFIGURABLE); + + name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); + if (JS_IsException(name1)) + goto exception; + if (!JS_IsString(name1)) { + JS_FreeValue(ctx, name1); + name1 = JS_AtomToString(ctx, JS_ATOM_empty_string); + } + name1 = JS_ConcatString3(ctx, "bound ", name1, ""); + if (JS_IsException(name1)) + goto exception; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, JS_PROP_CONFIGURABLE); + return func_obj; +exception: + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +JSValue js_function_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSObject* p; + JSFunctionKindEnum func_kind = JS_FUNC_NORMAL; + + if (check_function(ctx, this_val)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_OBJ(this_val); + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode* b = p->u.func.function_bytecode; + if (b->has_debug && b->debug.source) { + return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len); + } + func_kind = b->func_kind; + } + { + JSValue name; + const char *pref, *suff; + + switch (func_kind) { + default: + case JS_FUNC_NORMAL: + pref = "function "; + break; + case JS_FUNC_GENERATOR: + pref = "function *"; + break; + case JS_FUNC_ASYNC: + pref = "async function "; + break; + case JS_FUNC_ASYNC_GENERATOR: + pref = "async function *"; + break; + } + suff = "() {\n [native code]\n}"; + name = JS_GetProperty(ctx, this_val, JS_ATOM_name); + if (JS_IsUndefined(name)) + name = JS_AtomToString(ctx, JS_ATOM_empty_string); + return JS_ConcatString3(ctx, pref, name, suff); + } +} + +JSValue js_function_hasInstance(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + int ret; + ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +/* XXX: not 100% compatible, but mozilla seems to use a similar + implementation to ensure that caller in non strict mode does not + throw (ES5 compatibility) */ +JSValue js_function_proto_caller(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSFunctionBytecode* b = JS_GetFunctionBytecode(this_val); + if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) { + return js_throw_type_error(ctx, this_val, 0, NULL); + } + return JS_UNDEFINED; +} + +JSValue js_function_proto_fileName(JSContext* ctx, JSValueConst this_val) { + JSFunctionBytecode* b = JS_GetFunctionBytecode(this_val); + if (b && b->has_debug) { + return JS_AtomToString(ctx, b->debug.filename); + } + return JS_UNDEFINED; +} + +JSValue js_function_proto_lineNumber(JSContext* ctx, JSValueConst this_val) { + JSFunctionBytecode* b = JS_GetFunctionBytecode(this_val); + if (b && b->has_debug) { + return JS_NewInt32(ctx, b->debug.line_num); + } + return JS_UNDEFINED; +} + +int js_arguments_define_own_property(JSContext* ctx, + JSValueConst this_obj, + JSAtom prop, + JSValueConst val, + JSValueConst getter, + JSValueConst setter, + int flags) { + JSObject* p; + uint32_t idx; + p = JS_VALUE_GET_OBJ(this_obj); + /* convert to normal array when redefining an existing numeric field */ + if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && idx < p->u.array.count) { + if (convert_fast_array_to_array(ctx, p)) + return -1; + } + /* run the default define own property */ + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, flags | JS_PROP_NO_EXOTIC); +} + +JSValue js_build_arguments(JSContext* ctx, int argc, JSValueConst* argv) { + JSValue val, *tab; + JSProperty* pr; + JSObject* p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = JS_NewInt32(ctx, argc); + + /* initialize the fast array part */ + tab = NULL; + if (argc > 0) { + tab = js_malloc(ctx, sizeof(tab[0]) * argc); + if (!tab) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + for (i = 0; i < argc; i++) { + tab[i] = JS_DupValue(ctx, argv[i]); + } + } + p->u.array.u.values = tab; + p->u.array.count = argc; + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, JS_DupValue(ctx, ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* add callee property to throw a TypeError in strict mode */ + JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, ctx->throw_type_error, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET); + return val; +} + +/* legacy arguments object: add references to the function arguments */ +JSValue js_build_mapped_arguments(JSContext* ctx, + int argc, + JSValueConst* argv, + JSStackFrame* sf, + int arg_count) { + JSValue val; + JSProperty* pr; + JSObject* p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_MAPPED_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = JS_NewInt32(ctx, argc); + + for (i = 0; i < arg_count; i++) { + JSVarRef* var_ref; + var_ref = get_var_ref(ctx, sf, i, TRUE); + if (!var_ref) + goto fail; + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); + if (!pr) { + free_var_ref(ctx->rt, var_ref); + goto fail; + } + pr->u.var_ref = var_ref; + } + + /* the arguments not mapped to the arguments of the function can + be normal properties */ + for (i = arg_count; i < argc; i++) { + if (JS_DefinePropertyValueUint32(ctx, val, i, JS_DupValue(ctx, argv[i]), JS_PROP_C_W_E) < 0) + goto fail; + } + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, JS_DupValue(ctx, ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* callee returns this function in non strict mode */ + JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + return val; +fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + + +/* return NULL without exception if not a function or no bytecode */ +JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(val); + if (!js_class_has_bytecode(p->class_id)) + return NULL; + return p->u.func.function_bytecode; +} + +void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, + JSValueConst home_obj) +{ + JSObject *p, *p1; + JSFunctionBytecode *b; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + return; + b = p->u.func.function_bytecode; + if (b->need_home_object) { + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) + p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj)); + else + p1 = NULL; + p->u.func.home_object = p1; + } +} + +JSValue js_get_function_name(JSContext *ctx, JSAtom name) +{ + JSValue name_str; + + name_str = JS_AtomToString(ctx, name); + if (JS_AtomSymbolHasDescription(ctx, name)) { + name_str = JS_ConcatString3(ctx, "[", name_str, "]"); + } + return name_str; +} + +/* Modify the name of a method according to the atom and + 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and + JS_PROP_HAS_SET. Also set the home object of the method. + Return < 0 if exception. */ +int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, + JSAtom name, int flags, JSValueConst home_obj) +{ + JSValue name_str; + + name_str = js_get_function_name(ctx, name); + if (flags & JS_PROP_HAS_GET) { + name_str = JS_ConcatString3(ctx, "get ", name_str, ""); + } else if (flags & JS_PROP_HAS_SET) { + name_str = JS_ConcatString3(ctx, "set ", name_str, ""); + } + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, + JS_PROP_CONFIGURABLE) < 0) + return -1; + js_method_set_home_object(ctx, func_obj, home_obj); + return 0; +} diff --git a/src/core/builtins/js-function.h b/src/core/builtins/js-function.h index 9f45018f9..ef3870a00 100644 --- a/src/core/builtins/js-function.h +++ b/src/core/builtins/js-function.h @@ -1,114 +1,114 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_FUNCTION_H -#define QUICKJS_JS_FUNCTION_H - -#include "quickjs/quickjs.h" -#include "../types.h" - -#define GLOBAL_VAR_OFFSET 0x40000000 -#define ARGUMENT_VAR_OFFSET 0x20000000 - -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, -}; - -JSValue js_function_apply(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -JSValue js_function_proto_caller(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -/* magic value: 0 = normal apply, 1 = apply for constructor, 2 = - Reflect.apply */ -JSValue js_function_apply(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -JSValue js_function_call(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_function_bind(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_function_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_function_hasInstance(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -/* XXX: not 100% compatible, but mozilla seems to use a similar - implementation to ensure that caller in non strict mode does not - throw (ES5 compatibility) */ -JSValue js_function_proto_caller(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_function_proto_fileName(JSContext* ctx, JSValueConst this_val); -JSValue js_function_proto_lineNumber(JSContext* ctx, JSValueConst this_val); - -void js_c_function_finalizer(JSRuntime* rt, JSValue val); -void js_c_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - -void js_bytecode_function_finalizer(JSRuntime* rt, JSValue val); -void js_bytecode_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - -void js_bound_function_finalizer(JSRuntime* rt, JSValue val); -void js_bound_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - -void free_arg_list(JSContext* ctx, JSValue* tab, uint32_t len); -JSValue js_function_proto(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); - -/* XXX: add a specific eval mode so that Function("}), ({") is rejected */ -JSValue js_function_constructor(JSContext* ctx, - JSValueConst new_target, - int argc, - JSValueConst* argv, - int magic); -__exception int js_get_length32(JSContext* ctx, uint32_t* pres, JSValueConst obj); -__exception int js_get_length64(JSContext* ctx, int64_t* pres, JSValueConst obj); -/* XXX: should use ValueArray */ -JSValue* build_arg_list(JSContext* ctx, uint32_t* plen, JSValueConst array_arg); - -void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, - JSAtom name, int len); - -int js_arguments_define_own_property(JSContext* ctx, - JSValueConst this_obj, - JSAtom prop, - JSValueConst val, - JSValueConst getter, - JSValueConst setter, - int flags); -JSValue js_build_arguments(JSContext* ctx, int argc, JSValueConst* argv); - -/* legacy arguments object: add references to the function arguments */ -JSValue js_build_mapped_arguments(JSContext* ctx, - int argc, - JSValueConst* argv, - JSStackFrame* sf, - int arg_count); - -/* return NULL without exception if not a function or no bytecode */ -JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val); - -void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, - JSValueConst home_obj); -JSValue js_get_function_name(JSContext *ctx, JSAtom name); -/* Modify the name of a method according to the atom and - 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and - JS_PROP_HAS_SET. Also set the home object of the method. - Return < 0 if exception. */ -int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, - JSAtom name, int flags, JSValueConst home_obj); - - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_FUNCTION_H +#define QUICKJS_JS_FUNCTION_H + +#include "quickjs/quickjs.h" +#include "../types.h" + +#define GLOBAL_VAR_OFFSET 0x40000000 +#define ARGUMENT_VAR_OFFSET 0x20000000 + +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, +}; + +JSValue js_function_apply(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +JSValue js_function_proto_caller(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +/* magic value: 0 = normal apply, 1 = apply for constructor, 2 = + Reflect.apply */ +JSValue js_function_apply(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +JSValue js_function_call(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_function_bind(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_function_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_function_hasInstance(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +/* XXX: not 100% compatible, but mozilla seems to use a similar + implementation to ensure that caller in non strict mode does not + throw (ES5 compatibility) */ +JSValue js_function_proto_caller(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_function_proto_fileName(JSContext* ctx, JSValueConst this_val); +JSValue js_function_proto_lineNumber(JSContext* ctx, JSValueConst this_val); + +void js_c_function_finalizer(JSRuntime* rt, JSValue val); +void js_c_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + +void js_bytecode_function_finalizer(JSRuntime* rt, JSValue val); +void js_bytecode_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + +void js_bound_function_finalizer(JSRuntime* rt, JSValue val); +void js_bound_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + +void free_arg_list(JSContext* ctx, JSValue* tab, uint32_t len); +JSValue js_function_proto(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); + +/* XXX: add a specific eval mode so that Function("}), ({") is rejected */ +JSValue js_function_constructor(JSContext* ctx, + JSValueConst new_target, + int argc, + JSValueConst* argv, + int magic); +__exception int js_get_length32(JSContext* ctx, uint32_t* pres, JSValueConst obj); +__exception int js_get_length64(JSContext* ctx, int64_t* pres, JSValueConst obj); +/* XXX: should use ValueArray */ +JSValue* build_arg_list(JSContext* ctx, uint32_t* plen, JSValueConst array_arg); + +void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, + JSAtom name, int len); + +int js_arguments_define_own_property(JSContext* ctx, + JSValueConst this_obj, + JSAtom prop, + JSValueConst val, + JSValueConst getter, + JSValueConst setter, + int flags); +JSValue js_build_arguments(JSContext* ctx, int argc, JSValueConst* argv); + +/* legacy arguments object: add references to the function arguments */ +JSValue js_build_mapped_arguments(JSContext* ctx, + int argc, + JSValueConst* argv, + JSStackFrame* sf, + int arg_count); + +/* return NULL without exception if not a function or no bytecode */ +JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val); + +void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, + JSValueConst home_obj); +JSValue js_get_function_name(JSContext *ctx, JSAtom name); +/* Modify the name of a method according to the atom and + 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and + JS_PROP_HAS_SET. Also set the home object of the method. + Return < 0 if exception. */ +int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, + JSAtom name, int flags, JSValueConst home_obj); + + #endif \ No newline at end of file diff --git a/src/core/builtins/js-generator.c b/src/core/builtins/js-generator.c index 400263360..10b73c6d1 100644 --- a/src/core/builtins/js-generator.c +++ b/src/core/builtins/js-generator.c @@ -1,187 +1,187 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-generator.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "js-async-function.h" -#include "quickjs/cutils.h" - -/* Generators */ -void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) -{ - if (s->state == JS_GENERATOR_STATE_COMPLETED) - return; - async_func_free(rt, &s->func_state); - s->state = JS_GENERATOR_STATE_COMPLETED; -} - -void js_generator_finalizer(JSRuntime *rt, JSValue obj) -{ - JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); - - if (s) { - free_generator_stack_rt(rt, s); - js_free_rt(rt, s); - } -} - -void free_generator_stack(JSContext *ctx, JSGeneratorData *s) -{ - free_generator_stack_rt(ctx->rt, s); -} - -void js_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSGeneratorData *s = p->u.generator_data; - - if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) - return; - async_func_mark(rt, &s->func_state, mark_func); -} - -JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic) -{ - JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); - JSStackFrame *sf; - JSValue ret, func_ret; - - *pdone = TRUE; - if (!s) - return JS_ThrowTypeError(ctx, "not a generator"); - sf = &s->func_state.frame; - switch(s->state) { - default: - case JS_GENERATOR_STATE_SUSPENDED_START: - if (magic == GEN_MAGIC_NEXT) { - goto exec_no_arg; - } else { - free_generator_stack(ctx, s); - goto done; - } - break; - case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: - 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 && - s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { - JS_Throw(ctx, ret); - s->func_state.throw_flag = TRUE; - } else { - sf->cur_sp[-1] = ret; - sf->cur_sp[0] = JS_NewInt32(ctx, magic); - sf->cur_sp++; - exec_no_arg: - s->func_state.throw_flag = FALSE; - } - s->state = JS_GENERATOR_STATE_EXECUTING; - func_ret = async_func_resume(ctx, &s->func_state); - s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; - if (JS_IsException(func_ret)) { - /* finalize the execution in case of exception */ - free_generator_stack(ctx, s); - return func_ret; - } - if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { - /* get the returned yield value at the top of the stack */ - ret = sf->cur_sp[-1]; - sf->cur_sp[-1] = JS_UNDEFINED; - if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { - s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; - /* return (value, done) object */ - *pdone = 2; - } else { - *pdone = FALSE; - } - } else { - /* end of iterator */ - ret = sf->cur_sp[-1]; - sf->cur_sp[-1] = JS_UNDEFINED; - JS_FreeValue(ctx, func_ret); - free_generator_stack(ctx, s); - } - break; - case JS_GENERATOR_STATE_COMPLETED: - done: - /* execution is finished */ - switch(magic) { - default: - case GEN_MAGIC_NEXT: - ret = JS_UNDEFINED; - break; - case GEN_MAGIC_RETURN: - ret = JS_DupValue(ctx, argv[0]); - break; - case GEN_MAGIC_THROW: - ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0])); - break; - } - break; - case JS_GENERATOR_STATE_EXECUTING: - ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); - break; - } - return ret; -} - -JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags) -{ - JSValue obj, func_ret; - JSGeneratorData *s; - - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - return JS_EXCEPTION; - s->state = JS_GENERATOR_STATE_SUSPENDED_START; - if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { - s->state = JS_GENERATOR_STATE_COMPLETED; - goto fail; - } - - /* execute the function up to 'OP_initial_yield' */ - func_ret = async_func_resume(ctx, &s->func_state); - if (JS_IsException(func_ret)) - goto fail; - JS_FreeValue(ctx, func_ret); - - obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); - if (JS_IsException(obj)) - goto fail; - JS_SetOpaque(obj, s); - return obj; -fail: - free_generator_stack_rt(ctx->rt, s); - js_free(ctx, s); - return JS_EXCEPTION; -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-generator.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "js-async-function.h" +#include "quickjs/cutils.h" + +/* Generators */ +void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) +{ + if (s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_free(rt, &s->func_state); + s->state = JS_GENERATOR_STATE_COMPLETED; +} + +void js_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); + + if (s) { + free_generator_stack_rt(rt, s); + js_free_rt(rt, s); + } +} + +void free_generator_stack(JSContext *ctx, JSGeneratorData *s) +{ + free_generator_stack_rt(ctx->rt, s); +} + +void js_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSGeneratorData *s = p->u.generator_data; + + if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_mark(rt, &s->func_state, mark_func); +} + +JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); + JSStackFrame *sf; + JSValue ret, func_ret; + + *pdone = TRUE; + if (!s) + return JS_ThrowTypeError(ctx, "not a generator"); + sf = &s->func_state.frame; + switch(s->state) { + default: + case JS_GENERATOR_STATE_SUSPENDED_START: + if (magic == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + free_generator_stack(ctx, s); + goto done; + } + break; + case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + 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 && + s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, ret); + s->func_state.throw_flag = TRUE; + } else { + sf->cur_sp[-1] = ret; + sf->cur_sp[0] = JS_NewInt32(ctx, magic); + sf->cur_sp++; + exec_no_arg: + s->func_state.throw_flag = FALSE; + } + s->state = JS_GENERATOR_STATE_EXECUTING; + func_ret = async_func_resume(ctx, &s->func_state); + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; + if (JS_IsException(func_ret)) { + /* finalize the execution in case of exception */ + free_generator_stack(ctx, s); + return func_ret; + } + if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + /* get the returned yield value at the top of the stack */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + /* return (value, done) object */ + *pdone = 2; + } else { + *pdone = FALSE; + } + } else { + /* end of iterator */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + JS_FreeValue(ctx, func_ret); + free_generator_stack(ctx, s); + } + break; + case JS_GENERATOR_STATE_COMPLETED: + done: + /* execution is finished */ + switch(magic) { + default: + case GEN_MAGIC_NEXT: + ret = JS_UNDEFINED; + break; + case GEN_MAGIC_RETURN: + ret = JS_DupValue(ctx, argv[0]); + break; + case GEN_MAGIC_THROW: + ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0])); + break; + } + break; + case JS_GENERATOR_STATE_EXECUTING: + ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); + break; + } + return ret; +} + +JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags) +{ + JSValue obj, func_ret; + JSGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_GENERATOR_STATE_SUSPENDED_START; + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); + if (JS_IsException(obj)) + goto fail; + JS_SetOpaque(obj, s); + return obj; +fail: + free_generator_stack_rt(ctx->rt, s); + js_free(ctx, s); + return JS_EXCEPTION; +} diff --git a/src/core/builtins/js-generator.h b/src/core/builtins/js-generator.h index 7bd1ff9d5..555132924 100644 --- a/src/core/builtins/js-generator.h +++ b/src/core/builtins/js-generator.h @@ -1,66 +1,66 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_GENERATOR_H -#define QUICKJS_JS_GENERATOR_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "../types.h" - -/* XXX: use enum */ -#define GEN_MAGIC_NEXT 0 -#define GEN_MAGIC_RETURN 1 -#define GEN_MAGIC_THROW 2 - -typedef enum JSGeneratorStateEnum { - JS_GENERATOR_STATE_SUSPENDED_START, - JS_GENERATOR_STATE_SUSPENDED_YIELD, - JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, - JS_GENERATOR_STATE_EXECUTING, - JS_GENERATOR_STATE_COMPLETED, -} JSGeneratorStateEnum; - -typedef struct JSGeneratorData { - JSGeneratorStateEnum state; - JSAsyncFunctionState func_state; -} JSGeneratorData; - -void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s); -void js_generator_finalizer(JSRuntime *rt, JSValue obj); -void free_generator_stack(JSContext *ctx, JSGeneratorData *s); -void js_generator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); - -JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic); -JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, - int flags); - - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_GENERATOR_H +#define QUICKJS_JS_GENERATOR_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "../types.h" + +/* XXX: use enum */ +#define GEN_MAGIC_NEXT 0 +#define GEN_MAGIC_RETURN 1 +#define GEN_MAGIC_THROW 2 + +typedef enum JSGeneratorStateEnum { + JS_GENERATOR_STATE_SUSPENDED_START, + JS_GENERATOR_STATE_SUSPENDED_YIELD, + JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_GENERATOR_STATE_EXECUTING, + JS_GENERATOR_STATE_COMPLETED, +} JSGeneratorStateEnum; + +typedef struct JSGeneratorData { + JSGeneratorStateEnum state; + JSAsyncFunctionState func_state; +} JSGeneratorData; + +void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s); +void js_generator_finalizer(JSRuntime *rt, JSValue obj); +void free_generator_stack(JSContext *ctx, JSGeneratorData *s); +void js_generator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func); + +JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic); +JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, + int flags); + + #endif \ No newline at end of file diff --git a/src/core/builtins/js-json.c b/src/core/builtins/js-json.c index 8d2bb45fd..043286831 100644 --- a/src/core/builtins/js-json.c +++ b/src/core/builtins/js-json.c @@ -1,738 +1,738 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-json.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../parser.h" -#include "../runtime.h" -#include "../string.h" -#include "../types.h" -#include "js-array.h" -#include "js-function.h" -#include "js-object.h" - -/* JSON */ - -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); -} - -JSValue json_parse_value(JSParseState *s) -{ - JSContext *ctx = s->ctx; - JSValue val = JS_NULL; - int ret; - - switch(s->token.val) { - case '{': - { - JSValue prop_val; - JSAtom prop_name; - - if (json_next_token(s)) - goto fail; - val = JS_NewObject(ctx); - if (JS_IsException(val)) - goto fail; - if (s->token.val != '}') { - for(;;) { - if (s->token.val == TOK_STRING) { - prop_name = JS_ValueToAtom(ctx, s->token.u.str.str); - if (prop_name == JS_ATOM_NULL) - goto fail; - } else if (s->ext_json && s->token.val == TOK_IDENT) { - prop_name = JS_DupAtom(ctx, s->token.u.ident.atom); - } else { - js_parse_error(s, "expecting property name"); - goto fail; - } - if (json_next_token(s)) - goto fail1; - if (json_parse_expect(s, ':')) - goto fail1; - prop_val = json_parse_value(s); - if (JS_IsException(prop_val)) { - fail1: - JS_FreeAtom(ctx, prop_name); - goto fail; - } - ret = JS_DefinePropertyValue(ctx, val, prop_name, - prop_val, JS_PROP_C_W_E); - JS_FreeAtom(ctx, prop_name); - if (ret < 0) - goto fail; - - if (s->token.val != ',') - break; - if (json_next_token(s)) - goto fail; - if (s->ext_json && s->token.val == '}') - break; - } - } - if (json_parse_expect(s, '}')) - goto fail; - } - break; - case '[': - { - JSValue el; - uint32_t idx; - - if (json_next_token(s)) - goto fail; - val = JS_NewArray(ctx); - if (JS_IsException(val)) - goto fail; - if (s->token.val != ']') { - idx = 0; - for(;;) { - el = json_parse_value(s); - if (JS_IsException(el)) - goto fail; - ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E); - if (ret < 0) - goto fail; - if (s->token.val != ',') - break; - if (json_next_token(s)) - goto fail; - idx++; - if (s->ext_json && s->token.val == ']') - break; - } - } - if (json_parse_expect(s, ']')) - goto fail; - } - break; - case TOK_STRING: - val = JS_DupValue(ctx, s->token.u.str.str); - if (json_next_token(s)) - goto fail; - break; - case TOK_NUMBER: - val = s->token.u.num.val; - if (json_next_token(s)) - goto fail; - break; - case TOK_IDENT: - if (s->token.u.ident.atom == JS_ATOM_false || - s->token.u.ident.atom == JS_ATOM_true) { - val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true)); - } else if (s->token.u.ident.atom == JS_ATOM_null) { - val = JS_NULL; - } else { - goto def_token; - } - if (json_next_token(s)) - goto fail; - break; - default: - def_token: - if (s->token.val == TOK_EOF) { - js_parse_error(s, "unexpected end of input"); - } else { - js_parse_error(s, "unexpected token: '%.*s'", - (int)(s->buf_ptr - s->token.ptr), s->token.ptr); - } - goto fail; - } - return val; -fail: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename, int flags) -{ - JSParseState s1, *s = &s1; - JSValue val = JS_UNDEFINED; - - js_parse_init(ctx, s, buf, buf_len, filename); - s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0); - if (json_next_token(s)) - goto fail; - val = json_parse_value(s); - if (JS_IsException(val)) - goto fail; - if (s->token.val != TOK_EOF) { - if (js_parse_error(s, "unexpected data at the end")) - goto fail; - } - return val; -fail: - JS_FreeValue(ctx, val); - free_token(s, &s->token); - return JS_EXCEPTION; -} - -JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, - const char *filename) -{ - return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); -} - -JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, - JSAtom name, JSValueConst reviver) -{ - JSValue val, new_el, name_val, res; - JSValueConst args[2]; - int ret, is_array; - uint32_t i, len = 0; - JSAtom prop; - JSPropertyEnum *atoms = NULL; - - if (js_check_stack_overflow(ctx->rt, 0)) { - return JS_ThrowStackOverflow(ctx); - } - - val = JS_GetProperty(ctx, holder, name); - if (JS_IsException(val)) - return val; - if (JS_IsObject(val)) { - is_array = JS_IsArray(ctx, val); - if (is_array < 0) - goto fail; - if (is_array) { - if (js_get_length32(ctx, &len, val)) - goto fail; - } else { - ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK); - if (ret < 0) - goto fail; - } - for(i = 0; i < len; i++) { - if (is_array) { - prop = JS_NewAtomUInt32(ctx, i); - if (prop == JS_ATOM_NULL) - goto fail; - } else { - prop = JS_DupAtom(ctx, atoms[i].atom); - } - new_el = internalize_json_property(ctx, val, prop, reviver); - if (JS_IsException(new_el)) { - JS_FreeAtom(ctx, prop); - goto fail; - } - if (JS_IsUndefined(new_el)) { - ret = JS_DeleteProperty(ctx, val, prop, 0); - } else { - ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E); - } - JS_FreeAtom(ctx, prop); - if (ret < 0) - goto fail; - } - } - js_free_prop_enum(ctx, atoms, len); - atoms = NULL; - name_val = JS_AtomToValue(ctx, name); - if (JS_IsException(name_val)) - goto fail; - args[0] = name_val; - args[1] = val; - res = JS_Call(ctx, reviver, holder, 2, args); - JS_FreeValue(ctx, name_val); - JS_FreeValue(ctx, val); - return res; -fail: - js_free_prop_enum(ctx, atoms, len); - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -JSValue js_json_parse(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj, root; - JSValueConst reviver; - const char *str; - size_t len; - - str = JS_ToCStringLen(ctx, &len, argv[0]); - if (!str) - return JS_EXCEPTION; - obj = JS_ParseJSON(ctx, str, len, ""); - JS_FreeCString(ctx, str); - if (JS_IsException(obj)) - return obj; - if (argc > 1 && JS_IsFunction(ctx, argv[1])) { - reviver = argv[1]; - root = JS_NewObject(ctx); - if (JS_IsException(root)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj, - JS_PROP_C_W_E) < 0) { - JS_FreeValue(ctx, root); - return JS_EXCEPTION; - } - obj = internalize_json_property(ctx, root, JS_ATOM_empty_string, - reviver); - JS_FreeValue(ctx, root); - } - return obj; -} - -typedef struct JSONStringifyContext { - JSValueConst replacer_func; - JSValue stack; - JSValue property_list; - JSValue gap; - JSValue empty; - StringBuffer *b; -} JSONStringifyContext; - -JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { - JSValue r = JS_ToQuotedString(ctx, val); - JS_FreeValue(ctx, val); - return r; -} - -JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, - JSValueConst holder, JSValue val, JSValueConst key) -{ - JSValue v; - JSValueConst args[2]; - - if (JS_IsObject(val) -#ifdef CONFIG_BIGNUM - || JS_IsBigInt(ctx, val) /* XXX: probably useless */ -#endif - ) { - JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); - if (JS_IsException(f)) - goto exception; - if (JS_IsFunction(ctx, f)) { - v = JS_CallFree(ctx, f, val, 1, &key); - JS_FreeValue(ctx, val); - val = v; - if (JS_IsException(val)) - goto exception; - } else { - JS_FreeValue(ctx, f); - } - } - - if (!JS_IsUndefined(jsc->replacer_func)) { - args[0] = key; - args[1] = val; - v = JS_Call(ctx, jsc->replacer_func, holder, 2, args); - JS_FreeValue(ctx, val); - val = v; - if (JS_IsException(val)) - goto exception; - } - - switch (JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_OBJECT: - if (JS_IsFunction(ctx, val)) - break; - case JS_TAG_STRING: - case JS_TAG_INT: - case JS_TAG_FLOAT64: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif - case JS_TAG_BOOL: - case JS_TAG_NULL: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: -#endif - case JS_TAG_EXCEPTION: - return val; - default: - break; - } - JS_FreeValue(ctx, val); - return JS_UNDEFINED; - -exception: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, - JSValueConst holder, JSValue val, - JSValueConst indent) -{ - JSValue indent1, sep, sep1, tab, v, prop; - JSObject *p; - int64_t i, len; - int cl, ret; - BOOL has_content; - - indent1 = JS_UNDEFINED; - sep = JS_UNDEFINED; - sep1 = JS_UNDEFINED; - tab = JS_UNDEFINED; - prop = JS_UNDEFINED; - - switch (JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_OBJECT: - p = JS_VALUE_GET_OBJ(val); - cl = p->class_id; - if (cl == JS_CLASS_STRING) { - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - goto exception; - val = JS_ToQuotedStringFree(ctx, val); - if (JS_IsException(val)) - goto exception; - return string_buffer_concat_value_free(jsc->b, val); - } else if (cl == JS_CLASS_NUMBER) { - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - goto exception; - return string_buffer_concat_value_free(jsc->b, val); - } else if (cl == JS_CLASS_BOOLEAN) { - ret = string_buffer_concat_value(jsc->b, p->u.object_data); - JS_FreeValue(ctx, val); - return ret; - } -#ifdef CONFIG_BIGNUM - else if (cl == JS_CLASS_BIG_FLOAT) { - return string_buffer_concat_value_free(jsc->b, val); - } else if (cl == JS_CLASS_BIG_INT) { - JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); - goto exception; - } -#endif - v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); - if (JS_IsException(v)) - goto exception; - if (JS_ToBoolFree(ctx, v)) { - JS_ThrowTypeError(ctx, "circular reference"); - goto exception; - } - indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap)); - if (JS_IsException(indent1)) - goto exception; - if (!JS_IsEmptyString(jsc->gap)) { - sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), ""); - if (JS_IsException(sep)) - goto exception; - sep1 = JS_NewString(ctx, " "); - if (JS_IsException(sep1)) - goto exception; - } else { - sep = JS_DupValue(ctx, jsc->empty); - sep1 = JS_DupValue(ctx, jsc->empty); - } - v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); - if (check_exception_free(ctx, v)) - goto exception; - ret = JS_IsArray(ctx, val); - if (ret < 0) - goto exception; - if (ret) { - if (js_get_length64(ctx, &len, val)) - goto exception; - string_buffer_putc8(jsc->b, '['); - for(i = 0; i < len; i++) { - if (i > 0) - string_buffer_putc8(jsc->b, ','); - string_buffer_concat_value(jsc->b, sep); - v = JS_GetPropertyInt64(ctx, val, i); - if (JS_IsException(v)) - goto exception; - /* XXX: could do this string conversion only when needed */ - prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i)); - if (JS_IsException(prop)) - goto exception; - v = js_json_check(ctx, jsc, val, v, prop); - JS_FreeValue(ctx, prop); - prop = JS_UNDEFINED; - if (JS_IsException(v)) - goto exception; - if (JS_IsUndefined(v)) - v = JS_NULL; - if (js_json_to_str(ctx, jsc, val, v, indent1)) - goto exception; - } - if (len > 0 && !JS_IsEmptyString(jsc->gap)) { - string_buffer_putc8(jsc->b, '\n'); - string_buffer_concat_value(jsc->b, indent); - } - string_buffer_putc8(jsc->b, ']'); - } else { - if (!JS_IsUndefined(jsc->property_list)) - tab = JS_DupValue(ctx, jsc->property_list); - else - tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY); - if (JS_IsException(tab)) - goto exception; - if (js_get_length64(ctx, &len, tab)) - goto exception; - string_buffer_putc8(jsc->b, '{'); - has_content = FALSE; - for(i = 0; i < len; i++) { - JS_FreeValue(ctx, prop); - prop = JS_GetPropertyInt64(ctx, tab, i); - if (JS_IsException(prop)) - goto exception; - v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop)); - if (JS_IsException(v)) - goto exception; - v = js_json_check(ctx, jsc, val, v, prop); - if (JS_IsException(v)) - goto exception; - if (!JS_IsUndefined(v)) { - if (has_content) - string_buffer_putc8(jsc->b, ','); - prop = JS_ToQuotedStringFree(ctx, prop); - if (JS_IsException(prop)) { - JS_FreeValue(ctx, v); - goto exception; - } - string_buffer_concat_value(jsc->b, sep); - string_buffer_concat_value(jsc->b, prop); - string_buffer_putc8(jsc->b, ':'); - string_buffer_concat_value(jsc->b, sep1); - if (js_json_to_str(ctx, jsc, val, v, indent1)) - goto exception; - has_content = TRUE; - } - } - if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) { - string_buffer_putc8(jsc->b, '\n'); - string_buffer_concat_value(jsc->b, indent); - } - string_buffer_putc8(jsc->b, '}'); - } - if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0))) - goto exception; - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, tab); - JS_FreeValue(ctx, sep); - JS_FreeValue(ctx, sep1); - JS_FreeValue(ctx, indent1); - JS_FreeValue(ctx, prop); - return 0; - case JS_TAG_STRING: - val = JS_ToQuotedStringFree(ctx, val); - if (JS_IsException(val)) - goto exception; - goto concat_value; - case JS_TAG_FLOAT64: - if (!isfinite(JS_VALUE_GET_FLOAT64(val))) { - val = JS_NULL; - } - goto concat_value; - case JS_TAG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif - case JS_TAG_BOOL: - case JS_TAG_NULL: - concat_value: - return string_buffer_concat_value_free(jsc->b, val); -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); - goto exception; -#endif - default: - JS_FreeValue(ctx, val); - return 0; - } - -exception: - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, tab); - JS_FreeValue(ctx, sep); - JS_FreeValue(ctx, sep1); - JS_FreeValue(ctx, indent1); - JS_FreeValue(ctx, prop); - return -1; -} - -JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, - JSValueConst replacer, JSValueConst space0) -{ - StringBuffer b_s; - JSONStringifyContext jsc_s, *jsc = &jsc_s; - JSValue val, v, space, ret, wrapper; - int res; - int64_t i, j, n; - - jsc->replacer_func = JS_UNDEFINED; - jsc->stack = JS_UNDEFINED; - jsc->property_list = JS_UNDEFINED; - jsc->gap = JS_UNDEFINED; - jsc->b = &b_s; - jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string); - ret = JS_UNDEFINED; - wrapper = JS_UNDEFINED; - - string_buffer_init(ctx, jsc->b, 0); - jsc->stack = JS_NewArray(ctx); - if (JS_IsException(jsc->stack)) - goto exception; - if (JS_IsFunction(ctx, replacer)) { - jsc->replacer_func = replacer; - } else { - res = JS_IsArray(ctx, replacer); - if (res < 0) - goto exception; - if (res) { - /* XXX: enumeration is not fully correct */ - jsc->property_list = JS_NewArray(ctx); - if (JS_IsException(jsc->property_list)) - goto exception; - if (js_get_length64(ctx, &n, replacer)) - goto exception; - for (i = j = 0; i < n; i++) { - JSValue present; - v = JS_GetPropertyInt64(ctx, replacer, i); - if (JS_IsException(v)) - goto exception; - if (JS_IsObject(v)) { - JSObject *p = JS_VALUE_GET_OBJ(v); - if (p->class_id == JS_CLASS_STRING || - p->class_id == JS_CLASS_NUMBER) { - v = JS_ToStringFree(ctx, v); - if (JS_IsException(v)) - goto exception; - } else { - JS_FreeValue(ctx, v); - continue; - } - } else if (JS_IsNumber(v)) { - v = JS_ToStringFree(ctx, v); - if (JS_IsException(v)) - goto exception; - } else if (!JS_IsString(v)) { - JS_FreeValue(ctx, v); - continue; - } - present = js_array_includes(ctx, jsc->property_list, - 1, (JSValueConst *)&v); - if (JS_IsException(present)) { - JS_FreeValue(ctx, v); - goto exception; - } - if (!JS_ToBoolFree(ctx, present)) { - JS_SetPropertyInt64(ctx, jsc->property_list, j++, v); - } else { - JS_FreeValue(ctx, v); - } - } - } - } - space = JS_DupValue(ctx, space0); - if (JS_IsObject(space)) { - JSObject *p = JS_VALUE_GET_OBJ(space); - if (p->class_id == JS_CLASS_NUMBER) { - space = JS_ToNumberFree(ctx, space); - } else if (p->class_id == JS_CLASS_STRING) { - space = JS_ToStringFree(ctx, space); - } - if (JS_IsException(space)) { - JS_FreeValue(ctx, space); - goto exception; - } - } - if (JS_IsNumber(space)) { - int n; - if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0)) - goto exception; - jsc->gap = JS_NewStringLen(ctx, " ", n); - } else if (JS_IsString(space)) { - JSString *p = JS_VALUE_GET_STRING(space); - jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10)); - } else { - jsc->gap = JS_DupValue(ctx, jsc->empty); - } - JS_FreeValue(ctx, space); - if (JS_IsException(jsc->gap)) - goto exception; - wrapper = JS_NewObject(ctx); - if (JS_IsException(wrapper)) - goto exception; - if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string, - JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0) - goto exception; - val = JS_DupValue(ctx, obj); - - val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); - if (JS_IsException(val)) - goto exception; - if (JS_IsUndefined(val)) { - ret = JS_UNDEFINED; - goto done1; - } - if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty)) - goto exception; - - ret = string_buffer_end(jsc->b); - goto done; - -exception: - ret = JS_EXCEPTION; -done1: - string_buffer_free(jsc->b); -done: - JS_FreeValue(ctx, wrapper); - JS_FreeValue(ctx, jsc->empty); - JS_FreeValue(ctx, jsc->gap); - JS_FreeValue(ctx, jsc->property_list); - JS_FreeValue(ctx, jsc->stack); - return ret; -} - -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]); -} - -const JSCFunctionListEntry js_json_funcs[] = { - JS_CFUNC_DEF("parse", 2, js_json_parse ), - JS_CFUNC_DEF("stringify", 3, js_json_stringify ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ), -}; - -const JSCFunctionListEntry js_json_obj[] = { - JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), -}; - -void JS_AddIntrinsicJSON(JSContext *ctx) -{ - /* add JSON as autoinit object */ - JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj)); +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-json.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../parser.h" +#include "../runtime.h" +#include "../string.h" +#include "../types.h" +#include "js-array.h" +#include "js-function.h" +#include "js-object.h" + +/* JSON */ + +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); +} + +JSValue json_parse_value(JSParseState *s) +{ + JSContext *ctx = s->ctx; + JSValue val = JS_NULL; + int ret; + + switch(s->token.val) { + case '{': + { + JSValue prop_val; + JSAtom prop_name; + + if (json_next_token(s)) + goto fail; + val = JS_NewObject(ctx); + if (JS_IsException(val)) + goto fail; + if (s->token.val != '}') { + for(;;) { + if (s->token.val == TOK_STRING) { + prop_name = JS_ValueToAtom(ctx, s->token.u.str.str); + if (prop_name == JS_ATOM_NULL) + goto fail; + } else if (s->ext_json && s->token.val == TOK_IDENT) { + prop_name = JS_DupAtom(ctx, s->token.u.ident.atom); + } else { + js_parse_error(s, "expecting property name"); + goto fail; + } + if (json_next_token(s)) + goto fail1; + if (json_parse_expect(s, ':')) + goto fail1; + prop_val = json_parse_value(s); + if (JS_IsException(prop_val)) { + fail1: + JS_FreeAtom(ctx, prop_name); + goto fail; + } + ret = JS_DefinePropertyValue(ctx, val, prop_name, + prop_val, JS_PROP_C_W_E); + JS_FreeAtom(ctx, prop_name); + if (ret < 0) + goto fail; + + if (s->token.val != ',') + break; + if (json_next_token(s)) + goto fail; + if (s->ext_json && s->token.val == '}') + break; + } + } + if (json_parse_expect(s, '}')) + goto fail; + } + break; + case '[': + { + JSValue el; + uint32_t idx; + + if (json_next_token(s)) + goto fail; + val = JS_NewArray(ctx); + if (JS_IsException(val)) + goto fail; + if (s->token.val != ']') { + idx = 0; + for(;;) { + el = json_parse_value(s); + if (JS_IsException(el)) + goto fail; + ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E); + if (ret < 0) + goto fail; + if (s->token.val != ',') + break; + if (json_next_token(s)) + goto fail; + idx++; + if (s->ext_json && s->token.val == ']') + break; + } + } + if (json_parse_expect(s, ']')) + goto fail; + } + break; + case TOK_STRING: + val = JS_DupValue(ctx, s->token.u.str.str); + if (json_next_token(s)) + goto fail; + break; + case TOK_NUMBER: + val = s->token.u.num.val; + if (json_next_token(s)) + goto fail; + break; + case TOK_IDENT: + if (s->token.u.ident.atom == JS_ATOM_false || + s->token.u.ident.atom == JS_ATOM_true) { + val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true)); + } else if (s->token.u.ident.atom == JS_ATOM_null) { + val = JS_NULL; + } else { + goto def_token; + } + if (json_next_token(s)) + goto fail; + break; + default: + def_token: + if (s->token.val == TOK_EOF) { + js_parse_error(s, "unexpected end of input"); + } else { + js_parse_error(s, "unexpected token: '%.*s'", + (int)(s->buf_ptr - s->token.ptr), s->token.ptr); + } + goto fail; + } + return val; +fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename, int flags) +{ + JSParseState s1, *s = &s1; + JSValue val = JS_UNDEFINED; + + js_parse_init(ctx, s, buf, buf_len, filename); + s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0); + if (json_next_token(s)) + goto fail; + val = json_parse_value(s); + if (JS_IsException(val)) + goto fail; + if (s->token.val != TOK_EOF) { + if (js_parse_error(s, "unexpected data at the end")) + goto fail; + } + return val; +fail: + JS_FreeValue(ctx, val); + free_token(s, &s->token); + return JS_EXCEPTION; +} + +JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, + const char *filename) +{ + return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); +} + +JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, + JSAtom name, JSValueConst reviver) +{ + JSValue val, new_el, name_val, res; + JSValueConst args[2]; + int ret, is_array; + uint32_t i, len = 0; + JSAtom prop; + JSPropertyEnum *atoms = NULL; + + if (js_check_stack_overflow(ctx->rt, 0)) { + return JS_ThrowStackOverflow(ctx); + } + + val = JS_GetProperty(ctx, holder, name); + if (JS_IsException(val)) + return val; + if (JS_IsObject(val)) { + is_array = JS_IsArray(ctx, val); + if (is_array < 0) + goto fail; + if (is_array) { + if (js_get_length32(ctx, &len, val)) + goto fail; + } else { + ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK); + if (ret < 0) + goto fail; + } + for(i = 0; i < len; i++) { + if (is_array) { + prop = JS_NewAtomUInt32(ctx, i); + if (prop == JS_ATOM_NULL) + goto fail; + } else { + prop = JS_DupAtom(ctx, atoms[i].atom); + } + new_el = internalize_json_property(ctx, val, prop, reviver); + if (JS_IsException(new_el)) { + JS_FreeAtom(ctx, prop); + goto fail; + } + if (JS_IsUndefined(new_el)) { + ret = JS_DeleteProperty(ctx, val, prop, 0); + } else { + ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E); + } + JS_FreeAtom(ctx, prop); + if (ret < 0) + goto fail; + } + } + js_free_prop_enum(ctx, atoms, len); + atoms = NULL; + name_val = JS_AtomToValue(ctx, name); + if (JS_IsException(name_val)) + goto fail; + args[0] = name_val; + args[1] = val; + res = JS_Call(ctx, reviver, holder, 2, args); + JS_FreeValue(ctx, name_val); + JS_FreeValue(ctx, val); + return res; +fail: + js_free_prop_enum(ctx, atoms, len); + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +JSValue js_json_parse(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, root; + JSValueConst reviver; + const char *str; + size_t len; + + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + obj = JS_ParseJSON(ctx, str, len, ""); + JS_FreeCString(ctx, str); + if (JS_IsException(obj)) + return obj; + if (argc > 1 && JS_IsFunction(ctx, argv[1])) { + reviver = argv[1]; + root = JS_NewObject(ctx); + if (JS_IsException(root)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj, + JS_PROP_C_W_E) < 0) { + JS_FreeValue(ctx, root); + return JS_EXCEPTION; + } + obj = internalize_json_property(ctx, root, JS_ATOM_empty_string, + reviver); + JS_FreeValue(ctx, root); + } + return obj; +} + +typedef struct JSONStringifyContext { + JSValueConst replacer_func; + JSValue stack; + JSValue property_list; + JSValue gap; + JSValue empty; + StringBuffer *b; +} JSONStringifyContext; + +JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { + JSValue r = JS_ToQuotedString(ctx, val); + JS_FreeValue(ctx, val); + return r; +} + +JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, + JSValueConst holder, JSValue val, JSValueConst key) +{ + JSValue v; + JSValueConst args[2]; + + if (JS_IsObject(val) +#ifdef CONFIG_BIGNUM + || JS_IsBigInt(ctx, val) /* XXX: probably useless */ +#endif + ) { + JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); + if (JS_IsException(f)) + goto exception; + if (JS_IsFunction(ctx, f)) { + v = JS_CallFree(ctx, f, val, 1, &key); + JS_FreeValue(ctx, val); + val = v; + if (JS_IsException(val)) + goto exception; + } else { + JS_FreeValue(ctx, f); + } + } + + if (!JS_IsUndefined(jsc->replacer_func)) { + args[0] = key; + args[1] = val; + v = JS_Call(ctx, jsc->replacer_func, holder, 2, args); + JS_FreeValue(ctx, val); + val = v; + if (JS_IsException(val)) + goto exception; + } + + switch (JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_OBJECT: + if (JS_IsFunction(ctx, val)) + break; + case JS_TAG_STRING: + case JS_TAG_INT: + case JS_TAG_FLOAT64: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: +#endif + case JS_TAG_BOOL: + case JS_TAG_NULL: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: +#endif + case JS_TAG_EXCEPTION: + return val; + default: + break; + } + JS_FreeValue(ctx, val); + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, + JSValueConst holder, JSValue val, + JSValueConst indent) +{ + JSValue indent1, sep, sep1, tab, v, prop; + JSObject *p; + int64_t i, len; + int cl, ret; + BOOL has_content; + + indent1 = JS_UNDEFINED; + sep = JS_UNDEFINED; + sep1 = JS_UNDEFINED; + tab = JS_UNDEFINED; + prop = JS_UNDEFINED; + + switch (JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_OBJECT: + p = JS_VALUE_GET_OBJ(val); + cl = p->class_id; + if (cl == JS_CLASS_STRING) { + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + goto exception; + val = JS_ToQuotedStringFree(ctx, val); + if (JS_IsException(val)) + goto exception; + return string_buffer_concat_value_free(jsc->b, val); + } else if (cl == JS_CLASS_NUMBER) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + goto exception; + return string_buffer_concat_value_free(jsc->b, val); + } else if (cl == JS_CLASS_BOOLEAN) { + ret = string_buffer_concat_value(jsc->b, p->u.object_data); + JS_FreeValue(ctx, val); + return ret; + } +#ifdef CONFIG_BIGNUM + else if (cl == JS_CLASS_BIG_FLOAT) { + return string_buffer_concat_value_free(jsc->b, val); + } else if (cl == JS_CLASS_BIG_INT) { + JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); + goto exception; + } +#endif + v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); + if (JS_IsException(v)) + goto exception; + if (JS_ToBoolFree(ctx, v)) { + JS_ThrowTypeError(ctx, "circular reference"); + goto exception; + } + indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap)); + if (JS_IsException(indent1)) + goto exception; + if (!JS_IsEmptyString(jsc->gap)) { + sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), ""); + if (JS_IsException(sep)) + goto exception; + sep1 = JS_NewString(ctx, " "); + if (JS_IsException(sep1)) + goto exception; + } else { + sep = JS_DupValue(ctx, jsc->empty); + sep1 = JS_DupValue(ctx, jsc->empty); + } + v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); + if (check_exception_free(ctx, v)) + goto exception; + ret = JS_IsArray(ctx, val); + if (ret < 0) + goto exception; + if (ret) { + if (js_get_length64(ctx, &len, val)) + goto exception; + string_buffer_putc8(jsc->b, '['); + for(i = 0; i < len; i++) { + if (i > 0) + string_buffer_putc8(jsc->b, ','); + string_buffer_concat_value(jsc->b, sep); + v = JS_GetPropertyInt64(ctx, val, i); + if (JS_IsException(v)) + goto exception; + /* XXX: could do this string conversion only when needed */ + prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i)); + if (JS_IsException(prop)) + goto exception; + v = js_json_check(ctx, jsc, val, v, prop); + JS_FreeValue(ctx, prop); + prop = JS_UNDEFINED; + if (JS_IsException(v)) + goto exception; + if (JS_IsUndefined(v)) + v = JS_NULL; + if (js_json_to_str(ctx, jsc, val, v, indent1)) + goto exception; + } + if (len > 0 && !JS_IsEmptyString(jsc->gap)) { + string_buffer_putc8(jsc->b, '\n'); + string_buffer_concat_value(jsc->b, indent); + } + string_buffer_putc8(jsc->b, ']'); + } else { + if (!JS_IsUndefined(jsc->property_list)) + tab = JS_DupValue(ctx, jsc->property_list); + else + tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY); + if (JS_IsException(tab)) + goto exception; + if (js_get_length64(ctx, &len, tab)) + goto exception; + string_buffer_putc8(jsc->b, '{'); + has_content = FALSE; + for(i = 0; i < len; i++) { + JS_FreeValue(ctx, prop); + prop = JS_GetPropertyInt64(ctx, tab, i); + if (JS_IsException(prop)) + goto exception; + v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop)); + if (JS_IsException(v)) + goto exception; + v = js_json_check(ctx, jsc, val, v, prop); + if (JS_IsException(v)) + goto exception; + if (!JS_IsUndefined(v)) { + if (has_content) + string_buffer_putc8(jsc->b, ','); + prop = JS_ToQuotedStringFree(ctx, prop); + if (JS_IsException(prop)) { + JS_FreeValue(ctx, v); + goto exception; + } + string_buffer_concat_value(jsc->b, sep); + string_buffer_concat_value(jsc->b, prop); + string_buffer_putc8(jsc->b, ':'); + string_buffer_concat_value(jsc->b, sep1); + if (js_json_to_str(ctx, jsc, val, v, indent1)) + goto exception; + has_content = TRUE; + } + } + if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) { + string_buffer_putc8(jsc->b, '\n'); + string_buffer_concat_value(jsc->b, indent); + } + string_buffer_putc8(jsc->b, '}'); + } + if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0))) + goto exception; + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, sep1); + JS_FreeValue(ctx, indent1); + JS_FreeValue(ctx, prop); + return 0; + case JS_TAG_STRING: + val = JS_ToQuotedStringFree(ctx, val); + if (JS_IsException(val)) + goto exception; + goto concat_value; + case JS_TAG_FLOAT64: + if (!isfinite(JS_VALUE_GET_FLOAT64(val))) { + val = JS_NULL; + } + goto concat_value; + case JS_TAG_INT: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: +#endif + case JS_TAG_BOOL: + case JS_TAG_NULL: + concat_value: + return string_buffer_concat_value_free(jsc->b, val); +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); + goto exception; +#endif + default: + JS_FreeValue(ctx, val); + return 0; + } + +exception: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, sep); + JS_FreeValue(ctx, sep1); + JS_FreeValue(ctx, indent1); + JS_FreeValue(ctx, prop); + return -1; +} + +JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, + JSValueConst replacer, JSValueConst space0) +{ + StringBuffer b_s; + JSONStringifyContext jsc_s, *jsc = &jsc_s; + JSValue val, v, space, ret, wrapper; + int res; + int64_t i, j, n; + + jsc->replacer_func = JS_UNDEFINED; + jsc->stack = JS_UNDEFINED; + jsc->property_list = JS_UNDEFINED; + jsc->gap = JS_UNDEFINED; + jsc->b = &b_s; + jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string); + ret = JS_UNDEFINED; + wrapper = JS_UNDEFINED; + + string_buffer_init(ctx, jsc->b, 0); + jsc->stack = JS_NewArray(ctx); + if (JS_IsException(jsc->stack)) + goto exception; + if (JS_IsFunction(ctx, replacer)) { + jsc->replacer_func = replacer; + } else { + res = JS_IsArray(ctx, replacer); + if (res < 0) + goto exception; + if (res) { + /* XXX: enumeration is not fully correct */ + jsc->property_list = JS_NewArray(ctx); + if (JS_IsException(jsc->property_list)) + goto exception; + if (js_get_length64(ctx, &n, replacer)) + goto exception; + for (i = j = 0; i < n; i++) { + JSValue present; + v = JS_GetPropertyInt64(ctx, replacer, i); + if (JS_IsException(v)) + goto exception; + if (JS_IsObject(v)) { + JSObject *p = JS_VALUE_GET_OBJ(v); + if (p->class_id == JS_CLASS_STRING || + p->class_id == JS_CLASS_NUMBER) { + v = JS_ToStringFree(ctx, v); + if (JS_IsException(v)) + goto exception; + } else { + JS_FreeValue(ctx, v); + continue; + } + } else if (JS_IsNumber(v)) { + v = JS_ToStringFree(ctx, v); + if (JS_IsException(v)) + goto exception; + } else if (!JS_IsString(v)) { + JS_FreeValue(ctx, v); + continue; + } + present = js_array_includes(ctx, jsc->property_list, + 1, (JSValueConst *)&v); + if (JS_IsException(present)) { + JS_FreeValue(ctx, v); + goto exception; + } + if (!JS_ToBoolFree(ctx, present)) { + JS_SetPropertyInt64(ctx, jsc->property_list, j++, v); + } else { + JS_FreeValue(ctx, v); + } + } + } + } + space = JS_DupValue(ctx, space0); + if (JS_IsObject(space)) { + JSObject *p = JS_VALUE_GET_OBJ(space); + if (p->class_id == JS_CLASS_NUMBER) { + space = JS_ToNumberFree(ctx, space); + } else if (p->class_id == JS_CLASS_STRING) { + space = JS_ToStringFree(ctx, space); + } + if (JS_IsException(space)) { + JS_FreeValue(ctx, space); + goto exception; + } + } + if (JS_IsNumber(space)) { + int n; + if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0)) + goto exception; + jsc->gap = JS_NewStringLen(ctx, " ", n); + } else if (JS_IsString(space)) { + JSString *p = JS_VALUE_GET_STRING(space); + jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10)); + } else { + jsc->gap = JS_DupValue(ctx, jsc->empty); + } + JS_FreeValue(ctx, space); + if (JS_IsException(jsc->gap)) + goto exception; + wrapper = JS_NewObject(ctx); + if (JS_IsException(wrapper)) + goto exception; + if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string, + JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0) + goto exception; + val = JS_DupValue(ctx, obj); + + val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); + if (JS_IsException(val)) + goto exception; + if (JS_IsUndefined(val)) { + ret = JS_UNDEFINED; + goto done1; + } + if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty)) + goto exception; + + ret = string_buffer_end(jsc->b); + goto done; + +exception: + ret = JS_EXCEPTION; +done1: + string_buffer_free(jsc->b); +done: + JS_FreeValue(ctx, wrapper); + JS_FreeValue(ctx, jsc->empty); + JS_FreeValue(ctx, jsc->gap); + JS_FreeValue(ctx, jsc->property_list); + JS_FreeValue(ctx, jsc->stack); + return ret; +} + +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]); +} + +const JSCFunctionListEntry js_json_funcs[] = { + JS_CFUNC_DEF("parse", 2, js_json_parse ), + JS_CFUNC_DEF("stringify", 3, js_json_stringify ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ), +}; + +const JSCFunctionListEntry js_json_obj[] = { + JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicJSON(JSContext *ctx) +{ + /* add JSON as autoinit object */ + JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj)); } \ No newline at end of file diff --git a/src/core/builtins/js-json.h b/src/core/builtins/js-json.h index d35e86c48..a48ca2426 100644 --- a/src/core/builtins/js-json.h +++ b/src/core/builtins/js-json.h @@ -1,31 +1,31 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_JSON_H -#define QUICKJS_JS_JSON_H - -#include "quickjs/quickjs.h" - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_JSON_H +#define QUICKJS_JS_JSON_H + +#include "quickjs/quickjs.h" + #endif \ No newline at end of file diff --git a/src/core/builtins/js-map.c b/src/core/builtins/js-map.c index 4262596b8..8cef9998b 100644 --- a/src/core/builtins/js-map.c +++ b/src/core/builtins/js-map.c @@ -1,811 +1,811 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-map.h" -#include "../exception.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "js-array.h" -#include "js-operator.h" - -/* Set/Map/WeakSet/WeakMap */ - -typedef struct JSMapRecord { - int ref_count; /* used during enumeration to avoid freeing the record */ - BOOL empty; /* TRUE if the record is deleted */ - struct JSMapState *map; - struct JSMapRecord *next_weak_ref; - struct list_head link; - struct list_head hash_link; - JSValue key; - JSValue value; -} JSMapRecord; - -typedef struct JSMapState { - BOOL is_weak; /* TRUE if WeakSet/WeakMap */ - struct list_head records; /* list of JSMapRecord.link */ - uint32_t record_count; - struct list_head *hash_table; - uint32_t hash_size; /* must be a power of two */ - uint32_t record_count_threshold; /* count at which a hash table - resize is needed */ -} JSMapState; - -#define MAGIC_SET (1 << 0) -#define MAGIC_WEAK (1 << 1) - -JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s; - JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; - JSValueConst arr; - BOOL is_set, is_weak; - - is_set = magic & MAGIC_SET; - is_weak = ((magic & MAGIC_WEAK) != 0); - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic); - if (JS_IsException(obj)) - return JS_EXCEPTION; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - goto fail; - init_list_head(&s->records); - s->is_weak = is_weak; - JS_SetOpaque(obj, s); - s->hash_size = 1; - s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size); - if (!s->hash_table) - goto fail; - init_list_head(&s->hash_table[0]); - s->record_count_threshold = 4; - - arr = JS_UNDEFINED; - if (argc > 0) - arr = argv[0]; - if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) { - JSValue item, ret; - BOOL done; - - adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set); - if (JS_IsException(adder)) - goto fail; - if (!JS_IsFunction(ctx, adder)) { - JS_ThrowTypeError(ctx, "set/add is not a function"); - goto fail; - } - - iter = JS_GetIterator(ctx, arr, FALSE); - if (JS_IsException(iter)) - goto fail; - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail; - - for(;;) { - item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(item)) - goto fail; - if (done) { - JS_FreeValue(ctx, item); - break; - } - if (is_set) { - ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); - if (JS_IsException(ret)) { - JS_FreeValue(ctx, item); - goto fail; - } - } else { - JSValue key, value; - JSValueConst args[2]; - key = JS_UNDEFINED; - value = JS_UNDEFINED; - if (!JS_IsObject(item)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail1; - } - key = JS_GetPropertyUint32(ctx, item, 0); - if (JS_IsException(key)) - goto fail1; - value = JS_GetPropertyUint32(ctx, item, 1); - if (JS_IsException(value)) - goto fail1; - args[0] = key; - args[1] = value; - ret = JS_Call(ctx, adder, obj, 2, args); - if (JS_IsException(ret)) { - fail1: - JS_FreeValue(ctx, item); - JS_FreeValue(ctx, key); - JS_FreeValue(ctx, value); - goto fail; - } - JS_FreeValue(ctx, key); - JS_FreeValue(ctx, value); - } - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, item); - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, adder); - } - return obj; -fail: - if (JS_IsObject(iter)) { - /* close the iterator object, preserving pending exception */ - JS_IteratorClose(ctx, iter, TRUE); - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, adder); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -/* XXX: could normalize strings to speed up comparison */ -JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key) -{ - uint32_t tag = JS_VALUE_GET_TAG(key); - /* convert -0.0 to +0.0 */ - if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) { - key = JS_NewInt32(ctx, 0); - } - return key; -} - -/* XXX: better hash ? */ -uint32_t map_hash_key(JSContext *ctx, JSValueConst key) -{ - uint32_t tag = JS_VALUE_GET_NORM_TAG(key); - uint32_t h; - double d; - JSFloat64Union u; - - switch(tag) { - case JS_TAG_BOOL: - h = JS_VALUE_GET_INT(key); - break; - case JS_TAG_STRING: - h = hash_string(JS_VALUE_GET_STRING(key), 0); - break; - case JS_TAG_OBJECT: - case JS_TAG_SYMBOL: - h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; - break; - case JS_TAG_INT: - d = JS_VALUE_GET_INT(key) * 3163; - goto hash_float64; - case JS_TAG_FLOAT64: - d = JS_VALUE_GET_FLOAT64(key); - /* normalize the NaN */ - if (isnan(d)) - d = JS_FLOAT64_NAN; - hash_float64: - u.d = d; - h = (u.u32[0] ^ u.u32[1]) * 3163; - break; - default: - h = 0; /* XXX: bignum support */ - break; - } - h ^= tag; - return h; -} - -JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, - JSValueConst key) -{ - struct list_head *el; - JSMapRecord *mr; - uint32_t h; - h = map_hash_key(ctx, key) & (s->hash_size - 1); - list_for_each(el, &s->hash_table[h]) { - mr = list_entry(el, JSMapRecord, hash_link); - if (js_same_value_zero(ctx, mr->key, key)) - return mr; - } - return NULL; -} - -void map_hash_resize(JSContext *ctx, JSMapState *s) -{ - uint32_t new_hash_size, i, h; - size_t slack; - struct list_head *new_hash_table, *el; - JSMapRecord *mr; - - /* XXX: no reporting of memory allocation failure */ - if (s->hash_size == 1) - new_hash_size = 4; - else - new_hash_size = s->hash_size * 2; - new_hash_table = js_realloc2(ctx, s->hash_table, - sizeof(new_hash_table[0]) * new_hash_size, &slack); - if (!new_hash_table) - return; - new_hash_size += slack / sizeof(*new_hash_table); - - for(i = 0; i < new_hash_size; i++) - init_list_head(&new_hash_table[i]); - - list_for_each(el, &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) { - h = map_hash_key(ctx, mr->key) & (new_hash_size - 1); - list_add_tail(&mr->hash_link, &new_hash_table[h]); - } - } - s->hash_table = new_hash_table; - s->hash_size = new_hash_size; - s->record_count_threshold = new_hash_size * 2; -} - -JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, - JSValueConst key) -{ - uint32_t h; - JSMapRecord *mr; - - mr = js_malloc(ctx, sizeof(*mr)); - if (!mr) - return NULL; - mr->ref_count = 1; - mr->map = s; - mr->empty = FALSE; - if (s->is_weak) { - JSObject *p = JS_VALUE_GET_OBJ(key); - /* Add the weak reference */ - mr->next_weak_ref = p->first_weak_ref; - p->first_weak_ref = mr; - } else { - JS_DupValue(ctx, key); - } - mr->key = (JSValue)key; - h = map_hash_key(ctx, key) & (s->hash_size - 1); - list_add_tail(&mr->hash_link, &s->hash_table[h]); - list_add_tail(&mr->link, &s->records); - s->record_count++; - if (s->record_count >= s->record_count_threshold) { - map_hash_resize(ctx, s); - } - return mr; -} - -/* Remove the weak reference from the object weak - reference list. we don't use a doubly linked list to - save space, assuming a given object has few weak - references to it */ -void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr) -{ - JSMapRecord **pmr, *mr1; - JSObject *p; - - p = JS_VALUE_GET_OBJ(mr->key); - pmr = &p->first_weak_ref; - for(;;) { - mr1 = *pmr; - assert(mr1 != NULL); - if (mr1 == mr) - break; - pmr = &mr1->next_weak_ref; - } - *pmr = mr1->next_weak_ref; -} - -void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) -{ - if (mr->empty) - return; - list_del(&mr->hash_link); - if (s->is_weak) { - delete_weak_ref(rt, mr); - } else { - JS_FreeValueRT(rt, mr->key); - } - JS_FreeValueRT(rt, mr->value); - if (--mr->ref_count == 0) { - list_del(&mr->link); - js_free_rt(rt, mr); - } else { - /* keep a zombie record for iterators */ - mr->empty = TRUE; - mr->key = JS_UNDEFINED; - mr->value = JS_UNDEFINED; - } - s->record_count--; -} - -void map_decref_record(JSRuntime *rt, JSMapRecord *mr) -{ - if (--mr->ref_count == 0) { - /* the record can be safely removed */ - assert(mr->empty); - list_del(&mr->link); - js_free_rt(rt, mr); - } -} - -void reset_weak_ref(JSRuntime *rt, JSObject *p) -{ - JSMapRecord *mr, *mr_next; - JSMapState *s; - - /* first pass to remove the records from the WeakMap/WeakSet - lists */ - for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) { - s = mr->map; - assert(s->is_weak); - assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ - list_del(&mr->hash_link); - list_del(&mr->link); - } - - /* second pass to free the values to avoid modifying the weak - reference list while traversing it. */ - for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) { - mr_next = mr->next_weak_ref; - JS_FreeValueRT(rt, mr->value); - js_free_rt(rt, mr); - } - - p->first_weak_ref = NULL; /* fail safe */ -} - -JSValue js_map_set(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; - JSValueConst key, value; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - if (s->is_weak && !JS_IsObject(key)) - return JS_ThrowTypeErrorNotAnObject(ctx); - if (magic & MAGIC_SET) - value = JS_UNDEFINED; - else - value = argv[1]; - mr = map_find_record(ctx, s, key); - if (mr) { - JS_FreeValue(ctx, mr->value); - } else { - mr = map_add_record(ctx, s, key); - if (!mr) - return JS_EXCEPTION; - } - mr->value = JS_DupValue(ctx, value); - return JS_DupValue(ctx, this_val); -} - -JSValue js_map_get(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; - JSValueConst key; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - mr = map_find_record(ctx, s, key); - if (!mr) - return JS_UNDEFINED; - else - return JS_DupValue(ctx, mr->value); -} - -JSValue js_map_has(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; - JSValueConst key; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - mr = map_find_record(ctx, s, key); - return JS_NewBool(ctx, (mr != NULL)); -} - -JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSMapRecord *mr; - JSValueConst key; - - if (!s) - return JS_EXCEPTION; - key = map_normalize_key(ctx, argv[0]); - mr = map_find_record(ctx, s, key); - if (!mr) - return JS_FALSE; - map_delete_record(ctx->rt, s, mr); - return JS_TRUE; -} - -JSValue js_map_clear(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - struct list_head *el, *el1; - JSMapRecord *mr; - - if (!s) - return JS_EXCEPTION; - list_for_each_safe(el, el1, &s->records) { - mr = list_entry(el, JSMapRecord, link); - map_delete_record(ctx->rt, s, mr); - } - return JS_UNDEFINED; -} - -JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - if (!s) - return JS_EXCEPTION; - return JS_NewUint32(ctx, s->record_count); -} - -JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - JSValueConst func, this_arg; - JSValue ret, args[3]; - struct list_head *el; - JSMapRecord *mr; - - if (!s) - return JS_EXCEPTION; - func = argv[0]; - if (argc > 1) - this_arg = argv[1]; - else - this_arg = JS_UNDEFINED; - if (check_function(ctx, func)) - return JS_EXCEPTION; - /* Note: the list can be modified while traversing it, but the - current element is locked */ - el = s->records.next; - while (el != &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) { - mr->ref_count++; - /* must duplicate in case the record is deleted */ - args[1] = JS_DupValue(ctx, mr->key); - if (magic) - args[0] = args[1]; - else - args[0] = JS_DupValue(ctx, mr->value); - args[2] = (JSValue)this_val; - ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); - JS_FreeValue(ctx, args[0]); - if (!magic) - JS_FreeValue(ctx, args[1]); - el = el->next; - map_decref_record(ctx->rt, mr); - if (JS_IsException(ret)) - return ret; - JS_FreeValue(ctx, ret); - } else { - el = el->next; - } - } - return JS_UNDEFINED; -} - -void js_map_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p; - JSMapState *s; - struct list_head *el, *el1; - JSMapRecord *mr; - - p = JS_VALUE_GET_OBJ(val); - s = p->u.map_state; - if (s) { - /* if the object is deleted we are sure that no iterator is - using it */ - list_for_each_safe(el, el1, &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) { - if (s->is_weak) - delete_weak_ref(rt, mr); - else - JS_FreeValueRT(rt, mr->key); - JS_FreeValueRT(rt, mr->value); - } - js_free_rt(rt, mr); - } - js_free_rt(rt, s->hash_table); - js_free_rt(rt, s); - } -} - -void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSMapState *s; - struct list_head *el; - JSMapRecord *mr; - - s = p->u.map_state; - if (s) { - list_for_each(el, &s->records) { - mr = list_entry(el, JSMapRecord, link); - if (!s->is_weak) - JS_MarkValue(rt, mr->key, mark_func); - JS_MarkValue(rt, mr->value, mark_func); - } - } -} - -/* Map Iterator */ - -typedef struct JSMapIteratorData { - JSValue obj; - JSIteratorKindEnum kind; - JSMapRecord *cur_record; -} JSMapIteratorData; - -void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p; - JSMapIteratorData *it; - - p = JS_VALUE_GET_OBJ(val); - it = p->u.map_iterator_data; - if (it) { - /* During the GC sweep phase the Map finalizer may be - called before the Map iterator finalizer */ - if (JS_IsLiveObject(rt, it->obj) && it->cur_record) { - map_decref_record(rt, it->cur_record); - } - JS_FreeValueRT(rt, it->obj); - js_free_rt(rt, it); - } -} - -void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSMapIteratorData *it; - it = p->u.map_iterator_data; - if (it) { - /* the record is already marked by the object */ - JS_MarkValue(rt, it->obj, mark_func); - } -} - -JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSIteratorKindEnum kind; - JSMapState *s; - JSMapIteratorData *it; - JSValue enum_obj; - - kind = magic >> 2; - magic &= 3; - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); - if (!s) - return JS_EXCEPTION; - enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic); - if (JS_IsException(enum_obj)) - goto fail; - it = js_malloc(ctx, sizeof(*it)); - if (!it) { - JS_FreeValue(ctx, enum_obj); - goto fail; - } - it->obj = JS_DupValue(ctx, this_val); - it->kind = kind; - it->cur_record = NULL; - JS_SetOpaque(enum_obj, it); - return enum_obj; -fail: - return JS_EXCEPTION; -} - -JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic) -{ - JSMapIteratorData *it; - JSMapState *s; - JSMapRecord *mr; - struct list_head *el; - - it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic); - if (!it) { - *pdone = FALSE; - return JS_EXCEPTION; - } - if (JS_IsUndefined(it->obj)) - goto done; - s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic); - assert(s != NULL); - if (!it->cur_record) { - el = s->records.next; - } else { - mr = it->cur_record; - el = mr->link.next; - map_decref_record(ctx->rt, mr); /* the record can be freed here */ - } - for(;;) { - if (el == &s->records) { - /* no more record */ - it->cur_record = NULL; - JS_FreeValue(ctx, it->obj); - it->obj = JS_UNDEFINED; - done: - /* end of enumeration */ - *pdone = TRUE; - return JS_UNDEFINED; - } - mr = list_entry(el, JSMapRecord, link); - if (!mr->empty) - break; - /* get the next record */ - el = mr->link.next; - } - - /* lock the record so that it won't be freed */ - mr->ref_count++; - it->cur_record = mr; - *pdone = FALSE; - - if (it->kind == JS_ITERATOR_KIND_KEY) { - return JS_DupValue(ctx, mr->key); - } else { - JSValueConst args[2]; - args[0] = mr->key; - if (magic) - args[1] = mr->key; - else - args[1] = mr->value; - if (it->kind == JS_ITERATOR_KIND_VALUE) { - return JS_DupValue(ctx, args[1]); - } else { - return js_create_array(ctx, 2, args); - } - } -} - -const JSCFunctionListEntry js_map_funcs[] = { - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), -}; - -const JSCFunctionListEntry js_map_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ), - JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ), - JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ), - JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ), - JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ), - JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0), - JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ), - JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ), - JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ), - JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ), - JS_ALIAS_DEF("[Symbol.iterator]", "entries" ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ), -}; - -const JSCFunctionListEntry js_map_iterator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ), -}; - -const JSCFunctionListEntry js_set_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ), - JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ), - JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ), - JS_ALIAS_DEF("keys", "values" ), - JS_ALIAS_DEF("[Symbol.iterator]", "values" ), - JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ), -}; - -const JSCFunctionListEntry js_set_iterator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ), -}; - -const JSCFunctionListEntry js_weak_map_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ), -}; - -const JSCFunctionListEntry js_weak_set_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ), - JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ), -}; - -const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = { - js_map_proto_funcs, - js_set_proto_funcs, - js_weak_map_proto_funcs, - js_weak_set_proto_funcs, - js_map_iterator_proto_funcs, - js_set_iterator_proto_funcs, -}; - -const uint8_t js_map_proto_funcs_count[6] = { - countof(js_map_proto_funcs), - countof(js_set_proto_funcs), - countof(js_weak_map_proto_funcs), - countof(js_weak_set_proto_funcs), - countof(js_map_iterator_proto_funcs), - countof(js_set_iterator_proto_funcs), -}; - -void JS_AddIntrinsicMapSet(JSContext *ctx) -{ - int i; - JSValue obj1; - char buf[ATOM_GET_STR_BUF_SIZE]; - - for(i = 0; i < 4; i++) { - const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf), - JS_ATOM_Map + i); - ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i], - js_map_proto_funcs_ptr[i], - js_map_proto_funcs_count[i]); - obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0, - JS_CFUNC_constructor_magic, i); - if (i < 2) { - JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs, - countof(js_map_funcs)); - } - JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]); - } - - for(i = 0; i < 2; i++) { - ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] = - JS_NewObjectProto(ctx, ctx->iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i], - js_map_proto_funcs_ptr[i + 4], - js_map_proto_funcs_count[i + 4]); - } +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-map.h" +#include "../exception.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "js-array.h" +#include "js-operator.h" + +/* Set/Map/WeakSet/WeakMap */ + +typedef struct JSMapRecord { + int ref_count; /* used during enumeration to avoid freeing the record */ + BOOL empty; /* TRUE if the record is deleted */ + struct JSMapState *map; + struct JSMapRecord *next_weak_ref; + struct list_head link; + struct list_head hash_link; + JSValue key; + JSValue value; +} JSMapRecord; + +typedef struct JSMapState { + BOOL is_weak; /* TRUE if WeakSet/WeakMap */ + struct list_head records; /* list of JSMapRecord.link */ + uint32_t record_count; + struct list_head *hash_table; + uint32_t hash_size; /* must be a power of two */ + uint32_t record_count_threshold; /* count at which a hash table + resize is needed */ +} JSMapState; + +#define MAGIC_SET (1 << 0) +#define MAGIC_WEAK (1 << 1) + +JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s; + JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; + JSValueConst arr; + BOOL is_set, is_weak; + + is_set = magic & MAGIC_SET; + is_weak = ((magic & MAGIC_WEAK) != 0); + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic); + if (JS_IsException(obj)) + return JS_EXCEPTION; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + init_list_head(&s->records); + s->is_weak = is_weak; + JS_SetOpaque(obj, s); + s->hash_size = 1; + s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size); + if (!s->hash_table) + goto fail; + init_list_head(&s->hash_table[0]); + s->record_count_threshold = 4; + + arr = JS_UNDEFINED; + if (argc > 0) + arr = argv[0]; + if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) { + JSValue item, ret; + BOOL done; + + adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set); + if (JS_IsException(adder)) + goto fail; + if (!JS_IsFunction(ctx, adder)) { + JS_ThrowTypeError(ctx, "set/add is not a function"); + goto fail; + } + + iter = JS_GetIterator(ctx, arr, FALSE); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + + for(;;) { + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail; + if (done) { + JS_FreeValue(ctx, item); + break; + } + if (is_set) { + ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, item); + goto fail; + } + } else { + JSValue key, value; + JSValueConst args[2]; + key = JS_UNDEFINED; + value = JS_UNDEFINED; + if (!JS_IsObject(item)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail1; + } + key = JS_GetPropertyUint32(ctx, item, 0); + if (JS_IsException(key)) + goto fail1; + value = JS_GetPropertyUint32(ctx, item, 1); + if (JS_IsException(value)) + goto fail1; + args[0] = key; + args[1] = value; + ret = JS_Call(ctx, adder, obj, 2, args); + if (JS_IsException(ret)) { + fail1: + JS_FreeValue(ctx, item); + JS_FreeValue(ctx, key); + JS_FreeValue(ctx, value); + goto fail; + } + JS_FreeValue(ctx, key); + JS_FreeValue(ctx, value); + } + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, item); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, adder); + } + return obj; +fail: + if (JS_IsObject(iter)) { + /* close the iterator object, preserving pending exception */ + JS_IteratorClose(ctx, iter, TRUE); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, adder); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +/* XXX: could normalize strings to speed up comparison */ +JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key) +{ + uint32_t tag = JS_VALUE_GET_TAG(key); + /* convert -0.0 to +0.0 */ + if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) { + key = JS_NewInt32(ctx, 0); + } + return key; +} + +/* XXX: better hash ? */ +uint32_t map_hash_key(JSContext *ctx, JSValueConst key) +{ + uint32_t tag = JS_VALUE_GET_NORM_TAG(key); + uint32_t h; + double d; + JSFloat64Union u; + + switch(tag) { + case JS_TAG_BOOL: + h = JS_VALUE_GET_INT(key); + break; + case JS_TAG_STRING: + h = hash_string(JS_VALUE_GET_STRING(key), 0); + break; + case JS_TAG_OBJECT: + case JS_TAG_SYMBOL: + h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; + break; + case JS_TAG_INT: + d = JS_VALUE_GET_INT(key) * 3163; + goto hash_float64; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(key); + /* normalize the NaN */ + if (isnan(d)) + d = JS_FLOAT64_NAN; + hash_float64: + u.d = d; + h = (u.u32[0] ^ u.u32[1]) * 3163; + break; + default: + h = 0; /* XXX: bignum support */ + break; + } + h ^= tag; + return h; +} + +JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, + JSValueConst key) +{ + struct list_head *el; + JSMapRecord *mr; + uint32_t h; + h = map_hash_key(ctx, key) & (s->hash_size - 1); + list_for_each(el, &s->hash_table[h]) { + mr = list_entry(el, JSMapRecord, hash_link); + if (js_same_value_zero(ctx, mr->key, key)) + return mr; + } + return NULL; +} + +void map_hash_resize(JSContext *ctx, JSMapState *s) +{ + uint32_t new_hash_size, i, h; + size_t slack; + struct list_head *new_hash_table, *el; + JSMapRecord *mr; + + /* XXX: no reporting of memory allocation failure */ + if (s->hash_size == 1) + new_hash_size = 4; + else + new_hash_size = s->hash_size * 2; + new_hash_table = js_realloc2(ctx, s->hash_table, + sizeof(new_hash_table[0]) * new_hash_size, &slack); + if (!new_hash_table) + return; + new_hash_size += slack / sizeof(*new_hash_table); + + for(i = 0; i < new_hash_size; i++) + init_list_head(&new_hash_table[i]); + + list_for_each(el, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + h = map_hash_key(ctx, mr->key) & (new_hash_size - 1); + list_add_tail(&mr->hash_link, &new_hash_table[h]); + } + } + s->hash_table = new_hash_table; + s->hash_size = new_hash_size; + s->record_count_threshold = new_hash_size * 2; +} + +JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, + JSValueConst key) +{ + uint32_t h; + JSMapRecord *mr; + + mr = js_malloc(ctx, sizeof(*mr)); + if (!mr) + return NULL; + mr->ref_count = 1; + mr->map = s; + mr->empty = FALSE; + if (s->is_weak) { + JSObject *p = JS_VALUE_GET_OBJ(key); + /* Add the weak reference */ + mr->next_weak_ref = p->first_weak_ref; + p->first_weak_ref = mr; + } else { + JS_DupValue(ctx, key); + } + mr->key = (JSValue)key; + h = map_hash_key(ctx, key) & (s->hash_size - 1); + list_add_tail(&mr->hash_link, &s->hash_table[h]); + list_add_tail(&mr->link, &s->records); + s->record_count++; + if (s->record_count >= s->record_count_threshold) { + map_hash_resize(ctx, s); + } + return mr; +} + +/* Remove the weak reference from the object weak + reference list. we don't use a doubly linked list to + save space, assuming a given object has few weak + references to it */ +void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr) +{ + JSMapRecord **pmr, *mr1; + JSObject *p; + + p = JS_VALUE_GET_OBJ(mr->key); + pmr = &p->first_weak_ref; + for(;;) { + mr1 = *pmr; + assert(mr1 != NULL); + if (mr1 == mr) + break; + pmr = &mr1->next_weak_ref; + } + *pmr = mr1->next_weak_ref; +} + +void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) +{ + if (mr->empty) + return; + list_del(&mr->hash_link); + if (s->is_weak) { + delete_weak_ref(rt, mr); + } else { + JS_FreeValueRT(rt, mr->key); + } + JS_FreeValueRT(rt, mr->value); + if (--mr->ref_count == 0) { + list_del(&mr->link); + js_free_rt(rt, mr); + } else { + /* keep a zombie record for iterators */ + mr->empty = TRUE; + mr->key = JS_UNDEFINED; + mr->value = JS_UNDEFINED; + } + s->record_count--; +} + +void map_decref_record(JSRuntime *rt, JSMapRecord *mr) +{ + if (--mr->ref_count == 0) { + /* the record can be safely removed */ + assert(mr->empty); + list_del(&mr->link); + js_free_rt(rt, mr); + } +} + +void reset_weak_ref(JSRuntime *rt, JSObject *p) +{ + JSMapRecord *mr, *mr_next; + JSMapState *s; + + /* first pass to remove the records from the WeakMap/WeakSet + lists */ + for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) { + s = mr->map; + assert(s->is_weak); + assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ + list_del(&mr->hash_link); + list_del(&mr->link); + } + + /* second pass to free the values to avoid modifying the weak + reference list while traversing it. */ + for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) { + mr_next = mr->next_weak_ref; + JS_FreeValueRT(rt, mr->value); + js_free_rt(rt, mr); + } + + p->first_weak_ref = NULL; /* fail safe */ +} + +JSValue js_map_set(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValueConst key, value; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + if (s->is_weak && !JS_IsObject(key)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (magic & MAGIC_SET) + value = JS_UNDEFINED; + else + value = argv[1]; + mr = map_find_record(ctx, s, key); + if (mr) { + JS_FreeValue(ctx, mr->value); + } else { + mr = map_add_record(ctx, s, key); + if (!mr) + return JS_EXCEPTION; + } + mr->value = JS_DupValue(ctx, value); + return JS_DupValue(ctx, this_val); +} + +JSValue js_map_get(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValueConst key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + if (!mr) + return JS_UNDEFINED; + else + return JS_DupValue(ctx, mr->value); +} + +JSValue js_map_has(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValueConst key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + return JS_NewBool(ctx, (mr != NULL)); +} + +JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSMapRecord *mr; + JSValueConst key; + + if (!s) + return JS_EXCEPTION; + key = map_normalize_key(ctx, argv[0]); + mr = map_find_record(ctx, s, key); + if (!mr) + return JS_FALSE; + map_delete_record(ctx->rt, s, mr); + return JS_TRUE; +} + +JSValue js_map_clear(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + struct list_head *el, *el1; + JSMapRecord *mr; + + if (!s) + return JS_EXCEPTION; + list_for_each_safe(el, el1, &s->records) { + mr = list_entry(el, JSMapRecord, link); + map_delete_record(ctx->rt, s, mr); + } + return JS_UNDEFINED; +} + +JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + if (!s) + return JS_EXCEPTION; + return JS_NewUint32(ctx, s->record_count); +} + +JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + JSValueConst func, this_arg; + JSValue ret, args[3]; + struct list_head *el; + JSMapRecord *mr; + + if (!s) + return JS_EXCEPTION; + func = argv[0]; + if (argc > 1) + this_arg = argv[1]; + else + this_arg = JS_UNDEFINED; + if (check_function(ctx, func)) + return JS_EXCEPTION; + /* Note: the list can be modified while traversing it, but the + current element is locked */ + el = s->records.next; + while (el != &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + mr->ref_count++; + /* must duplicate in case the record is deleted */ + args[1] = JS_DupValue(ctx, mr->key); + if (magic) + args[0] = args[1]; + else + args[0] = JS_DupValue(ctx, mr->value); + args[2] = (JSValue)this_val; + ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); + JS_FreeValue(ctx, args[0]); + if (!magic) + JS_FreeValue(ctx, args[1]); + el = el->next; + map_decref_record(ctx->rt, mr); + if (JS_IsException(ret)) + return ret; + JS_FreeValue(ctx, ret); + } else { + el = el->next; + } + } + return JS_UNDEFINED; +} + +void js_map_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p; + JSMapState *s; + struct list_head *el, *el1; + JSMapRecord *mr; + + p = JS_VALUE_GET_OBJ(val); + s = p->u.map_state; + if (s) { + /* if the object is deleted we are sure that no iterator is + using it */ + list_for_each_safe(el, el1, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) { + if (s->is_weak) + delete_weak_ref(rt, mr); + else + JS_FreeValueRT(rt, mr->key); + JS_FreeValueRT(rt, mr->value); + } + js_free_rt(rt, mr); + } + js_free_rt(rt, s->hash_table); + js_free_rt(rt, s); + } +} + +void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSMapState *s; + struct list_head *el; + JSMapRecord *mr; + + s = p->u.map_state; + if (s) { + list_for_each(el, &s->records) { + mr = list_entry(el, JSMapRecord, link); + if (!s->is_weak) + JS_MarkValue(rt, mr->key, mark_func); + JS_MarkValue(rt, mr->value, mark_func); + } + } +} + +/* Map Iterator */ + +typedef struct JSMapIteratorData { + JSValue obj; + JSIteratorKindEnum kind; + JSMapRecord *cur_record; +} JSMapIteratorData; + +void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p; + JSMapIteratorData *it; + + p = JS_VALUE_GET_OBJ(val); + it = p->u.map_iterator_data; + if (it) { + /* During the GC sweep phase the Map finalizer may be + called before the Map iterator finalizer */ + if (JS_IsLiveObject(rt, it->obj) && it->cur_record) { + map_decref_record(rt, it->cur_record); + } + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); + } +} + +void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSMapIteratorData *it; + it = p->u.map_iterator_data; + if (it) { + /* the record is already marked by the object */ + JS_MarkValue(rt, it->obj, mark_func); + } +} + +JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSIteratorKindEnum kind; + JSMapState *s; + JSMapIteratorData *it; + JSValue enum_obj; + + kind = magic >> 2; + magic &= 3; + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); + if (!s) + return JS_EXCEPTION; + enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic); + if (JS_IsException(enum_obj)) + goto fail; + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, enum_obj); + goto fail; + } + it->obj = JS_DupValue(ctx, this_val); + it->kind = kind; + it->cur_record = NULL; + JS_SetOpaque(enum_obj, it); + return enum_obj; +fail: + return JS_EXCEPTION; +} + +JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSMapIteratorData *it; + JSMapState *s; + JSMapRecord *mr; + struct list_head *el; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic); + if (!it) { + *pdone = FALSE; + return JS_EXCEPTION; + } + if (JS_IsUndefined(it->obj)) + goto done; + s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic); + assert(s != NULL); + if (!it->cur_record) { + el = s->records.next; + } else { + mr = it->cur_record; + el = mr->link.next; + map_decref_record(ctx->rt, mr); /* the record can be freed here */ + } + for(;;) { + if (el == &s->records) { + /* no more record */ + it->cur_record = NULL; + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + /* end of enumeration */ + *pdone = TRUE; + return JS_UNDEFINED; + } + mr = list_entry(el, JSMapRecord, link); + if (!mr->empty) + break; + /* get the next record */ + el = mr->link.next; + } + + /* lock the record so that it won't be freed */ + mr->ref_count++; + it->cur_record = mr; + *pdone = FALSE; + + if (it->kind == JS_ITERATOR_KIND_KEY) { + return JS_DupValue(ctx, mr->key); + } else { + JSValueConst args[2]; + args[0] = mr->key; + if (magic) + args[1] = mr->key; + else + args[1] = mr->value; + if (it->kind == JS_ITERATOR_KIND_VALUE) { + return JS_DupValue(ctx, args[1]); + } else { + return js_create_array(ctx, 2, args); + } + } +} + +const JSCFunctionListEntry js_map_funcs[] = { + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), +}; + +const JSCFunctionListEntry js_map_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ), + JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ), + JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ), + JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ), + JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ), + JS_ALIAS_DEF("[Symbol.iterator]", "entries" ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ), +}; + +const JSCFunctionListEntry js_map_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ), +}; + +const JSCFunctionListEntry js_set_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ), + JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ), + JS_ALIAS_DEF("keys", "values" ), + JS_ALIAS_DEF("[Symbol.iterator]", "values" ), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ), +}; + +const JSCFunctionListEntry js_set_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ), +}; + +const JSCFunctionListEntry js_weak_map_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ), +}; + +const JSCFunctionListEntry js_weak_set_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ), + JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ), +}; + +const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = { + js_map_proto_funcs, + js_set_proto_funcs, + js_weak_map_proto_funcs, + js_weak_set_proto_funcs, + js_map_iterator_proto_funcs, + js_set_iterator_proto_funcs, +}; + +const uint8_t js_map_proto_funcs_count[6] = { + countof(js_map_proto_funcs), + countof(js_set_proto_funcs), + countof(js_weak_map_proto_funcs), + countof(js_weak_set_proto_funcs), + countof(js_map_iterator_proto_funcs), + countof(js_set_iterator_proto_funcs), +}; + +void JS_AddIntrinsicMapSet(JSContext *ctx) +{ + int i; + JSValue obj1; + char buf[ATOM_GET_STR_BUF_SIZE]; + + for(i = 0; i < 4; i++) { + const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf), + JS_ATOM_Map + i); + ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i], + js_map_proto_funcs_ptr[i], + js_map_proto_funcs_count[i]); + obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0, + JS_CFUNC_constructor_magic, i); + if (i < 2) { + JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs, + countof(js_map_funcs)); + } + JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]); + } + + for(i = 0; i < 2; i++) { + ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] = + JS_NewObjectProto(ctx, ctx->iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i], + js_map_proto_funcs_ptr[i + 4], + js_map_proto_funcs_count[i + 4]); + } } \ No newline at end of file diff --git a/src/core/builtins/js-map.h b/src/core/builtins/js-map.h index f3482d403..d230bfb12 100644 --- a/src/core/builtins/js-map.h +++ b/src/core/builtins/js-map.h @@ -1,33 +1,33 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_MAP_H -#define QUICKJS_JS_MAP_H - -#include "quickjs/quickjs.h" - -void reset_weak_ref(JSRuntime *rt, JSObject *p); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_MAP_H +#define QUICKJS_JS_MAP_H + +#include "quickjs/quickjs.h" + +void reset_weak_ref(JSRuntime *rt, JSObject *p); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-math.c b/src/core/builtins/js-math.c index 7ddc9bc1a..fe27f7f71 100644 --- a/src/core/builtins/js-math.c +++ b/src/core/builtins/js-math.c @@ -1,238 +1,238 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-math.h" - -/* Math */ - -/* precondition: a and b are not NaN */ -double js_fmin(double a, double b) -{ - if (a == 0 && b == 0) { - JSFloat64Union a1, b1; - a1.d = a; - b1.d = b; - a1.u64 |= b1.u64; - return a1.d; - } else { - return fmin(a, b); - } -} - -/* precondition: a and b are not NaN */ -double js_fmax(double a, double b) -{ - if (a == 0 && b == 0) { - JSFloat64Union a1, b1; - a1.d = a; - b1.d = b; - a1.u64 &= b1.u64; - return a1.d; - } else { - return fmax(a, b); - } -} - -JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - BOOL is_max = magic; - double r, a; - int i; - uint32_t tag; - - if (unlikely(argc == 0)) { - return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); - } - - tag = JS_VALUE_GET_TAG(argv[0]); - if (tag == JS_TAG_INT) { - int a1, r1 = JS_VALUE_GET_INT(argv[0]); - for(i = 1; i < argc; i++) { - tag = JS_VALUE_GET_TAG(argv[i]); - if (tag != JS_TAG_INT) { - r = r1; - goto generic_case; - } - a1 = JS_VALUE_GET_INT(argv[i]); - if (is_max) - r1 = max_int(r1, a1); - else - r1 = min_int(r1, a1); - - } - return JS_NewInt32(ctx, r1); - } else { - if (JS_ToFloat64(ctx, &r, argv[0])) - return JS_EXCEPTION; - i = 1; - generic_case: - while (i < argc) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isnan(r)) { - if (isnan(a)) { - r = a; - } else { - if (is_max) - r = js_fmax(r, a); - else - r = js_fmin(r, a); - } - } - i++; - } - return JS_NewFloat64(ctx, r); - } -} - -double js_math_sign(double a) -{ - if (isnan(a) || a == 0.0) - return a; - if (a < 0) - return -1; - else - return 1; -} - -double js_math_round(double a) -{ - JSFloat64Union u; - uint64_t frac_mask, one; - unsigned int e, s; - - u.d = a; - e = (u.u64 >> 52) & 0x7ff; - if (e < 1023) { - /* abs(a) < 1 */ - if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) { - /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */ - u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52); - } else { - /* return +/-0.0 */ - u.u64 &= (uint64_t)1 << 63; - } - } else if (e < (1023 + 52)) { - s = u.u64 >> 63; - one = (uint64_t)1 << (52 - (e - 1023)); - frac_mask = one - 1; - u.u64 += (one >> 1) - s; - u.u64 &= ~frac_mask; /* truncate to an integer */ - } - /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */ - return u.d; -} - -JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double r, a; - int i; - - r = 0; - if (argc > 0) { - if (JS_ToFloat64(ctx, &r, argv[0])) - return JS_EXCEPTION; - if (argc == 1) { - r = fabs(r); - } else { - /* use the built-in function to minimize precision loss */ - for (i = 1; i < argc; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - r = hypot(r, a); - } - } - } - return JS_NewFloat64(ctx, r); -} - -double js_math_fround(double a) -{ - return (float)a; -} - -JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int a, b; - - if (JS_ToInt32(ctx, &a, argv[0])) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &b, argv[1])) - return JS_EXCEPTION; - /* purposely ignoring overflow */ - return JS_NewInt32(ctx, a * b); -} - -JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - uint32_t a, r; - - if (JS_ToUint32(ctx, &a, argv[0])) - return JS_EXCEPTION; - if (a == 0) - r = 32; - else - r = clz32(a); - return JS_NewInt32(ctx, r); -} - -/* xorshift* random number generator by Marsaglia */ -uint64_t xorshift64star(uint64_t *pstate) -{ - uint64_t x; - x = *pstate; - x ^= x >> 12; - x ^= x << 25; - x ^= x >> 27; - *pstate = x; - return x * 0x2545F4914F6CDD1D; -} - -void js_random_init(JSContext *ctx) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; - /* the state must be non zero */ - if (ctx->random_state == 0) - ctx->random_state = 1; -} - -JSValue js_math_random(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSFloat64Union u; - uint64_t v; - - v = xorshift64star(&ctx->random_state); - /* 1.0 <= u.d < 2 */ - u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); - return __JS_NewFloat64(ctx, u.d - 1.0); -} - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-math.h" + +/* Math */ + +/* precondition: a and b are not NaN */ +double js_fmin(double a, double b) +{ + if (a == 0 && b == 0) { + JSFloat64Union a1, b1; + a1.d = a; + b1.d = b; + a1.u64 |= b1.u64; + return a1.d; + } else { + return fmin(a, b); + } +} + +/* precondition: a and b are not NaN */ +double js_fmax(double a, double b) +{ + if (a == 0 && b == 0) { + JSFloat64Union a1, b1; + a1.d = a; + b1.d = b; + a1.u64 &= b1.u64; + return a1.d; + } else { + return fmax(a, b); + } +} + +JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + BOOL is_max = magic; + double r, a; + int i; + uint32_t tag; + + if (unlikely(argc == 0)) { + return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); + } + + tag = JS_VALUE_GET_TAG(argv[0]); + if (tag == JS_TAG_INT) { + int a1, r1 = JS_VALUE_GET_INT(argv[0]); + for(i = 1; i < argc; i++) { + tag = JS_VALUE_GET_TAG(argv[i]); + if (tag != JS_TAG_INT) { + r = r1; + goto generic_case; + } + a1 = JS_VALUE_GET_INT(argv[i]); + if (is_max) + r1 = max_int(r1, a1); + else + r1 = min_int(r1, a1); + + } + return JS_NewInt32(ctx, r1); + } else { + if (JS_ToFloat64(ctx, &r, argv[0])) + return JS_EXCEPTION; + i = 1; + generic_case: + while (i < argc) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isnan(r)) { + if (isnan(a)) { + r = a; + } else { + if (is_max) + r = js_fmax(r, a); + else + r = js_fmin(r, a); + } + } + i++; + } + return JS_NewFloat64(ctx, r); + } +} + +double js_math_sign(double a) +{ + if (isnan(a) || a == 0.0) + return a; + if (a < 0) + return -1; + else + return 1; +} + +double js_math_round(double a) +{ + JSFloat64Union u; + uint64_t frac_mask, one; + unsigned int e, s; + + u.d = a; + e = (u.u64 >> 52) & 0x7ff; + if (e < 1023) { + /* abs(a) < 1 */ + if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) { + /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */ + u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52); + } else { + /* return +/-0.0 */ + u.u64 &= (uint64_t)1 << 63; + } + } else if (e < (1023 + 52)) { + s = u.u64 >> 63; + one = (uint64_t)1 << (52 - (e - 1023)); + frac_mask = one - 1; + u.u64 += (one >> 1) - s; + u.u64 &= ~frac_mask; /* truncate to an integer */ + } + /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */ + return u.d; +} + +JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + double r, a; + int i; + + r = 0; + if (argc > 0) { + if (JS_ToFloat64(ctx, &r, argv[0])) + return JS_EXCEPTION; + if (argc == 1) { + r = fabs(r); + } else { + /* use the built-in function to minimize precision loss */ + for (i = 1; i < argc; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + r = hypot(r, a); + } + } + } + return JS_NewFloat64(ctx, r); +} + +double js_math_fround(double a) +{ + return (float)a; +} + +JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int a, b; + + if (JS_ToInt32(ctx, &a, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &b, argv[1])) + return JS_EXCEPTION; + /* purposely ignoring overflow */ + return JS_NewInt32(ctx, a * b); +} + +JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + uint32_t a, r; + + if (JS_ToUint32(ctx, &a, argv[0])) + return JS_EXCEPTION; + if (a == 0) + r = 32; + else + r = clz32(a); + return JS_NewInt32(ctx, r); +} + +/* xorshift* random number generator by Marsaglia */ +uint64_t xorshift64star(uint64_t *pstate) +{ + uint64_t x; + x = *pstate; + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + *pstate = x; + return x * 0x2545F4914F6CDD1D; +} + +void js_random_init(JSContext *ctx) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; + /* the state must be non zero */ + if (ctx->random_state == 0) + ctx->random_state = 1; +} + +JSValue js_math_random(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSFloat64Union u; + uint64_t v; + + v = xorshift64star(&ctx->random_state); + /* 1.0 <= u.d < 2 */ + u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); + return __JS_NewFloat64(ctx, u.d - 1.0); +} + diff --git a/src/core/builtins/js-math.h b/src/core/builtins/js-math.h index 9f1991133..ed8e2981b 100644 --- a/src/core/builtins/js-math.h +++ b/src/core/builtins/js-math.h @@ -1,54 +1,54 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_MATH_H -#define QUICKJS_JS_MATH_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "../types.h" - -/* precondition: a and b are not NaN */ -double js_fmin(double a, double b); -/* precondition: a and b are not NaN */ -double js_fmax(double a, double b); -JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -double js_math_sign(double a); -double js_math_round(double a); -JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -double js_math_fround(double a); -JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -/* xorshift* random number generator by Marsaglia */ -uint64_t xorshift64star(uint64_t *pstate); -void js_random_init(JSContext *ctx); -JSValue js_math_random(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_MATH_H +#define QUICKJS_JS_MATH_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "../types.h" + +/* precondition: a and b are not NaN */ +double js_fmin(double a, double b); +/* precondition: a and b are not NaN */ +double js_fmax(double a, double b); +JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +double js_math_sign(double a); +double js_math_round(double a); +JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +double js_math_fround(double a); +JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +/* xorshift* random number generator by Marsaglia */ +uint64_t xorshift64star(uint64_t *pstate); +void js_random_init(JSContext *ctx); +JSValue js_math_random(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-number.c b/src/core/builtins/js-number.c index 24227bb7b..15ab5096d 100644 --- a/src/core/builtins/js-number.c +++ b/src/core/builtins/js-number.c @@ -1,308 +1,308 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-number.h" -#include "../convertion.h" -#include "../exception.h" -#include "../object.h" -#include "../runtime.h" - -/* Number */ -JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue val, obj; - if (argc == 0) { - val = JS_NewInt32(ctx, 0); - } else { - val = JS_ToNumeric(ctx, argv[0]); - if (JS_IsException(val)) - return val; - switch(JS_VALUE_GET_TAG(val)) { -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - { - JSBigFloat *p = JS_VALUE_GET_PTR(val); - double d; - bf_get_float64(&p->num, &d, BF_RNDN); - JS_FreeValue(ctx, val); - val = __JS_NewFloat64(ctx, d); - } - break; - case JS_TAG_BIG_DECIMAL: - val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) - return val; - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return val; - break; -#endif - default: - break; - } - } - if (!JS_IsUndefined(new_target)) { - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER); - if (!JS_IsException(obj)) - JS_SetObjectData(ctx, obj, val); - return obj; - } else { - return val; - } -} - -#if 0 -JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0])); -} - -JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t v; - if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0]))) - return JS_EXCEPTION; - return JS_NewInt64(ctx, v); -} -#endif - -JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (!JS_IsNumber(argv[0])) - return JS_FALSE; - return js_global_isNaN(ctx, this_val, argc, argv); -} - -JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (!JS_IsNumber(argv[0])) - return JS_FALSE; - return js_global_isFinite(ctx, this_val, argc, argv); -} - -JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int ret; - ret = JS_NumberIsInteger(ctx, argv[0]); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - double d; - if (!JS_IsNumber(argv[0])) - return JS_FALSE; - if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) - return JS_EXCEPTION; - return JS_NewBool(ctx, is_safe_integer(d)); -} - -JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_IsNumber(this_val)) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_NUMBER) { - if (JS_IsNumber(p->u.object_data)) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a number"); -} - -JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisNumberValue(ctx, this_val); -} - -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; -} - -JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue val; - int base; - double d; - - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (magic || JS_IsUndefined(argv[0])) { - base = 10; - } else { - base = js_get_radix(ctx, argv[0]); - if (base < 0) - goto fail; - } - if (JS_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; -} - -JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int f; - double d; - - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToFloat64Free(ctx, &d, val)) - return JS_EXCEPTION; - if (JS_ToInt32Sat(ctx, &f, argv[0])) - return JS_EXCEPTION; - if (f < 0 || f > 100) - return JS_ThrowRangeError(ctx, "invalid number of digits"); - if (fabs(d) >= 1e21) { - return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); - } else { - return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); - } -} - -JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int f, flags; - double d; - - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToFloat64Free(ctx, &d, val)) - return JS_EXCEPTION; - if (JS_ToInt32Sat(ctx, &f, argv[0])) - return JS_EXCEPTION; - if (!isfinite(d)) { - return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); - } - if (JS_IsUndefined(argv[0])) { - flags = 0; - f = 0; - } else { - if (f < 0 || f > 100) - return JS_ThrowRangeError(ctx, "invalid number of digits"); - f++; - flags = JS_DTOA_FIXED_FORMAT; - } - return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP); -} - -JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - int p; - double d; - - val = js_thisNumberValue(ctx, this_val); - if (JS_IsException(val)) - return val; - if (JS_ToFloat64Free(ctx, &d, val)) - return JS_EXCEPTION; - if (JS_IsUndefined(argv[0])) - goto to_string; - if (JS_ToInt32Sat(ctx, &p, argv[0])) - return JS_EXCEPTION; - if (!isfinite(d)) { - to_string: - return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); - } - if (p < 1 || p > 100) - return JS_ThrowRangeError(ctx, "invalid number of digits"); - return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT); -} - -JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *str, *p; - int radix, flags; - JSValue ret; - - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - if (JS_ToInt32(ctx, &radix, argv[1])) { - JS_FreeCString(ctx, str); - return JS_EXCEPTION; - } - if (radix != 0 && (radix < 2 || radix > 36)) { - ret = JS_NAN; - } else { - p = str; - p += skip_spaces(p); - flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; - ret = js_atof(ctx, p, NULL, radix, flags); - } - JS_FreeCString(ctx, str); - return ret; -} - -JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - const char *str, *p; - JSValue ret; - - str = JS_ToCString(ctx, argv[0]); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - ret = js_atof(ctx, p, NULL, 10, 0); - JS_FreeCString(ctx, str); - return ret; +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-number.h" +#include "../convertion.h" +#include "../exception.h" +#include "../object.h" +#include "../runtime.h" + +/* Number */ +JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue val, obj; + if (argc == 0) { + val = JS_NewInt32(ctx, 0); + } else { + val = JS_ToNumeric(ctx, argv[0]); + if (JS_IsException(val)) + return val; + switch(JS_VALUE_GET_TAG(val)) { +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + { + JSBigFloat *p = JS_VALUE_GET_PTR(val); + double d; + bf_get_float64(&p->num, &d, BF_RNDN); + JS_FreeValue(ctx, val); + val = __JS_NewFloat64(ctx, d); + } + break; + case JS_TAG_BIG_DECIMAL: + val = JS_ToStringFree(ctx, val); + if (JS_IsException(val)) + return val; + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; + break; +#endif + default: + break; + } + } + if (!JS_IsUndefined(new_target)) { + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER); + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, val); + return obj; + } else { + return val; + } +} + +#if 0 +JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0])); +} + +JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t v; + if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0]))) + return JS_EXCEPTION; + return JS_NewInt64(ctx, v); +} +#endif + +JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + if (!JS_IsNumber(argv[0])) + return JS_FALSE; + return js_global_isNaN(ctx, this_val, argc, argv); +} + +JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + if (!JS_IsNumber(argv[0])) + return JS_FALSE; + return js_global_isFinite(ctx, this_val, argc, argv); +} + +JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int ret; + ret = JS_NumberIsInteger(ctx, argv[0]); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + double d; + if (!JS_IsNumber(argv[0])) + return JS_FALSE; + if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) + return JS_EXCEPTION; + return JS_NewBool(ctx, is_safe_integer(d)); +} + +JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_IsNumber(this_val)) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_NUMBER) { + if (JS_IsNumber(p->u.object_data)) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a number"); +} + +JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisNumberValue(ctx, this_val); +} + +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; +} + +JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue val; + int base; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (magic || JS_IsUndefined(argv[0])) { + base = 10; + } else { + base = js_get_radix(ctx, argv[0]); + if (base < 0) + goto fail; + } + if (JS_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; +} + +JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int f; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_ToInt32Sat(ctx, &f, argv[0])) + return JS_EXCEPTION; + if (f < 0 || f > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + if (fabs(d) >= 1e21) { + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); + } else { + return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); + } +} + +JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int f, flags; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_ToInt32Sat(ctx, &f, argv[0])) + return JS_EXCEPTION; + if (!isfinite(d)) { + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); + } + if (JS_IsUndefined(argv[0])) { + flags = 0; + f = 0; + } else { + if (f < 0 || f > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + f++; + flags = JS_DTOA_FIXED_FORMAT; + } + return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP); +} + +JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + int p; + double d; + + val = js_thisNumberValue(ctx, this_val); + if (JS_IsException(val)) + return val; + if (JS_ToFloat64Free(ctx, &d, val)) + return JS_EXCEPTION; + if (JS_IsUndefined(argv[0])) + goto to_string; + if (JS_ToInt32Sat(ctx, &p, argv[0])) + return JS_EXCEPTION; + if (!isfinite(d)) { + to_string: + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); + } + if (p < 1 || p > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT); +} + +JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *str, *p; + int radix, flags; + JSValue ret; + + str = JS_ToCString(ctx, argv[0]); + if (!str) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &radix, argv[1])) { + JS_FreeCString(ctx, str); + return JS_EXCEPTION; + } + if (radix != 0 && (radix < 2 || radix > 36)) { + ret = JS_NAN; + } else { + p = str; + p += skip_spaces(p); + flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; + ret = js_atof(ctx, p, NULL, radix, flags); + } + JS_FreeCString(ctx, str); + return ret; +} + +JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + const char *str, *p; + JSValue ret; + + str = JS_ToCString(ctx, argv[0]); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + ret = js_atof(ctx, p, NULL, 10, 0); + JS_FreeCString(ctx, str); + return ret; } \ No newline at end of file diff --git a/src/core/builtins/js-number.h b/src/core/builtins/js-number.h index a49099383..ec81c030f 100644 --- a/src/core/builtins/js-number.h +++ b/src/core/builtins/js-number.h @@ -1,64 +1,64 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_NUMBER_H -#define QUICKJS_JS_NUMBER_H - -#include "quickjs/quickjs.h" - -/* Number */ -JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv); - -JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val); -JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -int js_get_radix(JSContext *ctx, JSValueConst val); -JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); -JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -JSValue js_parseInt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_NUMBER_H +#define QUICKJS_JS_NUMBER_H + +#include "quickjs/quickjs.h" + +/* Number */ +JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv); + +JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val); +JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +int js_get_radix(JSContext *ctx, JSValueConst val); +JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); +JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +JSValue js_parseInt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-object.c b/src/core/builtins/js-object.c index a5b557bcf..1a92576a5 100644 --- a/src/core/builtins/js-object.c +++ b/src/core/builtins/js-object.c @@ -1,1090 +1,1090 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-object.h" -#include "../convertion.h" -#include "../exception.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "js-operator.h" - -void js_object_data_finalizer(JSRuntime* rt, JSValue val) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JS_FreeValueRT(rt, p->u.object_data); - p->u.object_data = JS_UNDEFINED; -} - -void js_object_data_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JS_MarkValue(rt, p->u.object_data, mark_func); -} - -/* Object class */ - -JSValue JS_ToObject(JSContext* ctx, JSValueConst val) { - int tag = JS_VALUE_GET_NORM_TAG(val); - JSValue obj; - - switch (tag) { - default: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - return JS_ThrowTypeError(ctx, "cannot convert to object"); - case JS_TAG_OBJECT: - case JS_TAG_EXCEPTION: - return JS_DupValue(ctx, val); -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); - goto set_value; - case JS_TAG_BIG_FLOAT: - obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); - goto set_value; - case JS_TAG_BIG_DECIMAL: - obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL); - goto set_value; -#endif - case JS_TAG_INT: - case JS_TAG_FLOAT64: - obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); - goto set_value; - case JS_TAG_STRING: - /* XXX: should call the string constructor */ - { - JSString* p1 = JS_VALUE_GET_STRING(val); - obj = JS_NewObjectClass(ctx, JS_CLASS_STRING); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); - } - goto set_value; - case JS_TAG_BOOL: - obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN); - goto set_value; - case JS_TAG_SYMBOL: - obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL); - set_value: - if (!JS_IsException(obj)) - JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val)); - return obj; - } -} - -JSValue JS_ToObjectFree(JSContext* ctx, JSValue val) { - JSValue obj = JS_ToObject(ctx, val); - JS_FreeValue(ctx, val); - return obj; -} - -int js_obj_to_desc(JSContext* ctx, JSPropertyDescriptor* d, JSValueConst desc) { - JSValue val, getter, setter; - int flags; - - if (!JS_IsObject(desc)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - flags = 0; - val = JS_UNDEFINED; - getter = JS_UNDEFINED; - setter = JS_UNDEFINED; - if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) { - JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); - if (JS_IsException(prop)) - goto fail; - flags |= JS_PROP_HAS_CONFIGURABLE; - if (JS_ToBoolFree(ctx, prop)) - flags |= JS_PROP_CONFIGURABLE; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) { - JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); - if (JS_IsException(prop)) - goto fail; - flags |= JS_PROP_HAS_WRITABLE; - if (JS_ToBoolFree(ctx, prop)) - flags |= JS_PROP_WRITABLE; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { - JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); - if (JS_IsException(prop)) - goto fail; - flags |= JS_PROP_HAS_ENUMERABLE; - if (JS_ToBoolFree(ctx, prop)) - flags |= JS_PROP_ENUMERABLE; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { - flags |= JS_PROP_HAS_VALUE; - val = JS_GetProperty(ctx, desc, JS_ATOM_value); - if (JS_IsException(val)) - goto fail; - } - if (JS_HasProperty(ctx, desc, JS_ATOM_get)) { - flags |= JS_PROP_HAS_GET; - getter = JS_GetProperty(ctx, desc, JS_ATOM_get); - if (JS_IsException(getter) || !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { - JS_ThrowTypeError(ctx, "invalid getter"); - goto fail; - } - } - if (JS_HasProperty(ctx, desc, JS_ATOM_set)) { - flags |= JS_PROP_HAS_SET; - setter = JS_GetProperty(ctx, desc, JS_ATOM_set); - if (JS_IsException(setter) || !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { - JS_ThrowTypeError(ctx, "invalid setter"); - goto fail; - } - } - if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) && (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) { - JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable"); - goto fail; - } - d->flags = flags; - d->value = val; - d->getter = getter; - d->setter = setter; - return 0; -fail: - JS_FreeValue(ctx, val); - JS_FreeValue(ctx, getter); - JS_FreeValue(ctx, setter); - return -1; -} - -__exception int JS_DefinePropertyDesc(JSContext* ctx, - JSValueConst obj, - JSAtom prop, - JSValueConst desc, - int flags) { - JSPropertyDescriptor d; - int ret; - - if (js_obj_to_desc(ctx, &d, desc) < 0) - return -1; - - ret = JS_DefineProperty(ctx, obj, prop, d.value, d.getter, d.setter, d.flags | flags); - js_free_desc(ctx, &d); - return ret; -} - -__exception int JS_ObjectDefineProperties(JSContext* ctx, JSValueConst obj, JSValueConst properties) { - JSValue props, desc; - JSObject* p; - JSPropertyEnum* atoms; - uint32_t len, i; - int ret = -1; - - if (!JS_IsObject(obj)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - desc = JS_UNDEFINED; - props = JS_ToObject(ctx, properties); - if (JS_IsException(props)) - return -1; - p = JS_VALUE_GET_OBJ(props); - if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < - 0) - goto exception; - for (i = 0; i < len; i++) { - JS_FreeValue(ctx, desc); - desc = JS_GetProperty(ctx, props, atoms[i].atom); - if (JS_IsException(desc)) - goto exception; - if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0) - goto exception; - } - ret = 0; - -exception: - js_free_prop_enum(ctx, atoms, len); - JS_FreeValue(ctx, props); - JS_FreeValue(ctx, desc); - return ret; -} - -JSValue js_object_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { - JSValue ret; - if (!JS_IsUndefined(new_target) && JS_VALUE_GET_OBJ(new_target) != JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) { - ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); - } else { - int tag = JS_VALUE_GET_NORM_TAG(argv[0]); - switch (tag) { - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_NewObject(ctx); - break; - default: - ret = JS_ToObject(ctx, argv[0]); - break; - } - } - return ret; -} - -JSValue js_object_create(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValueConst proto, props; - JSValue obj; - - proto = argv[0]; - if (!JS_IsObject(proto) && !JS_IsNull(proto)) - return JS_ThrowTypeError(ctx, "not a prototype"); - obj = JS_NewObjectProto(ctx, proto); - if (JS_IsException(obj)) - return JS_EXCEPTION; - props = argv[1]; - if (!JS_IsUndefined(props)) { - if (JS_ObjectDefineProperties(ctx, obj, props)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } - return obj; -} - -JSValue js_object_getPrototypeOf(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int magic) { - JSValueConst val; - - val = argv[0]; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) { - /* ES6 feature non compatible with ES5.1: primitive types are - accepted */ - if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL || JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) - return JS_ThrowTypeErrorNotAnObject(ctx); - } - return JS_GetPrototype(ctx, val); -} - -JSValue js_object_setPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValueConst obj; - obj = argv[0]; - if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0) - return JS_EXCEPTION; - return JS_DupValue(ctx, obj); -} - -/* magic = 1 if called as Reflect.defineProperty */ -JSValue js_object_defineProperty(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int magic) { - JSValueConst obj, prop, desc; - int ret, flags; - JSAtom atom; - - obj = argv[0]; - prop = argv[1]; - desc = argv[2]; - - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - flags = 0; - if (!magic) - flags |= JS_PROP_THROW; - ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags); - JS_FreeAtom(ctx, atom); - if (ret < 0) { - return JS_EXCEPTION; - } else if (magic) { - return JS_NewBool(ctx, ret); - } else { - return JS_DupValue(ctx, obj); - } -} - -JSValue js_object_defineProperties(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // defineProperties(obj, properties) - JSValueConst obj = argv[0]; - - if (JS_ObjectDefineProperties(ctx, obj, argv[1])) - return JS_EXCEPTION; - else - return JS_DupValue(ctx, obj); -} - -/* magic = 1 if called as __defineSetter__ */ -JSValue js_object___defineGetter__(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int magic) { - JSValue obj; - JSValueConst prop, value, get, set; - int ret, flags; - JSAtom atom; - - prop = argv[0]; - value = argv[1]; - - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - return JS_EXCEPTION; - - if (check_function(ctx, value)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - flags = JS_PROP_THROW | JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE; - if (magic) { - get = JS_UNDEFINED; - set = value; - flags |= JS_PROP_HAS_SET; - } else { - get = value; - set = JS_UNDEFINED; - flags |= JS_PROP_HAS_GET; - } - ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags); - JS_FreeValue(ctx, obj); - JS_FreeAtom(ctx, atom); - if (ret < 0) { - return JS_EXCEPTION; - } else { - return JS_UNDEFINED; - } -} - -JSValue js_object_getOwnPropertyDescriptor(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int magic) { - JSValueConst prop; - JSAtom atom; - JSValue ret, obj; - JSPropertyDescriptor desc; - int res, flags; - - if (magic) { - /* Reflect.getOwnPropertyDescriptor case */ - if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - obj = JS_DupValue(ctx, argv[0]); - } else { - obj = JS_ToObject(ctx, argv[0]); - if (JS_IsException(obj)) - return obj; - } - prop = argv[1]; - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - ret = JS_UNDEFINED; - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom); - if (res < 0) - goto exception; - if (res) { - ret = JS_NewObject(ctx); - if (JS_IsException(ret)) - goto exception1; - flags = JS_PROP_C_W_E | JS_PROP_THROW; - if (desc.flags & JS_PROP_GETSET) { - if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0 || - JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0) - goto exception1; - } else { - if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0 || - JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), - flags) < 0) - goto exception1; - } - if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), - flags) < 0 || - JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, - JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0) - goto exception1; - js_free_desc(ctx, &desc); - } - } - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, obj); - return ret; - -exception1: - js_free_desc(ctx, &desc); - JS_FreeValue(ctx, ret); -exception: - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_object_getOwnPropertyDescriptors(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv) { - // getOwnPropertyDescriptors(obj) - JSValue obj, r; - JSObject* p; - JSPropertyEnum* props; - uint32_t len, i; - - r = JS_UNDEFINED; - obj = JS_ToObject(ctx, argv[0]); - if (JS_IsException(obj)) - return JS_EXCEPTION; - - p = JS_VALUE_GET_OBJ(obj); - if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) - goto exception; - r = JS_NewObject(ctx); - if (JS_IsException(r)) - goto exception; - for (i = 0; i < len; i++) { - JSValue atomValue, desc; - JSValueConst args[2]; - - atomValue = JS_AtomToValue(ctx, props[i].atom); - if (JS_IsException(atomValue)) - goto exception; - args[0] = obj; - args[1] = atomValue; - desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0); - JS_FreeValue(ctx, atomValue); - if (JS_IsException(desc)) - goto exception; - if (!JS_IsUndefined(desc)) { - if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - } - js_free_prop_enum(ctx, props, len); - JS_FreeValue(ctx, obj); - return r; - -exception: - js_free_prop_enum(ctx, props, len); - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, r); - return JS_EXCEPTION; -} - -JSValue js_object_getOwnPropertyNames(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY); -} - -JSValue js_object_getOwnPropertySymbols(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); -} - -JSValue js_object_keys(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int kind) { - return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind); -} - -JSValue js_object_isExtensible(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int reflect) { - JSValueConst obj; - int ret; - - obj = argv[0]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { - if (reflect) - return JS_ThrowTypeErrorNotAnObject(ctx); - else - return JS_FALSE; - } - ret = JS_IsExtensible(ctx, obj); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_object_preventExtensions(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int reflect) { - JSValueConst obj; - int ret; - - obj = argv[0]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { - if (reflect) - return JS_ThrowTypeErrorNotAnObject(ctx); - else - return JS_DupValue(ctx, obj); - } - ret = JS_PreventExtensions(ctx, obj); - if (ret < 0) - return JS_EXCEPTION; - if (reflect) { - return JS_NewBool(ctx, ret); - } else { - if (!ret) - return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); - return JS_DupValue(ctx, obj); - } -} - -JSValue js_object_hasOwnProperty(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj; - JSAtom atom; - JSObject* p; - BOOL ret; - - atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */ - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) { - JS_FreeAtom(ctx, atom); - return obj; - } - p = JS_VALUE_GET_OBJ(obj); - ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, obj); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_object_valueOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_ToObject(ctx, this_val); -} - -JSValue js_object_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, tag; - int is_array; - JSAtom atom; - JSObject* p; - - if (JS_IsNull(this_val)) { - tag = JS_NewString(ctx, "Null"); - } else if (JS_IsUndefined(this_val)) { - tag = JS_NewString(ctx, "Undefined"); - } else { - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - return obj; - is_array = JS_IsArray(ctx, obj); - if (is_array < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - if (is_array) { - atom = JS_ATOM_Array; - } else if (JS_IsFunction(ctx, obj)) { - atom = JS_ATOM_Function; - } else { - p = JS_VALUE_GET_OBJ(obj); - switch (p->class_id) { - case JS_CLASS_STRING: - case JS_CLASS_ARGUMENTS: - case JS_CLASS_MAPPED_ARGUMENTS: - case JS_CLASS_ERROR: - case JS_CLASS_BOOLEAN: - case JS_CLASS_NUMBER: - case JS_CLASS_DATE: - case JS_CLASS_REGEXP: - atom = ctx->rt->class_array[p->class_id].class_name; - break; - default: - atom = JS_ATOM_Object; - break; - } - } - tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag); - JS_FreeValue(ctx, obj); - if (JS_IsException(tag)) - return JS_EXCEPTION; - if (!JS_IsString(tag)) { - JS_FreeValue(ctx, tag); - tag = JS_AtomToString(ctx, atom); - } - } - return JS_ConcatString3(ctx, "[object ", tag, "]"); -} - -JSValue js_object_toLocaleString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL); -} - -JSValue js_object_assign(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // Object.assign(obj, source1) - JSValue obj, s; - int i; - - s = JS_UNDEFINED; - obj = JS_ToObject(ctx, argv[0]); - if (JS_IsException(obj)) - goto exception; - for (i = 1; i < argc; i++) { - if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) { - s = JS_ToObject(ctx, argv[i]); - if (JS_IsException(s)) - goto exception; - if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE)) - goto exception; - JS_FreeValue(ctx, s); - } - } - return obj; -exception: - JS_FreeValue(ctx, obj); - JS_FreeValue(ctx, s); - return JS_EXCEPTION; -} - -JSValue js_object_seal(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int freeze_flag) { - JSValueConst obj = argv[0]; - JSObject* p; - JSPropertyEnum* props; - uint32_t len, i; - int flags, desc_flags, res; - - if (!JS_IsObject(obj)) - return JS_DupValue(ctx, obj); - - res = JS_PreventExtensions(ctx, obj); - if (res < 0) - return JS_EXCEPTION; - if (!res) { - return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); - } - - p = JS_VALUE_GET_OBJ(obj); - flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; - if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) - return JS_EXCEPTION; - - for (i = 0; i < len; i++) { - JSPropertyDescriptor desc; - JSAtom prop = props[i].atom; - - desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE; - if (freeze_flag) { - res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (res < 0) - goto exception; - if (res) { - if (desc.flags & JS_PROP_WRITABLE) - desc_flags |= JS_PROP_HAS_WRITABLE; - js_free_desc(ctx, &desc); - } - } - if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED, JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0) - goto exception; - } - js_free_prop_enum(ctx, props, len); - return JS_DupValue(ctx, obj); - -exception: - js_free_prop_enum(ctx, props, len); - return JS_EXCEPTION; -} - -JSValue js_object_isSealed(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int is_frozen) { - JSValueConst obj = argv[0]; - JSObject* p; - JSPropertyEnum* props; - uint32_t len, i; - int flags, res; - - if (!JS_IsObject(obj)) - return JS_TRUE; - - p = JS_VALUE_GET_OBJ(obj); - flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; - if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) - return JS_EXCEPTION; - - for (i = 0; i < len; i++) { - JSPropertyDescriptor desc; - JSAtom prop = props[i].atom; - - res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (res < 0) - goto exception; - if (res) { - js_free_desc(ctx, &desc); - if ((desc.flags & JS_PROP_CONFIGURABLE) || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) { - res = FALSE; - goto done; - } - } - } - res = JS_IsExtensible(ctx, obj); - if (res < 0) - return JS_EXCEPTION; - res ^= 1; -done: - js_free_prop_enum(ctx, props, len); - return JS_NewBool(ctx, res); - -exception: - js_free_prop_enum(ctx, props, len); - return JS_EXCEPTION; -} - -JSValue js_object_fromEntries(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, iter, next_method = JS_UNDEFINED; - JSValueConst iterable; - BOOL done; - - /* RequireObjectCoercible() not necessary because it is tested in - JS_GetIterator() by JS_GetProperty() */ - iterable = argv[0]; - - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return obj; - - iter = JS_GetIterator(ctx, iterable, FALSE); - if (JS_IsException(iter)) - goto fail; - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail; - - for (;;) { - JSValue key, value, item; - item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(item)) - goto fail; - if (done) { - JS_FreeValue(ctx, item); - break; - } - - key = JS_UNDEFINED; - value = JS_UNDEFINED; - if (!JS_IsObject(item)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail1; - } - key = JS_GetPropertyUint32(ctx, item, 0); - if (JS_IsException(key)) - goto fail1; - value = JS_GetPropertyUint32(ctx, item, 1); - if (JS_IsException(value)) { - JS_FreeValue(ctx, key); - goto fail1; - } - if (JS_DefinePropertyValueValue(ctx, obj, key, value, JS_PROP_C_W_E | JS_PROP_THROW) < 0) { - fail1: - JS_FreeValue(ctx, item); - goto fail; - } - JS_FreeValue(ctx, item); - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - return obj; -fail: - if (JS_IsObject(iter)) { - /* close the iterator object, preserving pending exception */ - JS_IteratorClose(ctx, iter, TRUE); - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -#if 0 -/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */ -JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int ret; - ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]), - JS_DupValue(ctx, argv[2]), - JS_PROP_C_W_E | JS_PROP_THROW); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToObject(ctx, argv[0]); -} - -JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int hint = HINT_NONE; - - if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT) - hint = JS_VALUE_GET_INT(argv[1]); - - return JS_ToPrimitive(ctx, argv[0], hint); -} -#endif - -/* return an empty string if not an object */ -JSValue js_object___getClass(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSAtom atom; - JSObject* p; - uint32_t tag; - int class_id; - - tag = JS_VALUE_GET_NORM_TAG(argv[0]); - if (tag == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(argv[0]); - class_id = p->class_id; - if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0])) - class_id = JS_CLASS_BYTECODE_FUNCTION; - atom = ctx->rt->class_array[class_id].class_name; - } else { - atom = JS_ATOM_empty_string; - } - return JS_AtomToString(ctx, atom); -} - -JSValue js_object_is(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1])); -} - -#if 0 -JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_GetObjectData(ctx, argv[0]); -} - -JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1]))) - return JS_EXCEPTION; - return JS_DupValue(ctx, argv[1]); -} - -JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToPropertyKey(ctx, argv[0]); -} - -JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewBool(ctx, JS_IsObject(argv[0])); -} - -JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1])); -} - -JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0])); -} -#endif - -JSValue JS_SpeciesConstructor(JSContext* ctx, JSValueConst obj, JSValueConst defaultConstructor) { - JSValue ctor, species; - - if (!JS_IsObject(obj)) - return JS_ThrowTypeErrorNotAnObject(ctx); - ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); - if (JS_IsException(ctor)) - return ctor; - if (JS_IsUndefined(ctor)) - return JS_DupValue(ctx, defaultConstructor); - if (!JS_IsObject(ctor)) { - JS_FreeValue(ctx, ctor); - return JS_ThrowTypeErrorNotAnObject(ctx); - } - species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); - JS_FreeValue(ctx, ctor); - if (JS_IsException(species)) - return species; - if (JS_IsUndefined(species) || JS_IsNull(species)) - return JS_DupValue(ctx, defaultConstructor); - if (!JS_IsConstructor(ctx, species)) { - JS_FreeValue(ctx, species); - return JS_ThrowTypeError(ctx, "not a constructor"); - } - return species; -} - -#if 0 -JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_SpeciesConstructor(ctx, argv[0], argv[1]); -} -#endif - -JSValue js_object_get___proto__(JSContext* ctx, JSValueConst this_val) { - JSValue val, ret; - - val = JS_ToObject(ctx, this_val); - if (JS_IsException(val)) - return val; - ret = JS_GetPrototype(ctx, val); - JS_FreeValue(ctx, val); - return ret; -} - -JSValue js_object_set___proto__(JSContext* ctx, JSValueConst this_val, JSValueConst proto) { - if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - if (!JS_IsObject(proto) && !JS_IsNull(proto)) - return JS_UNDEFINED; - if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0) - return JS_EXCEPTION; - else - return JS_UNDEFINED; -} - -JSValue js_object_isPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, v1; - JSValueConst v; - int res; - - v = argv[0]; - if (!JS_IsObject(v)) - return JS_FALSE; - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - return JS_EXCEPTION; - v1 = JS_DupValue(ctx, v); - for (;;) { - v1 = JS_GetPrototypeFree(ctx, v1); - if (JS_IsException(v1)) - goto exception; - if (JS_IsNull(v1)) { - res = FALSE; - break; - } - if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { - res = TRUE; - break; - } - /* avoid infinite loop (possible with proxies) */ - if (js_poll_interrupts(ctx)) - goto exception; - } - JS_FreeValue(ctx, v1); - JS_FreeValue(ctx, obj); - return JS_NewBool(ctx, res); - -exception: - JS_FreeValue(ctx, v1); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_object_propertyIsEnumerable(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj, res = JS_EXCEPTION; - JSAtom prop = JS_ATOM_NULL; - JSPropertyDescriptor desc; - int has_prop; - - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - goto exception; - prop = JS_ValueToAtom(ctx, argv[0]); - if (unlikely(prop == JS_ATOM_NULL)) - goto exception; - - has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); - if (has_prop < 0) - goto exception; - if (has_prop) { - res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0); - js_free_desc(ctx, &desc); - } else { - res = JS_FALSE; - } - -exception: - JS_FreeAtom(ctx, prop); - JS_FreeValue(ctx, obj); - return res; -} - -JSValue js_object___lookupGetter__(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int setter) { - JSValue obj, res = JS_EXCEPTION; - JSAtom prop = JS_ATOM_NULL; - JSPropertyDescriptor desc; - int has_prop; - - obj = JS_ToObject(ctx, this_val); - if (JS_IsException(obj)) - goto exception; - prop = JS_ValueToAtom(ctx, argv[0]); - if (unlikely(prop == JS_ATOM_NULL)) - goto exception; - - for (;;) { - has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); - if (has_prop < 0) - goto exception; - if (has_prop) { - if (desc.flags & JS_PROP_GETSET) - res = JS_DupValue(ctx, setter ? desc.setter : desc.getter); - else - res = JS_UNDEFINED; - js_free_desc(ctx, &desc); - break; - } - obj = JS_GetPrototypeFree(ctx, obj); - if (JS_IsException(obj)) - goto exception; - if (JS_IsNull(obj)) { - res = JS_UNDEFINED; - break; - } - /* avoid infinite loop (possible with proxies) */ - if (js_poll_interrupts(ctx)) - goto exception; - } - -exception: - JS_FreeAtom(ctx, prop); - JS_FreeValue(ctx, obj); - return res; -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-object.h" +#include "../convertion.h" +#include "../exception.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "js-operator.h" + +void js_object_data_finalizer(JSRuntime* rt, JSValue val) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JS_FreeValueRT(rt, p->u.object_data); + p->u.object_data = JS_UNDEFINED; +} + +void js_object_data_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JS_MarkValue(rt, p->u.object_data, mark_func); +} + +/* Object class */ + +JSValue JS_ToObject(JSContext* ctx, JSValueConst val) { + int tag = JS_VALUE_GET_NORM_TAG(val); + JSValue obj; + + switch (tag) { + default: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_ThrowTypeError(ctx, "cannot convert to object"); + case JS_TAG_OBJECT: + case JS_TAG_EXCEPTION: + return JS_DupValue(ctx, val); +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); + goto set_value; + case JS_TAG_BIG_FLOAT: + obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); + goto set_value; + case JS_TAG_BIG_DECIMAL: + obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL); + goto set_value; +#endif + case JS_TAG_INT: + case JS_TAG_FLOAT64: + obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); + goto set_value; + case JS_TAG_STRING: + /* XXX: should call the string constructor */ + { + JSString* p1 = JS_VALUE_GET_STRING(val); + obj = JS_NewObjectClass(ctx, JS_CLASS_STRING); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); + } + goto set_value; + case JS_TAG_BOOL: + obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN); + goto set_value; + case JS_TAG_SYMBOL: + obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL); + set_value: + if (!JS_IsException(obj)) + JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val)); + return obj; + } +} + +JSValue JS_ToObjectFree(JSContext* ctx, JSValue val) { + JSValue obj = JS_ToObject(ctx, val); + JS_FreeValue(ctx, val); + return obj; +} + +int js_obj_to_desc(JSContext* ctx, JSPropertyDescriptor* d, JSValueConst desc) { + JSValue val, getter, setter; + int flags; + + if (!JS_IsObject(desc)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + flags = 0; + val = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_CONFIGURABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_CONFIGURABLE; + } + if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_WRITABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_WRITABLE; + } + if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { + JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); + if (JS_IsException(prop)) + goto fail; + flags |= JS_PROP_HAS_ENUMERABLE; + if (JS_ToBoolFree(ctx, prop)) + flags |= JS_PROP_ENUMERABLE; + } + if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { + flags |= JS_PROP_HAS_VALUE; + val = JS_GetProperty(ctx, desc, JS_ATOM_value); + if (JS_IsException(val)) + goto fail; + } + if (JS_HasProperty(ctx, desc, JS_ATOM_get)) { + flags |= JS_PROP_HAS_GET; + getter = JS_GetProperty(ctx, desc, JS_ATOM_get); + if (JS_IsException(getter) || !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { + JS_ThrowTypeError(ctx, "invalid getter"); + goto fail; + } + } + if (JS_HasProperty(ctx, desc, JS_ATOM_set)) { + flags |= JS_PROP_HAS_SET; + setter = JS_GetProperty(ctx, desc, JS_ATOM_set); + if (JS_IsException(setter) || !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { + JS_ThrowTypeError(ctx, "invalid setter"); + goto fail; + } + } + if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) && (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) { + JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable"); + goto fail; + } + d->flags = flags; + d->value = val; + d->getter = getter; + d->setter = setter; + return 0; +fail: + JS_FreeValue(ctx, val); + JS_FreeValue(ctx, getter); + JS_FreeValue(ctx, setter); + return -1; +} + +__exception int JS_DefinePropertyDesc(JSContext* ctx, + JSValueConst obj, + JSAtom prop, + JSValueConst desc, + int flags) { + JSPropertyDescriptor d; + int ret; + + if (js_obj_to_desc(ctx, &d, desc) < 0) + return -1; + + ret = JS_DefineProperty(ctx, obj, prop, d.value, d.getter, d.setter, d.flags | flags); + js_free_desc(ctx, &d); + return ret; +} + +__exception int JS_ObjectDefineProperties(JSContext* ctx, JSValueConst obj, JSValueConst properties) { + JSValue props, desc; + JSObject* p; + JSPropertyEnum* atoms; + uint32_t len, i; + int ret = -1; + + if (!JS_IsObject(obj)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + desc = JS_UNDEFINED; + props = JS_ToObject(ctx, properties); + if (JS_IsException(props)) + return -1; + p = JS_VALUE_GET_OBJ(props); + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < + 0) + goto exception; + for (i = 0; i < len; i++) { + JS_FreeValue(ctx, desc); + desc = JS_GetProperty(ctx, props, atoms[i].atom); + if (JS_IsException(desc)) + goto exception; + if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0) + goto exception; + } + ret = 0; + +exception: + js_free_prop_enum(ctx, atoms, len); + JS_FreeValue(ctx, props); + JS_FreeValue(ctx, desc); + return ret; +} + +JSValue js_object_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { + JSValue ret; + if (!JS_IsUndefined(new_target) && JS_VALUE_GET_OBJ(new_target) != JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) { + ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + } else { + int tag = JS_VALUE_GET_NORM_TAG(argv[0]); + switch (tag) { + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_NewObject(ctx); + break; + default: + ret = JS_ToObject(ctx, argv[0]); + break; + } + } + return ret; +} + +JSValue js_object_create(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValueConst proto, props; + JSValue obj; + + proto = argv[0]; + if (!JS_IsObject(proto) && !JS_IsNull(proto)) + return JS_ThrowTypeError(ctx, "not a prototype"); + obj = JS_NewObjectProto(ctx, proto); + if (JS_IsException(obj)) + return JS_EXCEPTION; + props = argv[1]; + if (!JS_IsUndefined(props)) { + if (JS_ObjectDefineProperties(ctx, obj, props)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +JSValue js_object_getPrototypeOf(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int magic) { + JSValueConst val; + + val = argv[0]; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) { + /* ES6 feature non compatible with ES5.1: primitive types are + accepted */ + if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL || JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return JS_GetPrototype(ctx, val); +} + +JSValue js_object_setPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValueConst obj; + obj = argv[0]; + if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0) + return JS_EXCEPTION; + return JS_DupValue(ctx, obj); +} + +/* magic = 1 if called as Reflect.defineProperty */ +JSValue js_object_defineProperty(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int magic) { + JSValueConst obj, prop, desc; + int ret, flags; + JSAtom atom; + + obj = argv[0]; + prop = argv[1]; + desc = argv[2]; + + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + flags = 0; + if (!magic) + flags |= JS_PROP_THROW; + ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags); + JS_FreeAtom(ctx, atom); + if (ret < 0) { + return JS_EXCEPTION; + } else if (magic) { + return JS_NewBool(ctx, ret); + } else { + return JS_DupValue(ctx, obj); + } +} + +JSValue js_object_defineProperties(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // defineProperties(obj, properties) + JSValueConst obj = argv[0]; + + if (JS_ObjectDefineProperties(ctx, obj, argv[1])) + return JS_EXCEPTION; + else + return JS_DupValue(ctx, obj); +} + +/* magic = 1 if called as __defineSetter__ */ +JSValue js_object___defineGetter__(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int magic) { + JSValue obj; + JSValueConst prop, value, get, set; + int ret, flags; + JSAtom atom; + + prop = argv[0]; + value = argv[1]; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + + if (check_function(ctx, value)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + flags = JS_PROP_THROW | JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE; + if (magic) { + get = JS_UNDEFINED; + set = value; + flags |= JS_PROP_HAS_SET; + } else { + get = value; + set = JS_UNDEFINED; + flags |= JS_PROP_HAS_GET; + } + ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags); + JS_FreeValue(ctx, obj); + JS_FreeAtom(ctx, atom); + if (ret < 0) { + return JS_EXCEPTION; + } else { + return JS_UNDEFINED; + } +} + +JSValue js_object_getOwnPropertyDescriptor(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int magic) { + JSValueConst prop; + JSAtom atom; + JSValue ret, obj; + JSPropertyDescriptor desc; + int res, flags; + + if (magic) { + /* Reflect.getOwnPropertyDescriptor case */ + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + obj = JS_DupValue(ctx, argv[0]); + } else { + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + return obj; + } + prop = argv[1]; + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_UNDEFINED; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom); + if (res < 0) + goto exception; + if (res) { + ret = JS_NewObject(ctx); + if (JS_IsException(ret)) + goto exception1; + flags = JS_PROP_C_W_E | JS_PROP_THROW; + if (desc.flags & JS_PROP_GETSET) { + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0 || + JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0) + goto exception1; + } else { + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0 || + JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), + flags) < 0) + goto exception1; + } + if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), + flags) < 0 || + JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, + JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0) + goto exception1; + js_free_desc(ctx, &desc); + } + } + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + return ret; + +exception1: + js_free_desc(ctx, &desc); + JS_FreeValue(ctx, ret); +exception: + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_object_getOwnPropertyDescriptors(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv) { + // getOwnPropertyDescriptors(obj) + JSValue obj, r; + JSObject* p; + JSPropertyEnum* props; + uint32_t len, i; + + r = JS_UNDEFINED; + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_OBJ(obj); + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) + goto exception; + r = JS_NewObject(ctx); + if (JS_IsException(r)) + goto exception; + for (i = 0; i < len; i++) { + JSValue atomValue, desc; + JSValueConst args[2]; + + atomValue = JS_AtomToValue(ctx, props[i].atom); + if (JS_IsException(atomValue)) + goto exception; + args[0] = obj; + args[1] = atomValue; + desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0); + JS_FreeValue(ctx, atomValue); + if (JS_IsException(desc)) + goto exception; + if (!JS_IsUndefined(desc)) { + if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + } + js_free_prop_enum(ctx, props, len); + JS_FreeValue(ctx, obj); + return r; + +exception: + js_free_prop_enum(ctx, props, len); + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, r); + return JS_EXCEPTION; +} + +JSValue js_object_getOwnPropertyNames(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY); +} + +JSValue js_object_getOwnPropertySymbols(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); +} + +JSValue js_object_keys(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int kind) { + return JS_GetOwnPropertyNames2(ctx, argv[0], JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind); +} + +JSValue js_object_isExtensible(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int reflect) { + JSValueConst obj; + int ret; + + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + if (reflect) + return JS_ThrowTypeErrorNotAnObject(ctx); + else + return JS_FALSE; + } + ret = JS_IsExtensible(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_object_preventExtensions(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int reflect) { + JSValueConst obj; + int ret; + + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + if (reflect) + return JS_ThrowTypeErrorNotAnObject(ctx); + else + return JS_DupValue(ctx, obj); + } + ret = JS_PreventExtensions(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + if (reflect) { + return JS_NewBool(ctx, ret); + } else { + if (!ret) + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); + return JS_DupValue(ctx, obj); + } +} + +JSValue js_object_hasOwnProperty(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj; + JSAtom atom; + JSObject* p; + BOOL ret; + + atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */ + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) { + JS_FreeAtom(ctx, atom); + return obj; + } + p = JS_VALUE_GET_OBJ(obj); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_object_valueOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_ToObject(ctx, this_val); +} + +JSValue js_object_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, tag; + int is_array; + JSAtom atom; + JSObject* p; + + if (JS_IsNull(this_val)) { + tag = JS_NewString(ctx, "Null"); + } else if (JS_IsUndefined(this_val)) { + tag = JS_NewString(ctx, "Undefined"); + } else { + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return obj; + is_array = JS_IsArray(ctx, obj); + if (is_array < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + if (is_array) { + atom = JS_ATOM_Array; + } else if (JS_IsFunction(ctx, obj)) { + atom = JS_ATOM_Function; + } else { + p = JS_VALUE_GET_OBJ(obj); + switch (p->class_id) { + case JS_CLASS_STRING: + case JS_CLASS_ARGUMENTS: + case JS_CLASS_MAPPED_ARGUMENTS: + case JS_CLASS_ERROR: + case JS_CLASS_BOOLEAN: + case JS_CLASS_NUMBER: + case JS_CLASS_DATE: + case JS_CLASS_REGEXP: + atom = ctx->rt->class_array[p->class_id].class_name; + break; + default: + atom = JS_ATOM_Object; + break; + } + } + tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag); + JS_FreeValue(ctx, obj); + if (JS_IsException(tag)) + return JS_EXCEPTION; + if (!JS_IsString(tag)) { + JS_FreeValue(ctx, tag); + tag = JS_AtomToString(ctx, atom); + } + } + return JS_ConcatString3(ctx, "[object ", tag, "]"); +} + +JSValue js_object_toLocaleString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL); +} + +JSValue js_object_assign(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // Object.assign(obj, source1) + JSValue obj, s; + int i; + + s = JS_UNDEFINED; + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + goto exception; + for (i = 1; i < argc; i++) { + if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) { + s = JS_ToObject(ctx, argv[i]); + if (JS_IsException(s)) + goto exception; + if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE)) + goto exception; + JS_FreeValue(ctx, s); + } + } + return obj; +exception: + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, s); + return JS_EXCEPTION; +} + +JSValue js_object_seal(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int freeze_flag) { + JSValueConst obj = argv[0]; + JSObject* p; + JSPropertyEnum* props; + uint32_t len, i; + int flags, desc_flags, res; + + if (!JS_IsObject(obj)) + return JS_DupValue(ctx, obj); + + res = JS_PreventExtensions(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + if (!res) { + return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); + } + + p = JS_VALUE_GET_OBJ(obj); + flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) + return JS_EXCEPTION; + + for (i = 0; i < len; i++) { + JSPropertyDescriptor desc; + JSAtom prop = props[i].atom; + + desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE; + if (freeze_flag) { + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + goto exception; + if (res) { + if (desc.flags & JS_PROP_WRITABLE) + desc_flags |= JS_PROP_HAS_WRITABLE; + js_free_desc(ctx, &desc); + } + } + if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED, JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0) + goto exception; + } + js_free_prop_enum(ctx, props, len); + return JS_DupValue(ctx, obj); + +exception: + js_free_prop_enum(ctx, props, len); + return JS_EXCEPTION; +} + +JSValue js_object_isSealed(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int is_frozen) { + JSValueConst obj = argv[0]; + JSObject* p; + JSPropertyEnum* props; + uint32_t len, i; + int flags, res; + + if (!JS_IsObject(obj)) + return JS_TRUE; + + p = JS_VALUE_GET_OBJ(obj); + flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; + if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) + return JS_EXCEPTION; + + for (i = 0; i < len; i++) { + JSPropertyDescriptor desc; + JSAtom prop = props[i].atom; + + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + goto exception; + if (res) { + js_free_desc(ctx, &desc); + if ((desc.flags & JS_PROP_CONFIGURABLE) || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) { + res = FALSE; + goto done; + } + } + } + res = JS_IsExtensible(ctx, obj); + if (res < 0) + return JS_EXCEPTION; + res ^= 1; +done: + js_free_prop_enum(ctx, props, len); + return JS_NewBool(ctx, res); + +exception: + js_free_prop_enum(ctx, props, len); + return JS_EXCEPTION; +} + +JSValue js_object_fromEntries(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, iter, next_method = JS_UNDEFINED; + JSValueConst iterable; + BOOL done; + + /* RequireObjectCoercible() not necessary because it is tested in + JS_GetIterator() by JS_GetProperty() */ + iterable = argv[0]; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return obj; + + iter = JS_GetIterator(ctx, iterable, FALSE); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + + for (;;) { + JSValue key, value, item; + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail; + if (done) { + JS_FreeValue(ctx, item); + break; + } + + key = JS_UNDEFINED; + value = JS_UNDEFINED; + if (!JS_IsObject(item)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail1; + } + key = JS_GetPropertyUint32(ctx, item, 0); + if (JS_IsException(key)) + goto fail1; + value = JS_GetPropertyUint32(ctx, item, 1); + if (JS_IsException(value)) { + JS_FreeValue(ctx, key); + goto fail1; + } + if (JS_DefinePropertyValueValue(ctx, obj, key, value, JS_PROP_C_W_E | JS_PROP_THROW) < 0) { + fail1: + JS_FreeValue(ctx, item); + goto fail; + } + JS_FreeValue(ctx, item); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + return obj; +fail: + if (JS_IsObject(iter)) { + /* close the iterator object, preserving pending exception */ + JS_IteratorClose(ctx, iter, TRUE); + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +#if 0 +/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */ +JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int ret; + ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]), + JS_DupValue(ctx, argv[2]), + JS_PROP_C_W_E | JS_PROP_THROW); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToObject(ctx, argv[0]); +} + +JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int hint = HINT_NONE; + + if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT) + hint = JS_VALUE_GET_INT(argv[1]); + + return JS_ToPrimitive(ctx, argv[0], hint); +} +#endif + +/* return an empty string if not an object */ +JSValue js_object___getClass(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSAtom atom; + JSObject* p; + uint32_t tag; + int class_id; + + tag = JS_VALUE_GET_NORM_TAG(argv[0]); + if (tag == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(argv[0]); + class_id = p->class_id; + if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0])) + class_id = JS_CLASS_BYTECODE_FUNCTION; + atom = ctx->rt->class_array[class_id].class_name; + } else { + atom = JS_ATOM_empty_string; + } + return JS_AtomToString(ctx, atom); +} + +JSValue js_object_is(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1])); +} + +#if 0 +JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_GetObjectData(ctx, argv[0]); +} + +JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1]))) + return JS_EXCEPTION; + return JS_DupValue(ctx, argv[1]); +} + +JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToPropertyKey(ctx, argv[0]); +} + +JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewBool(ctx, JS_IsObject(argv[0])); +} + +JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1])); +} + +JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0])); +} +#endif + +JSValue JS_SpeciesConstructor(JSContext* ctx, JSValueConst obj, JSValueConst defaultConstructor) { + JSValue ctor, species; + + if (!JS_IsObject(obj)) + return JS_ThrowTypeErrorNotAnObject(ctx); + ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsUndefined(ctor)) + return JS_DupValue(ctx, defaultConstructor); + if (!JS_IsObject(ctor)) { + JS_FreeValue(ctx, ctor); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); + JS_FreeValue(ctx, ctor); + if (JS_IsException(species)) + return species; + if (JS_IsUndefined(species) || JS_IsNull(species)) + return JS_DupValue(ctx, defaultConstructor); + if (!JS_IsConstructor(ctx, species)) { + JS_FreeValue(ctx, species); + return JS_ThrowTypeError(ctx, "not a constructor"); + } + return species; +} + +#if 0 +JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_SpeciesConstructor(ctx, argv[0], argv[1]); +} +#endif + +JSValue js_object_get___proto__(JSContext* ctx, JSValueConst this_val) { + JSValue val, ret; + + val = JS_ToObject(ctx, this_val); + if (JS_IsException(val)) + return val; + ret = JS_GetPrototype(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +JSValue js_object_set___proto__(JSContext* ctx, JSValueConst this_val, JSValueConst proto) { + if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (!JS_IsObject(proto) && !JS_IsNull(proto)) + return JS_UNDEFINED; + if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0) + return JS_EXCEPTION; + else + return JS_UNDEFINED; +} + +JSValue js_object_isPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, v1; + JSValueConst v; + int res; + + v = argv[0]; + if (!JS_IsObject(v)) + return JS_FALSE; + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + return JS_EXCEPTION; + v1 = JS_DupValue(ctx, v); + for (;;) { + v1 = JS_GetPrototypeFree(ctx, v1); + if (JS_IsException(v1)) + goto exception; + if (JS_IsNull(v1)) { + res = FALSE; + break; + } + if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { + res = TRUE; + break; + } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; + } + JS_FreeValue(ctx, v1); + JS_FreeValue(ctx, obj); + return JS_NewBool(ctx, res); + +exception: + JS_FreeValue(ctx, v1); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_object_propertyIsEnumerable(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj, res = JS_EXCEPTION; + JSAtom prop = JS_ATOM_NULL; + JSPropertyDescriptor desc; + int has_prop; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + prop = JS_ValueToAtom(ctx, argv[0]); + if (unlikely(prop == JS_ATOM_NULL)) + goto exception; + + has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); + if (has_prop < 0) + goto exception; + if (has_prop) { + res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0); + js_free_desc(ctx, &desc); + } else { + res = JS_FALSE; + } + +exception: + JS_FreeAtom(ctx, prop); + JS_FreeValue(ctx, obj); + return res; +} + +JSValue js_object___lookupGetter__(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int setter) { + JSValue obj, res = JS_EXCEPTION; + JSAtom prop = JS_ATOM_NULL; + JSPropertyDescriptor desc; + int has_prop; + + obj = JS_ToObject(ctx, this_val); + if (JS_IsException(obj)) + goto exception; + prop = JS_ValueToAtom(ctx, argv[0]); + if (unlikely(prop == JS_ATOM_NULL)) + goto exception; + + for (;;) { + has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); + if (has_prop < 0) + goto exception; + if (has_prop) { + if (desc.flags & JS_PROP_GETSET) + res = JS_DupValue(ctx, setter ? desc.setter : desc.getter); + else + res = JS_UNDEFINED; + js_free_desc(ctx, &desc); + break; + } + obj = JS_GetPrototypeFree(ctx, obj); + if (JS_IsException(obj)) + goto exception; + if (JS_IsNull(obj)) { + res = JS_UNDEFINED; + break; + } + /* avoid infinite loop (possible with proxies) */ + if (js_poll_interrupts(ctx)) + goto exception; + } + +exception: + JS_FreeAtom(ctx, prop); + JS_FreeValue(ctx, obj); + return res; +} diff --git a/src/core/builtins/js-object.h b/src/core/builtins/js-object.h index 4af695542..e9329094c 100644 --- a/src/core/builtins/js-object.h +++ b/src/core/builtins/js-object.h @@ -1,80 +1,80 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_OBJECT_H -#define QUICKJS_JS_OBJECT_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "quickjs/list.h" -#include "../types.h" - -JSValue JS_ToObject(JSContext* ctx, JSValueConst val); -JSValue JS_ToObjectFree(JSContext* ctx, JSValue val); -int js_obj_to_desc(JSContext* ctx, JSPropertyDescriptor* d, JSValueConst desc); -__exception int JS_DefinePropertyDesc(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValueConst desc, int flags); -__exception int JS_ObjectDefineProperties(JSContext* ctx, JSValueConst obj, JSValueConst properties); -JSValue js_object_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); -JSValue js_object_create(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_getPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -JSValue js_object_setPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -/* magic = 1 if called as Reflect.defineProperty */ -JSValue js_object_defineProperty(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -JSValue js_object_defineProperties(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -/* magic = 1 if called as __defineSetter__ */ -JSValue js_object___defineGetter__(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -JSValue js_object_getOwnPropertyDescriptor(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -JSValue js_object_getOwnPropertyDescriptors(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_getOwnPropertyNames(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_getOwnPropertySymbols(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_keys(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int kind); -JSValue js_object_isExtensible(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int reflect); -JSValue js_object_preventExtensions(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int reflect); -JSValue js_object_hasOwnProperty(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_valueOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_toLocaleString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_assign(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_seal(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int freeze_flag); -JSValue js_object_isSealed(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int is_frozen); -JSValue js_object_fromEntries(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -/* return an empty string if not an object */ -JSValue js_object___getClass(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_is(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue JS_SpeciesConstructor(JSContext* ctx, JSValueConst obj, JSValueConst defaultConstructor); -JSValue js_object_get___proto__(JSContext* ctx, JSValueConst this_val); -JSValue js_object_set___proto__(JSContext* ctx, JSValueConst this_val, JSValueConst proto); -JSValue js_object_isPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object_propertyIsEnumerable(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_object___lookupGetter__(JSContext* ctx, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int setter); - -void js_object_data_finalizer(JSRuntime* rt, JSValue val); -void js_object_data_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_OBJECT_H +#define QUICKJS_JS_OBJECT_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "quickjs/list.h" +#include "../types.h" + +JSValue JS_ToObject(JSContext* ctx, JSValueConst val); +JSValue JS_ToObjectFree(JSContext* ctx, JSValue val); +int js_obj_to_desc(JSContext* ctx, JSPropertyDescriptor* d, JSValueConst desc); +__exception int JS_DefinePropertyDesc(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValueConst desc, int flags); +__exception int JS_ObjectDefineProperties(JSContext* ctx, JSValueConst obj, JSValueConst properties); +JSValue js_object_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); +JSValue js_object_create(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_getPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +JSValue js_object_setPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +/* magic = 1 if called as Reflect.defineProperty */ +JSValue js_object_defineProperty(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +JSValue js_object_defineProperties(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +/* magic = 1 if called as __defineSetter__ */ +JSValue js_object___defineGetter__(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +JSValue js_object_getOwnPropertyDescriptor(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +JSValue js_object_getOwnPropertyDescriptors(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_getOwnPropertyNames(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_getOwnPropertySymbols(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_keys(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int kind); +JSValue js_object_isExtensible(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int reflect); +JSValue js_object_preventExtensions(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int reflect); +JSValue js_object_hasOwnProperty(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_valueOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_toLocaleString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_assign(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_seal(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int freeze_flag); +JSValue js_object_isSealed(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int is_frozen); +JSValue js_object_fromEntries(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +/* return an empty string if not an object */ +JSValue js_object___getClass(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_is(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue JS_SpeciesConstructor(JSContext* ctx, JSValueConst obj, JSValueConst defaultConstructor); +JSValue js_object_get___proto__(JSContext* ctx, JSValueConst this_val); +JSValue js_object_set___proto__(JSContext* ctx, JSValueConst this_val, JSValueConst proto); +JSValue js_object_isPrototypeOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object_propertyIsEnumerable(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_object___lookupGetter__(JSContext* ctx, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int setter); + +void js_object_data_finalizer(JSRuntime* rt, JSValue val); +void js_object_data_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-operator.c b/src/core/builtins/js-operator.c index 0b85de124..904c3f291 100644 --- a/src/core/builtins/js-operator.c +++ b/src/core/builtins/js-operator.c @@ -1,813 +1,813 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-operator.h" - -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "js-object.h" - -void js_for_in_iterator_finalizer(JSRuntime* rt, JSValue val) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSForInIterator* it = p->u.for_in_iterator; - JS_FreeValueRT(rt, it->obj); - js_free_rt(rt, it); -} - -void js_for_in_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSForInIterator* it = p->u.for_in_iterator; - JS_MarkValue(rt, it->obj, mark_func); -} - -double js_pow(double a, double b) { - if (unlikely(!isfinite(b)) && fabs(a) == 1) { - /* not compatible with IEEE 754 */ - return JS_FLOAT64_NAN; - } else { - return pow(a, b); - } -} - -/* XXX: Should take JSValueConst arguments */ -BOOL js_strict_eq2(JSContext* ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode) { - BOOL res; - int tag1, tag2; - double d1, d2; - - tag1 = JS_VALUE_GET_NORM_TAG(op1); - tag2 = JS_VALUE_GET_NORM_TAG(op2); - switch (tag1) { - case JS_TAG_BOOL: - if (tag1 != tag2) { - res = FALSE; - } else { - res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); - goto done_no_free; - } - break; - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - res = (tag1 == tag2); - break; - case JS_TAG_STRING: { - JSString *p1, *p2; - if (tag1 != tag2) { - res = FALSE; - } else { - p1 = JS_VALUE_GET_STRING(op1); - p2 = JS_VALUE_GET_STRING(op2); - res = (js_string_compare(ctx, p1, p2) == 0); - } - } break; - case JS_TAG_SYMBOL: { - JSAtomStruct *p1, *p2; - if (tag1 != tag2) { - res = FALSE; - } else { - p1 = JS_VALUE_GET_PTR(op1); - p2 = JS_VALUE_GET_PTR(op2); - res = (p1 == p2); - } - } break; - case JS_TAG_OBJECT: - if (tag1 != tag2) - res = FALSE; - else - res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); - break; - case JS_TAG_INT: - d1 = JS_VALUE_GET_INT(op1); - if (tag2 == JS_TAG_INT) { - d2 = JS_VALUE_GET_INT(op2); - goto number_test; - } else if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - goto number_test; - } else { - res = FALSE; - } - break; - case JS_TAG_FLOAT64: - d1 = JS_VALUE_GET_FLOAT64(op1); - if (tag2 == JS_TAG_FLOAT64) { - d2 = JS_VALUE_GET_FLOAT64(op2); - } else if (tag2 == JS_TAG_INT) { - d2 = JS_VALUE_GET_INT(op2); - } else { - res = FALSE; - break; - } - number_test: - if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { - JSFloat64Union u1, u2; - /* NaN is not always normalized, so this test is necessary */ - if (isnan(d1) || isnan(d2)) { - res = isnan(d1) == isnan(d2); - } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { - res = (d1 == d2); /* +0 == -0 */ - } else { - u1.d = d1; - u2.d = d2; - res = (u1.u64 == u2.u64); /* +0 != -0 */ - } - } else { - res = (d1 == d2); /* if NaN return false and +0 == -0 */ - } - goto done_no_free; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: { - bf_t a_s, *a, b_s, *b; - if (tag1 != tag2) { - res = FALSE; - break; - } - a = JS_ToBigFloat(ctx, &a_s, op1); - b = JS_ToBigFloat(ctx, &b_s, op2); - res = bf_cmp_eq(a, b); - if (a == &a_s) - bf_delete(a); - if (b == &b_s) - bf_delete(b); - } break; - case JS_TAG_BIG_FLOAT: { - JSBigFloat *p1, *p2; - const bf_t *a, *b; - if (tag1 != tag2) { - res = FALSE; - break; - } - p1 = JS_VALUE_GET_PTR(op1); - p2 = JS_VALUE_GET_PTR(op2); - a = &p1->num; - b = &p2->num; - if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { - if (eq_mode == JS_EQ_SAME_VALUE_ZERO && a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) { - res = TRUE; - } else { - res = (bf_cmp_full(a, b) == 0); - } - } else { - res = bf_cmp_eq(a, b); - } - } break; - case JS_TAG_BIG_DECIMAL: { - JSBigDecimal *p1, *p2; - const bfdec_t *a, *b; - if (tag1 != tag2) { - res = FALSE; - break; - } - p1 = JS_VALUE_GET_PTR(op1); - p2 = JS_VALUE_GET_PTR(op2); - a = &p1->num; - b = &p2->num; - res = bfdec_cmp_eq(a, b); - } break; -#endif - default: - res = FALSE; - break; - } - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); -done_no_free: - return res; -} - -BOOL js_strict_eq(JSContext* ctx, JSValue op1, JSValue op2) { - return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); -} - -BOOL js_same_value(JSContext* ctx, JSValueConst op1, JSValueConst op2) { - return js_strict_eq2(ctx, JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), JS_EQ_SAME_VALUE); -} - -BOOL js_same_value_zero(JSContext* ctx, JSValueConst op1, JSValueConst op2) { - return js_strict_eq2(ctx, JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), JS_EQ_SAME_VALUE_ZERO); -} - -no_inline int js_strict_eq_slow(JSContext* ctx, JSValue* sp, BOOL is_neq) { - BOOL res; - res = js_strict_eq(ctx, sp[-2], sp[-1]); - sp[-2] = JS_NewBool(ctx, res ^ is_neq); - return 0; -} - -__exception int js_operator_in(JSContext* ctx, JSValue* sp) { - JSValue op1, op2; - JSAtom atom; - int ret; - - op1 = sp[-2]; - op2 = sp[-1]; - - if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { - JS_ThrowTypeError(ctx, "invalid 'in' operand"); - return -1; - } - atom = JS_ValueToAtom(ctx, op1); - if (unlikely(atom == JS_ATOM_NULL)) - return -1; - ret = JS_HasProperty(ctx, op2, atom); - JS_FreeAtom(ctx, atom); - if (ret < 0) - return -1; - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); - return 0; -} - -__exception int js_has_unscopable(JSContext* ctx, JSValueConst obj, JSAtom atom) { - JSValue arr, val; - int ret; - - arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); - if (JS_IsException(arr)) - return -1; - ret = 0; - if (JS_IsObject(arr)) { - val = JS_GetProperty(ctx, arr, atom); - ret = JS_ToBoolFree(ctx, val); - } - JS_FreeValue(ctx, arr); - return ret; -} - -__exception int js_operator_instanceof(JSContext* ctx, JSValue* sp) { - JSValue op1, op2; - BOOL ret; - - op1 = sp[-2]; - op2 = sp[-1]; - ret = JS_IsInstanceOf(ctx, op1, op2); - if (ret < 0) - return ret; - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); - return 0; -} - -__exception int js_operator_typeof(JSContext* ctx, JSValueConst op1) { - JSAtom atom; - uint32_t tag; - - tag = JS_VALUE_GET_NORM_TAG(op1); - switch (tag) { -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - atom = JS_ATOM_bigint; - break; - case JS_TAG_BIG_FLOAT: - atom = JS_ATOM_bigfloat; - break; - case JS_TAG_BIG_DECIMAL: - atom = JS_ATOM_bigdecimal; - break; -#endif - case JS_TAG_INT: - case JS_TAG_FLOAT64: - atom = JS_ATOM_number; - break; - case JS_TAG_UNDEFINED: - atom = JS_ATOM_undefined; - break; - case JS_TAG_BOOL: - atom = JS_ATOM_boolean; - break; - case JS_TAG_STRING: - atom = JS_ATOM_string; - break; - case JS_TAG_OBJECT: { - JSObject* p; - p = JS_VALUE_GET_OBJ(op1); - if (unlikely(p->is_HTMLDDA)) - atom = JS_ATOM_undefined; - else if (JS_IsFunction(ctx, op1)) - atom = JS_ATOM_function; - else - goto obj_type; - } break; - case JS_TAG_NULL: - obj_type: - atom = JS_ATOM_object; - break; - case JS_TAG_SYMBOL: - atom = JS_ATOM_symbol; - break; - default: - atom = JS_ATOM_unknown; - break; - } - return atom; -} - -__exception int js_operator_delete(JSContext* ctx, JSValue* sp) { - JSValue op1, op2; - JSAtom atom; - int ret; - - op1 = sp[-2]; - op2 = sp[-1]; - atom = JS_ValueToAtom(ctx, op2); - if (unlikely(atom == JS_ATOM_NULL)) - return -1; - ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); - JS_FreeAtom(ctx, atom); - if (unlikely(ret < 0)) - return -1; - JS_FreeValue(ctx, op1); - JS_FreeValue(ctx, op2); - sp[-2] = JS_NewBool(ctx, ret); - return 0; -} - -JSValue js_throw_type_error(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_ThrowTypeError(ctx, "invalid property access"); -} - -JSValue js_build_rest(JSContext* ctx, int first, int argc, JSValueConst* argv) { - JSValue val; - int i, ret; - - val = JS_NewArray(ctx); - if (JS_IsException(val)) - return val; - for (i = first; i < argc; i++) { - ret = JS_DefinePropertyValueUint32(ctx, val, i - first, JS_DupValue(ctx, argv[i]), JS_PROP_C_W_E); - if (ret < 0) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - } - return val; -} - -JSValue build_for_in_iterator(JSContext* ctx, JSValue obj) { - JSObject* p; - JSPropertyEnum* tab_atom; - int i; - JSValue enum_obj, obj1; - JSForInIterator* it; - uint32_t tag, tab_atom_count; - - tag = JS_VALUE_GET_TAG(obj); - if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { - obj = JS_ToObjectFree(ctx, obj); - } - - it = js_malloc(ctx, sizeof(*it)); - if (!it) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); - if (JS_IsException(enum_obj)) { - js_free(ctx, it); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - it->is_array = FALSE; - it->obj = obj; - it->idx = 0; - p = JS_VALUE_GET_OBJ(enum_obj); - p->u.for_in_iterator = it; - - if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) - return enum_obj; - - /* fast path: assume no enumerable properties in the prototype chain */ - obj1 = JS_DupValue(ctx, obj); - for (;;) { - obj1 = JS_GetPrototypeFree(ctx, obj1); - if (JS_IsNull(obj1)) - break; - if (JS_IsException(obj1)) - goto fail; - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, JS_VALUE_GET_OBJ(obj1), - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - if (tab_atom_count != 0) { - JS_FreeValue(ctx, obj1); - goto slow_path; - } - /* must check for timeout to avoid infinite loop */ - if (js_poll_interrupts(ctx)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - } - - p = JS_VALUE_GET_OBJ(obj); - - if (p->fast_array) { - JSShape* sh; - JSShapeProperty* prs; - /* check that there are no enumerable normal fields */ - sh = p->shape; - for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - if (prs->flags & JS_PROP_ENUMERABLE) - goto normal_case; - } - /* for fast arrays, we only store the number of elements */ - it->is_array = TRUE; - it->array_length = p->u.array.count; - } else { - normal_case: - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) - goto fail; - for (i = 0; i < tab_atom_count; i++) { - JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - } - return enum_obj; - -slow_path: - /* non enumerable properties hide the enumerables ones in the - prototype chain */ - obj1 = JS_DupValue(ctx, obj); - for (;;) { - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, JS_VALUE_GET_OBJ(obj1), - JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - for (i = 0; i < tab_atom_count; i++) { - JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, - (tab_atom[i].is_enumerable ? JS_PROP_ENUMERABLE : 0)); - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - obj1 = JS_GetPrototypeFree(ctx, obj1); - if (JS_IsNull(obj1)) - break; - if (JS_IsException(obj1)) - goto fail; - /* must check for timeout to avoid infinite loop */ - if (js_poll_interrupts(ctx)) { - JS_FreeValue(ctx, obj1); - goto fail; - } - } - return enum_obj; - -fail: - JS_FreeValue(ctx, enum_obj); - return JS_EXCEPTION; -} - -/* obj -> enum_obj */ -__exception int js_for_in_start(JSContext* ctx, JSValue* sp) { - sp[-1] = build_for_in_iterator(ctx, sp[-1]); - if (JS_IsException(sp[-1])) - return -1; - return 0; -} - -/* enum_obj -> enum_obj value done */ -__exception int js_for_in_next(JSContext* ctx, JSValue* sp) { - JSValueConst enum_obj; - JSObject* p; - JSAtom prop; - JSForInIterator* it; - int ret; - - enum_obj = sp[-1]; - /* fail safe */ - if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) - goto done; - p = JS_VALUE_GET_OBJ(enum_obj); - if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) - goto done; - it = p->u.for_in_iterator; - - for (;;) { - if (it->is_array) { - if (it->idx >= it->array_length) - goto done; - prop = __JS_AtomFromUInt32(it->idx); - it->idx++; - } else { - JSShape* sh = p->shape; - JSShapeProperty* prs; - if (it->idx >= sh->prop_count) - goto done; - prs = get_shape_prop(sh) + it->idx; - prop = prs->atom; - it->idx++; - if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) - continue; - } - /* check if the property was deleted */ - ret = JS_HasProperty(ctx, it->obj, prop); - if (ret < 0) - return ret; - if (ret) - break; - } - /* return the property */ - sp[0] = JS_AtomToValue(ctx, prop); - sp[1] = JS_FALSE; - return 0; -done: - /* return the end */ - sp[0] = JS_UNDEFINED; - sp[1] = JS_TRUE; - return 0; -} - -JSValue JS_GetIterator2(JSContext* ctx, JSValueConst obj, JSValueConst method) { - JSValue enum_obj; - - enum_obj = JS_Call(ctx, method, obj, 0, NULL); - if (JS_IsException(enum_obj)) - return enum_obj; - if (!JS_IsObject(enum_obj)) { - JS_FreeValue(ctx, enum_obj); - return JS_ThrowTypeErrorNotAnObject(ctx); - } - return enum_obj; -} - - -JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, - JSValueConst sync_iter) -{ - JSValue async_iter, next_method; - JSAsyncFromSyncIteratorData *s; - - next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next); - if (JS_IsException(next_method)) - return JS_EXCEPTION; - async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); - if (JS_IsException(async_iter)) { - JS_FreeValue(ctx, next_method); - return async_iter; - } - s = js_mallocz(ctx, sizeof(*s)); - if (!s) { - JS_FreeValue(ctx, async_iter); - JS_FreeValue(ctx, next_method); - return JS_EXCEPTION; - } - s->sync_iter = JS_DupValue(ctx, sync_iter); - s->next_method = next_method; - JS_SetOpaque(async_iter, s); - return async_iter; -} - -JSValue JS_GetIterator(JSContext* ctx, JSValueConst obj, BOOL is_async) { - JSValue method, ret, sync_iter; - - if (is_async) { - method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); - if (JS_IsException(method)) - return method; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); - if (JS_IsException(method)) - return method; - sync_iter = JS_GetIterator2(ctx, obj, method); - JS_FreeValue(ctx, method); - if (JS_IsException(sync_iter)) - return sync_iter; - ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); - JS_FreeValue(ctx, sync_iter); - return ret; - } - } else { - method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); - if (JS_IsException(method)) - return method; - } - if (!JS_IsFunction(ctx, method)) { - JS_FreeValue(ctx, method); - return JS_ThrowTypeError(ctx, "value is not iterable"); - } - ret = JS_GetIterator2(ctx, obj, method); - JS_FreeValue(ctx, method); - return ret; -} - -/* return *pdone = 2 if the iterator object is not parsed */ -JSValue JS_IteratorNext2(JSContext* ctx, - JSValueConst enum_obj, - JSValueConst method, - int argc, - JSValueConst* argv, - int* pdone) { - JSValue obj; - - /* fast path for the built-in iterators (avoid creating the - intermediate result object) */ - if (JS_IsObject(method)) { - JSObject* p = JS_VALUE_GET_OBJ(method); - if (p->class_id == JS_CLASS_C_FUNCTION && p->u.cfunc.cproto == JS_CFUNC_iterator_next) { - JSCFunctionType func; - JSValueConst args[1]; - - /* in case the function expects one argument */ - if (argc == 0) { - args[0] = JS_UNDEFINED; - argv = args; - } - func = p->u.cfunc.c_function; - return func.iterator_next(ctx, enum_obj, argc, argv, pdone, p->u.cfunc.magic); - } - } - obj = JS_Call(ctx, method, enum_obj, argc, argv); - if (JS_IsException(obj)) - goto fail; - if (!JS_IsObject(obj)) { - JS_FreeValue(ctx, obj); - JS_ThrowTypeError(ctx, "iterator must return an object"); - goto fail; - } - *pdone = 2; - return obj; -fail: - *pdone = FALSE; - return JS_EXCEPTION; -} - -JSValue JS_IteratorNext(JSContext* ctx, - JSValueConst enum_obj, - JSValueConst method, - int argc, - JSValueConst* argv, - BOOL* pdone) { - JSValue obj, value, done_val; - int done; - - obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); - if (JS_IsException(obj)) - goto fail; - if (done != 2) { - *pdone = done; - return obj; - } else { - done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); - if (JS_IsException(done_val)) - goto fail; - *pdone = JS_ToBoolFree(ctx, done_val); - value = JS_UNDEFINED; - if (!*pdone) { - value = JS_GetProperty(ctx, obj, JS_ATOM_value); - } - JS_FreeValue(ctx, obj); - return value; - } -fail: - JS_FreeValue(ctx, obj); - *pdone = FALSE; - return JS_EXCEPTION; -} - -/* return < 0 in case of exception */ -int JS_IteratorClose(JSContext* ctx, JSValueConst enum_obj, BOOL is_exception_pending) { - JSValue method, ret, ex_obj; - int res; - - if (is_exception_pending) { - ex_obj = ctx->rt->current_exception; - ctx->rt->current_exception = JS_NULL; - res = -1; - } else { - ex_obj = JS_UNDEFINED; - res = 0; - } - method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); - if (JS_IsException(method)) { - res = -1; - goto done; - } - if (JS_IsUndefined(method) || JS_IsNull(method)) { - goto done; - } - ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); - if (!is_exception_pending) { - if (JS_IsException(ret)) { - res = -1; - } else if (!JS_IsObject(ret)) { - JS_ThrowTypeErrorNotAnObject(ctx); - res = -1; - } - } - JS_FreeValue(ctx, ret); -done: - if (is_exception_pending) { - JS_Throw(ctx, ex_obj); - } - return res; -} - -/* obj -> enum_rec (3 slots) */ -__exception int js_for_of_start(JSContext* ctx, JSValue* sp, BOOL is_async) { - JSValue op1, obj, method; - op1 = sp[-1]; - obj = JS_GetIterator(ctx, op1, is_async); - if (JS_IsException(obj)) - return -1; - JS_FreeValue(ctx, op1); - sp[-1] = obj; - method = JS_GetProperty(ctx, obj, JS_ATOM_next); - if (JS_IsException(method)) - return -1; - sp[0] = method; - return 0; -} - -/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' - objs. If 'done' is true or in case of exception, 'enum_rec' is set - to undefined. If 'done' is true, 'value' is always set to - undefined. */ -__exception int js_for_of_next(JSContext* ctx, JSValue* sp, int offset) { - JSValue value = JS_UNDEFINED; - int done = 1; - - if (likely(!JS_IsUndefined(sp[offset]))) { - value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); - if (JS_IsException(value)) - done = -1; - if (done) { - /* value is JS_UNDEFINED or JS_EXCEPTION */ - /* replace the iteration object with undefined */ - JS_FreeValue(ctx, sp[offset]); - sp[offset] = JS_UNDEFINED; - if (done < 0) { - return -1; - } else { - JS_FreeValue(ctx, value); - value = JS_UNDEFINED; - } - } - } - sp[0] = value; - sp[1] = JS_NewBool(ctx, done); - return 0; -} - -JSValue JS_IteratorGetCompleteValue(JSContext* ctx, JSValueConst obj, BOOL* pdone) { - JSValue done_val, value; - BOOL done; - done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); - if (JS_IsException(done_val)) - goto fail; - done = JS_ToBoolFree(ctx, done_val); - value = JS_GetProperty(ctx, obj, JS_ATOM_value); - if (JS_IsException(value)) - goto fail; - *pdone = done; - return value; -fail: - *pdone = FALSE; - return JS_EXCEPTION; -} - -__exception int js_iterator_get_value_done(JSContext* ctx, JSValue* sp) { - JSValue obj, value; - BOOL done; - obj = sp[-1]; - if (!JS_IsObject(obj)) { - JS_ThrowTypeError(ctx, "iterator must return an object"); - return -1; - } - value = JS_IteratorGetCompleteValue(ctx, obj, &done); - if (JS_IsException(value)) - return -1; - JS_FreeValue(ctx, obj); - sp[-1] = value; - sp[0] = JS_NewBool(ctx, done); - return 0; +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-operator.h" + +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "js-object.h" + +void js_for_in_iterator_finalizer(JSRuntime* rt, JSValue val) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSForInIterator* it = p->u.for_in_iterator; + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); +} + +void js_for_in_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSForInIterator* it = p->u.for_in_iterator; + JS_MarkValue(rt, it->obj, mark_func); +} + +double js_pow(double a, double b) { + if (unlikely(!isfinite(b)) && fabs(a) == 1) { + /* not compatible with IEEE 754 */ + return JS_FLOAT64_NAN; + } else { + return pow(a, b); + } +} + +/* XXX: Should take JSValueConst arguments */ +BOOL js_strict_eq2(JSContext* ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode) { + BOOL res; + int tag1, tag2; + double d1, d2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + switch (tag1) { + case JS_TAG_BOOL: + if (tag1 != tag2) { + res = FALSE; + } else { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + goto done_no_free; + } + break; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = (tag1 == tag2); + break; + case JS_TAG_STRING: { + JSString *p1, *p2; + if (tag1 != tag2) { + res = FALSE; + } else { + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = (js_string_compare(ctx, p1, p2) == 0); + } + } break; + case JS_TAG_SYMBOL: { + JSAtomStruct *p1, *p2; + if (tag1 != tag2) { + res = FALSE; + } else { + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + res = (p1 == p2); + } + } break; + case JS_TAG_OBJECT: + if (tag1 != tag2) + res = FALSE; + else + res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); + break; + case JS_TAG_INT: + d1 = JS_VALUE_GET_INT(op1); + if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + goto number_test; + } else if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } else { + res = FALSE; + } + break; + case JS_TAG_FLOAT64: + d1 = JS_VALUE_GET_FLOAT64(op1); + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + } else { + res = FALSE; + break; + } + number_test: + if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { + JSFloat64Union u1, u2; + /* NaN is not always normalized, so this test is necessary */ + if (isnan(d1) || isnan(d2)) { + res = isnan(d1) == isnan(d2); + } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { + res = (d1 == d2); /* +0 == -0 */ + } else { + u1.d = d1; + u2.d = d2; + res = (u1.u64 == u2.u64); /* +0 != -0 */ + } + } else { + res = (d1 == d2); /* if NaN return false and +0 == -0 */ + } + goto done_no_free; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: { + bf_t a_s, *a, b_s, *b; + if (tag1 != tag2) { + res = FALSE; + break; + } + a = JS_ToBigFloat(ctx, &a_s, op1); + b = JS_ToBigFloat(ctx, &b_s, op2); + res = bf_cmp_eq(a, b); + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + } break; + case JS_TAG_BIG_FLOAT: { + JSBigFloat *p1, *p2; + const bf_t *a, *b; + if (tag1 != tag2) { + res = FALSE; + break; + } + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + a = &p1->num; + b = &p2->num; + if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { + if (eq_mode == JS_EQ_SAME_VALUE_ZERO && a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) { + res = TRUE; + } else { + res = (bf_cmp_full(a, b) == 0); + } + } else { + res = bf_cmp_eq(a, b); + } + } break; + case JS_TAG_BIG_DECIMAL: { + JSBigDecimal *p1, *p2; + const bfdec_t *a, *b; + if (tag1 != tag2) { + res = FALSE; + break; + } + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + a = &p1->num; + b = &p2->num; + res = bfdec_cmp_eq(a, b); + } break; +#endif + default: + res = FALSE; + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); +done_no_free: + return res; +} + +BOOL js_strict_eq(JSContext* ctx, JSValue op1, JSValue op2) { + return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); +} + +BOOL js_same_value(JSContext* ctx, JSValueConst op1, JSValueConst op2) { + return js_strict_eq2(ctx, JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), JS_EQ_SAME_VALUE); +} + +BOOL js_same_value_zero(JSContext* ctx, JSValueConst op1, JSValueConst op2) { + return js_strict_eq2(ctx, JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), JS_EQ_SAME_VALUE_ZERO); +} + +no_inline int js_strict_eq_slow(JSContext* ctx, JSValue* sp, BOOL is_neq) { + BOOL res; + res = js_strict_eq(ctx, sp[-2], sp[-1]); + sp[-2] = JS_NewBool(ctx, res ^ is_neq); + return 0; +} + +__exception int js_operator_in(JSContext* ctx, JSValue* sp) { + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + + if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + atom = JS_ValueToAtom(ctx, op1); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_HasProperty(ctx, op2, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +__exception int js_has_unscopable(JSContext* ctx, JSValueConst obj, JSAtom atom) { + JSValue arr, val; + int ret; + + arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); + if (JS_IsException(arr)) + return -1; + ret = 0; + if (JS_IsObject(arr)) { + val = JS_GetProperty(ctx, arr, atom); + ret = JS_ToBoolFree(ctx, val); + } + JS_FreeValue(ctx, arr); + return ret; +} + +__exception int js_operator_instanceof(JSContext* ctx, JSValue* sp) { + JSValue op1, op2; + BOOL ret; + + op1 = sp[-2]; + op2 = sp[-1]; + ret = JS_IsInstanceOf(ctx, op1, op2); + if (ret < 0) + return ret; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +__exception int js_operator_typeof(JSContext* ctx, JSValueConst op1) { + JSAtom atom; + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(op1); + switch (tag) { +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + atom = JS_ATOM_bigint; + break; + case JS_TAG_BIG_FLOAT: + atom = JS_ATOM_bigfloat; + break; + case JS_TAG_BIG_DECIMAL: + atom = JS_ATOM_bigdecimal; + break; +#endif + case JS_TAG_INT: + case JS_TAG_FLOAT64: + atom = JS_ATOM_number; + break; + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + break; + case JS_TAG_BOOL: + atom = JS_ATOM_boolean; + break; + case JS_TAG_STRING: + atom = JS_ATOM_string; + break; + case JS_TAG_OBJECT: { + JSObject* p; + p = JS_VALUE_GET_OBJ(op1); + if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined; + else if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + } break; + case JS_TAG_NULL: + obj_type: + atom = JS_ATOM_object; + break; + case JS_TAG_SYMBOL: + atom = JS_ATOM_symbol; + break; + default: + atom = JS_ATOM_unknown; + break; + } + return atom; +} + +__exception int js_operator_delete(JSContext* ctx, JSValue* sp) { + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + if (unlikely(ret < 0)) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +JSValue js_throw_type_error(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_ThrowTypeError(ctx, "invalid property access"); +} + +JSValue js_build_rest(JSContext* ctx, int first, int argc, JSValueConst* argv) { + JSValue val; + int i, ret; + + val = JS_NewArray(ctx); + if (JS_IsException(val)) + return val; + for (i = first; i < argc; i++) { + ret = JS_DefinePropertyValueUint32(ctx, val, i - first, JS_DupValue(ctx, argv[i]), JS_PROP_C_W_E); + if (ret < 0) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + } + return val; +} + +JSValue build_for_in_iterator(JSContext* ctx, JSValue obj) { + JSObject* p; + JSPropertyEnum* tab_atom; + int i; + JSValue enum_obj, obj1; + JSForInIterator* it; + uint32_t tag, tab_atom_count; + + tag = JS_VALUE_GET_TAG(obj); + if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { + obj = JS_ToObjectFree(ctx, obj); + } + + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); + if (JS_IsException(enum_obj)) { + js_free(ctx, it); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + it->is_array = FALSE; + it->obj = obj; + it->idx = 0; + p = JS_VALUE_GET_OBJ(enum_obj); + p->u.for_in_iterator = it; + + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return enum_obj; + + /* fast path: assume no enumerable properties in the prototype chain */ + obj1 = JS_DupValue(ctx, obj); + for (;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + if (tab_atom_count != 0) { + JS_FreeValue(ctx, obj1); + goto slow_path; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + + p = JS_VALUE_GET_OBJ(obj); + + if (p->fast_array) { + JSShape* sh; + JSShapeProperty* prs; + /* check that there are no enumerable normal fields */ + sh = p->shape; + for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->flags & JS_PROP_ENUMERABLE) + goto normal_case; + } + /* for fast arrays, we only store the number of elements */ + it->is_array = TRUE; + it->array_length = p->u.array.count; + } else { + normal_case: + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + goto fail; + for (i = 0; i < tab_atom_count; i++) { + JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + } + return enum_obj; + +slow_path: + /* non enumerable properties hide the enumerables ones in the + prototype chain */ + obj1 = JS_DupValue(ctx, obj); + for (;;) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + for (i = 0; i < tab_atom_count; i++) { + JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, + (tab_atom[i].is_enumerable ? JS_PROP_ENUMERABLE : 0)); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + return enum_obj; + +fail: + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; +} + +/* obj -> enum_obj */ +__exception int js_for_in_start(JSContext* ctx, JSValue* sp) { + sp[-1] = build_for_in_iterator(ctx, sp[-1]); + if (JS_IsException(sp[-1])) + return -1; + return 0; +} + +/* enum_obj -> enum_obj value done */ +__exception int js_for_in_next(JSContext* ctx, JSValue* sp) { + JSValueConst enum_obj; + JSObject* p; + JSAtom prop; + JSForInIterator* it; + int ret; + + enum_obj = sp[-1]; + /* fail safe */ + if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) + goto done; + p = JS_VALUE_GET_OBJ(enum_obj); + if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) + goto done; + it = p->u.for_in_iterator; + + for (;;) { + if (it->is_array) { + if (it->idx >= it->array_length) + goto done; + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + JSShape* sh = p->shape; + JSShapeProperty* prs; + if (it->idx >= sh->prop_count) + goto done; + prs = get_shape_prop(sh) + it->idx; + prop = prs->atom; + it->idx++; + if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) + continue; + } + /* check if the property was deleted */ + ret = JS_HasProperty(ctx, it->obj, prop); + if (ret < 0) + return ret; + if (ret) + break; + } + /* return the property */ + sp[0] = JS_AtomToValue(ctx, prop); + sp[1] = JS_FALSE; + return 0; +done: + /* return the end */ + sp[0] = JS_UNDEFINED; + sp[1] = JS_TRUE; + return 0; +} + +JSValue JS_GetIterator2(JSContext* ctx, JSValueConst obj, JSValueConst method) { + JSValue enum_obj; + + enum_obj = JS_Call(ctx, method, obj, 0, NULL); + if (JS_IsException(enum_obj)) + return enum_obj; + if (!JS_IsObject(enum_obj)) { + JS_FreeValue(ctx, enum_obj); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return enum_obj; +} + + +JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, + JSValueConst sync_iter) +{ + JSValue async_iter, next_method; + JSAsyncFromSyncIteratorData *s; + + next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next); + if (JS_IsException(next_method)) + return JS_EXCEPTION; + async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (JS_IsException(async_iter)) { + JS_FreeValue(ctx, next_method); + return async_iter; + } + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, async_iter); + JS_FreeValue(ctx, next_method); + return JS_EXCEPTION; + } + s->sync_iter = JS_DupValue(ctx, sync_iter); + s->next_method = next_method; + JS_SetOpaque(async_iter, s); + return async_iter; +} + +JSValue JS_GetIterator(JSContext* ctx, JSValueConst obj, BOOL is_async) { + JSValue method, ret, sync_iter; + + if (is_async) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); + if (JS_IsException(method)) + return method; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + sync_iter = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + if (JS_IsException(sync_iter)) + return sync_iter; + ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); + JS_FreeValue(ctx, sync_iter); + return ret; + } + } else { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + } + if (!JS_IsFunction(ctx, method)) { + JS_FreeValue(ctx, method); + return JS_ThrowTypeError(ctx, "value is not iterable"); + } + ret = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + return ret; +} + +/* return *pdone = 2 if the iterator object is not parsed */ +JSValue JS_IteratorNext2(JSContext* ctx, + JSValueConst enum_obj, + JSValueConst method, + int argc, + JSValueConst* argv, + int* pdone) { + JSValue obj; + + /* fast path for the built-in iterators (avoid creating the + intermediate result object) */ + if (JS_IsObject(method)) { + JSObject* p = JS_VALUE_GET_OBJ(method); + if (p->class_id == JS_CLASS_C_FUNCTION && p->u.cfunc.cproto == JS_CFUNC_iterator_next) { + JSCFunctionType func; + JSValueConst args[1]; + + /* in case the function expects one argument */ + if (argc == 0) { + args[0] = JS_UNDEFINED; + argv = args; + } + func = p->u.cfunc.c_function; + return func.iterator_next(ctx, enum_obj, argc, argv, pdone, p->u.cfunc.magic); + } + } + obj = JS_Call(ctx, method, enum_obj, argc, argv); + if (JS_IsException(obj)) + goto fail; + if (!JS_IsObject(obj)) { + JS_FreeValue(ctx, obj); + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto fail; + } + *pdone = 2; + return obj; +fail: + *pdone = FALSE; + return JS_EXCEPTION; +} + +JSValue JS_IteratorNext(JSContext* ctx, + JSValueConst enum_obj, + JSValueConst method, + int argc, + JSValueConst* argv, + BOOL* pdone) { + JSValue obj, value, done_val; + int done; + + obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); + if (JS_IsException(obj)) + goto fail; + if (done != 2) { + *pdone = done; + return obj; + } else { + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + *pdone = JS_ToBoolFree(ctx, done_val); + value = JS_UNDEFINED; + if (!*pdone) { + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + } + JS_FreeValue(ctx, obj); + return value; + } +fail: + JS_FreeValue(ctx, obj); + *pdone = FALSE; + return JS_EXCEPTION; +} + +/* return < 0 in case of exception */ +int JS_IteratorClose(JSContext* ctx, JSValueConst enum_obj, BOOL is_exception_pending) { + JSValue method, ret, ex_obj; + int res; + + if (is_exception_pending) { + ex_obj = ctx->rt->current_exception; + ctx->rt->current_exception = JS_NULL; + res = -1; + } else { + ex_obj = JS_UNDEFINED; + res = 0; + } + method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); + if (JS_IsException(method)) { + res = -1; + goto done; + } + if (JS_IsUndefined(method) || JS_IsNull(method)) { + goto done; + } + ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); + if (!is_exception_pending) { + if (JS_IsException(ret)) { + res = -1; + } else if (!JS_IsObject(ret)) { + JS_ThrowTypeErrorNotAnObject(ctx); + res = -1; + } + } + JS_FreeValue(ctx, ret); +done: + if (is_exception_pending) { + JS_Throw(ctx, ex_obj); + } + return res; +} + +/* obj -> enum_rec (3 slots) */ +__exception int js_for_of_start(JSContext* ctx, JSValue* sp, BOOL is_async) { + JSValue op1, obj, method; + op1 = sp[-1]; + obj = JS_GetIterator(ctx, op1, is_async); + if (JS_IsException(obj)) + return -1; + JS_FreeValue(ctx, op1); + sp[-1] = obj; + method = JS_GetProperty(ctx, obj, JS_ATOM_next); + if (JS_IsException(method)) + return -1; + sp[0] = method; + return 0; +} + +/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' + objs. If 'done' is true or in case of exception, 'enum_rec' is set + to undefined. If 'done' is true, 'value' is always set to + undefined. */ +__exception int js_for_of_next(JSContext* ctx, JSValue* sp, int offset) { + JSValue value = JS_UNDEFINED; + int done = 1; + + if (likely(!JS_IsUndefined(sp[offset]))) { + value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); + if (JS_IsException(value)) + done = -1; + if (done) { + /* value is JS_UNDEFINED or JS_EXCEPTION */ + /* replace the iteration object with undefined */ + JS_FreeValue(ctx, sp[offset]); + sp[offset] = JS_UNDEFINED; + if (done < 0) { + return -1; + } else { + JS_FreeValue(ctx, value); + value = JS_UNDEFINED; + } + } + } + sp[0] = value; + sp[1] = JS_NewBool(ctx, done); + return 0; +} + +JSValue JS_IteratorGetCompleteValue(JSContext* ctx, JSValueConst obj, BOOL* pdone) { + JSValue done_val, value; + BOOL done; + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + done = JS_ToBoolFree(ctx, done_val); + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + if (JS_IsException(value)) + goto fail; + *pdone = done; + return value; +fail: + *pdone = FALSE; + return JS_EXCEPTION; +} + +__exception int js_iterator_get_value_done(JSContext* ctx, JSValue* sp) { + JSValue obj, value; + BOOL done; + obj = sp[-1]; + if (!JS_IsObject(obj)) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + return -1; + } + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + if (JS_IsException(value)) + return -1; + JS_FreeValue(ctx, obj); + sp[-1] = value; + sp[0] = JS_NewBool(ctx, done); + return 0; } \ No newline at end of file diff --git a/src/core/builtins/js-operator.h b/src/core/builtins/js-operator.h index a7e5e4cfe..51afaa8cd 100644 --- a/src/core/builtins/js-operator.h +++ b/src/core/builtins/js-operator.h @@ -1,79 +1,79 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_OPERATOR_H -#define QUICKJS_JS_OPERATOR_H - -#include "quickjs/cutils.h" -#include "quickjs/quickjs.h" -#include "../types.h" - -typedef struct JSAsyncFromSyncIteratorData { - JSValue sync_iter; - JSValue next_method; -} JSAsyncFromSyncIteratorData; - -void js_for_in_iterator_finalizer(JSRuntime* rt, JSValue val); -void js_for_in_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - -void js_for_in_iterator_finalizer(JSRuntime* rt, JSValue val); -void js_for_in_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - -JSValue JS_GetIterator2(JSContext* ctx, JSValueConst obj, JSValueConst method); -JSValue JS_CreateAsyncFromSyncIterator(JSContext* ctx, JSValueConst sync_iter); -JSValue JS_GetIterator(JSContext* ctx, JSValueConst obj, BOOL is_async); -JSValue JS_IteratorGetCompleteValue(JSContext* ctx, JSValueConst obj, BOOL* pdone); - -/* return *pdone = 2 if the iterator object is not parsed */ -JSValue JS_IteratorNext2(JSContext* ctx, JSValueConst enum_obj, JSValueConst method, int argc, JSValueConst* argv, int* pdone); -JSValue JS_IteratorNext(JSContext* ctx, JSValueConst enum_obj, JSValueConst method, int argc, JSValueConst* argv, BOOL* pdone); -/* return < 0 in case of exception */ -int JS_IteratorClose(JSContext* ctx, JSValueConst enum_obj, BOOL is_exception_pending); -/* obj -> enum_rec (3 slots) */ -__exception int js_for_of_start(JSContext* ctx, JSValue* sp, BOOL is_async); -double js_pow(double a, double b); -JSValue js_throw_type_error(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_build_rest(JSContext* ctx, int first, int argc, JSValueConst* argv); -JSValue build_for_in_iterator(JSContext* ctx, JSValue obj); -/* obj -> enum_obj */ -__exception int js_for_in_start(JSContext* ctx, JSValue* sp); -/* enum_obj -> enum_obj value done */ -__exception int js_for_in_next(JSContext* ctx, JSValue* sp); -__exception int js_for_of_next(JSContext* ctx, JSValue* sp, int offset); - -BOOL js_strict_eq2(JSContext* ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode); -BOOL js_strict_eq(JSContext* ctx, JSValue op1, JSValue op2); -BOOL js_same_value(JSContext* ctx, JSValueConst op1, JSValueConst op2); -BOOL js_same_value_zero(JSContext* ctx, JSValueConst op1, JSValueConst op2); -no_inline int js_strict_eq_slow(JSContext* ctx, JSValue* sp, BOOL is_neq); -__exception int js_operator_in(JSContext* ctx, JSValue* sp); -__exception int js_has_unscopable(JSContext* ctx, JSValueConst obj, JSAtom atom); -__exception int js_operator_instanceof(JSContext* ctx, JSValue* sp); -__exception int js_operator_typeof(JSContext* ctx, JSValueConst op1); -__exception int js_operator_delete(JSContext* ctx, JSValue* sp); - -__exception int js_iterator_get_value_done(JSContext* ctx, JSValue* sp); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_OPERATOR_H +#define QUICKJS_JS_OPERATOR_H + +#include "quickjs/cutils.h" +#include "quickjs/quickjs.h" +#include "../types.h" + +typedef struct JSAsyncFromSyncIteratorData { + JSValue sync_iter; + JSValue next_method; +} JSAsyncFromSyncIteratorData; + +void js_for_in_iterator_finalizer(JSRuntime* rt, JSValue val); +void js_for_in_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + +void js_for_in_iterator_finalizer(JSRuntime* rt, JSValue val); +void js_for_in_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + +JSValue JS_GetIterator2(JSContext* ctx, JSValueConst obj, JSValueConst method); +JSValue JS_CreateAsyncFromSyncIterator(JSContext* ctx, JSValueConst sync_iter); +JSValue JS_GetIterator(JSContext* ctx, JSValueConst obj, BOOL is_async); +JSValue JS_IteratorGetCompleteValue(JSContext* ctx, JSValueConst obj, BOOL* pdone); + +/* return *pdone = 2 if the iterator object is not parsed */ +JSValue JS_IteratorNext2(JSContext* ctx, JSValueConst enum_obj, JSValueConst method, int argc, JSValueConst* argv, int* pdone); +JSValue JS_IteratorNext(JSContext* ctx, JSValueConst enum_obj, JSValueConst method, int argc, JSValueConst* argv, BOOL* pdone); +/* return < 0 in case of exception */ +int JS_IteratorClose(JSContext* ctx, JSValueConst enum_obj, BOOL is_exception_pending); +/* obj -> enum_rec (3 slots) */ +__exception int js_for_of_start(JSContext* ctx, JSValue* sp, BOOL is_async); +double js_pow(double a, double b); +JSValue js_throw_type_error(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_build_rest(JSContext* ctx, int first, int argc, JSValueConst* argv); +JSValue build_for_in_iterator(JSContext* ctx, JSValue obj); +/* obj -> enum_obj */ +__exception int js_for_in_start(JSContext* ctx, JSValue* sp); +/* enum_obj -> enum_obj value done */ +__exception int js_for_in_next(JSContext* ctx, JSValue* sp); +__exception int js_for_of_next(JSContext* ctx, JSValue* sp, int offset); + +BOOL js_strict_eq2(JSContext* ctx, JSValue op1, JSValue op2, JSStrictEqModeEnum eq_mode); +BOOL js_strict_eq(JSContext* ctx, JSValue op1, JSValue op2); +BOOL js_same_value(JSContext* ctx, JSValueConst op1, JSValueConst op2); +BOOL js_same_value_zero(JSContext* ctx, JSValueConst op1, JSValueConst op2); +no_inline int js_strict_eq_slow(JSContext* ctx, JSValue* sp, BOOL is_neq); +__exception int js_operator_in(JSContext* ctx, JSValue* sp); +__exception int js_has_unscopable(JSContext* ctx, JSValueConst obj, JSAtom atom); +__exception int js_operator_instanceof(JSContext* ctx, JSValue* sp); +__exception int js_operator_typeof(JSContext* ctx, JSValueConst op1); +__exception int js_operator_delete(JSContext* ctx, JSValue* sp); + +__exception int js_iterator_get_value_done(JSContext* ctx, JSValue* sp); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-promise.c b/src/core/builtins/js-promise.c index 0f6d87d5c..63673aba2 100644 --- a/src/core/builtins/js-promise.c +++ b/src/core/builtins/js-promise.c @@ -1,1321 +1,1321 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-promise.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../runtime.h" -#include "js-array.h" -#include "js-async-function.h" -#include "js-async-generator.h" -#include "js-function.h" -#include "js-generator.h" -#include "js-object.h" -#include "js-operator.h" - -/* Promise */ - -typedef enum JSPromiseStateEnum { - JS_PROMISE_PENDING, - JS_PROMISE_FULFILLED, - JS_PROMISE_REJECTED, -} JSPromiseStateEnum; - -typedef struct JSPromiseData { - JSPromiseStateEnum promise_state; - /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ - struct list_head promise_reactions[2]; - BOOL is_handled; /* Note: only useful to debug */ - JSValue promise_result; -} JSPromiseData; - -typedef struct JSPromiseFunctionDataResolved { - int ref_count; - BOOL already_resolved; -} JSPromiseFunctionDataResolved; - -typedef struct JSPromiseFunctionData { - JSValue promise; - JSPromiseFunctionDataResolved *presolved; -} JSPromiseFunctionData; - -typedef struct JSPromiseReactionData { - struct list_head link; /* not used in promise_reaction_job */ - JSValue resolving_funcs[2]; - JSValue handler; -} JSPromiseReactionData; - -int js_create_resolving_functions(JSContext *ctx, JSValue *args, - JSValueConst promise); - -void promise_reaction_data_free(JSRuntime *rt, - JSPromiseReactionData *rd) -{ - JS_FreeValueRT(rt, rd->resolving_funcs[0]); - JS_FreeValueRT(rt, rd->resolving_funcs[1]); - JS_FreeValueRT(rt, rd->handler); - js_free_rt(rt, rd); -} - -JSValue promise_reaction_job(JSContext *ctx, int argc, - JSValueConst *argv) -{ - JSValueConst handler, arg, func; - JSValue res, res2; - BOOL is_reject; - - assert(argc == 5); - handler = argv[2]; - is_reject = JS_ToBool(ctx, argv[3]); - arg = argv[4]; -#ifdef DUMP_PROMISE - printf("promise_reaction_job: is_reject=%d\n", is_reject); -#endif - - if (JS_IsUndefined(handler)) { - if (is_reject) { - res = JS_Throw(ctx, JS_DupValue(ctx, arg)); - } else { - res = JS_DupValue(ctx, arg); - } - } else { - res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg); - } - is_reject = JS_IsException(res); - if (is_reject) - res = JS_GetException(ctx); - func = argv[is_reject]; - /* as an extension, we support undefined as value to avoid - creating a dummy promise in the 'await' implementation of async - functions */ - if (!JS_IsUndefined(func)) { - res2 = JS_Call(ctx, func, JS_UNDEFINED, - 1, (JSValueConst *)&res); - } else { - res2 = JS_UNDEFINED; - } - JS_FreeValue(ctx, res); - - return res2; -} - -void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, - JSHostPromiseRejectionTracker *cb, - void *opaque) -{ - rt->host_promise_rejection_tracker = cb; - rt->host_promise_rejection_tracker_opaque = opaque; -} - -void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, - JSValueConst value, BOOL is_reject) -{ - JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); - struct list_head *el, *el1; - JSPromiseReactionData *rd; - JSValueConst args[5]; - - if (!s || s->promise_state != JS_PROMISE_PENDING) - return; /* should never happen */ - set_value(ctx, &s->promise_result, JS_DupValue(ctx, value)); - s->promise_state = JS_PROMISE_FULFILLED + is_reject; -#ifdef DUMP_PROMISE - printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject); -#endif - if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { - JSRuntime *rt = ctx->rt; - if (rt->host_promise_rejection_tracker) { - rt->host_promise_rejection_tracker(ctx, promise, value, FALSE, - rt->host_promise_rejection_tracker_opaque); - } - } - - list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { - rd = list_entry(el, JSPromiseReactionData, link); - args[0] = rd->resolving_funcs[0]; - args[1] = rd->resolving_funcs[1]; - args[2] = rd->handler; - args[3] = JS_NewBool(ctx, is_reject); - args[4] = value; - JS_EnqueueJob(ctx, promise_reaction_job, 5, args); - list_del(&rd->link); - promise_reaction_data_free(ctx->rt, rd); - } - - list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) { - rd = list_entry(el, JSPromiseReactionData, link); - list_del(&rd->link); - promise_reaction_data_free(ctx->rt, rd); - } -} - -void reject_promise(JSContext *ctx, JSValueConst promise, - JSValueConst value) -{ - fulfill_or_reject_promise(ctx, promise, value, TRUE); -} - -JSValue js_promise_resolve_thenable_job(JSContext *ctx, - int argc, JSValueConst *argv) -{ - JSValueConst promise, thenable, then; - JSValue args[2], res; - -#ifdef DUMP_PROMISE - printf("js_promise_resolve_thenable_job\n"); -#endif - assert(argc == 3); - promise = argv[0]; - thenable = argv[1]; - then = argv[2]; - if (js_create_resolving_functions(ctx, args, promise) < 0) - return JS_EXCEPTION; - res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); - if (JS_IsException(res)) { - JSValue error = JS_GetException(ctx); - res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - } - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); - return res; -} - -void js_promise_resolve_function_free_resolved(JSRuntime *rt, - JSPromiseFunctionDataResolved *sr) -{ - if (--sr->ref_count == 0) { - js_free_rt(rt, sr); - } -} - -int js_create_resolving_functions(JSContext *ctx, - JSValue *resolving_funcs, - JSValueConst promise) - -{ - JSValue obj; - JSPromiseFunctionData *s; - JSPromiseFunctionDataResolved *sr; - int i, ret; - - sr = js_malloc(ctx, sizeof(*sr)); - if (!sr) - return -1; - sr->ref_count = 1; - sr->already_resolved = FALSE; /* must be shared between the two functions */ - ret = 0; - for(i = 0; i < 2; i++) { - obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_PROMISE_RESOLVE_FUNCTION + i); - if (JS_IsException(obj)) - goto fail; - s = js_malloc(ctx, sizeof(*s)); - if (!s) { - JS_FreeValue(ctx, obj); - fail: - - if (i != 0) - JS_FreeValue(ctx, resolving_funcs[0]); - ret = -1; - break; - } - sr->ref_count++; - s->presolved = sr; - s->promise = JS_DupValue(ctx, promise); - JS_SetOpaque(obj, s); - js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1); - resolving_funcs[i] = obj; - } - js_promise_resolve_function_free_resolved(ctx->rt, sr); - return ret; -} - -void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) -{ - JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; - if (s) { - js_promise_resolve_function_free_resolved(rt, s->presolved); - JS_FreeValueRT(rt, s->promise); - js_free_rt(rt, s); - } -} - -void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; - if (s) { - JS_MarkValue(rt, s->promise, mark_func); - } -} - -JSValue js_promise_resolve_function_call(JSContext *ctx, - JSValueConst func_obj, - JSValueConst this_val, - int argc, JSValueConst *argv, - int flags) -{ - JSObject *p = JS_VALUE_GET_OBJ(func_obj); - JSPromiseFunctionData *s; - JSValueConst resolution, args[3]; - JSValue then; - BOOL is_reject; - - s = p->u.promise_function_data; - if (!s || s->presolved->already_resolved) - return JS_UNDEFINED; - s->presolved->already_resolved = TRUE; - is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION; - if (argc > 0) - resolution = argv[0]; - else - resolution = JS_UNDEFINED; -#ifdef DUMP_PROMISE - printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject); - JS_DumpValue(ctx, resolution); - printf("\n"); -#endif - if (is_reject || !JS_IsObject(resolution)) { - goto done; - } else if (js_same_value(ctx, resolution, s->promise)) { - JS_ThrowTypeError(ctx, "promise self resolution"); - goto fail_reject; - } - then = JS_GetProperty(ctx, resolution, JS_ATOM_then); - if (JS_IsException(then)) { - JSValue error; - fail_reject: - error = JS_GetException(ctx); - reject_promise(ctx, s->promise, error); - JS_FreeValue(ctx, error); - } else if (!JS_IsFunction(ctx, then)) { - JS_FreeValue(ctx, then); - done: - fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject); - } else { - args[0] = s->promise; - args[1] = resolution; - args[2] = then; - JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args); - JS_FreeValue(ctx, then); - } - return JS_UNDEFINED; -} - -void js_promise_finalizer(JSRuntime *rt, JSValue val) -{ - JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); - struct list_head *el, *el1; - int i; - - if (!s) - return; - for(i = 0; i < 2; i++) { - list_for_each_safe(el, el1, &s->promise_reactions[i]) { - JSPromiseReactionData *rd = - list_entry(el, JSPromiseReactionData, link); - promise_reaction_data_free(rt, rd); - } - } - JS_FreeValueRT(rt, s->promise_result); - js_free_rt(rt, s); -} - -void js_promise_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); - struct list_head *el; - int i; - - if (!s) - return; - for(i = 0; i < 2; i++) { - list_for_each(el, &s->promise_reactions[i]) { - JSPromiseReactionData *rd = - list_entry(el, JSPromiseReactionData, link); - JS_MarkValue(rt, rd->resolving_funcs[0], mark_func); - JS_MarkValue(rt, rd->resolving_funcs[1], mark_func); - JS_MarkValue(rt, rd->handler, mark_func); - } - } - JS_MarkValue(rt, s->promise_result, mark_func); -} - -JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValueConst executor; - JSValue obj; - JSPromiseData *s; - JSValue args[2], ret; - int i; - - executor = argv[0]; - if (check_function(ctx, executor)) - return JS_EXCEPTION; - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE); - if (JS_IsException(obj)) - return JS_EXCEPTION; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) - goto fail; - s->promise_state = JS_PROMISE_PENDING; - s->is_handled = FALSE; - for(i = 0; i < 2; i++) - init_list_head(&s->promise_reactions[i]); - s->promise_result = JS_UNDEFINED; - JS_SetOpaque(obj, s); - if (js_create_resolving_functions(ctx, args, obj)) - goto fail; - ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args); - if (JS_IsException(ret)) { - JSValue ret2, error; - error = JS_GetException(ctx); - ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret2)) - goto fail1; - JS_FreeValue(ctx, ret2); - } - JS_FreeValue(ctx, ret); - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); - return obj; -fail1: - JS_FreeValue(ctx, args[0]); - JS_FreeValue(ctx, args[1]); -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_promise_executor(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - int i; - - for(i = 0; i < 2; i++) { - if (!JS_IsUndefined(func_data[i])) - return JS_ThrowTypeError(ctx, "resolving function already set"); - func_data[i] = JS_DupValue(ctx, argv[i]); - } - return JS_UNDEFINED; -} - -JSValue js_promise_executor_new(JSContext *ctx) -{ - JSValueConst func_data[2]; - - func_data[0] = JS_UNDEFINED; - func_data[1] = JS_UNDEFINED; - return JS_NewCFunctionData(ctx, js_promise_executor, 2, - 0, 2, func_data); -} - -JSValue js_new_promise_capability(JSContext *ctx, - JSValue *resolving_funcs, - JSValueConst ctor) -{ - JSValue executor, result_promise; - JSCFunctionDataRecord *s; - int i; - - executor = js_promise_executor_new(ctx); - if (JS_IsException(executor)) - return executor; - - if (JS_IsUndefined(ctor)) { - result_promise = js_promise_constructor(ctx, ctor, 1, - (JSValueConst *)&executor); - } else { - result_promise = JS_CallConstructor(ctx, ctor, 1, - (JSValueConst *)&executor); - } - if (JS_IsException(result_promise)) - goto fail; - s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA); - for(i = 0; i < 2; i++) { - if (check_function(ctx, s->data[i])) - goto fail; - } - for(i = 0; i < 2; i++) - resolving_funcs[i] = JS_DupValue(ctx, s->data[i]); - JS_FreeValue(ctx, executor); - return result_promise; -fail: - JS_FreeValue(ctx, executor); - JS_FreeValue(ctx, result_promise); - return JS_EXCEPTION; -} - -JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) -{ - return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); -} - -JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue result_promise, resolving_funcs[2], ret; - BOOL is_reject = magic; - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) { - JSValue ctor; - BOOL is_same; - ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor); - if (JS_IsException(ctor)) - return ctor; - is_same = js_same_value(ctx, ctor, this_val); - JS_FreeValue(ctx, ctor); - if (is_same) - return JS_DupValue(ctx, argv[0]); - } - result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); - if (JS_IsException(result_promise)) - return result_promise; - ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - if (JS_IsException(ret)) { - JS_FreeValue(ctx, result_promise); - return ret; - } - JS_FreeValue(ctx, ret); - return result_promise; -} - -#if 0 -JSValue js_promise___newPromiseCapability(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue result_promise, resolving_funcs[2], obj; - JSValueConst ctor; - ctor = argv[0]; - if (!JS_IsObject(ctor)) - return JS_ThrowTypeErrorNotAnObject(ctx); - result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); - if (JS_IsException(result_promise)) - return result_promise; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - JS_FreeValue(ctx, result_promise); - return JS_EXCEPTION; - } - JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); - return obj; -} -#endif - -__exception int remainingElementsCount_add(JSContext *ctx, - JSValueConst resolve_element_env, - int addend) -{ - JSValue val; - int remainingElementsCount; - - val = JS_GetPropertyUint32(ctx, resolve_element_env, 0); - if (JS_IsException(val)) - return -1; - if (JS_ToInt32Free(ctx, &remainingElementsCount, val)) - return -1; - remainingElementsCount += addend; - if (JS_SetPropertyUint32(ctx, resolve_element_env, 0, - JS_NewInt32(ctx, remainingElementsCount)) < 0) - return -1; - return (remainingElementsCount == 0); -} - -#define PROMISE_MAGIC_all 0 -#define PROMISE_MAGIC_allSettled 1 -#define PROMISE_MAGIC_any 2 - -JSValue js_promise_all_resolve_element(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, - JSValue *func_data) -{ - int resolve_type = magic & 3; - int is_reject = magic & 4; - BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]); - JSValueConst values = func_data[2]; - JSValueConst resolve = func_data[3]; - JSValueConst resolve_element_env = func_data[4]; - JSValue ret, obj; - int is_zero, index; - - if (JS_ToInt32(ctx, &index, func_data[1])) - return JS_EXCEPTION; - if (alreadyCalled) - return JS_UNDEFINED; - func_data[0] = JS_NewBool(ctx, TRUE); - - if (resolve_type == PROMISE_MAGIC_allSettled) { - JSValue str; - - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled"); - if (JS_IsException(str)) - goto fail1; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status, - str, - JS_PROP_C_W_E) < 0) - goto fail1; - if (JS_DefinePropertyValue(ctx, obj, - is_reject ? JS_ATOM_reason : JS_ATOM_value, - JS_DupValue(ctx, argv[0]), - JS_PROP_C_W_E) < 0) { - fail1: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } else { - obj = JS_DupValue(ctx, argv[0]); - } - if (JS_DefinePropertyValueUint32(ctx, values, index, - obj, JS_PROP_C_W_E) < 0) - return JS_EXCEPTION; - - is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); - if (is_zero < 0) - return JS_EXCEPTION; - if (is_zero) { - if (resolve_type == PROMISE_MAGIC_any) { - JSValue error; - error = js_aggregate_error_constructor(ctx, values); - if (JS_IsException(error)) - return JS_EXCEPTION; - ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); - JS_FreeValue(ctx, error); - } else { - ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); - } - if (JS_IsException(ret)) - return ret; - JS_FreeValue(ctx, ret); - } - return JS_UNDEFINED; -} - -/* magic = 0: Promise.all 1: Promise.allSettled */ -JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic) -{ - JSValue result_promise, resolving_funcs[2], item, next_promise, ret; - JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; - JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; - JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; - JSValueConst then_args[2], resolve_element_data[5]; - BOOL done; - int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); - if (JS_IsException(result_promise)) - return result_promise; - promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); - if (JS_IsException(promise_resolve) || - check_function(ctx, promise_resolve)) - goto fail_reject; - iter = JS_GetIterator(ctx, argv[0], FALSE); - if (JS_IsException(iter)) { - JSValue error; - fail_reject: - error = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, - (JSValueConst *)&error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret)) - goto fail; - JS_FreeValue(ctx, ret); - } else { - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail_reject; - values = JS_NewArray(ctx); - if (JS_IsException(values)) - goto fail_reject; - resolve_element_env = JS_NewArray(ctx); - if (JS_IsException(resolve_element_env)) - goto fail_reject; - /* remainingElementsCount field */ - if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0, - JS_NewInt32(ctx, 1), - JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) - goto fail_reject; - - index = 0; - for(;;) { - /* XXX: conformance: should close the iterator if error on 'done' - access, but not on 'value' access */ - item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(item)) - goto fail_reject; - if (done) - break; - next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, (JSValueConst *)&item); - JS_FreeValue(ctx, item); - if (JS_IsException(next_promise)) { - fail_reject1: - JS_IteratorClose(ctx, iter, TRUE); - goto fail_reject; - } - resolve_element_data[0] = JS_NewBool(ctx, FALSE); - resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); - resolve_element_data[2] = values; - resolve_element_data[3] = resolving_funcs[is_promise_any]; - resolve_element_data[4] = resolve_element_env; - resolve_element = - JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, - magic, 5, resolve_element_data); - if (JS_IsException(resolve_element)) { - JS_FreeValue(ctx, next_promise); - goto fail_reject1; - } - - if (magic == PROMISE_MAGIC_allSettled) { - reject_element = - JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, - magic | 4, 5, resolve_element_data); - if (JS_IsException(reject_element)) { - JS_FreeValue(ctx, next_promise); - goto fail_reject1; - } - } else if (magic == PROMISE_MAGIC_any) { - if (JS_DefinePropertyValueUint32(ctx, values, index, - JS_UNDEFINED, JS_PROP_C_W_E) < 0) - goto fail_reject1; - reject_element = resolve_element; - resolve_element = JS_DupValue(ctx, resolving_funcs[0]); - } else { - reject_element = JS_DupValue(ctx, resolving_funcs[1]); - } - - if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) { - JS_FreeValue(ctx, next_promise); - JS_FreeValue(ctx, resolve_element); - JS_FreeValue(ctx, reject_element); - goto fail_reject1; - } - - then_args[0] = resolve_element; - then_args[1] = reject_element; - ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args); - JS_FreeValue(ctx, resolve_element); - JS_FreeValue(ctx, reject_element); - if (check_exception_free(ctx, ret)) - goto fail_reject1; - index++; - } - - is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); - if (is_zero < 0) - goto fail_reject; - if (is_zero) { - if (magic == PROMISE_MAGIC_any) { - JSValue error; - error = js_aggregate_error_constructor(ctx, values); - if (JS_IsException(error)) - goto fail_reject; - JS_FreeValue(ctx, values); - values = error; - } - ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, - 1, (JSValueConst *)&values); - if (check_exception_free(ctx, ret)) - goto fail_reject; - } - } -done: - JS_FreeValue(ctx, promise_resolve); - JS_FreeValue(ctx, resolve_element_env); - JS_FreeValue(ctx, values); - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return result_promise; -fail: - JS_FreeValue(ctx, result_promise); - result_promise = JS_EXCEPTION; - goto done; -} - -JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue result_promise, resolving_funcs[2], item, next_promise, ret; - JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; - JSValue promise_resolve = JS_UNDEFINED; - BOOL done; - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); - if (JS_IsException(result_promise)) - return result_promise; - promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); - if (JS_IsException(promise_resolve) || - check_function(ctx, promise_resolve)) - goto fail_reject; - iter = JS_GetIterator(ctx, argv[0], FALSE); - if (JS_IsException(iter)) { - JSValue error; - fail_reject: - error = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, - (JSValueConst *)&error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret)) - goto fail; - JS_FreeValue(ctx, ret); - } else { - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail_reject; - - for(;;) { - /* XXX: conformance: should close the iterator if error on 'done' - access, but not on 'value' access */ - item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(item)) - goto fail_reject; - if (done) - break; - next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, (JSValueConst *)&item); - JS_FreeValue(ctx, item); - if (JS_IsException(next_promise)) { - fail_reject1: - JS_IteratorClose(ctx, iter, TRUE); - goto fail_reject; - } - ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, - (JSValueConst *)resolving_funcs); - if (check_exception_free(ctx, ret)) - goto fail_reject1; - } - } -done: - JS_FreeValue(ctx, promise_resolve); - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return result_promise; -fail: - //JS_FreeValue(ctx, next_method); // why not??? - JS_FreeValue(ctx, result_promise); - result_promise = JS_EXCEPTION; - goto done; -} - -__exception int perform_promise_then(JSContext *ctx, - JSValueConst promise, - JSValueConst *resolve_reject, - JSValueConst *cap_resolving_funcs) -{ - JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); - JSPromiseReactionData *rd_array[2], *rd; - int i, j; - - rd_array[0] = NULL; - rd_array[1] = NULL; - for(i = 0; i < 2; i++) { - JSValueConst handler; - rd = js_mallocz(ctx, sizeof(*rd)); - if (!rd) { - if (i == 1) - promise_reaction_data_free(ctx->rt, rd_array[0]); - return -1; - } - for(j = 0; j < 2; j++) - rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]); - handler = resolve_reject[i]; - if (!JS_IsFunction(ctx, handler)) - handler = JS_UNDEFINED; - rd->handler = JS_DupValue(ctx, handler); - rd_array[i] = rd; - } - - if (s->promise_state == JS_PROMISE_PENDING) { - for(i = 0; i < 2; i++) - list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); - } else { - JSValueConst args[5]; - if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { - JSRuntime *rt = ctx->rt; - if (rt->host_promise_rejection_tracker) { - rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, - TRUE, rt->host_promise_rejection_tracker_opaque); - } - } - i = s->promise_state - JS_PROMISE_FULFILLED; - rd = rd_array[i]; - args[0] = rd->resolving_funcs[0]; - args[1] = rd->resolving_funcs[1]; - args[2] = rd->handler; - args[3] = JS_NewBool(ctx, i); - args[4] = s->promise_result; - JS_EnqueueJob(ctx, promise_reaction_job, 5, args); - for(i = 0; i < 2; i++) - promise_reaction_data_free(ctx->rt, rd_array[i]); - } - s->is_handled = TRUE; - return 0; -} - -JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue ctor, result_promise, resolving_funcs[2]; - JSPromiseData *s; - int i, ret; - - s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); - if (!s) - return JS_EXCEPTION; - - ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); - if (JS_IsException(ctor)) - return ctor; - result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); - JS_FreeValue(ctx, ctor); - if (JS_IsException(result_promise)) - return result_promise; - ret = perform_promise_then(ctx, this_val, argv, - (JSValueConst *)resolving_funcs); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (ret) { - JS_FreeValue(ctx, result_promise); - return JS_EXCEPTION; - } - return result_promise; -} - -JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst args[2]; - args[0] = JS_UNDEFINED; - args[1] = argv[0]; - return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); -} - -JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - return JS_DupValue(ctx, func_data[0]); -} - -JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - return JS_Throw(ctx, JS_DupValue(ctx, func_data[0])); -} - -JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - JSValueConst ctor = func_data[0]; - JSValueConst onFinally = func_data[1]; - JSValue res, promise, ret, then_func; - - res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); - if (JS_IsException(res)) - return res; - promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); - JS_FreeValue(ctx, res); - if (JS_IsException(promise)) - return promise; - if (magic == 0) { - then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, - 0, 1, argv); - } else { - then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0, - 0, 1, argv); - } - if (JS_IsException(then_func)) { - JS_FreeValue(ctx, promise); - return then_func; - } - ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func); - JS_FreeValue(ctx, then_func); - return ret; -} - -JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst onFinally = argv[0]; - JSValue ctor, ret; - JSValue then_funcs[2]; - JSValueConst func_data[2]; - int i; - - ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); - if (JS_IsException(ctor)) - return ctor; - if (!JS_IsFunction(ctx, onFinally)) { - then_funcs[0] = JS_DupValue(ctx, onFinally); - then_funcs[1] = JS_DupValue(ctx, onFinally); - } else { - func_data[0] = ctor; - func_data[1] = onFinally; - for(i = 0; i < 2; i++) { - then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data); - if (JS_IsException(then_funcs[i])) { - if (i == 1) - JS_FreeValue(ctx, then_funcs[0]); - JS_FreeValue(ctx, ctor); - return JS_EXCEPTION; - } - } - } - JS_FreeValue(ctx, ctor); - ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs); - JS_FreeValue(ctx, then_funcs[0]); - JS_FreeValue(ctx, then_funcs[1]); - return ret; -} - - -void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) -{ - JSAsyncFromSyncIteratorData *s = - JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); - if (s) { - JS_FreeValueRT(rt, s->sync_iter); - JS_FreeValueRT(rt, s->next_method); - js_free_rt(rt, s); - } -} - -void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSAsyncFromSyncIteratorData *s = - JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); - if (s) { - JS_MarkValue(rt, s->sync_iter, mark_func); - JS_MarkValue(rt, s->next_method, mark_func); - } -} - - -JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - int magic, JSValue *func_data) -{ - return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), - JS_ToBool(ctx, func_data[0])); -} - -JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, - BOOL done) -{ - JSValueConst func_data[1]; - - func_data[0] = (JSValueConst)JS_NewBool(ctx, done); - return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, - 1, 0, 1, func_data); -} - -/* AsyncIteratorPrototype */ - -const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { - JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ), -}; - -JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, - int magic) -{ - JSValue promise, resolving_funcs[2], value, err, method; - JSAsyncFromSyncIteratorData *s; - int done; - int is_reject; - - promise = JS_NewPromiseCapability(ctx, resolving_funcs); - if (JS_IsException(promise)) - return JS_EXCEPTION; - s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); - if (!s) { - JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator"); - goto reject; - } - - if (magic == GEN_MAGIC_NEXT) { - method = JS_DupValue(ctx, s->next_method); - } else { - method = JS_GetProperty(ctx, s->sync_iter, - magic == GEN_MAGIC_RETURN ? JS_ATOM_return : - JS_ATOM_throw); - if (JS_IsException(method)) - goto reject; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - if (magic == GEN_MAGIC_RETURN) { - err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE); - is_reject = 0; - } else { - err = JS_DupValue(ctx, argv[0]); - is_reject = 1; - } - goto done_resolve; - } - } - value = JS_IteratorNext2(ctx, s->sync_iter, method, - argc >= 1 ? 1 : 0, argv, &done); - JS_FreeValue(ctx, method); - if (JS_IsException(value)) - goto reject; - if (done == 2) { - JSValue obj = value; - value = JS_IteratorGetCompleteValue(ctx, obj, &done); - JS_FreeValue(ctx, obj); - if (JS_IsException(value)) - goto reject; - } - - if (JS_IsException(value)) { - JSValue res2; - reject: - err = JS_GetException(ctx); - is_reject = 1; - done_resolve: - res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, - 1, (JSValueConst *)&err); - JS_FreeValue(ctx, err); - JS_FreeValue(ctx, res2); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - return promise; - } - { - JSValue value_wrapper_promise, resolve_reject[2]; - int res; - - value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, (JSValueConst *)&value, 0); - if (JS_IsException(value_wrapper_promise)) { - JS_FreeValue(ctx, value); - goto reject; - } - - resolve_reject[0] = - js_async_from_sync_iterator_unwrap_func_create(ctx, done); - if (JS_IsException(resolve_reject[0])) { - JS_FreeValue(ctx, value_wrapper_promise); - goto fail; - } - JS_FreeValue(ctx, value); - resolve_reject[1] = JS_UNDEFINED; - - res = perform_promise_then(ctx, value_wrapper_promise, - (JSValueConst *)resolve_reject, - (JSValueConst *)resolving_funcs); - JS_FreeValue(ctx, resolve_reject[0]); - JS_FreeValue(ctx, value_wrapper_promise); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - if (res) { - JS_FreeValue(ctx, promise); - return JS_EXCEPTION; - } - } - return promise; -fail: - JS_FreeValue(ctx, value); - JS_FreeValue(ctx, resolving_funcs[0]); - JS_FreeValue(ctx, resolving_funcs[1]); - JS_FreeValue(ctx, promise); - return JS_EXCEPTION; -} - - -const JSCFunctionListEntry js_promise_funcs[] = { - JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ), - JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ), - JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), - JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), - JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), - JS_CFUNC_DEF("race", 1, js_promise_race ), - //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ), - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), -}; - -const JSCFunctionListEntry js_promise_proto_funcs[] = { - JS_CFUNC_DEF("then", 2, js_promise_then ), - JS_CFUNC_DEF("catch", 1, js_promise_catch ), - JS_CFUNC_DEF("finally", 1, js_promise_finally ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ), -}; - -/* AsyncFunction */ -const JSCFunctionListEntry js_async_function_proto_funcs[] = { - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), -}; - - -const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ), - JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ), - JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ), -}; - -/* AsyncGeneratorFunction */ - -const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = { - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ), -}; - -/* AsyncGenerator prototype */ - -const JSCFunctionListEntry js_async_generator_proto_funcs[] = { - JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ), - JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ), - JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ), -}; - -JSClassShortDef const js_async_class_def[] = { - { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */ - { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */ - { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */ - { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */ - { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */ - { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */ - { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ - { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */ - { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */ -}; - - -void JS_AddIntrinsicPromise(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValue obj1; - - if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { - init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, - countof(js_async_class_def)); - rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call; - rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call; - rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call; - rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call; - rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call; - rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call; - } - - /* Promise */ - ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE], - js_promise_proto_funcs, - countof(js_promise_proto_funcs)); - obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1, - JS_CFUNC_constructor, 0); - ctx->promise_ctor = JS_DupValue(ctx, obj1); - JS_SetPropertyFunctionList(ctx, obj1, - js_promise_funcs, - countof(js_promise_funcs)); - JS_NewGlobalCConstructor2(ctx, obj1, "Promise", - ctx->class_proto[JS_CLASS_PROMISE]); - - /* AsyncFunction */ - ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, - "AsyncFunction", 1, - JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, - ctx->function_ctor); - JS_SetPropertyFunctionList(ctx, - ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], - js_async_function_proto_funcs, - countof(js_async_function_proto_funcs)); - JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], - 0, JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); - - /* AsyncIteratorPrototype */ - ctx->async_iterator_proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto, - js_async_iterator_proto_funcs, - countof(js_async_iterator_proto_funcs)); - - /* AsyncFromSyncIteratorPrototype */ - ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] = - JS_NewObjectProto(ctx, ctx->async_iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR], - js_async_from_sync_iterator_proto_funcs, - countof(js_async_from_sync_iterator_proto_funcs)); - - /* AsyncGeneratorPrototype */ - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] = - JS_NewObjectProto(ctx, ctx->async_iterator_proto); - JS_SetPropertyFunctionList(ctx, - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], - js_async_generator_proto_funcs, - countof(js_async_generator_proto_funcs)); - - /* AsyncGeneratorFunction */ - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] = - JS_NewObjectProto(ctx, ctx->function_proto); - obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, - "AsyncGeneratorFunction", 1, - JS_CFUNC_constructor_or_func_magic, - JS_FUNC_ASYNC_GENERATOR, - ctx->function_ctor); - JS_SetPropertyFunctionList(ctx, - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], - js_async_generator_function_proto_funcs, - countof(js_async_generator_function_proto_funcs)); - JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], - ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], - JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); - JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], - 0, JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-promise.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../runtime.h" +#include "js-array.h" +#include "js-async-function.h" +#include "js-async-generator.h" +#include "js-function.h" +#include "js-generator.h" +#include "js-object.h" +#include "js-operator.h" + +/* Promise */ + +typedef enum JSPromiseStateEnum { + JS_PROMISE_PENDING, + JS_PROMISE_FULFILLED, + JS_PROMISE_REJECTED, +} JSPromiseStateEnum; + +typedef struct JSPromiseData { + JSPromiseStateEnum promise_state; + /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ + struct list_head promise_reactions[2]; + BOOL is_handled; /* Note: only useful to debug */ + JSValue promise_result; +} JSPromiseData; + +typedef struct JSPromiseFunctionDataResolved { + int ref_count; + BOOL already_resolved; +} JSPromiseFunctionDataResolved; + +typedef struct JSPromiseFunctionData { + JSValue promise; + JSPromiseFunctionDataResolved *presolved; +} JSPromiseFunctionData; + +typedef struct JSPromiseReactionData { + struct list_head link; /* not used in promise_reaction_job */ + JSValue resolving_funcs[2]; + JSValue handler; +} JSPromiseReactionData; + +int js_create_resolving_functions(JSContext *ctx, JSValue *args, + JSValueConst promise); + +void promise_reaction_data_free(JSRuntime *rt, + JSPromiseReactionData *rd) +{ + JS_FreeValueRT(rt, rd->resolving_funcs[0]); + JS_FreeValueRT(rt, rd->resolving_funcs[1]); + JS_FreeValueRT(rt, rd->handler); + js_free_rt(rt, rd); +} + +JSValue promise_reaction_job(JSContext *ctx, int argc, + JSValueConst *argv) +{ + JSValueConst handler, arg, func; + JSValue res, res2; + BOOL is_reject; + + assert(argc == 5); + handler = argv[2]; + is_reject = JS_ToBool(ctx, argv[3]); + arg = argv[4]; +#ifdef DUMP_PROMISE + printf("promise_reaction_job: is_reject=%d\n", is_reject); +#endif + + if (JS_IsUndefined(handler)) { + if (is_reject) { + res = JS_Throw(ctx, JS_DupValue(ctx, arg)); + } else { + res = JS_DupValue(ctx, arg); + } + } else { + res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg); + } + is_reject = JS_IsException(res); + if (is_reject) + res = JS_GetException(ctx); + func = argv[is_reject]; + /* as an extension, we support undefined as value to avoid + creating a dummy promise in the 'await' implementation of async + functions */ + if (!JS_IsUndefined(func)) { + res2 = JS_Call(ctx, func, JS_UNDEFINED, + 1, (JSValueConst *)&res); + } else { + res2 = JS_UNDEFINED; + } + JS_FreeValue(ctx, res); + + return res2; +} + +void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, + JSHostPromiseRejectionTracker *cb, + void *opaque) +{ + rt->host_promise_rejection_tracker = cb; + rt->host_promise_rejection_tracker_opaque = opaque; +} + +void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, + JSValueConst value, BOOL is_reject) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + struct list_head *el, *el1; + JSPromiseReactionData *rd; + JSValueConst args[5]; + + if (!s || s->promise_state != JS_PROMISE_PENDING) + return; /* should never happen */ + set_value(ctx, &s->promise_result, JS_DupValue(ctx, value)); + s->promise_state = JS_PROMISE_FULFILLED + is_reject; +#ifdef DUMP_PROMISE + printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject); +#endif + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) { + rt->host_promise_rejection_tracker(ctx, promise, value, FALSE, + rt->host_promise_rejection_tracker_opaque); + } + } + + list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { + rd = list_entry(el, JSPromiseReactionData, link); + args[0] = rd->resolving_funcs[0]; + args[1] = rd->resolving_funcs[1]; + args[2] = rd->handler; + args[3] = JS_NewBool(ctx, is_reject); + args[4] = value; + JS_EnqueueJob(ctx, promise_reaction_job, 5, args); + list_del(&rd->link); + promise_reaction_data_free(ctx->rt, rd); + } + + list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) { + rd = list_entry(el, JSPromiseReactionData, link); + list_del(&rd->link); + promise_reaction_data_free(ctx->rt, rd); + } +} + +void reject_promise(JSContext *ctx, JSValueConst promise, + JSValueConst value) +{ + fulfill_or_reject_promise(ctx, promise, value, TRUE); +} + +JSValue js_promise_resolve_thenable_job(JSContext *ctx, + int argc, JSValueConst *argv) +{ + JSValueConst promise, thenable, then; + JSValue args[2], res; + +#ifdef DUMP_PROMISE + printf("js_promise_resolve_thenable_job\n"); +#endif + assert(argc == 3); + promise = argv[0]; + thenable = argv[1]; + then = argv[2]; + if (js_create_resolving_functions(ctx, args, promise) < 0) + return JS_EXCEPTION; + res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); + if (JS_IsException(res)) { + JSValue error = JS_GetException(ctx); + res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + } + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + return res; +} + +void js_promise_resolve_function_free_resolved(JSRuntime *rt, + JSPromiseFunctionDataResolved *sr) +{ + if (--sr->ref_count == 0) { + js_free_rt(rt, sr); + } +} + +int js_create_resolving_functions(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst promise) + +{ + JSValue obj; + JSPromiseFunctionData *s; + JSPromiseFunctionDataResolved *sr; + int i, ret; + + sr = js_malloc(ctx, sizeof(*sr)); + if (!sr) + return -1; + sr->ref_count = 1; + sr->already_resolved = FALSE; /* must be shared between the two functions */ + ret = 0; + for(i = 0; i < 2; i++) { + obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_PROMISE_RESOLVE_FUNCTION + i); + if (JS_IsException(obj)) + goto fail; + s = js_malloc(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + fail: + + if (i != 0) + JS_FreeValue(ctx, resolving_funcs[0]); + ret = -1; + break; + } + sr->ref_count++; + s->presolved = sr; + s->promise = JS_DupValue(ctx, promise); + JS_SetOpaque(obj, s); + js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1); + resolving_funcs[i] = obj; + } + js_promise_resolve_function_free_resolved(ctx->rt, sr); + return ret; +} + +void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; + if (s) { + js_promise_resolve_function_free_resolved(rt, s->presolved); + JS_FreeValueRT(rt, s->promise); + js_free_rt(rt, s); + } +} + +void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; + if (s) { + JS_MarkValue(rt, s->promise, mark_func); + } +} + +JSValue js_promise_resolve_function_call(JSContext *ctx, + JSValueConst func_obj, + JSValueConst this_val, + int argc, JSValueConst *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSPromiseFunctionData *s; + JSValueConst resolution, args[3]; + JSValue then; + BOOL is_reject; + + s = p->u.promise_function_data; + if (!s || s->presolved->already_resolved) + return JS_UNDEFINED; + s->presolved->already_resolved = TRUE; + is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION; + if (argc > 0) + resolution = argv[0]; + else + resolution = JS_UNDEFINED; +#ifdef DUMP_PROMISE + printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject); + JS_DumpValue(ctx, resolution); + printf("\n"); +#endif + if (is_reject || !JS_IsObject(resolution)) { + goto done; + } else if (js_same_value(ctx, resolution, s->promise)) { + JS_ThrowTypeError(ctx, "promise self resolution"); + goto fail_reject; + } + then = JS_GetProperty(ctx, resolution, JS_ATOM_then); + if (JS_IsException(then)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + reject_promise(ctx, s->promise, error); + JS_FreeValue(ctx, error); + } else if (!JS_IsFunction(ctx, then)) { + JS_FreeValue(ctx, then); + done: + fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject); + } else { + args[0] = s->promise; + args[1] = resolution; + args[2] = then; + JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args); + JS_FreeValue(ctx, then); + } + return JS_UNDEFINED; +} + +void js_promise_finalizer(JSRuntime *rt, JSValue val) +{ + JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); + struct list_head *el, *el1; + int i; + + if (!s) + return; + for(i = 0; i < 2; i++) { + list_for_each_safe(el, el1, &s->promise_reactions[i]) { + JSPromiseReactionData *rd = + list_entry(el, JSPromiseReactionData, link); + promise_reaction_data_free(rt, rd); + } + } + JS_FreeValueRT(rt, s->promise_result); + js_free_rt(rt, s); +} + +void js_promise_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); + struct list_head *el; + int i; + + if (!s) + return; + for(i = 0; i < 2; i++) { + list_for_each(el, &s->promise_reactions[i]) { + JSPromiseReactionData *rd = + list_entry(el, JSPromiseReactionData, link); + JS_MarkValue(rt, rd->resolving_funcs[0], mark_func); + JS_MarkValue(rt, rd->resolving_funcs[1], mark_func); + JS_MarkValue(rt, rd->handler, mark_func); + } + } + JS_MarkValue(rt, s->promise_result, mark_func); +} + +JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValueConst executor; + JSValue obj; + JSPromiseData *s; + JSValue args[2], ret; + int i; + + executor = argv[0]; + if (check_function(ctx, executor)) + return JS_EXCEPTION; + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + s->promise_state = JS_PROMISE_PENDING; + s->is_handled = FALSE; + for(i = 0; i < 2; i++) + init_list_head(&s->promise_reactions[i]); + s->promise_result = JS_UNDEFINED; + JS_SetOpaque(obj, s); + if (js_create_resolving_functions(ctx, args, obj)) + goto fail; + ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args); + if (JS_IsException(ret)) { + JSValue ret2, error; + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret2)) + goto fail1; + JS_FreeValue(ctx, ret2); + } + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); + return obj; +fail1: + JS_FreeValue(ctx, args[0]); + JS_FreeValue(ctx, args[1]); +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_promise_executor(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + int i; + + for(i = 0; i < 2; i++) { + if (!JS_IsUndefined(func_data[i])) + return JS_ThrowTypeError(ctx, "resolving function already set"); + func_data[i] = JS_DupValue(ctx, argv[i]); + } + return JS_UNDEFINED; +} + +JSValue js_promise_executor_new(JSContext *ctx) +{ + JSValueConst func_data[2]; + + func_data[0] = JS_UNDEFINED; + func_data[1] = JS_UNDEFINED; + return JS_NewCFunctionData(ctx, js_promise_executor, 2, + 0, 2, func_data); +} + +JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValueConst ctor) +{ + JSValue executor, result_promise; + JSCFunctionDataRecord *s; + int i; + + executor = js_promise_executor_new(ctx); + if (JS_IsException(executor)) + return executor; + + if (JS_IsUndefined(ctor)) { + result_promise = js_promise_constructor(ctx, ctor, 1, + (JSValueConst *)&executor); + } else { + result_promise = JS_CallConstructor(ctx, ctor, 1, + (JSValueConst *)&executor); + } + if (JS_IsException(result_promise)) + goto fail; + s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA); + for(i = 0; i < 2; i++) { + if (check_function(ctx, s->data[i])) + goto fail; + } + for(i = 0; i < 2; i++) + resolving_funcs[i] = JS_DupValue(ctx, s->data[i]); + JS_FreeValue(ctx, executor); + return result_promise; +fail: + JS_FreeValue(ctx, executor); + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; +} + +JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) +{ + return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); +} + +JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue result_promise, resolving_funcs[2], ret; + BOOL is_reject = magic; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) { + JSValue ctor; + BOOL is_same; + ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + is_same = js_same_value(ctx, ctor, this_val); + JS_FreeValue(ctx, ctor); + if (is_same) + return JS_DupValue(ctx, argv[0]); + } + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (JS_IsException(ret)) { + JS_FreeValue(ctx, result_promise); + return ret; + } + JS_FreeValue(ctx, ret); + return result_promise; +} + +#if 0 +JSValue js_promise___newPromiseCapability(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue result_promise, resolving_funcs[2], obj; + JSValueConst ctor; + ctor = argv[0]; + if (!JS_IsObject(ctor)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + if (JS_IsException(result_promise)) + return result_promise; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; + } + JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); + return obj; +} +#endif + +__exception int remainingElementsCount_add(JSContext *ctx, + JSValueConst resolve_element_env, + int addend) +{ + JSValue val; + int remainingElementsCount; + + val = JS_GetPropertyUint32(ctx, resolve_element_env, 0); + if (JS_IsException(val)) + return -1; + if (JS_ToInt32Free(ctx, &remainingElementsCount, val)) + return -1; + remainingElementsCount += addend; + if (JS_SetPropertyUint32(ctx, resolve_element_env, 0, + JS_NewInt32(ctx, remainingElementsCount)) < 0) + return -1; + return (remainingElementsCount == 0); +} + +#define PROMISE_MAGIC_all 0 +#define PROMISE_MAGIC_allSettled 1 +#define PROMISE_MAGIC_any 2 + +JSValue js_promise_all_resolve_element(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, + JSValue *func_data) +{ + int resolve_type = magic & 3; + int is_reject = magic & 4; + BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]); + JSValueConst values = func_data[2]; + JSValueConst resolve = func_data[3]; + JSValueConst resolve_element_env = func_data[4]; + JSValue ret, obj; + int is_zero, index; + + if (JS_ToInt32(ctx, &index, func_data[1])) + return JS_EXCEPTION; + if (alreadyCalled) + return JS_UNDEFINED; + func_data[0] = JS_NewBool(ctx, TRUE); + + if (resolve_type == PROMISE_MAGIC_allSettled) { + JSValue str; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled"); + if (JS_IsException(str)) + goto fail1; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status, + str, + JS_PROP_C_W_E) < 0) + goto fail1; + if (JS_DefinePropertyValue(ctx, obj, + is_reject ? JS_ATOM_reason : JS_ATOM_value, + JS_DupValue(ctx, argv[0]), + JS_PROP_C_W_E) < 0) { + fail1: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } else { + obj = JS_DupValue(ctx, argv[0]); + } + if (JS_DefinePropertyValueUint32(ctx, values, index, + obj, JS_PROP_C_W_E) < 0) + return JS_EXCEPTION; + + is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); + if (is_zero < 0) + return JS_EXCEPTION; + if (is_zero) { + if (resolve_type == PROMISE_MAGIC_any) { + JSValue error; + error = js_aggregate_error_constructor(ctx, values); + if (JS_IsException(error)) + return JS_EXCEPTION; + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); + } else { + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); + } + if (JS_IsException(ret)) + return ret; + JS_FreeValue(ctx, ret); + } + return JS_UNDEFINED; +} + +/* magic = 0: Promise.all 1: Promise.allSettled */ +JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic) +{ + JSValue result_promise, resolving_funcs[2], item, next_promise, ret; + JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; + JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; + JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; + JSValueConst then_args[2], resolve_element_data[5]; + BOOL done; + int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; + iter = JS_GetIterator(ctx, argv[0], FALSE); + if (JS_IsException(iter)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, + (JSValueConst *)&error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret)) + goto fail; + JS_FreeValue(ctx, ret); + } else { + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail_reject; + values = JS_NewArray(ctx); + if (JS_IsException(values)) + goto fail_reject; + resolve_element_env = JS_NewArray(ctx); + if (JS_IsException(resolve_element_env)) + goto fail_reject; + /* remainingElementsCount field */ + if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0, + JS_NewInt32(ctx, 1), + JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) + goto fail_reject; + + index = 0; + for(;;) { + /* XXX: conformance: should close the iterator if error on 'done' + access, but not on 'value' access */ + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail_reject; + if (done) + break; + next_promise = JS_Call(ctx, promise_resolve, + this_val, 1, (JSValueConst *)&item); + JS_FreeValue(ctx, item); + if (JS_IsException(next_promise)) { + fail_reject1: + JS_IteratorClose(ctx, iter, TRUE); + goto fail_reject; + } + resolve_element_data[0] = JS_NewBool(ctx, FALSE); + resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); + resolve_element_data[2] = values; + resolve_element_data[3] = resolving_funcs[is_promise_any]; + resolve_element_data[4] = resolve_element_env; + resolve_element = + JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, + magic, 5, resolve_element_data); + if (JS_IsException(resolve_element)) { + JS_FreeValue(ctx, next_promise); + goto fail_reject1; + } + + if (magic == PROMISE_MAGIC_allSettled) { + reject_element = + JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, + magic | 4, 5, resolve_element_data); + if (JS_IsException(reject_element)) { + JS_FreeValue(ctx, next_promise); + goto fail_reject1; + } + } else if (magic == PROMISE_MAGIC_any) { + if (JS_DefinePropertyValueUint32(ctx, values, index, + JS_UNDEFINED, JS_PROP_C_W_E) < 0) + goto fail_reject1; + reject_element = resolve_element; + resolve_element = JS_DupValue(ctx, resolving_funcs[0]); + } else { + reject_element = JS_DupValue(ctx, resolving_funcs[1]); + } + + if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) { + JS_FreeValue(ctx, next_promise); + JS_FreeValue(ctx, resolve_element); + JS_FreeValue(ctx, reject_element); + goto fail_reject1; + } + + then_args[0] = resolve_element; + then_args[1] = reject_element; + ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args); + JS_FreeValue(ctx, resolve_element); + JS_FreeValue(ctx, reject_element); + if (check_exception_free(ctx, ret)) + goto fail_reject1; + index++; + } + + is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); + if (is_zero < 0) + goto fail_reject; + if (is_zero) { + if (magic == PROMISE_MAGIC_any) { + JSValue error; + error = js_aggregate_error_constructor(ctx, values); + if (JS_IsException(error)) + goto fail_reject; + JS_FreeValue(ctx, values); + values = error; + } + ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, + 1, (JSValueConst *)&values); + if (check_exception_free(ctx, ret)) + goto fail_reject; + } + } +done: + JS_FreeValue(ctx, promise_resolve); + JS_FreeValue(ctx, resolve_element_env); + JS_FreeValue(ctx, values); + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return result_promise; +fail: + JS_FreeValue(ctx, result_promise); + result_promise = JS_EXCEPTION; + goto done; +} + +JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue result_promise, resolving_funcs[2], item, next_promise, ret; + JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; + JSValue promise_resolve = JS_UNDEFINED; + BOOL done; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); + if (JS_IsException(result_promise)) + return result_promise; + promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); + if (JS_IsException(promise_resolve) || + check_function(ctx, promise_resolve)) + goto fail_reject; + iter = JS_GetIterator(ctx, argv[0], FALSE); + if (JS_IsException(iter)) { + JSValue error; + fail_reject: + error = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, + (JSValueConst *)&error); + JS_FreeValue(ctx, error); + if (JS_IsException(ret)) + goto fail; + JS_FreeValue(ctx, ret); + } else { + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail_reject; + + for(;;) { + /* XXX: conformance: should close the iterator if error on 'done' + access, but not on 'value' access */ + item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(item)) + goto fail_reject; + if (done) + break; + next_promise = JS_Call(ctx, promise_resolve, + this_val, 1, (JSValueConst *)&item); + JS_FreeValue(ctx, item); + if (JS_IsException(next_promise)) { + fail_reject1: + JS_IteratorClose(ctx, iter, TRUE); + goto fail_reject; + } + ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, + (JSValueConst *)resolving_funcs); + if (check_exception_free(ctx, ret)) + goto fail_reject1; + } + } +done: + JS_FreeValue(ctx, promise_resolve); + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return result_promise; +fail: + //JS_FreeValue(ctx, next_method); // why not??? + JS_FreeValue(ctx, result_promise); + result_promise = JS_EXCEPTION; + goto done; +} + +__exception int perform_promise_then(JSContext *ctx, + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + JSPromiseReactionData *rd_array[2], *rd; + int i, j; + + rd_array[0] = NULL; + rd_array[1] = NULL; + for(i = 0; i < 2; i++) { + JSValueConst handler; + rd = js_mallocz(ctx, sizeof(*rd)); + if (!rd) { + if (i == 1) + promise_reaction_data_free(ctx->rt, rd_array[0]); + return -1; + } + for(j = 0; j < 2; j++) + rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]); + handler = resolve_reject[i]; + if (!JS_IsFunction(ctx, handler)) + handler = JS_UNDEFINED; + rd->handler = JS_DupValue(ctx, handler); + rd_array[i] = rd; + } + + if (s->promise_state == JS_PROMISE_PENDING) { + for(i = 0; i < 2; i++) + list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); + } else { + JSValueConst args[5]; + if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { + JSRuntime *rt = ctx->rt; + if (rt->host_promise_rejection_tracker) { + rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, + TRUE, rt->host_promise_rejection_tracker_opaque); + } + } + i = s->promise_state - JS_PROMISE_FULFILLED; + rd = rd_array[i]; + args[0] = rd->resolving_funcs[0]; + args[1] = rd->resolving_funcs[1]; + args[2] = rd->handler; + args[3] = JS_NewBool(ctx, i); + args[4] = s->promise_result; + JS_EnqueueJob(ctx, promise_reaction_job, 5, args); + for(i = 0; i < 2; i++) + promise_reaction_data_free(ctx->rt, rd_array[i]); + } + s->is_handled = TRUE; + return 0; +} + +JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue ctor, result_promise, resolving_funcs[2]; + JSPromiseData *s; + int i, ret; + + s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); + if (!s) + return JS_EXCEPTION; + + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + JS_FreeValue(ctx, ctor); + if (JS_IsException(result_promise)) + return result_promise; + ret = perform_promise_then(ctx, this_val, argv, + (JSValueConst *)resolving_funcs); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (ret) { + JS_FreeValue(ctx, result_promise); + return JS_EXCEPTION; + } + return result_promise; +} + +JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst args[2]; + args[0] = JS_UNDEFINED; + args[1] = argv[0]; + return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); +} + +JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return JS_DupValue(ctx, func_data[0]); +} + +JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return JS_Throw(ctx, JS_DupValue(ctx, func_data[0])); +} + +JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + JSValueConst ctor = func_data[0]; + JSValueConst onFinally = func_data[1]; + JSValue res, promise, ret, then_func; + + res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); + if (JS_IsException(res)) + return res; + promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); + JS_FreeValue(ctx, res); + if (JS_IsException(promise)) + return promise; + if (magic == 0) { + then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, + 0, 1, argv); + } else { + then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0, + 0, 1, argv); + } + if (JS_IsException(then_func)) { + JS_FreeValue(ctx, promise); + return then_func; + } + ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func); + JS_FreeValue(ctx, then_func); + return ret; +} + +JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst onFinally = argv[0]; + JSValue ctor, ret; + JSValue then_funcs[2]; + JSValueConst func_data[2]; + int i; + + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + if (!JS_IsFunction(ctx, onFinally)) { + then_funcs[0] = JS_DupValue(ctx, onFinally); + then_funcs[1] = JS_DupValue(ctx, onFinally); + } else { + func_data[0] = ctor; + func_data[1] = onFinally; + for(i = 0; i < 2; i++) { + then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data); + if (JS_IsException(then_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, then_funcs[0]); + JS_FreeValue(ctx, ctor); + return JS_EXCEPTION; + } + } + } + JS_FreeValue(ctx, ctor); + ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs); + JS_FreeValue(ctx, then_funcs[0]); + JS_FreeValue(ctx, then_funcs[1]); + return ret; +} + + +void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSAsyncFromSyncIteratorData *s = + JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (s) { + JS_FreeValueRT(rt, s->sync_iter); + JS_FreeValueRT(rt, s->next_method); + js_free_rt(rt, s); + } +} + +void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSAsyncFromSyncIteratorData *s = + JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (s) { + JS_MarkValue(rt, s->sync_iter, mark_func); + JS_MarkValue(rt, s->next_method, mark_func); + } +} + + +JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + int magic, JSValue *func_data) +{ + return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), + JS_ToBool(ctx, func_data[0])); +} + +JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, + BOOL done) +{ + JSValueConst func_data[1]; + + func_data[0] = (JSValueConst)JS_NewBool(ctx, done); + return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, + 1, 0, 1, func_data); +} + +/* AsyncIteratorPrototype */ + +const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { + JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ), +}; + +JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, + int magic) +{ + JSValue promise, resolving_funcs[2], value, err, method; + JSAsyncFromSyncIteratorData *s; + int done; + int is_reject; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); + if (!s) { + JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator"); + goto reject; + } + + if (magic == GEN_MAGIC_NEXT) { + method = JS_DupValue(ctx, s->next_method); + } else { + method = JS_GetProperty(ctx, s->sync_iter, + magic == GEN_MAGIC_RETURN ? JS_ATOM_return : + JS_ATOM_throw); + if (JS_IsException(method)) + goto reject; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + if (magic == GEN_MAGIC_RETURN) { + err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE); + is_reject = 0; + } else { + err = JS_DupValue(ctx, argv[0]); + is_reject = 1; + } + goto done_resolve; + } + } + value = JS_IteratorNext2(ctx, s->sync_iter, method, + argc >= 1 ? 1 : 0, argv, &done); + JS_FreeValue(ctx, method); + if (JS_IsException(value)) + goto reject; + if (done == 2) { + JSValue obj = value; + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + JS_FreeValue(ctx, obj); + if (JS_IsException(value)) + goto reject; + } + + if (JS_IsException(value)) { + JSValue res2; + reject: + err = JS_GetException(ctx); + is_reject = 1; + done_resolve: + res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + { + JSValue value_wrapper_promise, resolve_reject[2]; + int res; + + value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&value, 0); + if (JS_IsException(value_wrapper_promise)) { + JS_FreeValue(ctx, value); + goto reject; + } + + resolve_reject[0] = + js_async_from_sync_iterator_unwrap_func_create(ctx, done); + if (JS_IsException(resolve_reject[0])) { + JS_FreeValue(ctx, value_wrapper_promise); + goto fail; + } + JS_FreeValue(ctx, value); + resolve_reject[1] = JS_UNDEFINED; + + res = perform_promise_then(ctx, value_wrapper_promise, + (JSValueConst *)resolve_reject, + (JSValueConst *)resolving_funcs); + JS_FreeValue(ctx, resolve_reject[0]); + JS_FreeValue(ctx, value_wrapper_promise); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + if (res) { + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; + } + } + return promise; +fail: + JS_FreeValue(ctx, value); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + + +const JSCFunctionListEntry js_promise_funcs[] = { + JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ), + JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ), + JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), + JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), + JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), + JS_CFUNC_DEF("race", 1, js_promise_race ), + //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), +}; + +const JSCFunctionListEntry js_promise_proto_funcs[] = { + JS_CFUNC_DEF("then", 2, js_promise_then ), + JS_CFUNC_DEF("catch", 1, js_promise_catch ), + JS_CFUNC_DEF("finally", 1, js_promise_finally ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ), +}; + +/* AsyncFunction */ +const JSCFunctionListEntry js_async_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), +}; + + +const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ), + JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ), + JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ), +}; + +/* AsyncGeneratorFunction */ + +const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = { + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ), +}; + +/* AsyncGenerator prototype */ + +const JSCFunctionListEntry js_async_generator_proto_funcs[] = { + JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ), + JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ), + JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ), +}; + +JSClassShortDef const js_async_class_def[] = { + { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */ + { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */ + { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */ + { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */ + { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */ + { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */ + { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */ +}; + + +void JS_AddIntrinsicPromise(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + + if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { + init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, + countof(js_async_class_def)); + rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call; + rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call; + rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call; + rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call; + } + + /* Promise */ + ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE], + js_promise_proto_funcs, + countof(js_promise_proto_funcs)); + obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1, + JS_CFUNC_constructor, 0); + ctx->promise_ctor = JS_DupValue(ctx, obj1); + JS_SetPropertyFunctionList(ctx, obj1, + js_promise_funcs, + countof(js_promise_funcs)); + JS_NewGlobalCConstructor2(ctx, obj1, "Promise", + ctx->class_proto[JS_CLASS_PROMISE]); + + /* AsyncFunction */ + ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); + obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + "AsyncFunction", 1, + JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, + ctx->function_ctor); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], + js_async_function_proto_funcs, + countof(js_async_function_proto_funcs)); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); + + /* AsyncIteratorPrototype */ + ctx->async_iterator_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto, + js_async_iterator_proto_funcs, + countof(js_async_iterator_proto_funcs)); + + /* AsyncFromSyncIteratorPrototype */ + ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] = + JS_NewObjectProto(ctx, ctx->async_iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR], + js_async_from_sync_iterator_proto_funcs, + countof(js_async_from_sync_iterator_proto_funcs)); + + /* AsyncGeneratorPrototype */ + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] = + JS_NewObjectProto(ctx, ctx->async_iterator_proto); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], + js_async_generator_proto_funcs, + countof(js_async_generator_proto_funcs)); + + /* AsyncGeneratorFunction */ + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] = + JS_NewObjectProto(ctx, ctx->function_proto); + obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, + "AsyncGeneratorFunction", 1, + JS_CFUNC_constructor_or_func_magic, + JS_FUNC_ASYNC_GENERATOR, + ctx->function_ctor); + JS_SetPropertyFunctionList(ctx, + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + js_async_generator_function_proto_funcs, + countof(js_async_generator_function_proto_funcs)); + JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], + JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); + JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], + 0, JS_PROP_CONFIGURABLE); + JS_FreeValue(ctx, obj1); } \ No newline at end of file diff --git a/src/core/builtins/js-promise.h b/src/core/builtins/js-promise.h index 87ccd2729..8fac2f476 100644 --- a/src/core/builtins/js-promise.h +++ b/src/core/builtins/js-promise.h @@ -1,40 +1,40 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_PROMISE_H -#define QUICKJS_JS_PROMISE_H - -#include "quickjs/quickjs.h" -#include "../types.h" - -JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); - -__exception int perform_promise_then(JSContext *ctx, - JSValueConst promise, - JSValueConst *resolve_reject, - JSValueConst *cap_resolving_funcs); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_PROMISE_H +#define QUICKJS_JS_PROMISE_H + +#include "quickjs/quickjs.h" +#include "../types.h" + +JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic); + +__exception int perform_promise_then(JSContext *ctx, + JSValueConst promise, + JSValueConst *resolve_reject, + JSValueConst *cap_resolving_funcs); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-proxy.c b/src/core/builtins/js-proxy.c index f027ffe0b..bd6966041 100644 --- a/src/core/builtins/js-proxy.c +++ b/src/core/builtins/js-proxy.c @@ -1,989 +1,989 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-proxy.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../runtime.h" -#include "../types.h" -#include "js-array.h" -#include "js-function.h" -#include "js-object.h" -#include "js-operator.h" - -/* Proxy */ - -void js_proxy_finalizer(JSRuntime *rt, JSValue val) -{ - JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); - if (s) { - JS_FreeValueRT(rt, s->target); - JS_FreeValueRT(rt, s->handler); - js_free_rt(rt, s); - } -} - -void js_proxy_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); - if (s) { - JS_MarkValue(rt, s->target, mark_func); - JS_MarkValue(rt, s->handler, mark_func); - } -} - -JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx) -{ - return JS_ThrowTypeError(ctx, "revoked proxy"); -} - -JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, - JSValueConst obj, JSAtom name) -{ - JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); - JSValue method; - - /* safer to test recursion in all proxy methods */ - if (js_check_stack_overflow(ctx->rt, 0)) { - JS_ThrowStackOverflow(ctx); - return NULL; - } - - /* 's' should never be NULL */ - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - return NULL; - } - method = JS_GetProperty(ctx, s->handler, name); - if (JS_IsException(method)) - return NULL; - if (JS_IsNull(method)) - method = JS_UNDEFINED; - *pmethod = method; - return s; -} - -JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s; - JSValue method, ret, proto1; - int res; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf); - if (!s) - return JS_EXCEPTION; - if (JS_IsUndefined(method)) - return JS_GetPrototype(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); - if (JS_IsException(ret)) - return ret; - if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && - JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { - goto fail; - } - res = JS_IsExtensible(ctx, s->target); - if (res < 0) { - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - if (!res) { - /* check invariant */ - proto1 = JS_GetPrototype(ctx, s->target); - if (JS_IsException(proto1)) { - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) { - JS_FreeValue(ctx, proto1); - fail: - JS_FreeValue(ctx, ret); - return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); - } - JS_FreeValue(ctx, proto1); - } - return ret; -} - -int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val, BOOL throw_flag) -{ - JSProxyData *s; - JSValue method, ret, proto1; - JSValueConst args[2]; - BOOL res; - int res2; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf); - if (!s) - return -1; - if (JS_IsUndefined(method)) - return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag); - args[0] = s->target; - args[1] = proto_val; - ret = JS_CallFree(ctx, method, s->handler, 2, args); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); - if (!res) { - if (throw_flag) { - JS_ThrowTypeError(ctx, "proxy: bad prototype"); - return -1; - } else { - return FALSE; - } - } - res2 = JS_IsExtensible(ctx, s->target); - if (res2 < 0) - return -1; - if (!res2) { - proto1 = JS_GetPrototype(ctx, s->target); - if (JS_IsException(proto1)) - return -1; - if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) { - JS_FreeValue(ctx, proto1); - JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); - return -1; - } - JS_FreeValue(ctx, proto1); - } - return TRUE; -} - -int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s; - JSValue method, ret; - BOOL res; - int res2; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible); - if (!s) - return -1; - if (JS_IsUndefined(method)) - return JS_IsExtensible(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); - res2 = JS_IsExtensible(ctx, s->target); - if (res2 < 0) - return res2; - if (res != res2) { - JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible"); - return -1; - } - return res; -} - -int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s; - JSValue method, ret; - BOOL res; - int res2; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions); - if (!s) - return -1; - if (JS_IsUndefined(method)) - return JS_PreventExtensions(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); - if (res) { - res2 = JS_IsExtensible(ctx, s->target); - if (res2 < 0) - return res2; - if (res2) { - JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions"); - return -1; - } - } - return res; -} - -int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom) -{ - JSProxyData *s; - JSValue method, ret1, atom_val; - int ret, res; - JSObject *p; - JSValueConst args[2]; - BOOL res2; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_has); - if (!s) - return -1; - if (JS_IsUndefined(method)) - return JS_HasProperty(ctx, s->target, atom); - atom_val = JS_AtomToValue(ctx, atom); - if (JS_IsException(atom_val)) { - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = atom_val; - ret1 = JS_CallFree(ctx, method, s->handler, 2, args); - JS_FreeValue(ctx, atom_val); - if (JS_IsException(ret1)) - return -1; - ret = JS_ToBoolFree(ctx, ret1); - if (!ret) { - JSPropertyDescriptor desc; - p = JS_VALUE_GET_OBJ(s->target); - res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); - if (res < 0) - return -1; - if (res) { - res2 = !(desc.flags & JS_PROP_CONFIGURABLE); - js_free_desc(ctx, &desc); - if (res2 || !p->extensible) { - JS_ThrowTypeError(ctx, "proxy: inconsistent has"); - return -1; - } - } - } - return ret; -} - -JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst receiver) -{ - JSProxyData *s; - JSValue method, ret, atom_val; - int res; - JSValueConst args[3]; - JSPropertyDescriptor desc; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_get); - if (!s) - return JS_EXCEPTION; - /* Note: recursion is possible thru the prototype of s->target */ - if (JS_IsUndefined(method)) - return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE); - atom_val = JS_AtomToValue(ctx, atom); - if (JS_IsException(atom_val)) { - JS_FreeValue(ctx, method); - return JS_EXCEPTION; - } - args[0] = s->target; - args[1] = atom_val; - args[2] = receiver; - ret = JS_CallFree(ctx, method, s->handler, 3, args); - JS_FreeValue(ctx, atom_val); - if (JS_IsException(ret)) - return JS_EXCEPTION; - res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); - if (res < 0) - return JS_EXCEPTION; - if (res) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { - if (!js_same_value(ctx, desc.value, ret)) { - goto fail; - } - } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) { - if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) { - fail: - js_free_desc(ctx, &desc); - JS_FreeValue(ctx, ret); - return JS_ThrowTypeError(ctx, "proxy: inconsistent get"); - } - } - js_free_desc(ctx, &desc); - } - return ret; -} - -int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst value, JSValueConst receiver, int flags) -{ - JSProxyData *s; - JSValue method, ret1, atom_val; - int ret, res; - JSValueConst args[4]; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_set); - if (!s) - return -1; - if (JS_IsUndefined(method)) { - return JS_SetPropertyGeneric(ctx, s->target, atom, - JS_DupValue(ctx, value), receiver, - flags); - } - atom_val = JS_AtomToValue(ctx, atom); - if (JS_IsException(atom_val)) { - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = atom_val; - args[2] = value; - args[3] = receiver; - ret1 = JS_CallFree(ctx, method, s->handler, 4, args); - JS_FreeValue(ctx, atom_val); - if (JS_IsException(ret1)) - return -1; - ret = JS_ToBoolFree(ctx, ret1); - if (ret) { - JSPropertyDescriptor desc; - res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); - if (res < 0) - return -1; - if (res) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { - if (!js_same_value(ctx, desc.value, value)) { - goto fail; - } - } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) { - fail: - js_free_desc(ctx, &desc); - JS_ThrowTypeError(ctx, "proxy: inconsistent set"); - return -1; - } - js_free_desc(ctx, &desc); - } - } else { - if ((flags & JS_PROP_THROW) || - ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - JS_ThrowTypeError(ctx, "proxy: cannot set property"); - return -1; - } - } - return ret; -} - -JSValue js_create_desc(JSContext *ctx, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags) -{ - JSValue ret; - ret = JS_NewObject(ctx); - if (JS_IsException(ret)) - return ret; - if (flags & JS_PROP_HAS_GET) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter), - JS_PROP_C_W_E); - } - if (flags & JS_PROP_HAS_SET) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter), - JS_PROP_C_W_E); - } - if (flags & JS_PROP_HAS_VALUE) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val), - JS_PROP_C_W_E); - } - if (flags & JS_PROP_HAS_WRITABLE) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, - JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0), - JS_PROP_C_W_E); - } - if (flags & JS_PROP_HAS_ENUMERABLE) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, - JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0), - JS_PROP_C_W_E); - } - if (flags & JS_PROP_HAS_CONFIGURABLE) { - JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, - JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0), - JS_PROP_C_W_E); - } - return ret; -} - -int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, - JSValueConst obj, JSAtom prop) -{ - JSProxyData *s; - JSValue method, trap_result_obj, prop_val; - int res, target_desc_ret, ret; - JSObject *p; - JSValueConst args[2]; - JSPropertyDescriptor result_desc, target_desc; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor); - if (!s) - return -1; - p = JS_VALUE_GET_OBJ(s->target); - if (JS_IsUndefined(method)) { - return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop); - } - prop_val = JS_AtomToValue(ctx, prop); - if (JS_IsException(prop_val)) { - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = prop_val; - trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args); - JS_FreeValue(ctx, prop_val); - if (JS_IsException(trap_result_obj)) - return -1; - if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) { - JS_FreeValue(ctx, trap_result_obj); - goto fail; - } - target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop); - if (target_desc_ret < 0) { - JS_FreeValue(ctx, trap_result_obj); - return -1; - } - if (target_desc_ret) - js_free_desc(ctx, &target_desc); - if (JS_IsUndefined(trap_result_obj)) { - if (target_desc_ret) { - if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible) - goto fail; - } - ret = FALSE; - } else { - int flags1, extensible_target; - extensible_target = JS_IsExtensible(ctx, s->target); - if (extensible_target < 0) { - JS_FreeValue(ctx, trap_result_obj); - return -1; - } - res = js_obj_to_desc(ctx, &result_desc, trap_result_obj); - JS_FreeValue(ctx, trap_result_obj); - if (res < 0) - return -1; - - if (target_desc_ret) { - /* convert result_desc.flags to defineProperty flags */ - flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE; - if (result_desc.flags & JS_PROP_GETSET) - flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET; - else - flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE; - /* XXX: not complete check: need to compare value & - getter/setter as in defineproperty */ - if (!check_define_prop_flags(target_desc.flags, flags1)) - goto fail1; - } else { - if (!extensible_target) - goto fail1; - } - if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) { - if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE)) - goto fail1; - if ((result_desc.flags & - (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 && - target_desc_ret && - (target_desc.flags & JS_PROP_WRITABLE) != 0) { - /* proxy-missing-checks */ - fail1: - js_free_desc(ctx, &result_desc); - fail: - JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor"); - return -1; - } - } - ret = TRUE; - if (pdesc) { - *pdesc = result_desc; - } else { - js_free_desc(ctx, &result_desc); - } - } - return ret; -} - -int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags) -{ - JSProxyData *s; - JSValue method, ret1, prop_val, desc_val; - int res, ret; - JSObject *p; - JSValueConst args[3]; - JSPropertyDescriptor desc; - BOOL setting_not_configurable; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty); - if (!s) - return -1; - if (JS_IsUndefined(method)) { - return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags); - } - prop_val = JS_AtomToValue(ctx, prop); - if (JS_IsException(prop_val)) { - JS_FreeValue(ctx, method); - return -1; - } - desc_val = js_create_desc(ctx, val, getter, setter, flags); - if (JS_IsException(desc_val)) { - JS_FreeValue(ctx, prop_val); - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = prop_val; - args[2] = desc_val; - ret1 = JS_CallFree(ctx, method, s->handler, 3, args); - JS_FreeValue(ctx, prop_val); - JS_FreeValue(ctx, desc_val); - if (JS_IsException(ret1)) - return -1; - ret = JS_ToBoolFree(ctx, ret1); - if (!ret) { - if (flags & JS_PROP_THROW) { - JS_ThrowTypeError(ctx, "proxy: defineProperty exception"); - return -1; - } else { - return 0; - } - } - p = JS_VALUE_GET_OBJ(s->target); - res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (res < 0) - return -1; - setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE | - JS_PROP_CONFIGURABLE)) == - JS_PROP_HAS_CONFIGURABLE); - if (!res) { - if (!p->extensible || setting_not_configurable) - goto fail; - } else { - if (!check_define_prop_flags(desc.flags, flags) || - ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) { - goto fail1; - } - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == - JS_PROP_GETSET) { - if ((flags & JS_PROP_HAS_GET) && - !js_same_value(ctx, getter, desc.getter)) { - goto fail1; - } - if ((flags & JS_PROP_HAS_SET) && - !js_same_value(ctx, setter, desc.setter)) { - goto fail1; - } - } - } else if (flags & JS_PROP_HAS_VALUE) { - if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == - JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) { - /* missing-proxy-check feature */ - goto fail1; - } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && - !js_same_value(ctx, val, desc.value)) { - goto fail1; - } - } - if (flags & JS_PROP_HAS_WRITABLE) { - if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | - JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) { - /* proxy-missing-checks */ - fail1: - js_free_desc(ctx, &desc); - fail: - JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); - return -1; - } - } - js_free_desc(ctx, &desc); - } - return 1; -} - -int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, - JSAtom atom) -{ - JSProxyData *s; - JSValue method, ret, atom_val; - int res, res2, is_extensible; - JSValueConst args[2]; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty); - if (!s) - return -1; - if (JS_IsUndefined(method)) { - return JS_DeleteProperty(ctx, s->target, atom, 0); - } - atom_val = JS_AtomToValue(ctx, atom);; - if (JS_IsException(atom_val)) { - JS_FreeValue(ctx, method); - return -1; - } - args[0] = s->target; - args[1] = atom_val; - ret = JS_CallFree(ctx, method, s->handler, 2, args); - JS_FreeValue(ctx, atom_val); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); - if (res) { - JSPropertyDescriptor desc; - res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); - if (res2 < 0) - return -1; - if (res2) { - if (!(desc.flags & JS_PROP_CONFIGURABLE)) - goto fail; - is_extensible = JS_IsExtensible(ctx, s->target); - if (is_extensible < 0) - goto fail1; - if (!is_extensible) { - /* proxy-missing-checks */ - fail: - JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty"); - fail1: - js_free_desc(ctx, &desc); - return -1; - } - js_free_desc(ctx, &desc); - } - } - return res; -} - -/* return the index of the property or -1 if not found */ -int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom) -{ - int i; - for(i = 0; i < n; i++) { - if (tab[i].atom == atom) - return i; - } - return -1; -} - -int js_proxy_get_own_property_names(JSContext *ctx, - JSPropertyEnum **ptab, - uint32_t *plen, - JSValueConst obj) -{ - JSProxyData *s; - JSValue method, prop_array, val; - uint32_t len, i, len2; - JSPropertyEnum *tab, *tab2; - JSAtom atom; - JSPropertyDescriptor desc; - int res, is_extensible, idx; - - s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys); - if (!s) - return -1; - if (JS_IsUndefined(method)) { - return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, - JS_VALUE_GET_OBJ(s->target), - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); - } - prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); - if (JS_IsException(prop_array)) - return -1; - tab = NULL; - len = 0; - tab2 = NULL; - len2 = 0; - if (js_get_length32(ctx, &len, prop_array)) - goto fail; - if (len > 0) { - tab = js_mallocz(ctx, sizeof(tab[0]) * len); - if (!tab) - goto fail; - } - for(i = 0; i < len; i++) { - val = JS_GetPropertyUint32(ctx, prop_array, i); - if (JS_IsException(val)) - goto fail; - if (!JS_IsString(val) && !JS_IsSymbol(val)) { - JS_FreeValue(ctx, val); - JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols"); - goto fail; - } - atom = JS_ValueToAtom(ctx, val); - JS_FreeValue(ctx, val); - if (atom == JS_ATOM_NULL) - goto fail; - tab[i].atom = atom; - tab[i].is_enumerable = FALSE; /* XXX: redundant? */ - } - - /* check duplicate properties (XXX: inefficient, could store the - * properties an a temporary object to use the hash) */ - for(i = 1; i < len; i++) { - if (find_prop_key(tab, i, tab[i].atom) >= 0) { - JS_ThrowTypeError(ctx, "proxy: duplicate property"); - goto fail; - } - } - - is_extensible = JS_IsExtensible(ctx, s->target); - if (is_extensible < 0) - goto fail; - - /* check if there are non configurable properties */ - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - goto fail; - } - if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target), - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) - goto fail; - for(i = 0; i < len2; i++) { - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - goto fail; - } - res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), - tab2[i].atom); - if (res < 0) - goto fail; - if (res) { /* safety, property should be found */ - js_free_desc(ctx, &desc); - if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) { - idx = find_prop_key(tab, len, tab2[i].atom); - if (idx < 0) { - JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys"); - goto fail; - } - /* mark the property as found */ - if (!is_extensible) - tab[idx].is_enumerable = TRUE; - } - } - } - if (!is_extensible) { - /* check that all property in 'tab' were checked */ - for(i = 0; i < len; i++) { - if (!tab[i].is_enumerable) { - JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy"); - goto fail; - } - } - } - - js_free_prop_enum(ctx, tab2, len2); - JS_FreeValue(ctx, prop_array); - *ptab = tab; - *plen = len; - return 0; -fail: - js_free_prop_enum(ctx, tab2, len2); - js_free_prop_enum(ctx, tab, len); - JS_FreeValue(ctx, prop_array); - return -1; -} - -JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSProxyData *s; - JSValue method, arg_array, ret; - JSValueConst args[3]; - - s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct); - if (!s) - return JS_EXCEPTION; - if (!JS_IsConstructor(ctx, s->target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (JS_IsUndefined(method)) - return JS_CallConstructor2(ctx, s->target, new_target, argc, argv); - arg_array = js_create_array(ctx, argc, argv); - if (JS_IsException(arg_array)) { - ret = JS_EXCEPTION; - goto fail; - } - args[0] = s->target; - args[1] = arg_array; - args[2] = new_target; - ret = JS_Call(ctx, method, s->handler, 3, args); - if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { - JS_FreeValue(ctx, ret); - ret = JS_ThrowTypeErrorNotAnObject(ctx); - } -fail: - JS_FreeValue(ctx, method); - JS_FreeValue(ctx, arg_array); - return ret; -} - -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; -} - -int js_proxy_isArray(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); - if (!s) - return FALSE; - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - return -1; - } - return JS_IsArray(ctx, s->target); -} - -static const JSClassExoticMethods js_proxy_exotic_methods = { - .get_own_property = js_proxy_get_own_property, - .define_own_property = js_proxy_define_own_property, - .delete_property = js_proxy_delete_property, - .get_own_property_names = js_proxy_get_own_property_names, - .has_property = js_proxy_has, - .get_property = js_proxy_get, - .set_property = js_proxy_set, -}; - -JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst target, handler; - JSValue obj; - JSProxyData *s; - - target = argv[0]; - handler = argv[1]; - if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT || - JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - - obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY); - if (JS_IsException(obj)) - return obj; - s = js_malloc(ctx, sizeof(JSProxyData)); - if (!s) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - s->target = JS_DupValue(ctx, target); - s->handler = JS_DupValue(ctx, handler); - s->is_func = JS_IsFunction(ctx, target); - s->is_revoked = FALSE; - JS_SetOpaque(obj, s); - JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target)); - return obj; -} - -JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic, - JSValue *func_data) -{ - JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY); - if (s) { - /* We do not free the handler and target in case they are - referenced as constants in the C call stack */ - s->is_revoked = TRUE; - JS_FreeValue(ctx, func_data[0]); - func_data[0] = JS_NULL; - } - return JS_UNDEFINED; -} - -JSValue js_proxy_revoke_constructor(JSContext *ctx, - JSValueConst proxy_obj) -{ - return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj); -} - -JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj; - - proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv); - if (JS_IsException(proxy_obj)) - goto fail; - revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj); - if (JS_IsException(revoke_obj)) - goto fail; - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - goto fail; - // XXX: exceptions? - JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E); - return obj; -fail: - JS_FreeValue(ctx, proxy_obj); - JS_FreeValue(ctx, revoke_obj); - return JS_EXCEPTION; -} - -const JSCFunctionListEntry js_proxy_funcs[] = { - JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ), -}; - -const JSClassShortDef js_proxy_class_def[] = { - { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */ -}; - -void JS_AddIntrinsicProxy(JSContext *ctx) -{ - JSRuntime *rt = ctx->rt; - JSValue obj1; - - if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) { - init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY, - countof(js_proxy_class_def)); - rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods; - rt->class_array[JS_CLASS_PROXY].call = js_proxy_call; - } - - obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2, - JS_CFUNC_constructor, 0); - JS_SetConstructorBit(ctx, obj1, TRUE); - JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs, - countof(js_proxy_funcs)); - JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy", - obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-proxy.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../runtime.h" +#include "../types.h" +#include "js-array.h" +#include "js-function.h" +#include "js-object.h" +#include "js-operator.h" + +/* Proxy */ + +void js_proxy_finalizer(JSRuntime *rt, JSValue val) +{ + JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); + if (s) { + JS_FreeValueRT(rt, s->target); + JS_FreeValueRT(rt, s->handler); + js_free_rt(rt, s); + } +} + +void js_proxy_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); + if (s) { + JS_MarkValue(rt, s->target, mark_func); + JS_MarkValue(rt, s->handler, mark_func); + } +} + +JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "revoked proxy"); +} + +JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, + JSValueConst obj, JSAtom name) +{ + JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); + JSValue method; + + /* safer to test recursion in all proxy methods */ + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return NULL; + } + + /* 's' should never be NULL */ + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return NULL; + } + method = JS_GetProperty(ctx, s->handler, name); + if (JS_IsException(method)) + return NULL; + if (JS_IsNull(method)) + method = JS_UNDEFINED; + *pmethod = method; + return s; +} + +JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) +{ + JSProxyData *s; + JSValue method, ret, proto1; + int res; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf); + if (!s) + return JS_EXCEPTION; + if (JS_IsUndefined(method)) + return JS_GetPrototype(ctx, s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); + if (JS_IsException(ret)) + return ret; + if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && + JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + goto fail; + } + res = JS_IsExtensible(ctx, s->target); + if (res < 0) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (!res) { + /* check invariant */ + proto1 = JS_GetPrototype(ctx, s->target); + if (JS_IsException(proto1)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) { + JS_FreeValue(ctx, proto1); + fail: + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); + } + JS_FreeValue(ctx, proto1); + } + return ret; +} + +int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, BOOL throw_flag) +{ + JSProxyData *s; + JSValue method, ret, proto1; + JSValueConst args[2]; + BOOL res; + int res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag); + args[0] = s->target; + args[1] = proto_val; + ret = JS_CallFree(ctx, method, s->handler, 2, args); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + if (!res) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "proxy: bad prototype"); + return -1; + } else { + return FALSE; + } + } + res2 = JS_IsExtensible(ctx, s->target); + if (res2 < 0) + return -1; + if (!res2) { + proto1 = JS_GetPrototype(ctx, s->target); + if (JS_IsException(proto1)) + return -1; + if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) { + JS_FreeValue(ctx, proto1); + JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); + return -1; + } + JS_FreeValue(ctx, proto1); + } + return TRUE; +} + +int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) +{ + JSProxyData *s; + JSValue method, ret; + BOOL res; + int res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_IsExtensible(ctx, s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + res2 = JS_IsExtensible(ctx, s->target); + if (res2 < 0) + return res2; + if (res != res2) { + JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible"); + return -1; + } + return res; +} + +int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) +{ + JSProxyData *s; + JSValue method, ret; + BOOL res; + int res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_PreventExtensions(ctx, s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + if (res) { + res2 = JS_IsExtensible(ctx, s->target); + if (res2 < 0) + return res2; + if (res2) { + JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions"); + return -1; + } + } + return res; +} + +int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom) +{ + JSProxyData *s; + JSValue method, ret1, atom_val; + int ret, res; + JSObject *p; + JSValueConst args[2]; + BOOL res2; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_has); + if (!s) + return -1; + if (JS_IsUndefined(method)) + return JS_HasProperty(ctx, s->target, atom); + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + ret1 = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (!ret) { + JSPropertyDescriptor desc; + p = JS_VALUE_GET_OBJ(s->target); + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) + return -1; + if (res) { + res2 = !(desc.flags & JS_PROP_CONFIGURABLE); + js_free_desc(ctx, &desc); + if (res2 || !p->extensible) { + JS_ThrowTypeError(ctx, "proxy: inconsistent has"); + return -1; + } + } + } + return ret; +} + +JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst receiver) +{ + JSProxyData *s; + JSValue method, ret, atom_val; + int res; + JSValueConst args[3]; + JSPropertyDescriptor desc; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_get); + if (!s) + return JS_EXCEPTION; + /* Note: recursion is possible thru the prototype of s->target */ + if (JS_IsUndefined(method)) + return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE); + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return JS_EXCEPTION; + } + args[0] = s->target; + args[1] = atom_val; + args[2] = receiver; + ret = JS_CallFree(ctx, method, s->handler, 3, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret)) + return JS_EXCEPTION; + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res < 0) + return JS_EXCEPTION; + if (res) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { + if (!js_same_value(ctx, desc.value, ret)) { + goto fail; + } + } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) { + if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) { + fail: + js_free_desc(ctx, &desc); + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "proxy: inconsistent get"); + } + } + js_free_desc(ctx, &desc); + } + return ret; +} + +int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst value, JSValueConst receiver, int flags) +{ + JSProxyData *s; + JSValue method, ret1, atom_val; + int ret, res; + JSValueConst args[4]; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_set); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_SetPropertyGeneric(ctx, s->target, atom, + JS_DupValue(ctx, value), receiver, + flags); + } + atom_val = JS_AtomToValue(ctx, atom); + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + args[2] = value; + args[3] = receiver; + ret1 = JS_CallFree(ctx, method, s->handler, 4, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (ret) { + JSPropertyDescriptor desc; + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res < 0) + return -1; + if (res) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { + if (!js_same_value(ctx, desc.value, value)) { + goto fail; + } + } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) { + fail: + js_free_desc(ctx, &desc); + JS_ThrowTypeError(ctx, "proxy: inconsistent set"); + return -1; + } + js_free_desc(ctx, &desc); + } + } else { + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "proxy: cannot set property"); + return -1; + } + } + return ret; +} + +JSValue js_create_desc(JSContext *ctx, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags) +{ + JSValue ret; + ret = JS_NewObject(ctx); + if (JS_IsException(ret)) + return ret; + if (flags & JS_PROP_HAS_GET) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_SET) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_VALUE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_WRITABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, + JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_ENUMERABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, + JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0), + JS_PROP_C_W_E); + } + if (flags & JS_PROP_HAS_CONFIGURABLE) { + JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, + JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0), + JS_PROP_C_W_E); + } + return ret; +} + +int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, + JSValueConst obj, JSAtom prop) +{ + JSProxyData *s; + JSValue method, trap_result_obj, prop_val; + int res, target_desc_ret, ret; + JSObject *p; + JSValueConst args[2]; + JSPropertyDescriptor result_desc, target_desc; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor); + if (!s) + return -1; + p = JS_VALUE_GET_OBJ(s->target); + if (JS_IsUndefined(method)) { + return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop); + } + prop_val = JS_AtomToValue(ctx, prop); + if (JS_IsException(prop_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = prop_val; + trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, prop_val); + if (JS_IsException(trap_result_obj)) + return -1; + if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) { + JS_FreeValue(ctx, trap_result_obj); + goto fail; + } + target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop); + if (target_desc_ret < 0) { + JS_FreeValue(ctx, trap_result_obj); + return -1; + } + if (target_desc_ret) + js_free_desc(ctx, &target_desc); + if (JS_IsUndefined(trap_result_obj)) { + if (target_desc_ret) { + if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible) + goto fail; + } + ret = FALSE; + } else { + int flags1, extensible_target; + extensible_target = JS_IsExtensible(ctx, s->target); + if (extensible_target < 0) { + JS_FreeValue(ctx, trap_result_obj); + return -1; + } + res = js_obj_to_desc(ctx, &result_desc, trap_result_obj); + JS_FreeValue(ctx, trap_result_obj); + if (res < 0) + return -1; + + if (target_desc_ret) { + /* convert result_desc.flags to defineProperty flags */ + flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE; + if (result_desc.flags & JS_PROP_GETSET) + flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET; + else + flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE; + /* XXX: not complete check: need to compare value & + getter/setter as in defineproperty */ + if (!check_define_prop_flags(target_desc.flags, flags1)) + goto fail1; + } else { + if (!extensible_target) + goto fail1; + } + if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) { + if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE)) + goto fail1; + if ((result_desc.flags & + (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 && + target_desc_ret && + (target_desc.flags & JS_PROP_WRITABLE) != 0) { + /* proxy-missing-checks */ + fail1: + js_free_desc(ctx, &result_desc); + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor"); + return -1; + } + } + ret = TRUE; + if (pdesc) { + *pdesc = result_desc; + } else { + js_free_desc(ctx, &result_desc); + } + } + return ret; +} + +int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags) +{ + JSProxyData *s; + JSValue method, ret1, prop_val, desc_val; + int res, ret; + JSObject *p; + JSValueConst args[3]; + JSPropertyDescriptor desc; + BOOL setting_not_configurable; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags); + } + prop_val = JS_AtomToValue(ctx, prop); + if (JS_IsException(prop_val)) { + JS_FreeValue(ctx, method); + return -1; + } + desc_val = js_create_desc(ctx, val, getter, setter, flags); + if (JS_IsException(desc_val)) { + JS_FreeValue(ctx, prop_val); + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = prop_val; + args[2] = desc_val; + ret1 = JS_CallFree(ctx, method, s->handler, 3, args); + JS_FreeValue(ctx, prop_val); + JS_FreeValue(ctx, desc_val); + if (JS_IsException(ret1)) + return -1; + ret = JS_ToBoolFree(ctx, ret1); + if (!ret) { + if (flags & JS_PROP_THROW) { + JS_ThrowTypeError(ctx, "proxy: defineProperty exception"); + return -1; + } else { + return 0; + } + } + p = JS_VALUE_GET_OBJ(s->target); + res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (res < 0) + return -1; + setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE | + JS_PROP_CONFIGURABLE)) == + JS_PROP_HAS_CONFIGURABLE); + if (!res) { + if (!p->extensible || setting_not_configurable) + goto fail; + } else { + if (!check_define_prop_flags(desc.flags, flags) || + ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) { + goto fail1; + } + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == + JS_PROP_GETSET) { + if ((flags & JS_PROP_HAS_GET) && + !js_same_value(ctx, getter, desc.getter)) { + goto fail1; + } + if ((flags & JS_PROP_HAS_SET) && + !js_same_value(ctx, setter, desc.setter)) { + goto fail1; + } + } + } else if (flags & JS_PROP_HAS_VALUE) { + if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == + JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) { + /* missing-proxy-check feature */ + goto fail1; + } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && + !js_same_value(ctx, val, desc.value)) { + goto fail1; + } + } + if (flags & JS_PROP_HAS_WRITABLE) { + if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) { + /* proxy-missing-checks */ + fail1: + js_free_desc(ctx, &desc); + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); + return -1; + } + } + js_free_desc(ctx, &desc); + } + return 1; +} + +int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, + JSAtom atom) +{ + JSProxyData *s; + JSValue method, ret, atom_val; + int res, res2, is_extensible; + JSValueConst args[2]; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_DeleteProperty(ctx, s->target, atom, 0); + } + atom_val = JS_AtomToValue(ctx, atom);; + if (JS_IsException(atom_val)) { + JS_FreeValue(ctx, method); + return -1; + } + args[0] = s->target; + args[1] = atom_val; + ret = JS_CallFree(ctx, method, s->handler, 2, args); + JS_FreeValue(ctx, atom_val); + if (JS_IsException(ret)) + return -1; + res = JS_ToBoolFree(ctx, ret); + if (res) { + JSPropertyDescriptor desc; + res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); + if (res2 < 0) + return -1; + if (res2) { + if (!(desc.flags & JS_PROP_CONFIGURABLE)) + goto fail; + is_extensible = JS_IsExtensible(ctx, s->target); + if (is_extensible < 0) + goto fail1; + if (!is_extensible) { + /* proxy-missing-checks */ + fail: + JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty"); + fail1: + js_free_desc(ctx, &desc); + return -1; + } + js_free_desc(ctx, &desc); + } + } + return res; +} + +/* return the index of the property or -1 if not found */ +int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom) +{ + int i; + for(i = 0; i < n; i++) { + if (tab[i].atom == atom) + return i; + } + return -1; +} + +int js_proxy_get_own_property_names(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSValueConst obj) +{ + JSProxyData *s; + JSValue method, prop_array, val; + uint32_t len, i, len2; + JSPropertyEnum *tab, *tab2; + JSAtom atom; + JSPropertyDescriptor desc; + int res, is_extensible, idx; + + s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys); + if (!s) + return -1; + if (JS_IsUndefined(method)) { + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, + JS_VALUE_GET_OBJ(s->target), + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); + } + prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); + if (JS_IsException(prop_array)) + return -1; + tab = NULL; + len = 0; + tab2 = NULL; + len2 = 0; + if (js_get_length32(ctx, &len, prop_array)) + goto fail; + if (len > 0) { + tab = js_mallocz(ctx, sizeof(tab[0]) * len); + if (!tab) + goto fail; + } + for(i = 0; i < len; i++) { + val = JS_GetPropertyUint32(ctx, prop_array, i); + if (JS_IsException(val)) + goto fail; + if (!JS_IsString(val) && !JS_IsSymbol(val)) { + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols"); + goto fail; + } + atom = JS_ValueToAtom(ctx, val); + JS_FreeValue(ctx, val); + if (atom == JS_ATOM_NULL) + goto fail; + tab[i].atom = atom; + tab[i].is_enumerable = FALSE; /* XXX: redundant? */ + } + + /* check duplicate properties (XXX: inefficient, could store the + * properties an a temporary object to use the hash) */ + for(i = 1; i < len; i++) { + if (find_prop_key(tab, i, tab[i].atom) >= 0) { + JS_ThrowTypeError(ctx, "proxy: duplicate property"); + goto fail; + } + } + + is_extensible = JS_IsExtensible(ctx, s->target); + if (is_extensible < 0) + goto fail; + + /* check if there are non configurable properties */ + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + goto fail; + } + if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target), + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) + goto fail; + for(i = 0; i < len2; i++) { + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + goto fail; + } + res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), + tab2[i].atom); + if (res < 0) + goto fail; + if (res) { /* safety, property should be found */ + js_free_desc(ctx, &desc); + if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) { + idx = find_prop_key(tab, len, tab2[i].atom); + if (idx < 0) { + JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys"); + goto fail; + } + /* mark the property as found */ + if (!is_extensible) + tab[idx].is_enumerable = TRUE; + } + } + } + if (!is_extensible) { + /* check that all property in 'tab' were checked */ + for(i = 0; i < len; i++) { + if (!tab[i].is_enumerable) { + JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy"); + goto fail; + } + } + } + + js_free_prop_enum(ctx, tab2, len2); + JS_FreeValue(ctx, prop_array); + *ptab = tab; + *plen = len; + return 0; +fail: + js_free_prop_enum(ctx, tab2, len2); + js_free_prop_enum(ctx, tab, len); + JS_FreeValue(ctx, prop_array); + return -1; +} + +JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSProxyData *s; + JSValue method, arg_array, ret; + JSValueConst args[3]; + + s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct); + if (!s) + return JS_EXCEPTION; + if (!JS_IsConstructor(ctx, s->target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (JS_IsUndefined(method)) + return JS_CallConstructor2(ctx, s->target, new_target, argc, argv); + arg_array = js_create_array(ctx, argc, argv); + if (JS_IsException(arg_array)) { + ret = JS_EXCEPTION; + goto fail; + } + args[0] = s->target; + args[1] = arg_array; + args[2] = new_target; + ret = JS_Call(ctx, method, s->handler, 3, args); + if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, ret); + ret = JS_ThrowTypeErrorNotAnObject(ctx); + } +fail: + JS_FreeValue(ctx, method); + JS_FreeValue(ctx, arg_array); + return ret; +} + +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; +} + +int js_proxy_isArray(JSContext *ctx, JSValueConst obj) +{ + JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); + if (!s) + return FALSE; + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return -1; + } + return JS_IsArray(ctx, s->target); +} + +static const JSClassExoticMethods js_proxy_exotic_methods = { + .get_own_property = js_proxy_get_own_property, + .define_own_property = js_proxy_define_own_property, + .delete_property = js_proxy_delete_property, + .get_own_property_names = js_proxy_get_own_property_names, + .has_property = js_proxy_has, + .get_property = js_proxy_get, + .set_property = js_proxy_set, +}; + +JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst target, handler; + JSValue obj; + JSProxyData *s; + + target = argv[0]; + handler = argv[1]; + if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT || + JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY); + if (JS_IsException(obj)) + return obj; + s = js_malloc(ctx, sizeof(JSProxyData)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + s->target = JS_DupValue(ctx, target); + s->handler = JS_DupValue(ctx, handler); + s->is_func = JS_IsFunction(ctx, target); + s->is_revoked = FALSE; + JS_SetOpaque(obj, s); + JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target)); + return obj; +} + +JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, + JSValue *func_data) +{ + JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY); + if (s) { + /* We do not free the handler and target in case they are + referenced as constants in the C call stack */ + s->is_revoked = TRUE; + JS_FreeValue(ctx, func_data[0]); + func_data[0] = JS_NULL; + } + return JS_UNDEFINED; +} + +JSValue js_proxy_revoke_constructor(JSContext *ctx, + JSValueConst proxy_obj) +{ + return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj); +} + +JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj; + + proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv); + if (JS_IsException(proxy_obj)) + goto fail; + revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj); + if (JS_IsException(revoke_obj)) + goto fail; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + goto fail; + // XXX: exceptions? + JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E); + return obj; +fail: + JS_FreeValue(ctx, proxy_obj); + JS_FreeValue(ctx, revoke_obj); + return JS_EXCEPTION; +} + +const JSCFunctionListEntry js_proxy_funcs[] = { + JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ), +}; + +const JSClassShortDef js_proxy_class_def[] = { + { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */ +}; + +void JS_AddIntrinsicProxy(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + JSValue obj1; + + if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) { + init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY, + countof(js_proxy_class_def)); + rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods; + rt->class_array[JS_CLASS_PROXY].call = js_proxy_call; + } + + obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2, + JS_CFUNC_constructor, 0); + JS_SetConstructorBit(ctx, obj1, TRUE); + JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs, + countof(js_proxy_funcs)); + JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy", + obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } \ No newline at end of file diff --git a/src/core/builtins/js-proxy.h b/src/core/builtins/js-proxy.h index 3e9c6cbe6..fe42efca7 100644 --- a/src/core/builtins/js-proxy.h +++ b/src/core/builtins/js-proxy.h @@ -1,78 +1,78 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_PROXY_H -#define QUICKJS_JS_PROXY_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" - -int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); -int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); -int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom); -JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst receiver); -int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, - JSValueConst value, JSValueConst receiver, int flags); -JSValue js_create_desc(JSContext *ctx, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags); -int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, - JSValueConst obj, JSAtom prop); -int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst val, - JSValueConst getter, JSValueConst setter, - int flags); -int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, - JSAtom atom); -/* return the index of the property or -1 if not found */ -int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom); -int js_proxy_get_own_property_names(JSContext *ctx, - JSPropertyEnum **ptab, - uint32_t *plen, - JSValueConst obj); -JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst new_target, - int argc, JSValueConst *argv); -JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, - JSValueConst this_obj, - int argc, JSValueConst *argv, int flags); -int js_proxy_isArray(JSContext *ctx, JSValueConst obj); -JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic, - JSValue *func_data); -JSValue js_proxy_revoke_constructor(JSContext *ctx, - JSValueConst proxy_obj); -JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - - -JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); -int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val, BOOL throw_flag); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_PROXY_H +#define QUICKJS_JS_PROXY_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" + +int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); +int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); +int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom); +JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst receiver); +int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, + JSValueConst value, JSValueConst receiver, int flags); +JSValue js_create_desc(JSContext *ctx, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); +int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, + JSValueConst obj, JSAtom prop); +int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst val, + JSValueConst getter, JSValueConst setter, + int flags); +int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, + JSAtom atom); +/* return the index of the property or -1 if not found */ +int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom); +int js_proxy_get_own_property_names(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSValueConst obj); +JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, + JSValueConst new_target, + int argc, JSValueConst *argv); +JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, + JSValueConst this_obj, + int argc, JSValueConst *argv, int flags); +int js_proxy_isArray(JSContext *ctx, JSValueConst obj); +JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, + JSValue *func_data); +JSValue js_proxy_revoke_constructor(JSContext *ctx, + JSValueConst proxy_obj); +JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + + +JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); +int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, + JSValueConst proto_val, BOOL throw_flag); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-reflect.c b/src/core/builtins/js-reflect.c index 92d94cd77..2d793ea9a 100644 --- a/src/core/builtins/js-reflect.c +++ b/src/core/builtins/js-reflect.c @@ -1,176 +1,176 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-reflect.h" -#include "../exception.h" -#include "../object.h" -#include "../runtime.h" -#include "js-function.h" - -/* Reflect */ -JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); -} - -JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst func, array_arg, new_target; - JSValue *tab, ret; - uint32_t len; - - func = argv[0]; - array_arg = argv[1]; - if (argc > 2) { - new_target = argv[2]; - if (!JS_IsConstructor(ctx, new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - } else { - new_target = func; - } - tab = build_arg_list(ctx, &len, array_arg); - if (!tab) - return JS_EXCEPTION; - ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab); - free_arg_list(ctx, tab, len); - return ret; -} - -JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj; - JSAtom atom; - int ret; - - obj = argv[0]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - atom = JS_ValueToAtom(ctx, argv[1]); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_DeleteProperty(ctx, obj, atom, 0); - JS_FreeAtom(ctx, atom); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj, prop, receiver; - JSAtom atom; - JSValue ret; - - obj = argv[0]; - prop = argv[1]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - if (argc > 2) - receiver = argv[2]; - else - receiver = obj; - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE); - JS_FreeAtom(ctx, atom); - return ret; -} - -JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj, prop; - JSAtom atom; - int ret; - - obj = argv[0]; - prop = argv[1]; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_HasProperty(ctx, obj, atom); - JS_FreeAtom(ctx, atom); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst obj, prop, val, receiver; - int ret; - JSAtom atom; - - obj = argv[0]; - prop = argv[1]; - val = argv[2]; - if (argc > 3) - receiver = argv[3]; - else - receiver = obj; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - atom = JS_ValueToAtom(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_SetPropertyGeneric(ctx, obj, atom, - JS_DupValue(ctx, val), receiver, 0); - JS_FreeAtom(ctx, atom); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int ret; - ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - -JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - return JS_GetOwnPropertyNames2(ctx, argv[0], - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK, - JS_ITERATOR_KIND_KEY); -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-reflect.h" +#include "../exception.h" +#include "../object.h" +#include "../runtime.h" +#include "js-function.h" + +/* Reflect */ +JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); +} + +JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst func, array_arg, new_target; + JSValue *tab, ret; + uint32_t len; + + func = argv[0]; + array_arg = argv[1]; + if (argc > 2) { + new_target = argv[2]; + if (!JS_IsConstructor(ctx, new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + } else { + new_target = func; + } + tab = build_arg_list(ctx, &len, array_arg); + if (!tab) + return JS_EXCEPTION; + ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab); + free_arg_list(ctx, tab, len); + return ret; +} + +JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj; + JSAtom atom; + int ret; + + obj = argv[0]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, argv[1]); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_DeleteProperty(ctx, obj, atom, 0); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj, prop, receiver; + JSAtom atom; + JSValue ret; + + obj = argv[0]; + prop = argv[1]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (argc > 2) + receiver = argv[2]; + else + receiver = obj; + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE); + JS_FreeAtom(ctx, atom); + return ret; +} + +JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj, prop; + JSAtom atom; + int ret; + + obj = argv[0]; + prop = argv[1]; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_HasProperty(ctx, obj, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst obj, prop, val, receiver; + int ret; + JSAtom atom; + + obj = argv[0]; + prop = argv[1]; + val = argv[2]; + if (argc > 3) + receiver = argv[3]; + else + receiver = obj; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + atom = JS_ValueToAtom(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_SetPropertyGeneric(ctx, obj, atom, + JS_DupValue(ctx, val), receiver, 0); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int ret; + ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + +JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + return JS_GetOwnPropertyNames2(ctx, argv[0], + JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK, + JS_ITERATOR_KIND_KEY); +} diff --git a/src/core/builtins/js-reflect.h b/src/core/builtins/js-reflect.h index bba75e619..d1e0499ea 100644 --- a/src/core/builtins/js-reflect.h +++ b/src/core/builtins/js-reflect.h @@ -1,48 +1,48 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_REFLECT_H -#define QUICKJS_JS_REFLECT_H - -#include "quickjs/quickjs.h" - -JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_REFLECT_H +#define QUICKJS_JS_REFLECT_H + +#include "quickjs/quickjs.h" + +JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-regexp.c b/src/core/builtins/js-regexp.c index dcddca8e6..92db27e46 100644 --- a/src/core/builtins/js-regexp.c +++ b/src/core/builtins/js-regexp.c @@ -1,1491 +1,1491 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "quickjs/libregexp-opcode.h" -#include "quickjs/libregexp.h" - -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "js-array.h" -#include "js-function.h" -#include "js-object.h" -#include "js-operator.h" -#include "js-regexp.h" -#include "js-string.h" - -/* RegExp */ - -void js_regexp_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSRegExp *re = &p->u.regexp; - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); -} - -/* create a string containing the RegExp bytecode */ -JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, - JSValueConst flags) -{ - const char *str; - int re_flags, mask; - uint8_t *re_bytecode_buf; - size_t i, len; - int re_bytecode_len; - JSValue ret; - char error_msg[64]; - - re_flags = 0; - if (!JS_IsUndefined(flags)) { - str = JS_ToCStringLen(ctx, &len, flags); - if (!str) - return JS_EXCEPTION; - /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ - for (i = 0; i < len; i++) { - switch(str[i]) { - case 'g': - mask = LRE_FLAG_GLOBAL; - break; - case 'i': - mask = LRE_FLAG_IGNORECASE; - break; - case 'm': - mask = LRE_FLAG_MULTILINE; - break; - case 's': - mask = LRE_FLAG_DOTALL; - break; - case 'u': - mask = LRE_FLAG_UTF16; - break; - case 'y': - mask = LRE_FLAG_STICKY; - break; - default: - goto bad_flags; - } - if ((re_flags & mask) != 0) { - bad_flags: - JS_FreeCString(ctx, str); - return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); - } - re_flags |= mask; - } - JS_FreeCString(ctx, str); - } - - str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16)); - if (!str) - return JS_EXCEPTION; - re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, - sizeof(error_msg), str, len, re_flags, ctx); - JS_FreeCString(ctx, str); - if (!re_bytecode_buf) { - JS_ThrowSyntaxError(ctx, "%s", error_msg); - return JS_EXCEPTION; - } - - ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len); - js_free(ctx, re_bytecode_buf); - return ret; -} - -/* create a RegExp object from a string containing the RegExp bytecode - and the source pattern */ -JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, - JSValue pattern, JSValue bc) -{ - JSValue obj; - JSObject *p; - JSRegExp *re; - - /* sanity check */ - if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || - JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) { - JS_ThrowTypeError(ctx, "string expected"); - fail: - JS_FreeValue(ctx, bc); - JS_FreeValue(ctx, pattern); - return JS_EXCEPTION; - } - - obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP); - if (JS_IsException(obj)) - goto fail; - p = JS_VALUE_GET_OBJ(obj); - re = &p->u.regexp; - re->pattern = JS_VALUE_GET_STRING(pattern); - re->bytecode = JS_VALUE_GET_STRING(bc); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0), - JS_PROP_WRITABLE); - return obj; -} - -JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error) -{ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(obj); - if (p->class_id == JS_CLASS_REGEXP) - return &p->u.regexp; - } - if (throw_error) { - JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); - } - return NULL; -} - -/* return < 0 if exception or TRUE/FALSE */ -int js_is_regexp(JSContext *ctx, JSValueConst obj) -{ - JSValue m; - - if (!JS_IsObject(obj)) - return FALSE; - m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match); - if (JS_IsException(m)) - return -1; - if (!JS_IsUndefined(m)) - return JS_ToBoolFree(ctx, m); - return js_get_regexp(ctx, obj, FALSE) != NULL; -} - -JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue pattern, flags, bc, val; - JSValueConst pat, flags1; - JSRegExp *re; - int pat_is_regexp; - - pat = argv[0]; - flags1 = argv[1]; - pat_is_regexp = js_is_regexp(ctx, pat); - if (pat_is_regexp < 0) - return JS_EXCEPTION; - if (JS_IsUndefined(new_target)) { - /* called as a function */ - new_target = JS_GetActiveFunction(ctx); - if (pat_is_regexp && JS_IsUndefined(flags1)) { - JSValue ctor; - BOOL res; - ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor); - if (JS_IsException(ctor)) - return ctor; - res = js_same_value(ctx, ctor, new_target); - JS_FreeValue(ctx, ctor); - if (res) - return JS_DupValue(ctx, pat); - } - } - re = js_get_regexp(ctx, pat, FALSE); - if (re) { - pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); - if (JS_IsUndefined(flags1)) { - bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); - goto no_compilation; - } else { - flags = JS_ToString(ctx, flags1); - if (JS_IsException(flags)) - goto fail; - } - } else { - flags = JS_UNDEFINED; - if (pat_is_regexp) { - pattern = JS_GetProperty(ctx, pat, JS_ATOM_source); - if (JS_IsException(pattern)) - goto fail; - if (JS_IsUndefined(flags1)) { - flags = JS_GetProperty(ctx, pat, JS_ATOM_flags); - if (JS_IsException(flags)) - goto fail; - } else { - flags = JS_DupValue(ctx, flags1); - } - } else { - pattern = JS_DupValue(ctx, pat); - flags = JS_DupValue(ctx, flags1); - } - if (JS_IsUndefined(pattern)) { - pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); - } else { - val = pattern; - pattern = JS_ToString(ctx, val); - JS_FreeValue(ctx, val); - if (JS_IsException(pattern)) - goto fail; - } - } - bc = js_compile_regexp(ctx, pattern, flags); - if (JS_IsException(bc)) - goto fail; - JS_FreeValue(ctx, flags); -no_compilation: - return js_regexp_constructor_internal(ctx, new_target, pattern, bc); -fail: - JS_FreeValue(ctx, pattern); - JS_FreeValue(ctx, flags); - return JS_EXCEPTION; -} - -JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRegExp *re1, *re; - JSValueConst pattern1, flags1; - JSValue bc, pattern; - - re = js_get_regexp(ctx, this_val, TRUE); - if (!re) - return JS_EXCEPTION; - pattern1 = argv[0]; - flags1 = argv[1]; - re1 = js_get_regexp(ctx, pattern1, FALSE); - if (re1) { - if (!JS_IsUndefined(flags1)) - return JS_ThrowTypeError(ctx, "flags must be undefined"); - pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern)); - bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode)); - } else { - bc = JS_UNDEFINED; - if (JS_IsUndefined(pattern1)) - pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); - else - pattern = JS_ToString(ctx, pattern1); - if (JS_IsException(pattern)) - goto fail; - bc = js_compile_regexp(ctx, pattern, flags1); - if (JS_IsException(bc)) - goto fail; - } - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); - re->pattern = JS_VALUE_GET_STRING(pattern); - re->bytecode = JS_VALUE_GET_STRING(bc); - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, 0)) < 0) - return JS_EXCEPTION; - return JS_DupValue(ctx, this_val); -fail: - JS_FreeValue(ctx, pattern); - JS_FreeValue(ctx, bc); - return JS_EXCEPTION; -} - -#if 0 -JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val) -{ - JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); - if (!re) - return JS_EXCEPTION; - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); -} - -JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val) -{ - JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); - int flags; - - if (!re) - return JS_EXCEPTION; - flags = lre_get_flags(re->bytecode->u.str8); - return JS_NewInt32(ctx, flags); -} -#endif - -JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val) -{ - JSRegExp *re; - JSString *p; - StringBuffer b_s, *b = &b_s; - int i, n, c, c2, bra; - - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - - if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) - goto empty_regex; - - re = js_get_regexp(ctx, this_val, TRUE); - if (!re) - return JS_EXCEPTION; - - p = re->pattern; - - if (p->len == 0) { - empty_regex: - return JS_NewString(ctx, "(?:)"); - } - string_buffer_init2(ctx, b, p->len, p->is_wide_char); - - /* Escape '/' and newline sequences as needed */ - bra = 0; - for (i = 0, n = p->len; i < n;) { - c2 = -1; - switch (c = string_get(p, i++)) { - case '\\': - if (i < n) - c2 = string_get(p, i++); - break; - case ']': - bra = 0; - break; - case '[': - if (!bra) { - if (i < n && string_get(p, i) == ']') - c2 = string_get(p, i++); - bra = 1; - } - break; - case '\n': - c = '\\'; - c2 = 'n'; - break; - case '\r': - c = '\\'; - c2 = 'r'; - break; - case '/': - if (!bra) { - c = '\\'; - c2 = '/'; - } - break; - } - string_buffer_putc16(b, c); - if (c2 >= 0) - string_buffer_putc16(b, c2); - } - return string_buffer_end(b); -} - -JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask) -{ - JSRegExp *re; - int flags; - - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - - re = js_get_regexp(ctx, this_val, FALSE); - if (!re) { - if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) - return JS_UNDEFINED; - else - return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); - } - - flags = lre_get_flags(re->bytecode->u.str8); - return JS_NewBool(ctx, (flags & mask) != 0); -} - -JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) -{ - char str[8], *p = str; - int res; - - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - return JS_ThrowTypeErrorNotAnObject(ctx); - - res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global)); - if (res < 0) - goto exception; - if (res) - *p++ = 'g'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase")); - if (res < 0) - goto exception; - if (res) - *p++ = 'i'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline")); - if (res < 0) - goto exception; - if (res) - *p++ = 'm'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll")); - if (res < 0) - goto exception; - if (res) - *p++ = 's'; - res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode)); - if (res < 0) - goto exception; - if (res) - *p++ = 'u'; - res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky")); - if (res < 0) - goto exception; - if (res) - *p++ = 'y'; - return JS_NewStringLen(ctx, str, p - str); - -exception: - return JS_EXCEPTION; -} - -JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue pattern, flags; - StringBuffer b_s, *b = &b_s; - - if (!JS_IsObject(this_val)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - string_buffer_init(ctx, b, 0); - string_buffer_putc8(b, '/'); - pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source); - if (string_buffer_concat_value_free(b, pattern)) - goto fail; - string_buffer_putc8(b, '/'); - flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags); - if (string_buffer_concat_value_free(b, flags)) - goto fail; - return string_buffer_end(b); - -fail: - string_buffer_free(b); - return JS_EXCEPTION; -} - -BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) -{ - JSContext *ctx = opaque; - return js_check_stack_overflow(ctx->rt, alloca_size); -} - -void *lre_realloc(void *opaque, void *ptr, size_t size) -{ - JSContext *ctx = opaque; - /* No JS exception is raised here */ - return js_realloc_rt(ctx->rt, ptr, size); -} - -JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); - JSString *str; - JSValue str_val, obj, val, groups = JS_UNDEFINED; - uint8_t *re_bytecode; - int ret; - uint8_t **capture, *str_buf; - int capture_count, shift, i, re_flags; - int64_t last_index; - const char *group_name_ptr; - - if (!re) - return JS_EXCEPTION; - str_val = JS_ToString(ctx, argv[0]); - if (JS_IsException(str_val)) - return str_val; - val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); - if (JS_IsException(val) || - JS_ToLengthFree(ctx, &last_index, val)) { - JS_FreeValue(ctx, str_val); - return JS_EXCEPTION; - } - re_bytecode = re->bytecode->u.str8; - re_flags = lre_get_flags(re_bytecode); - if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { - last_index = 0; - } - str = JS_VALUE_GET_STRING(str_val); - capture_count = lre_get_capture_count(re_bytecode); - capture = NULL; - if (capture_count > 0) { - capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); - if (!capture) { - JS_FreeValue(ctx, str_val); - return JS_EXCEPTION; - } - } - shift = str->is_wide_char; - str_buf = str->u.str8; - if (last_index > str->len) { - ret = 2; - } else { - ret = lre_exec(capture, re_bytecode, - str_buf, last_index, str->len, - shift, ctx); - } - obj = JS_NULL; - if (ret != 1) { - if (ret >= 0) { - if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, 0)) < 0) - goto fail; - } - } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); - goto fail; - } - JS_FreeValue(ctx, str_val); - } else { - int prop_flags; - if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0) - goto fail; - } - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - goto fail; - prop_flags = JS_PROP_C_W_E | JS_PROP_THROW; - group_name_ptr = lre_get_groupnames(re_bytecode); - if (group_name_ptr) { - groups = JS_NewObjectProto(ctx, JS_NULL); - if (JS_IsException(groups)) - goto fail; - } - - for(i = 0; i < capture_count; i++) { - int start, end; - JSValue val; - if (capture[2 * i] == NULL || - capture[2 * i + 1] == NULL) { - val = JS_UNDEFINED; - } else { - start = (capture[2 * i] - str_buf) >> shift; - end = (capture[2 * i + 1] - str_buf) >> shift; - val = js_sub_string(ctx, str, start, end); - if (JS_IsException(val)) - goto fail; - } - if (group_name_ptr && i > 0) { - if (*group_name_ptr) { - if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr, - JS_DupValue(ctx, val), - prop_flags) < 0) { - JS_FreeValue(ctx, val); - goto fail; - } - } - group_name_ptr += strlen(group_name_ptr) + 1; - } - if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0) - goto fail; - } - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, - groups, prop_flags) < 0) - goto fail; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, - JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0) - goto fail; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0) - goto fail1; - } - js_free(ctx, capture); - return obj; -fail: - JS_FreeValue(ctx, groups); - JS_FreeValue(ctx, str_val); -fail1: - JS_FreeValue(ctx, obj); - js_free(ctx, capture); - return JS_EXCEPTION; -} - -/* delete portions of a string that match a given regex */ -JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg) -{ - JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); - JSString *str; - JSValue str_val, val; - uint8_t *re_bytecode; - int ret; - uint8_t **capture, *str_buf; - int capture_count, shift, re_flags; - int next_src_pos, start, end; - int64_t last_index; - StringBuffer b_s, *b = &b_s; - - if (!re) - return JS_EXCEPTION; - - string_buffer_init(ctx, b, 0); - - capture = NULL; - str_val = JS_ToString(ctx, arg); - if (JS_IsException(str_val)) - goto fail; - str = JS_VALUE_GET_STRING(str_val); - re_bytecode = re->bytecode->u.str8; - re_flags = lre_get_flags(re_bytecode); - if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { - last_index = 0; - } else { - val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); - if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) - goto fail; - } - capture_count = lre_get_capture_count(re_bytecode); - if (capture_count > 0) { - capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); - if (!capture) - goto fail; - } - shift = str->is_wide_char; - str_buf = str->u.str8; - next_src_pos = 0; - for (;;) { - if (last_index > str->len) - break; - - ret = lre_exec(capture, re_bytecode, - str_buf, last_index, str->len, shift, ctx); - if (ret != 1) { - if (ret >= 0) { - if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, 0)) < 0) - goto fail; - } - } else { - JS_ThrowInternalError(ctx, "out of memory in regexp execution"); - goto fail; - } - break; - } - start = (capture[0] - str_buf) >> shift; - end = (capture[1] - str_buf) >> shift; - last_index = end; - if (next_src_pos < start) { - if (string_buffer_concat(b, str, next_src_pos, start)) - goto fail; - } - next_src_pos = end; - if (!(re_flags & LRE_FLAG_GLOBAL)) { - if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, - JS_NewInt32(ctx, end)) < 0) - goto fail; - break; - } - if (end == start) { - if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) { - end++; - } else { - string_getc(str, &end); - } - } - last_index = end; - } - if (string_buffer_concat(b, str, next_src_pos, str->len)) - goto fail; - JS_FreeValue(ctx, str_val); - js_free(ctx, capture); - return string_buffer_end(b); -fail: - JS_FreeValue(ctx, str_val); - js_free(ctx, capture); - string_buffer_free(b); - return JS_EXCEPTION; -} - -JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s) -{ - JSValue method, ret; - - method = JS_GetProperty(ctx, r, JS_ATOM_exec); - if (JS_IsException(method)) - return method; - if (JS_IsFunction(ctx, method)) { - ret = JS_CallFree(ctx, method, r, 1, &s); - if (JS_IsException(ret)) - return ret; - if (!JS_IsObject(ret) && !JS_IsNull(ret)) { - JS_FreeValue(ctx, ret); - return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null"); - } - return ret; - } - JS_FreeValue(ctx, method); - return js_regexp_exec(ctx, r, 1, &s); -} - -#if 0 -JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_RegExpExec(ctx, argv[0], argv[1]); -} -JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_RegExpDelete(ctx, argv[0], argv[1]); -} -#endif - -JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val; - BOOL ret; - - val = JS_RegExpExec(ctx, this_val, argv[0]); - if (JS_IsException(val)) - return JS_EXCEPTION; - ret = !JS_IsNull(val); - JS_FreeValue(ctx, val); - return JS_NewBool(ctx, ret); -} - -JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // [Symbol.match](str) - JSValueConst rx = this_val; - JSValue A, S, result, matchStr; - int global, n, fullUnicode, isEmpty; - JSString *p; - - if (!JS_IsObject(rx)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - A = JS_UNDEFINED; - result = JS_UNDEFINED; - matchStr = JS_UNDEFINED; - S = JS_ToString(ctx, argv[0]); - if (JS_IsException(S)) - goto exception; - - global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global)); - if (global < 0) - goto exception; - - if (!global) { - A = JS_RegExpExec(ctx, rx, S); - } else { - fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); - if (fullUnicode < 0) - goto exception; - - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) - goto exception; - A = JS_NewArray(ctx); - if (JS_IsException(A)) - goto exception; - n = 0; - for(;;) { - JS_FreeValue(ctx, result); - result = JS_RegExpExec(ctx, rx, S); - if (JS_IsException(result)) - goto exception; - if (JS_IsNull(result)) - break; - matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); - if (JS_IsException(matchStr)) - goto exception; - isEmpty = JS_IsEmptyString(matchStr); - if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0) - goto exception; - if (isEmpty) { - int64_t thisIndex, nextIndex; - if (JS_ToLengthFree(ctx, &thisIndex, - JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) - goto exception; - p = JS_VALUE_GET_STRING(S); - nextIndex = string_advance_index(p, thisIndex, fullUnicode); - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) - goto exception; - } - } - if (n == 0) { - JS_FreeValue(ctx, A); - A = JS_NULL; - } - } - JS_FreeValue(ctx, result); - JS_FreeValue(ctx, S); - return A; - -exception: - JS_FreeValue(ctx, A); - JS_FreeValue(ctx, result); - JS_FreeValue(ctx, S); - return JS_EXCEPTION; -} - -typedef struct JSRegExpStringIteratorData { - JSValue iterating_regexp; - JSValue iterated_string; - BOOL global; - BOOL unicode; - BOOL done; -} JSRegExpStringIteratorData; - -void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; - if (it) { - JS_FreeValueRT(rt, it->iterating_regexp); - JS_FreeValueRT(rt, it->iterated_string); - js_free_rt(rt, it); - } -} - -void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func) -{ - JSObject *p = JS_VALUE_GET_OBJ(val); - JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; - if (it) { - JS_MarkValue(rt, it->iterating_regexp, mark_func); - JS_MarkValue(rt, it->iterated_string, mark_func); - } -} - -JSValue js_regexp_string_iterator_next(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv, - BOOL *pdone, int magic) -{ - JSRegExpStringIteratorData *it; - JSValueConst R, S; - JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED; - JSString *sp; - - it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR); - if (!it) - goto exception; - if (it->done) { - *pdone = TRUE; - return JS_UNDEFINED; - } - R = it->iterating_regexp; - S = it->iterated_string; - match = JS_RegExpExec(ctx, R, S); - if (JS_IsException(match)) - goto exception; - if (JS_IsNull(match)) { - it->done = TRUE; - *pdone = TRUE; - return JS_UNDEFINED; - } else if (it->global) { - matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0)); - if (JS_IsException(matchStr)) - goto exception; - if (JS_IsEmptyString(matchStr)) { - int64_t thisIndex, nextIndex; - if (JS_ToLengthFree(ctx, &thisIndex, - JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0) - goto exception; - sp = JS_VALUE_GET_STRING(S); - nextIndex = string_advance_index(sp, thisIndex, it->unicode); - if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex, - JS_NewInt64(ctx, nextIndex)) < 0) - goto exception; - } - JS_FreeValue(ctx, matchStr); - } else { - it->done = TRUE; - } - *pdone = FALSE; - return match; -exception: - JS_FreeValue(ctx, match); - JS_FreeValue(ctx, matchStr); - *pdone = FALSE; - return JS_EXCEPTION; -} - -JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // [Symbol.matchAll](str) - JSValueConst R = this_val; - JSValue S, C, flags, matcher, iter; - JSValueConst args[2]; - JSString *strp; - int64_t lastIndex; - JSRegExpStringIteratorData *it; - - if (!JS_IsObject(R)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - C = JS_UNDEFINED; - flags = JS_UNDEFINED; - matcher = JS_UNDEFINED; - iter = JS_UNDEFINED; - - S = JS_ToString(ctx, argv[0]); - if (JS_IsException(S)) - goto exception; - C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor); - if (JS_IsException(C)) - goto exception; - flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags)); - if (JS_IsException(flags)) - goto exception; - args[0] = R; - args[1] = flags; - matcher = JS_CallConstructor(ctx, C, 2, args); - if (JS_IsException(matcher)) - goto exception; - if (JS_ToLengthFree(ctx, &lastIndex, - JS_GetProperty(ctx, R, JS_ATOM_lastIndex))) - goto exception; - if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex, - JS_NewInt64(ctx, lastIndex)) < 0) - goto exception; - - iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); - if (JS_IsException(iter)) - goto exception; - it = js_malloc(ctx, sizeof(*it)); - if (!it) - goto exception; - it->iterating_regexp = matcher; - it->iterated_string = S; - strp = JS_VALUE_GET_STRING(flags); - it->global = string_indexof_char(strp, 'g', 0) >= 0; - it->unicode = string_indexof_char(strp, 'u', 0) >= 0; - it->done = FALSE; - JS_SetOpaque(iter, it); - - JS_FreeValue(ctx, C); - JS_FreeValue(ctx, flags); - return iter; -exception: - JS_FreeValue(ctx, S); - JS_FreeValue(ctx, C); - JS_FreeValue(ctx, flags); - JS_FreeValue(ctx, matcher); - JS_FreeValue(ctx, iter); - return JS_EXCEPTION; -} - -typedef struct ValueBuffer { - JSContext *ctx; - JSValue *arr; - JSValue def[4]; - int len; - int size; - int error_status; -} ValueBuffer; - -int value_buffer_init(JSContext *ctx, ValueBuffer *b) -{ - b->ctx = ctx; - b->len = 0; - b->size = 4; - b->error_status = 0; - b->arr = b->def; - return 0; -} - -void value_buffer_free(ValueBuffer *b) -{ - while (b->len > 0) - JS_FreeValue(b->ctx, b->arr[--b->len]); - if (b->arr != b->def) - js_free(b->ctx, b->arr); - b->arr = b->def; - b->size = 4; -} - -int value_buffer_append(ValueBuffer *b, JSValue val) -{ - if (b->error_status) - return -1; - - if (b->len >= b->size) { - int new_size = (b->len + (b->len >> 1) + 31) & ~16; - size_t slack; - JSValue *new_arr; - - if (b->arr == b->def) { - new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack); - if (new_arr) - memcpy(new_arr, b->def, sizeof b->def); - } else { - new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack); - } - if (!new_arr) { - value_buffer_free(b); - JS_FreeValue(b->ctx, val); - b->error_status = -1; - return -1; - } - new_size += slack / sizeof(*new_arr); - b->arr = new_arr; - b->size = new_size; - } - b->arr[b->len++] = val; - return 0; -} - -int js_is_standard_regexp(JSContext *ctx, JSValueConst rx) -{ - JSValue val; - int res; - - val = JS_GetProperty(ctx, rx, JS_ATOM_constructor); - if (JS_IsException(val)) - return -1; - // rx.constructor === RegExp - res = js_same_value(ctx, val, ctx->regexp_ctor); - JS_FreeValue(ctx, val); - if (res) { - val = JS_GetProperty(ctx, rx, JS_ATOM_exec); - if (JS_IsException(val)) - return -1; - // rx.exec === RE_exec - res = JS_IsCFunction(ctx, val, js_regexp_exec, 0); - JS_FreeValue(ctx, val); - } - return res; -} - -JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // [Symbol.replace](str, rep) - JSValueConst rx = this_val, rep = argv[1]; - JSValueConst args[6]; - JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res; - JSString *sp, *rp; - StringBuffer b_s, *b = &b_s; - ValueBuffer v_b, *results = &v_b; - int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode; - uint32_t nCaptures; - int64_t position; - - if (!JS_IsObject(rx)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - string_buffer_init(ctx, b, 0); - value_buffer_init(ctx, results); - - rep_val = JS_UNDEFINED; - matched = JS_UNDEFINED; - tab = JS_UNDEFINED; - rep_str = JS_UNDEFINED; - namedCaptures = JS_UNDEFINED; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - goto exception; - - sp = JS_VALUE_GET_STRING(str); - rp = NULL; - functionalReplace = JS_IsFunction(ctx, rep); - if (!functionalReplace) { - rep_val = JS_ToString(ctx, rep); - if (JS_IsException(rep_val)) - goto exception; - rp = JS_VALUE_GET_STRING(rep_val); - } - fullUnicode = 0; - is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global)); - if (is_global < 0) - goto exception; - if (is_global) { - fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); - if (fullUnicode < 0) - goto exception; - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) - goto exception; - } - - if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) { - /* use faster version for simple cases */ - res = JS_RegExpDelete(ctx, rx, str); - goto done; - } - for(;;) { - JSValue result; - result = JS_RegExpExec(ctx, rx, str); - if (JS_IsException(result)) - goto exception; - if (JS_IsNull(result)) - break; - if (value_buffer_append(results, result) < 0) - goto exception; - if (!is_global) - break; - JS_FreeValue(ctx, matched); - matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); - if (JS_IsException(matched)) - goto exception; - if (JS_IsEmptyString(matched)) { - /* always advance of at least one char */ - int64_t thisIndex, nextIndex; - if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) - goto exception; - nextIndex = string_advance_index(sp, thisIndex, fullUnicode); - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) - goto exception; - } - } - nextSourcePosition = 0; - for(j = 0; j < results->len; j++) { - JSValueConst result; - result = results->arr[j]; - if (js_get_length32(ctx, &nCaptures, result) < 0) - goto exception; - JS_FreeValue(ctx, matched); - matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); - if (JS_IsException(matched)) - goto exception; - if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index))) - goto exception; - if (position > sp->len) - position = sp->len; - else if (position < 0) - position = 0; - /* ignore substition if going backward (can happen - with custom regexp object) */ - JS_FreeValue(ctx, tab); - tab = JS_NewArray(ctx); - if (JS_IsException(tab)) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched), - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - for(n = 1; n < nCaptures; n++) { - JSValue capN; - capN = JS_GetPropertyInt64(ctx, result, n); - if (JS_IsException(capN)) - goto exception; - if (!JS_IsUndefined(capN)) { - capN = JS_ToStringFree(ctx, capN); - if (JS_IsException(capN)) - goto exception; - } - if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - JS_FreeValue(ctx, namedCaptures); - namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups); - if (JS_IsException(namedCaptures)) - goto exception; - if (functionalReplace) { - if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - if (!JS_IsUndefined(namedCaptures)) { - if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - } - args[0] = JS_UNDEFINED; - args[1] = tab; - JS_FreeValue(ctx, rep_str); - rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0)); - } else { - JSValue namedCaptures1; - if (!JS_IsUndefined(namedCaptures)) { - namedCaptures1 = JS_ToObject(ctx, namedCaptures); - if (JS_IsException(namedCaptures1)) - goto exception; - } else { - namedCaptures1 = JS_UNDEFINED; - } - args[0] = matched; - args[1] = str; - args[2] = JS_NewInt32(ctx, position); - args[3] = tab; - args[4] = namedCaptures1; - args[5] = rep_val; - JS_FreeValue(ctx, rep_str); - rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); - JS_FreeValue(ctx, namedCaptures1); - } - if (JS_IsException(rep_str)) - goto exception; - if (position >= nextSourcePosition) { - string_buffer_concat(b, sp, nextSourcePosition, position); - string_buffer_concat_value(b, rep_str); - nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len; - } - } - string_buffer_concat(b, sp, nextSourcePosition, sp->len); - res = string_buffer_end(b); - goto done1; - -exception: - res = JS_EXCEPTION; -done: - string_buffer_free(b); -done1: - value_buffer_free(results); - JS_FreeValue(ctx, rep_val); - JS_FreeValue(ctx, matched); - JS_FreeValue(ctx, tab); - JS_FreeValue(ctx, rep_str); - JS_FreeValue(ctx, namedCaptures); - JS_FreeValue(ctx, str); - return res; -} - -JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValueConst rx = this_val; - JSValue str, previousLastIndex, currentLastIndex, result, index; - - if (!JS_IsObject(rx)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - result = JS_UNDEFINED; - currentLastIndex = JS_UNDEFINED; - previousLastIndex = JS_UNDEFINED; - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - goto exception; - - previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); - if (JS_IsException(previousLastIndex)) - goto exception; - - if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) { - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) { - goto exception; - } - } - result = JS_RegExpExec(ctx, rx, str); - if (JS_IsException(result)) - goto exception; - currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); - if (JS_IsException(currentLastIndex)) - goto exception; - if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { - JS_FreeValue(ctx, previousLastIndex); - } else { - if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { - previousLastIndex = JS_UNDEFINED; - goto exception; - } - } - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, currentLastIndex); - - if (JS_IsNull(result)) { - return JS_NewInt32(ctx, -1); - } else { - index = JS_GetProperty(ctx, result, JS_ATOM_index); - JS_FreeValue(ctx, result); - return index; - } - -exception: - JS_FreeValue(ctx, result); - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, currentLastIndex); - JS_FreeValue(ctx, previousLastIndex); - return JS_EXCEPTION; -} - -JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - // [Symbol.split](str, limit) - JSValueConst rx = this_val; - JSValueConst args[2]; - JSValue str, ctor, splitter, A, flags, z, sub; - JSString *strp; - uint32_t lim, size, p, q; - int unicodeMatching; - int64_t lengthA, e, numberOfCaptures, i; - - if (!JS_IsObject(rx)) - return JS_ThrowTypeErrorNotAnObject(ctx); - - ctor = JS_UNDEFINED; - splitter = JS_UNDEFINED; - A = JS_UNDEFINED; - flags = JS_UNDEFINED; - z = JS_UNDEFINED; - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - goto exception; - ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor); - if (JS_IsException(ctor)) - goto exception; - flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags)); - if (JS_IsException(flags)) - goto exception; - strp = JS_VALUE_GET_STRING(flags); - unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0; - if (string_indexof_char(strp, 'y', 0) < 0) { - flags = JS_ConcatString3(ctx, "", flags, "y"); - if (JS_IsException(flags)) - goto exception; - } - args[0] = rx; - args[1] = flags; - splitter = JS_CallConstructor(ctx, ctor, 2, args); - if (JS_IsException(splitter)) - goto exception; - A = JS_NewArray(ctx); - if (JS_IsException(A)) - goto exception; - lengthA = 0; - if (JS_IsUndefined(argv[1])) { - lim = 0xffffffff; - } else { - if (JS_ToUint32(ctx, &lim, argv[1]) < 0) - goto exception; - if (lim == 0) - goto done; - } - strp = JS_VALUE_GET_STRING(str); - p = q = 0; - size = strp->len; - if (size == 0) { - z = JS_RegExpExec(ctx, splitter, str); - if (JS_IsException(z)) - goto exception; - if (JS_IsNull(z)) - goto add_tail; - goto done; - } - while (q < size) { - if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0) - goto exception; - JS_FreeValue(ctx, z); - z = JS_RegExpExec(ctx, splitter, str); - if (JS_IsException(z)) - goto exception; - if (JS_IsNull(z)) { - q = string_advance_index(strp, q, unicodeMatching); - } else { - if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex))) - goto exception; - if (e > size) - e = size; - if (e == p) { - q = string_advance_index(strp, q, unicodeMatching); - } else { - sub = js_sub_string(ctx, strp, p, q); - if (JS_IsException(sub)) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, - JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - if (lengthA == lim) - goto done; - p = e; - if (js_get_length64(ctx, &numberOfCaptures, z)) - goto exception; - for(i = 1; i < numberOfCaptures; i++) { - sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); - if (JS_IsException(sub)) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - if (lengthA == lim) - goto done; - } - q = p; - } - } - } -add_tail: - if (p > size) - p = size; - sub = js_sub_string(ctx, strp, p, size); - if (JS_IsException(sub)) - goto exception; - if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception; - goto done; -exception: - JS_FreeValue(ctx, A); - A = JS_EXCEPTION; -done: - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, ctor); - JS_FreeValue(ctx, splitter); - JS_FreeValue(ctx, flags); - JS_FreeValue(ctx, z); - return A; -} - -const JSCFunctionListEntry js_regexp_funcs[] = { - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), - //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ), - //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ), -}; - -const JSCFunctionListEntry js_regexp_proto_funcs[] = { - JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), - JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), - JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ), - JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ), - JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ), - JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ), - JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ), - JS_CFUNC_DEF("exec", 1, js_regexp_exec ), - JS_CFUNC_DEF("compile", 2, js_regexp_compile ), - JS_CFUNC_DEF("test", 1, js_regexp_test ), - JS_CFUNC_DEF("toString", 0, js_regexp_toString ), - JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ), - JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ), - JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ), - JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ), - JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ), - //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ), - //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ), -}; - -const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = { - JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ), -}; - -void JS_AddIntrinsicRegExpCompiler(JSContext *ctx) -{ - ctx->compile_regexp = js_compile_regexp; -} - -void JS_AddIntrinsicRegExp(JSContext *ctx) -{ - JSValueConst obj; - - JS_AddIntrinsicRegExpCompiler(ctx); - - ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs, - countof(js_regexp_proto_funcs)); - obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2, - ctx->class_proto[JS_CLASS_REGEXP]); - ctx->regexp_ctor = JS_DupValue(ctx, obj); - JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs)); - - ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] = - JS_NewObjectProto(ctx, ctx->iterator_proto); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR], - js_regexp_string_iterator_proto_funcs, - countof(js_regexp_string_iterator_proto_funcs)); -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "quickjs/libregexp-opcode.h" +#include "quickjs/libregexp.h" + +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "js-array.h" +#include "js-function.h" +#include "js-object.h" +#include "js-operator.h" +#include "js-regexp.h" +#include "js-string.h" + +/* RegExp */ + +void js_regexp_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExp *re = &p->u.regexp; + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); +} + +/* create a string containing the RegExp bytecode */ +JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, + JSValueConst flags) +{ + const char *str; + int re_flags, mask; + uint8_t *re_bytecode_buf; + size_t i, len; + int re_bytecode_len; + JSValue ret; + char error_msg[64]; + + re_flags = 0; + if (!JS_IsUndefined(flags)) { + str = JS_ToCStringLen(ctx, &len, flags); + if (!str) + return JS_EXCEPTION; + /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ + for (i = 0; i < len; i++) { + switch(str[i]) { + case 'g': + mask = LRE_FLAG_GLOBAL; + break; + case 'i': + mask = LRE_FLAG_IGNORECASE; + break; + case 'm': + mask = LRE_FLAG_MULTILINE; + break; + case 's': + mask = LRE_FLAG_DOTALL; + break; + case 'u': + mask = LRE_FLAG_UTF16; + break; + case 'y': + mask = LRE_FLAG_STICKY; + break; + default: + goto bad_flags; + } + if ((re_flags & mask) != 0) { + bad_flags: + JS_FreeCString(ctx, str); + return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); + } + re_flags |= mask; + } + JS_FreeCString(ctx, str); + } + + str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16)); + if (!str) + return JS_EXCEPTION; + re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, + sizeof(error_msg), str, len, re_flags, ctx); + JS_FreeCString(ctx, str); + if (!re_bytecode_buf) { + JS_ThrowSyntaxError(ctx, "%s", error_msg); + return JS_EXCEPTION; + } + + ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len); + js_free(ctx, re_bytecode_buf); + return ret; +} + +/* create a RegExp object from a string containing the RegExp bytecode + and the source pattern */ +JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, + JSValue pattern, JSValue bc) +{ + JSValue obj; + JSObject *p; + JSRegExp *re; + + /* sanity check */ + if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || + JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) { + JS_ThrowTypeError(ctx, "string expected"); + fail: + JS_FreeValue(ctx, bc); + JS_FreeValue(ctx, pattern); + return JS_EXCEPTION; + } + + obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP); + if (JS_IsException(obj)) + goto fail; + p = JS_VALUE_GET_OBJ(obj); + re = &p->u.regexp; + re->pattern = JS_VALUE_GET_STRING(pattern); + re->bytecode = JS_VALUE_GET_STRING(bc); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0), + JS_PROP_WRITABLE); + return obj; +} + +JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error) +{ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_REGEXP) + return &p->u.regexp; + } + if (throw_error) { + JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); + } + return NULL; +} + +/* return < 0 if exception or TRUE/FALSE */ +int js_is_regexp(JSContext *ctx, JSValueConst obj) +{ + JSValue m; + + if (!JS_IsObject(obj)) + return FALSE; + m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match); + if (JS_IsException(m)) + return -1; + if (!JS_IsUndefined(m)) + return JS_ToBoolFree(ctx, m); + return js_get_regexp(ctx, obj, FALSE) != NULL; +} + +JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue pattern, flags, bc, val; + JSValueConst pat, flags1; + JSRegExp *re; + int pat_is_regexp; + + pat = argv[0]; + flags1 = argv[1]; + pat_is_regexp = js_is_regexp(ctx, pat); + if (pat_is_regexp < 0) + return JS_EXCEPTION; + if (JS_IsUndefined(new_target)) { + /* called as a function */ + new_target = JS_GetActiveFunction(ctx); + if (pat_is_regexp && JS_IsUndefined(flags1)) { + JSValue ctor; + BOOL res; + ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor); + if (JS_IsException(ctor)) + return ctor; + res = js_same_value(ctx, ctor, new_target); + JS_FreeValue(ctx, ctor); + if (res) + return JS_DupValue(ctx, pat); + } + } + re = js_get_regexp(ctx, pat, FALSE); + if (re) { + pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); + if (JS_IsUndefined(flags1)) { + bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + goto no_compilation; + } else { + flags = JS_ToString(ctx, flags1); + if (JS_IsException(flags)) + goto fail; + } + } else { + flags = JS_UNDEFINED; + if (pat_is_regexp) { + pattern = JS_GetProperty(ctx, pat, JS_ATOM_source); + if (JS_IsException(pattern)) + goto fail; + if (JS_IsUndefined(flags1)) { + flags = JS_GetProperty(ctx, pat, JS_ATOM_flags); + if (JS_IsException(flags)) + goto fail; + } else { + flags = JS_DupValue(ctx, flags1); + } + } else { + pattern = JS_DupValue(ctx, pat); + flags = JS_DupValue(ctx, flags1); + } + if (JS_IsUndefined(pattern)) { + pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); + } else { + val = pattern; + pattern = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(pattern)) + goto fail; + } + } + bc = js_compile_regexp(ctx, pattern, flags); + if (JS_IsException(bc)) + goto fail; + JS_FreeValue(ctx, flags); +no_compilation: + return js_regexp_constructor_internal(ctx, new_target, pattern, bc); +fail: + JS_FreeValue(ctx, pattern); + JS_FreeValue(ctx, flags); + return JS_EXCEPTION; +} + +JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRegExp *re1, *re; + JSValueConst pattern1, flags1; + JSValue bc, pattern; + + re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + pattern1 = argv[0]; + flags1 = argv[1]; + re1 = js_get_regexp(ctx, pattern1, FALSE); + if (re1) { + if (!JS_IsUndefined(flags1)) + return JS_ThrowTypeError(ctx, "flags must be undefined"); + pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern)); + bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode)); + } else { + bc = JS_UNDEFINED; + if (JS_IsUndefined(pattern1)) + pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); + else + pattern = JS_ToString(ctx, pattern1); + if (JS_IsException(pattern)) + goto fail; + bc = js_compile_regexp(ctx, pattern, flags1); + if (JS_IsException(bc)) + goto fail; + } + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); + re->pattern = JS_VALUE_GET_STRING(pattern); + re->bytecode = JS_VALUE_GET_STRING(bc); + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, 0)) < 0) + return JS_EXCEPTION; + return JS_DupValue(ctx, this_val); +fail: + JS_FreeValue(ctx, pattern); + JS_FreeValue(ctx, bc); + return JS_EXCEPTION; +} + +#if 0 +JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); +} + +JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + int flags; + + if (!re) + return JS_EXCEPTION; + flags = lre_get_flags(re->bytecode->u.str8); + return JS_NewInt32(ctx, flags); +} +#endif + +JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val) +{ + JSRegExp *re; + JSString *p; + StringBuffer b_s, *b = &b_s; + int i, n, c, c2, bra; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) + goto empty_regex; + + re = js_get_regexp(ctx, this_val, TRUE); + if (!re) + return JS_EXCEPTION; + + p = re->pattern; + + if (p->len == 0) { + empty_regex: + return JS_NewString(ctx, "(?:)"); + } + string_buffer_init2(ctx, b, p->len, p->is_wide_char); + + /* Escape '/' and newline sequences as needed */ + bra = 0; + for (i = 0, n = p->len; i < n;) { + c2 = -1; + switch (c = string_get(p, i++)) { + case '\\': + if (i < n) + c2 = string_get(p, i++); + break; + case ']': + bra = 0; + break; + case '[': + if (!bra) { + if (i < n && string_get(p, i) == ']') + c2 = string_get(p, i++); + bra = 1; + } + break; + case '\n': + c = '\\'; + c2 = 'n'; + break; + case '\r': + c = '\\'; + c2 = 'r'; + break; + case '/': + if (!bra) { + c = '\\'; + c2 = '/'; + } + break; + } + string_buffer_putc16(b, c); + if (c2 >= 0) + string_buffer_putc16(b, c2); + } + return string_buffer_end(b); +} + +JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask) +{ + JSRegExp *re; + int flags; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + re = js_get_regexp(ctx, this_val, FALSE); + if (!re) { + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) + return JS_UNDEFINED; + else + return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); + } + + flags = lre_get_flags(re->bytecode->u.str8); + return JS_NewBool(ctx, (flags & mask) != 0); +} + +JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) +{ + char str[8], *p = str; + int res; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + + res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global)); + if (res < 0) + goto exception; + if (res) + *p++ = 'g'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase")); + if (res < 0) + goto exception; + if (res) + *p++ = 'i'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline")); + if (res < 0) + goto exception; + if (res) + *p++ = 'm'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll")); + if (res < 0) + goto exception; + if (res) + *p++ = 's'; + res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode)); + if (res < 0) + goto exception; + if (res) + *p++ = 'u'; + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky")); + if (res < 0) + goto exception; + if (res) + *p++ = 'y'; + return JS_NewStringLen(ctx, str, p - str); + +exception: + return JS_EXCEPTION; +} + +JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue pattern, flags; + StringBuffer b_s, *b = &b_s; + + if (!JS_IsObject(this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + string_buffer_init(ctx, b, 0); + string_buffer_putc8(b, '/'); + pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source); + if (string_buffer_concat_value_free(b, pattern)) + goto fail; + string_buffer_putc8(b, '/'); + flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags); + if (string_buffer_concat_value_free(b, flags)) + goto fail; + return string_buffer_end(b); + +fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) +{ + JSContext *ctx = opaque; + return js_check_stack_overflow(ctx->rt, alloca_size); +} + +void *lre_realloc(void *opaque, void *ptr, size_t size) +{ + JSContext *ctx = opaque; + /* No JS exception is raised here */ + return js_realloc_rt(ctx->rt, ptr, size); +} + +JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + JSString *str; + JSValue str_val, obj, val, groups = JS_UNDEFINED; + uint8_t *re_bytecode; + int ret; + uint8_t **capture, *str_buf; + int capture_count, shift, i, re_flags; + int64_t last_index; + const char *group_name_ptr; + + if (!re) + return JS_EXCEPTION; + str_val = JS_ToString(ctx, argv[0]); + if (JS_IsException(str_val)) + return str_val; + val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); + if (JS_IsException(val) || + JS_ToLengthFree(ctx, &last_index, val)) { + JS_FreeValue(ctx, str_val); + return JS_EXCEPTION; + } + re_bytecode = re->bytecode->u.str8; + re_flags = lre_get_flags(re_bytecode); + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { + last_index = 0; + } + str = JS_VALUE_GET_STRING(str_val); + capture_count = lre_get_capture_count(re_bytecode); + capture = NULL; + if (capture_count > 0) { + capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); + if (!capture) { + JS_FreeValue(ctx, str_val); + return JS_EXCEPTION; + } + } + shift = str->is_wide_char; + str_buf = str->u.str8; + if (last_index > str->len) { + ret = 2; + } else { + ret = lre_exec(capture, re_bytecode, + str_buf, last_index, str->len, + shift, ctx); + } + obj = JS_NULL; + if (ret != 1) { + if (ret >= 0) { + if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, 0)) < 0) + goto fail; + } + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + goto fail; + } + JS_FreeValue(ctx, str_val); + } else { + int prop_flags; + if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0) + goto fail; + } + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + goto fail; + prop_flags = JS_PROP_C_W_E | JS_PROP_THROW; + group_name_ptr = lre_get_groupnames(re_bytecode); + if (group_name_ptr) { + groups = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(groups)) + goto fail; + } + + for(i = 0; i < capture_count; i++) { + int start, end; + JSValue val; + if (capture[2 * i] == NULL || + capture[2 * i + 1] == NULL) { + val = JS_UNDEFINED; + } else { + start = (capture[2 * i] - str_buf) >> shift; + end = (capture[2 * i + 1] - str_buf) >> shift; + val = js_sub_string(ctx, str, start, end); + if (JS_IsException(val)) + goto fail; + } + if (group_name_ptr && i > 0) { + if (*group_name_ptr) { + if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr, + JS_DupValue(ctx, val), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + group_name_ptr += strlen(group_name_ptr) + 1; + } + if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0) + goto fail; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, + groups, prop_flags) < 0) + goto fail; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, + JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0) + goto fail; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0) + goto fail1; + } + js_free(ctx, capture); + return obj; +fail: + JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, str_val); +fail1: + JS_FreeValue(ctx, obj); + js_free(ctx, capture); + return JS_EXCEPTION; +} + +/* delete portions of a string that match a given regex */ +JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg) +{ + JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); + JSString *str; + JSValue str_val, val; + uint8_t *re_bytecode; + int ret; + uint8_t **capture, *str_buf; + int capture_count, shift, re_flags; + int next_src_pos, start, end; + int64_t last_index; + StringBuffer b_s, *b = &b_s; + + if (!re) + return JS_EXCEPTION; + + string_buffer_init(ctx, b, 0); + + capture = NULL; + str_val = JS_ToString(ctx, arg); + if (JS_IsException(str_val)) + goto fail; + str = JS_VALUE_GET_STRING(str_val); + re_bytecode = re->bytecode->u.str8; + re_flags = lre_get_flags(re_bytecode); + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { + last_index = 0; + } else { + val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); + if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) + goto fail; + } + capture_count = lre_get_capture_count(re_bytecode); + if (capture_count > 0) { + capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); + if (!capture) + goto fail; + } + shift = str->is_wide_char; + str_buf = str->u.str8; + next_src_pos = 0; + for (;;) { + if (last_index > str->len) + break; + + ret = lre_exec(capture, re_bytecode, + str_buf, last_index, str->len, shift, ctx); + if (ret != 1) { + if (ret >= 0) { + if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, 0)) < 0) + goto fail; + } + } else { + JS_ThrowInternalError(ctx, "out of memory in regexp execution"); + goto fail; + } + break; + } + start = (capture[0] - str_buf) >> shift; + end = (capture[1] - str_buf) >> shift; + last_index = end; + if (next_src_pos < start) { + if (string_buffer_concat(b, str, next_src_pos, start)) + goto fail; + } + next_src_pos = end; + if (!(re_flags & LRE_FLAG_GLOBAL)) { + if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, + JS_NewInt32(ctx, end)) < 0) + goto fail; + break; + } + if (end == start) { + if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) { + end++; + } else { + string_getc(str, &end); + } + } + last_index = end; + } + if (string_buffer_concat(b, str, next_src_pos, str->len)) + goto fail; + JS_FreeValue(ctx, str_val); + js_free(ctx, capture); + return string_buffer_end(b); +fail: + JS_FreeValue(ctx, str_val); + js_free(ctx, capture); + string_buffer_free(b); + return JS_EXCEPTION; +} + +JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s) +{ + JSValue method, ret; + + method = JS_GetProperty(ctx, r, JS_ATOM_exec); + if (JS_IsException(method)) + return method; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, r, 1, &s); + if (JS_IsException(ret)) + return ret; + if (!JS_IsObject(ret) && !JS_IsNull(ret)) { + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null"); + } + return ret; + } + JS_FreeValue(ctx, method); + return js_regexp_exec(ctx, r, 1, &s); +} + +#if 0 +JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_RegExpExec(ctx, argv[0], argv[1]); +} +JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_RegExpDelete(ctx, argv[0], argv[1]); +} +#endif + +JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val; + BOOL ret; + + val = JS_RegExpExec(ctx, this_val, argv[0]); + if (JS_IsException(val)) + return JS_EXCEPTION; + ret = !JS_IsNull(val); + JS_FreeValue(ctx, val); + return JS_NewBool(ctx, ret); +} + +JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // [Symbol.match](str) + JSValueConst rx = this_val; + JSValue A, S, result, matchStr; + int global, n, fullUnicode, isEmpty; + JSString *p; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + A = JS_UNDEFINED; + result = JS_UNDEFINED; + matchStr = JS_UNDEFINED; + S = JS_ToString(ctx, argv[0]); + if (JS_IsException(S)) + goto exception; + + global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global)); + if (global < 0) + goto exception; + + if (!global) { + A = JS_RegExpExec(ctx, rx, S); + } else { + fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); + if (fullUnicode < 0) + goto exception; + + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + n = 0; + for(;;) { + JS_FreeValue(ctx, result); + result = JS_RegExpExec(ctx, rx, S); + if (JS_IsException(result)) + goto exception; + if (JS_IsNull(result)) + break; + matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matchStr)) + goto exception; + isEmpty = JS_IsEmptyString(matchStr); + if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0) + goto exception; + if (isEmpty) { + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, + JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) + goto exception; + p = JS_VALUE_GET_STRING(S); + nextIndex = string_advance_index(p, thisIndex, fullUnicode); + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) + goto exception; + } + } + if (n == 0) { + JS_FreeValue(ctx, A); + A = JS_NULL; + } + } + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, S); + return A; + +exception: + JS_FreeValue(ctx, A); + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, S); + return JS_EXCEPTION; +} + +typedef struct JSRegExpStringIteratorData { + JSValue iterating_regexp; + JSValue iterated_string; + BOOL global; + BOOL unicode; + BOOL done; +} JSRegExpStringIteratorData; + +void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; + if (it) { + JS_FreeValueRT(rt, it->iterating_regexp); + JS_FreeValueRT(rt, it->iterated_string); + js_free_rt(rt, it); + } +} + +void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; + if (it) { + JS_MarkValue(rt, it->iterating_regexp, mark_func); + JS_MarkValue(rt, it->iterated_string, mark_func); + } +} + +JSValue js_regexp_string_iterator_next(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv, + BOOL *pdone, int magic) +{ + JSRegExpStringIteratorData *it; + JSValueConst R, S; + JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED; + JSString *sp; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR); + if (!it) + goto exception; + if (it->done) { + *pdone = TRUE; + return JS_UNDEFINED; + } + R = it->iterating_regexp; + S = it->iterated_string; + match = JS_RegExpExec(ctx, R, S); + if (JS_IsException(match)) + goto exception; + if (JS_IsNull(match)) { + it->done = TRUE; + *pdone = TRUE; + return JS_UNDEFINED; + } else if (it->global) { + matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0)); + if (JS_IsException(matchStr)) + goto exception; + if (JS_IsEmptyString(matchStr)) { + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, + JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0) + goto exception; + sp = JS_VALUE_GET_STRING(S); + nextIndex = string_advance_index(sp, thisIndex, it->unicode); + if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex, + JS_NewInt64(ctx, nextIndex)) < 0) + goto exception; + } + JS_FreeValue(ctx, matchStr); + } else { + it->done = TRUE; + } + *pdone = FALSE; + return match; +exception: + JS_FreeValue(ctx, match); + JS_FreeValue(ctx, matchStr); + *pdone = FALSE; + return JS_EXCEPTION; +} + +JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // [Symbol.matchAll](str) + JSValueConst R = this_val; + JSValue S, C, flags, matcher, iter; + JSValueConst args[2]; + JSString *strp; + int64_t lastIndex; + JSRegExpStringIteratorData *it; + + if (!JS_IsObject(R)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + C = JS_UNDEFINED; + flags = JS_UNDEFINED; + matcher = JS_UNDEFINED; + iter = JS_UNDEFINED; + + S = JS_ToString(ctx, argv[0]); + if (JS_IsException(S)) + goto exception; + C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor); + if (JS_IsException(C)) + goto exception; + flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags)); + if (JS_IsException(flags)) + goto exception; + args[0] = R; + args[1] = flags; + matcher = JS_CallConstructor(ctx, C, 2, args); + if (JS_IsException(matcher)) + goto exception; + if (JS_ToLengthFree(ctx, &lastIndex, + JS_GetProperty(ctx, R, JS_ATOM_lastIndex))) + goto exception; + if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex, + JS_NewInt64(ctx, lastIndex)) < 0) + goto exception; + + iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); + if (JS_IsException(iter)) + goto exception; + it = js_malloc(ctx, sizeof(*it)); + if (!it) + goto exception; + it->iterating_regexp = matcher; + it->iterated_string = S; + strp = JS_VALUE_GET_STRING(flags); + it->global = string_indexof_char(strp, 'g', 0) >= 0; + it->unicode = string_indexof_char(strp, 'u', 0) >= 0; + it->done = FALSE; + JS_SetOpaque(iter, it); + + JS_FreeValue(ctx, C); + JS_FreeValue(ctx, flags); + return iter; +exception: + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, C); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, matcher); + JS_FreeValue(ctx, iter); + return JS_EXCEPTION; +} + +typedef struct ValueBuffer { + JSContext *ctx; + JSValue *arr; + JSValue def[4]; + int len; + int size; + int error_status; +} ValueBuffer; + +int value_buffer_init(JSContext *ctx, ValueBuffer *b) +{ + b->ctx = ctx; + b->len = 0; + b->size = 4; + b->error_status = 0; + b->arr = b->def; + return 0; +} + +void value_buffer_free(ValueBuffer *b) +{ + while (b->len > 0) + JS_FreeValue(b->ctx, b->arr[--b->len]); + if (b->arr != b->def) + js_free(b->ctx, b->arr); + b->arr = b->def; + b->size = 4; +} + +int value_buffer_append(ValueBuffer *b, JSValue val) +{ + if (b->error_status) + return -1; + + if (b->len >= b->size) { + int new_size = (b->len + (b->len >> 1) + 31) & ~16; + size_t slack; + JSValue *new_arr; + + if (b->arr == b->def) { + new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack); + if (new_arr) + memcpy(new_arr, b->def, sizeof b->def); + } else { + new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack); + } + if (!new_arr) { + value_buffer_free(b); + JS_FreeValue(b->ctx, val); + b->error_status = -1; + return -1; + } + new_size += slack / sizeof(*new_arr); + b->arr = new_arr; + b->size = new_size; + } + b->arr[b->len++] = val; + return 0; +} + +int js_is_standard_regexp(JSContext *ctx, JSValueConst rx) +{ + JSValue val; + int res; + + val = JS_GetProperty(ctx, rx, JS_ATOM_constructor); + if (JS_IsException(val)) + return -1; + // rx.constructor === RegExp + res = js_same_value(ctx, val, ctx->regexp_ctor); + JS_FreeValue(ctx, val); + if (res) { + val = JS_GetProperty(ctx, rx, JS_ATOM_exec); + if (JS_IsException(val)) + return -1; + // rx.exec === RE_exec + res = JS_IsCFunction(ctx, val, js_regexp_exec, 0); + JS_FreeValue(ctx, val); + } + return res; +} + +JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // [Symbol.replace](str, rep) + JSValueConst rx = this_val, rep = argv[1]; + JSValueConst args[6]; + JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res; + JSString *sp, *rp; + StringBuffer b_s, *b = &b_s; + ValueBuffer v_b, *results = &v_b; + int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode; + uint32_t nCaptures; + int64_t position; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + string_buffer_init(ctx, b, 0); + value_buffer_init(ctx, results); + + rep_val = JS_UNDEFINED; + matched = JS_UNDEFINED; + tab = JS_UNDEFINED; + rep_str = JS_UNDEFINED; + namedCaptures = JS_UNDEFINED; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + + sp = JS_VALUE_GET_STRING(str); + rp = NULL; + functionalReplace = JS_IsFunction(ctx, rep); + if (!functionalReplace) { + rep_val = JS_ToString(ctx, rep); + if (JS_IsException(rep_val)) + goto exception; + rp = JS_VALUE_GET_STRING(rep_val); + } + fullUnicode = 0; + is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global)); + if (is_global < 0) + goto exception; + if (is_global) { + fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); + if (fullUnicode < 0) + goto exception; + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) + goto exception; + } + + if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) { + /* use faster version for simple cases */ + res = JS_RegExpDelete(ctx, rx, str); + goto done; + } + for(;;) { + JSValue result; + result = JS_RegExpExec(ctx, rx, str); + if (JS_IsException(result)) + goto exception; + if (JS_IsNull(result)) + break; + if (value_buffer_append(results, result) < 0) + goto exception; + if (!is_global) + break; + JS_FreeValue(ctx, matched); + matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matched)) + goto exception; + if (JS_IsEmptyString(matched)) { + /* always advance of at least one char */ + int64_t thisIndex, nextIndex; + if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) + goto exception; + nextIndex = string_advance_index(sp, thisIndex, fullUnicode); + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) + goto exception; + } + } + nextSourcePosition = 0; + for(j = 0; j < results->len; j++) { + JSValueConst result; + result = results->arr[j]; + if (js_get_length32(ctx, &nCaptures, result) < 0) + goto exception; + JS_FreeValue(ctx, matched); + matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); + if (JS_IsException(matched)) + goto exception; + if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index))) + goto exception; + if (position > sp->len) + position = sp->len; + else if (position < 0) + position = 0; + /* ignore substition if going backward (can happen + with custom regexp object) */ + JS_FreeValue(ctx, tab); + tab = JS_NewArray(ctx); + if (JS_IsException(tab)) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + for(n = 1; n < nCaptures; n++) { + JSValue capN; + capN = JS_GetPropertyInt64(ctx, result, n); + if (JS_IsException(capN)) + goto exception; + if (!JS_IsUndefined(capN)) { + capN = JS_ToStringFree(ctx, capN); + if (JS_IsException(capN)) + goto exception; + } + if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + JS_FreeValue(ctx, namedCaptures); + namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups); + if (JS_IsException(namedCaptures)) + goto exception; + if (functionalReplace) { + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + if (!JS_IsUndefined(namedCaptures)) { + if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + } + args[0] = JS_UNDEFINED; + args[1] = tab; + JS_FreeValue(ctx, rep_str); + rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0)); + } else { + JSValue namedCaptures1; + if (!JS_IsUndefined(namedCaptures)) { + namedCaptures1 = JS_ToObject(ctx, namedCaptures); + if (JS_IsException(namedCaptures1)) + goto exception; + } else { + namedCaptures1 = JS_UNDEFINED; + } + args[0] = matched; + args[1] = str; + args[2] = JS_NewInt32(ctx, position); + args[3] = tab; + args[4] = namedCaptures1; + args[5] = rep_val; + JS_FreeValue(ctx, rep_str); + rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); + JS_FreeValue(ctx, namedCaptures1); + } + if (JS_IsException(rep_str)) + goto exception; + if (position >= nextSourcePosition) { + string_buffer_concat(b, sp, nextSourcePosition, position); + string_buffer_concat_value(b, rep_str); + nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len; + } + } + string_buffer_concat(b, sp, nextSourcePosition, sp->len); + res = string_buffer_end(b); + goto done1; + +exception: + res = JS_EXCEPTION; +done: + string_buffer_free(b); +done1: + value_buffer_free(results); + JS_FreeValue(ctx, rep_val); + JS_FreeValue(ctx, matched); + JS_FreeValue(ctx, tab); + JS_FreeValue(ctx, rep_str); + JS_FreeValue(ctx, namedCaptures); + JS_FreeValue(ctx, str); + return res; +} + +JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValueConst rx = this_val; + JSValue str, previousLastIndex, currentLastIndex, result, index; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + result = JS_UNDEFINED; + currentLastIndex = JS_UNDEFINED; + previousLastIndex = JS_UNDEFINED; + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + + previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); + if (JS_IsException(previousLastIndex)) + goto exception; + + if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) { + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) { + goto exception; + } + } + result = JS_RegExpExec(ctx, rx, str); + if (JS_IsException(result)) + goto exception; + currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); + if (JS_IsException(currentLastIndex)) + goto exception; + if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { + JS_FreeValue(ctx, previousLastIndex); + } else { + if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { + previousLastIndex = JS_UNDEFINED; + goto exception; + } + } + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, currentLastIndex); + + if (JS_IsNull(result)) { + return JS_NewInt32(ctx, -1); + } else { + index = JS_GetProperty(ctx, result, JS_ATOM_index); + JS_FreeValue(ctx, result); + return index; + } + +exception: + JS_FreeValue(ctx, result); + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, currentLastIndex); + JS_FreeValue(ctx, previousLastIndex); + return JS_EXCEPTION; +} + +JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + // [Symbol.split](str, limit) + JSValueConst rx = this_val; + JSValueConst args[2]; + JSValue str, ctor, splitter, A, flags, z, sub; + JSString *strp; + uint32_t lim, size, p, q; + int unicodeMatching; + int64_t lengthA, e, numberOfCaptures, i; + + if (!JS_IsObject(rx)) + return JS_ThrowTypeErrorNotAnObject(ctx); + + ctor = JS_UNDEFINED; + splitter = JS_UNDEFINED; + A = JS_UNDEFINED; + flags = JS_UNDEFINED; + z = JS_UNDEFINED; + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + goto exception; + ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor); + if (JS_IsException(ctor)) + goto exception; + flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags)); + if (JS_IsException(flags)) + goto exception; + strp = JS_VALUE_GET_STRING(flags); + unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0; + if (string_indexof_char(strp, 'y', 0) < 0) { + flags = JS_ConcatString3(ctx, "", flags, "y"); + if (JS_IsException(flags)) + goto exception; + } + args[0] = rx; + args[1] = flags; + splitter = JS_CallConstructor(ctx, ctor, 2, args); + if (JS_IsException(splitter)) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + lengthA = 0; + if (JS_IsUndefined(argv[1])) { + lim = 0xffffffff; + } else { + if (JS_ToUint32(ctx, &lim, argv[1]) < 0) + goto exception; + if (lim == 0) + goto done; + } + strp = JS_VALUE_GET_STRING(str); + p = q = 0; + size = strp->len; + if (size == 0) { + z = JS_RegExpExec(ctx, splitter, str); + if (JS_IsException(z)) + goto exception; + if (JS_IsNull(z)) + goto add_tail; + goto done; + } + while (q < size) { + if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0) + goto exception; + JS_FreeValue(ctx, z); + z = JS_RegExpExec(ctx, splitter, str); + if (JS_IsException(z)) + goto exception; + if (JS_IsNull(z)) { + q = string_advance_index(strp, q, unicodeMatching); + } else { + if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex))) + goto exception; + if (e > size) + e = size; + if (e == p) { + q = string_advance_index(strp, q, unicodeMatching); + } else { + sub = js_sub_string(ctx, strp, p, q); + if (JS_IsException(sub)) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + if (lengthA == lim) + goto done; + p = e; + if (js_get_length64(ctx, &numberOfCaptures, z)) + goto exception; + for(i = 1; i < numberOfCaptures; i++) { + sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); + if (JS_IsException(sub)) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + if (lengthA == lim) + goto done; + } + q = p; + } + } + } +add_tail: + if (p > size) + p = size; + sub = js_sub_string(ctx, strp, p, size); + if (JS_IsException(sub)) + goto exception; + if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception; + goto done; +exception: + JS_FreeValue(ctx, A); + A = JS_EXCEPTION; +done: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, ctor); + JS_FreeValue(ctx, splitter); + JS_FreeValue(ctx, flags); + JS_FreeValue(ctx, z); + return A; +} + +const JSCFunctionListEntry js_regexp_funcs[] = { + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), + //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ), + //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ), +}; + +const JSCFunctionListEntry js_regexp_proto_funcs[] = { + JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), + JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), + JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ), + JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ), + JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ), + JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ), + JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ), + JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ), + JS_CFUNC_DEF("exec", 1, js_regexp_exec ), + JS_CFUNC_DEF("compile", 2, js_regexp_compile ), + JS_CFUNC_DEF("test", 1, js_regexp_test ), + JS_CFUNC_DEF("toString", 0, js_regexp_toString ), + JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ), + JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ), + JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ), + JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ), + JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ), + //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ), + //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ), +}; + +const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = { + JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ), +}; + +void JS_AddIntrinsicRegExpCompiler(JSContext *ctx) +{ + ctx->compile_regexp = js_compile_regexp; +} + +void JS_AddIntrinsicRegExp(JSContext *ctx) +{ + JSValueConst obj; + + JS_AddIntrinsicRegExpCompiler(ctx); + + ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs, + countof(js_regexp_proto_funcs)); + obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2, + ctx->class_proto[JS_CLASS_REGEXP]); + ctx->regexp_ctor = JS_DupValue(ctx, obj); + JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs)); + + ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] = + JS_NewObjectProto(ctx, ctx->iterator_proto); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR], + js_regexp_string_iterator_proto_funcs, + countof(js_regexp_string_iterator_proto_funcs)); +} diff --git a/src/core/builtins/js-regexp.h b/src/core/builtins/js-regexp.h index ae39742d6..4d5c79c3e 100644 --- a/src/core/builtins/js-regexp.h +++ b/src/core/builtins/js-regexp.h @@ -1,36 +1,36 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_REGEXP_H -#define QUICKJS_JS_REGEXP_H - -#include "quickjs/quickjs.h" - -JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, - JSValueConst flags); -JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, - JSValue pattern, JSValue bc); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_REGEXP_H +#define QUICKJS_JS_REGEXP_H + +#include "quickjs/quickjs.h" + +JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, + JSValueConst flags); +JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, + JSValue pattern, JSValue bc); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-string.c b/src/core/builtins/js-string.c index e706a01be..27dc8da91 100644 --- a/src/core/builtins/js-string.c +++ b/src/core/builtins/js-string.c @@ -1,1497 +1,1497 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-string.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../string.h" -#include "../types.h" -#include "js-function.h" -#include "js-object.h" -#include "js-array.h" -#include "quickjs/libregexp.h" - -/* String */ - -int js_string_get_own_property(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop) { - JSObject* p; - JSString* p1; - uint32_t idx, ch; - - /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ - if (__JS_AtomIsTaggedInt(prop)) { - p = JS_VALUE_GET_OBJ(obj); - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { - p1 = JS_VALUE_GET_STRING(p->u.object_data); - idx = __JS_AtomToUInt32(prop); - if (idx < p1->len) { - if (desc) { - if (p1->is_wide_char) - ch = p1->u.str16[idx]; - else - ch = p1->u.str8[idx]; - desc->flags = JS_PROP_ENUMERABLE; - desc->value = js_new_string_char(ctx, ch); - desc->getter = JS_UNDEFINED; - desc->setter = JS_UNDEFINED; - } - return TRUE; - } - } - } - return FALSE; -} - -int js_string_define_own_property(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags) { - uint32_t idx; - JSObject* p; - JSString *p1, *p2; - - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - p = JS_VALUE_GET_OBJ(this_obj); - if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) - goto def; - p1 = JS_VALUE_GET_STRING(p->u.object_data); - if (idx >= p1->len) - goto def; - if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags)) - goto fail; - /* check that the same value is configured */ - if (flags & JS_PROP_HAS_VALUE) { - if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) - goto fail; - p2 = JS_VALUE_GET_STRING(val); - if (p2->len != 1) - goto fail; - if (string_get(p1, idx) != string_get(p2, 0)) { - fail: - return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); - } - } - return TRUE; - } else { - def: - return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, flags | JS_PROP_NO_EXOTIC); - } -} - -int js_string_delete_property(JSContext* ctx, JSValueConst obj, JSAtom prop) { - uint32_t idx; - - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - if (idx < js_string_obj_get_length(ctx, obj)) { - return FALSE; - } - } - return TRUE; -} - -static const JSClassExoticMethods js_string_exotic_methods = { - .get_own_property = js_string_get_own_property, - .define_own_property = js_string_define_own_property, - .delete_property = js_string_delete_property, -}; - -JSValue js_string_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { - JSValue val, obj; - if (argc == 0) { - val = JS_AtomToString(ctx, JS_ATOM_empty_string); - } else { - if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) { - JSAtomStruct* p = JS_VALUE_GET_PTR(argv[0]); - val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")"); - } else { - val = JS_ToString(ctx, argv[0]); - } - if (JS_IsException(val)) - return val; - } - if (!JS_IsUndefined(new_target)) { - JSString* p1 = JS_VALUE_GET_STRING(val); - - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); - if (!JS_IsException(obj)) { - JS_SetObjectData(ctx, obj, val); - JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); - } - return obj; - } else { - return val; - } -} - -JSValue js_thisStringValue(JSContext* ctx, JSValueConst this_val) { - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject* p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_STRING) { - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a string"); -} - -JSValue js_string_fromCharCode(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - int i; - StringBuffer b_s, *b = &b_s; - - string_buffer_init(ctx, b, argc); - - for (i = 0; i < argc; i++) { - int32_t c; - if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) { - string_buffer_free(b); - return JS_EXCEPTION; - } - } - return string_buffer_end(b); -} - -JSValue js_string_fromCodePoint(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - double d; - int i, c; - StringBuffer b_s, *b = &b_s; - - /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */ - - if (string_buffer_init(ctx, b, argc)) - goto fail; - for (i = 0; i < argc; i++) { - if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) { - c = JS_VALUE_GET_INT(argv[i]); - if (c < 0 || c > 0x10ffff) - goto range_error; - } else { - if (JS_ToFloat64(ctx, &d, argv[i])) - goto fail; - if (d < 0 || d > 0x10ffff || (c = (int)d) != d) - goto range_error; - } - if (string_buffer_putc(b, c)) - goto fail; - } - return string_buffer_end(b); - -range_error: - JS_ThrowRangeError(ctx, "invalid code point"); -fail: - string_buffer_free(b); - return JS_EXCEPTION; -} - -JSValue js_string_raw(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // raw(temp,...a) - JSValue cooked, val, raw; - StringBuffer b_s, *b = &b_s; - int64_t i, n; - - string_buffer_init(ctx, b, 0); - raw = JS_UNDEFINED; - cooked = JS_ToObject(ctx, argv[0]); - if (JS_IsException(cooked)) - goto exception; - raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw)); - if (JS_IsException(raw)) - goto exception; - if (js_get_length64(ctx, &n, raw) < 0) - goto exception; - - for (i = 0; i < n; i++) { - val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i)); - if (JS_IsException(val)) - goto exception; - string_buffer_concat_value_free(b, val); - if (i < n - 1 && i + 1 < argc) { - if (string_buffer_concat_value(b, argv[i + 1])) - goto exception; - } - } - JS_FreeValue(ctx, cooked); - JS_FreeValue(ctx, raw); - return string_buffer_end(b); - -exception: - JS_FreeValue(ctx, cooked); - JS_FreeValue(ctx, raw); - string_buffer_free(b); - return JS_EXCEPTION; -} - -/* only used in test262 */ -JSValue js_string_codePointRange(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - uint32_t start, end, i, n; - StringBuffer b_s, *b = &b_s; - - if (JS_ToUint32(ctx, &start, argv[0]) || JS_ToUint32(ctx, &end, argv[1])) - return JS_EXCEPTION; - end = min_uint32(end, 0x10ffff + 1); - - if (start > end) { - start = end; - } - n = end - start; - if (end > 0x10000) { - n += end - max_uint32(start, 0x10000); - } - if (string_buffer_init2(ctx, b, n, end >= 0x100)) - return JS_EXCEPTION; - for (i = start; i < end; i++) { - string_buffer_putc(b, i); - } - return string_buffer_end(b); -} - -#if 0 -JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int c; - if (JS_ToInt32(ctx, &c, argv[0])) - return JS_EXCEPTION; - return JS_NewBool(ctx, lre_is_space(c)); -} -#endif - -JSValue js_string_charCodeAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue val, ret; - JSString* p; - int idx, c; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - if (JS_ToInt32Sat(ctx, &idx, argv[0])) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (idx < 0 || idx >= p->len) { - ret = JS_NAN; - } else { - if (p->is_wide_char) - c = p->u.str16[idx]; - else - c = p->u.str8[idx]; - ret = JS_NewInt32(ctx, c); - } - JS_FreeValue(ctx, val); - return ret; -} - -JSValue js_string_charAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue val, ret; - JSString* p; - int idx, c; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - if (JS_ToInt32Sat(ctx, &idx, argv[0])) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (idx < 0 || idx >= p->len) { - ret = js_new_string8(ctx, NULL, 0); - } else { - if (p->is_wide_char) - c = p->u.str16[idx]; - else - c = p->u.str8[idx]; - ret = js_new_string_char(ctx, c); - } - JS_FreeValue(ctx, val); - return ret; -} - -JSValue js_string_codePointAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue val, ret; - JSString* p; - int idx, c; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - if (JS_ToInt32Sat(ctx, &idx, argv[0])) { - JS_FreeValue(ctx, val); - return JS_EXCEPTION; - } - if (idx < 0 || idx >= p->len) { - ret = JS_UNDEFINED; - } else { - c = string_getc(p, &idx); - ret = JS_NewInt32(ctx, c); - } - JS_FreeValue(ctx, val); - return ret; -} - -JSValue js_string_concat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue r; - int i; - - /* XXX: Use more efficient method */ - /* XXX: This method is OK if r has a single refcount */ - /* XXX: should use string_buffer? */ - r = JS_ToStringCheckObject(ctx, this_val); - for (i = 0; i < argc; i++) { - if (JS_IsException(r)) - break; - r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i])); - } - return r; -} - -int string_cmp(JSString* p1, JSString* p2, int x1, int x2, int len) { - int i, c1, c2; - for (i = 0; i < len; i++) { - if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i))) - return c1 - c2; - } - return 0; -} - -int string_indexof_char(JSString* p, int c, int from) { - /* assuming 0 <= from <= p->len */ - int i, len = p->len; - if (p->is_wide_char) { - for (i = from; i < len; i++) { - if (p->u.str16[i] == c) - return i; - } - } else { - if ((c & ~0xff) == 0) { - for (i = from; i < len; i++) { - if (p->u.str8[i] == (uint8_t)c) - return i; - } - } - } - return -1; -} - -int string_indexof(JSString* p1, JSString* p2, int from) { - /* assuming 0 <= from <= p1->len */ - int c, i, j, len1 = p1->len, len2 = p2->len; - if (len2 == 0) - return from; - for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) { - j = string_indexof_char(p1, c, i); - if (j < 0 || j + len2 > len1) - break; - if (!string_cmp(p1, p2, j + 1, 1, len2 - 1)) - return j; - } - return -1; -} - -int64_t string_advance_index(JSString* p, int64_t index, BOOL unicode) { - if (!unicode || index >= p->len || !p->is_wide_char) { - index++; - } else { - int index32 = (int)index; - string_getc(p, &index32); - index = index32; - } - return index; -} - -JSValue js_string_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int lastIndexOf) { - JSValue str, v; - int i, len, v_len, pos, start, stop, ret, inc; - JSString* p; - JSString* p1; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - v = JS_ToString(ctx, argv[0]); - if (JS_IsException(v)) - goto fail; - p = JS_VALUE_GET_STRING(str); - p1 = JS_VALUE_GET_STRING(v); - len = p->len; - v_len = p1->len; - if (lastIndexOf) { - pos = len - v_len; - if (argc > 1) { - double d; - if (JS_ToFloat64(ctx, &d, argv[1])) - goto fail; - if (!isnan(d)) { - if (d <= 0) - pos = 0; - else if (d < pos) - pos = d; - } - } - start = pos; - stop = 0; - inc = -1; - } else { - pos = 0; - if (argc > 1) { - if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) - goto fail; - } - start = pos; - stop = len - v_len; - inc = 1; - } - ret = -1; - if (len >= v_len && inc * (stop - start) >= 0) { - for (i = start;; i += inc) { - if (!string_cmp(p, p1, i, 0, v_len)) { - ret = i; - break; - } - if (i == stop) - break; - } - } - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, v); - return JS_NewInt32(ctx, ret); - -fail: - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, v); - return JS_EXCEPTION; -} - -/* return < 0 if exception or TRUE/FALSE */ -int js_is_regexp(JSContext* ctx, JSValueConst obj); - -JSValue js_string_includes(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - JSValue str, v = JS_UNDEFINED; - int i, len, v_len, pos, start, stop, ret; - JSString* p; - JSString* p1; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - ret = js_is_regexp(ctx, argv[0]); - if (ret) { - if (ret > 0) - JS_ThrowTypeError(ctx, "regex not supported"); - goto fail; - } - v = JS_ToString(ctx, argv[0]); - if (JS_IsException(v)) - goto fail; - p = JS_VALUE_GET_STRING(str); - p1 = JS_VALUE_GET_STRING(v); - len = p->len; - v_len = p1->len; - pos = (magic == 2) ? len : 0; - if (argc > 1 && !JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) - goto fail; - } - len -= v_len; - ret = 0; - if (magic == 0) { - start = pos; - stop = len; - } else { - if (magic == 1) { - if (pos > len) - goto done; - } else { - pos -= v_len; - } - start = stop = pos; - } - if (start >= 0 && start <= stop) { - for (i = start;; i++) { - if (!string_cmp(p, p1, i, 0, v_len)) { - ret = 1; - break; - } - if (i == stop) - break; - } - } -done: - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, v); - return JS_NewBool(ctx, ret); - -fail: - JS_FreeValue(ctx, str); - JS_FreeValue(ctx, v); - return JS_EXCEPTION; -} - -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; -} - -JSValue js_string_match(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int atom) { - // match(rx), search(rx), matchAll(rx) - // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll - JSValueConst O = this_val, regexp = argv[0], args[2]; - JSValue matcher, S, rx, result, str; - int args_len; - - if (JS_IsUndefined(O) || JS_IsNull(O)) - return JS_ThrowTypeError(ctx, "cannot convert to object"); - - if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) { - matcher = JS_GetProperty(ctx, regexp, atom); - if (JS_IsException(matcher)) - return JS_EXCEPTION; - if (atom == JS_ATOM_Symbol_matchAll) { - if (check_regexp_g_flag(ctx, regexp) < 0) { - JS_FreeValue(ctx, matcher); - return JS_EXCEPTION; - } - } - if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) { - return JS_CallFree(ctx, matcher, regexp, 1, &O); - } - } - S = JS_ToString(ctx, O); - if (JS_IsException(S)) - return JS_EXCEPTION; - args_len = 1; - args[0] = regexp; - str = JS_UNDEFINED; - if (atom == JS_ATOM_Symbol_matchAll) { - str = JS_NewString(ctx, "g"); - if (JS_IsException(str)) - goto fail; - args[args_len++] = (JSValueConst)str; - } - rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); - JS_FreeValue(ctx, str); - if (JS_IsException(rx)) { - fail: - JS_FreeValue(ctx, S); - return JS_EXCEPTION; - } - result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst*)&S); - JS_FreeValue(ctx, S); - return result; -} - -JSValue js_string___GetSubstitution(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // GetSubstitution(matched, str, position, captures, namedCaptures, rep) - JSValueConst matched, str, captures, namedCaptures, rep; - JSValue capture, name, s; - uint32_t position, len, matched_len, captures_len; - int i, j, j0, k, k1; - int c, c1; - StringBuffer b_s, *b = &b_s; - JSString *sp, *rp; - - matched = argv[0]; - str = argv[1]; - captures = argv[3]; - namedCaptures = argv[4]; - rep = argv[5]; - - if (!JS_IsString(rep) || !JS_IsString(str)) - return JS_ThrowTypeError(ctx, "not a string"); - - sp = JS_VALUE_GET_STRING(str); - rp = JS_VALUE_GET_STRING(rep); - - string_buffer_init(ctx, b, 0); - - captures_len = 0; - if (!JS_IsUndefined(captures)) { - if (js_get_length32(ctx, &captures_len, captures)) - goto exception; - } - if (js_get_length32(ctx, &matched_len, matched)) - goto exception; - if (JS_ToUint32(ctx, &position, argv[2]) < 0) - goto exception; - - len = rp->len; - i = 0; - for (;;) { - j = string_indexof_char(rp, '$', i); - if (j < 0 || j + 1 >= len) - break; - string_buffer_concat(b, rp, i, j); - j0 = j++; - c = string_get(rp, j++); - if (c == '$') { - string_buffer_putc8(b, '$'); - } else if (c == '&') { - if (string_buffer_concat_value(b, matched)) - goto exception; - } else if (c == '`') { - string_buffer_concat(b, sp, 0, position); - } else if (c == '\'') { - string_buffer_concat(b, sp, position + matched_len, sp->len); - } else if (c >= '0' && c <= '9') { - k = c - '0'; - if (j < len) { - c1 = string_get(rp, j); - if (c1 >= '0' && c1 <= '9') { - /* This behavior is specified in ES6 and refined in ECMA 2019 */ - /* ECMA 2019 does not have the extra test, but - Test262 S15.5.4.11_A3_T1..3 require this behavior */ - k1 = k * 10 + c1 - '0'; - if (k1 >= 1 && k1 < captures_len) { - k = k1; - j++; - } - } - } - if (k >= 1 && k < captures_len) { - s = JS_GetPropertyInt64(ctx, captures, k); - if (JS_IsException(s)) - goto exception; - if (!JS_IsUndefined(s)) { - if (string_buffer_concat_value_free(b, s)) - goto exception; - } - } else { - goto norep; - } - } else if (c == '<' && !JS_IsUndefined(namedCaptures)) { - k = string_indexof_char(rp, '>', j); - if (k < 0) - goto norep; - name = js_sub_string(ctx, rp, j, k); - if (JS_IsException(name)) - goto exception; - capture = JS_GetPropertyValue(ctx, namedCaptures, name); - if (JS_IsException(capture)) - goto exception; - if (!JS_IsUndefined(capture)) { - if (string_buffer_concat_value_free(b, capture)) - goto exception; - } - j = k + 1; - } else { - norep: - string_buffer_concat(b, rp, j0, j); - } - i = j; - } - string_buffer_concat(b, rp, i, rp->len); - return string_buffer_end(b); -exception: - string_buffer_free(b); - return JS_EXCEPTION; -} - -JSValue js_string_replace(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int is_replaceAll) { - // replace(rx, rep) - JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1]; - JSValueConst args[6]; - JSValue str, search_str, replaceValue_str, repl_str; - JSString *sp, *searchp; - StringBuffer b_s, *b = &b_s; - int pos, functionalReplace, endOfLastMatch; - BOOL is_first; - - if (JS_IsUndefined(O) || JS_IsNull(O)) - return JS_ThrowTypeError(ctx, "cannot convert to object"); - - search_str = JS_UNDEFINED; - replaceValue_str = JS_UNDEFINED; - repl_str = JS_UNDEFINED; - - if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) { - JSValue replacer; - if (is_replaceAll) { - if (check_regexp_g_flag(ctx, searchValue) < 0) - return JS_EXCEPTION; - } - replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace); - if (JS_IsException(replacer)) - return JS_EXCEPTION; - if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) { - args[0] = O; - args[1] = replaceValue; - return JS_CallFree(ctx, replacer, searchValue, 2, args); - } - } - string_buffer_init(ctx, b, 0); - - str = JS_ToString(ctx, O); - if (JS_IsException(str)) - goto exception; - search_str = JS_ToString(ctx, searchValue); - if (JS_IsException(search_str)) - goto exception; - functionalReplace = JS_IsFunction(ctx, replaceValue); - if (!functionalReplace) { - replaceValue_str = JS_ToString(ctx, replaceValue); - if (JS_IsException(replaceValue_str)) - goto exception; - } - - sp = JS_VALUE_GET_STRING(str); - searchp = JS_VALUE_GET_STRING(search_str); - endOfLastMatch = 0; - is_first = TRUE; - for (;;) { - if (unlikely(searchp->len == 0)) { - if (is_first) - pos = 0; - else if (endOfLastMatch >= sp->len) - pos = -1; - else - pos = endOfLastMatch + 1; - } else { - pos = string_indexof(sp, searchp, endOfLastMatch); - } - if (pos < 0) { - if (is_first) { - string_buffer_free(b); - JS_FreeValue(ctx, search_str); - JS_FreeValue(ctx, replaceValue_str); - return str; - } else { - break; - } - } - if (functionalReplace) { - args[0] = search_str; - args[1] = JS_NewInt32(ctx, pos); - args[2] = str; - repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args)); - } else { - args[0] = search_str; - args[1] = str; - args[2] = JS_NewInt32(ctx, pos); - args[3] = JS_UNDEFINED; - args[4] = JS_UNDEFINED; - args[5] = replaceValue_str; - repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); - } - if (JS_IsException(repl_str)) - goto exception; - - string_buffer_concat(b, sp, endOfLastMatch, pos); - string_buffer_concat_value_free(b, repl_str); - endOfLastMatch = pos + searchp->len; - is_first = FALSE; - if (!is_replaceAll) - break; - } - string_buffer_concat(b, sp, endOfLastMatch, sp->len); - JS_FreeValue(ctx, search_str); - JS_FreeValue(ctx, replaceValue_str); - JS_FreeValue(ctx, str); - return string_buffer_end(b); - -exception: - string_buffer_free(b); - JS_FreeValue(ctx, search_str); - JS_FreeValue(ctx, replaceValue_str); - JS_FreeValue(ctx, str); - return JS_EXCEPTION; -} - -JSValue js_string_split(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // split(sep, limit) - JSValueConst O = this_val, separator = argv[0], limit = argv[1]; - JSValueConst args[2]; - JSValue S, A, R, T; - uint32_t lim, lengthA; - int64_t p, q, s, r, e; - JSString *sp, *rp; - - if (JS_IsUndefined(O) || JS_IsNull(O)) - return JS_ThrowTypeError(ctx, "cannot convert to object"); - - S = JS_UNDEFINED; - A = JS_UNDEFINED; - R = JS_UNDEFINED; - - if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) { - JSValue splitter; - splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split); - if (JS_IsException(splitter)) - return JS_EXCEPTION; - if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) { - args[0] = O; - args[1] = limit; - return JS_CallFree(ctx, splitter, separator, 2, args); - } - } - S = JS_ToString(ctx, O); - if (JS_IsException(S)) - goto exception; - A = JS_NewArray(ctx); - if (JS_IsException(A)) - goto exception; - lengthA = 0; - if (JS_IsUndefined(limit)) { - lim = 0xffffffff; - } else { - if (JS_ToUint32(ctx, &lim, limit) < 0) - goto exception; - } - sp = JS_VALUE_GET_STRING(S); - s = sp->len; - R = JS_ToString(ctx, separator); - if (JS_IsException(R)) - goto exception; - rp = JS_VALUE_GET_STRING(R); - r = rp->len; - p = 0; - if (lim == 0) - goto done; - if (JS_IsUndefined(separator)) - goto add_tail; - if (s == 0) { - if (r != 0) - goto add_tail; - goto done; - } - q = p; - for (q = p; (q += !r) <= s - r - !r; q = p = e + r) { - e = string_indexof(sp, rp, q); - if (e < 0) - break; - T = js_sub_string(ctx, sp, p, e); - if (JS_IsException(T)) - goto exception; - if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) - goto exception; - if (lengthA == lim) - goto done; - } -add_tail: - T = js_sub_string(ctx, sp, p, s); - if (JS_IsException(T)) - goto exception; - if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) - goto exception; -done: - JS_FreeValue(ctx, S); - JS_FreeValue(ctx, R); - return A; - -exception: - JS_FreeValue(ctx, A); - JS_FreeValue(ctx, S); - JS_FreeValue(ctx, R); - return JS_EXCEPTION; -} - -JSValue js_string_substring(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue str, ret; - int a, b, start, end; - JSString* p; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - p = JS_VALUE_GET_STRING(str); - if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - b = p->len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - } - if (a < b) { - start = a; - end = b; - } else { - start = b; - end = a; - } - ret = js_sub_string(ctx, p, start, end); - JS_FreeValue(ctx, str); - return ret; -} - -JSValue js_string_substr(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue str, ret; - int a, len, n; - JSString* p; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - p = JS_VALUE_GET_STRING(str); - len = p->len; - if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - n = len - a; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - } - ret = js_sub_string(ctx, p, a, a + n); - JS_FreeValue(ctx, str); - return ret; -} - -JSValue js_string_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue str, ret; - int len, start, end; - JSString* p; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - p = JS_VALUE_GET_STRING(str); - len = p->len; - if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - end = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - } - ret = js_sub_string(ctx, p, start, max_int(end, start)); - JS_FreeValue(ctx, str); - return ret; -} - -JSValue js_string_pad(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int padEnd) { - JSValue str, v = JS_UNDEFINED; - StringBuffer b_s, *b = &b_s; - JSString *p, *p1 = NULL; - int n, len, c = ' '; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - goto fail1; - if (JS_ToInt32Sat(ctx, &n, argv[0])) - goto fail2; - p = JS_VALUE_GET_STRING(str); - len = p->len; - if (len >= n) - return str; - if (argc > 1 && !JS_IsUndefined(argv[1])) { - v = JS_ToString(ctx, argv[1]); - if (JS_IsException(v)) - goto fail2; - p1 = JS_VALUE_GET_STRING(v); - if (p1->len == 0) { - JS_FreeValue(ctx, v); - return str; - } - if (p1->len == 1) { - c = string_get(p1, 0); - p1 = NULL; - } - } - if (n > JS_STRING_LEN_MAX) { - JS_ThrowInternalError(ctx, "string too long"); - goto fail2; - } - if (string_buffer_init(ctx, b, n)) - goto fail3; - n -= len; - if (padEnd) { - if (string_buffer_concat(b, p, 0, len)) - goto fail; - } - if (p1) { - while (n > 0) { - int chunk = min_int(n, p1->len); - if (string_buffer_concat(b, p1, 0, chunk)) - goto fail; - n -= chunk; - } - } else { - if (string_buffer_fill(b, c, n)) - goto fail; - } - if (!padEnd) { - if (string_buffer_concat(b, p, 0, len)) - goto fail; - } - JS_FreeValue(ctx, v); - JS_FreeValue(ctx, str); - return string_buffer_end(b); - -fail: - string_buffer_free(b); -fail3: - JS_FreeValue(ctx, v); -fail2: - JS_FreeValue(ctx, str); -fail1: - return JS_EXCEPTION; -} - -JSValue js_string_repeat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue str; - StringBuffer b_s, *b = &b_s; - JSString* p; - int64_t val; - int n, len; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - goto fail; - if (JS_ToInt64Sat(ctx, &val, argv[0])) - goto fail; - if (val < 0 || val > 2147483647) { - JS_ThrowRangeError(ctx, "invalid repeat count"); - goto fail; - } - n = val; - p = JS_VALUE_GET_STRING(str); - len = p->len; - if (len == 0 || n == 1) - return str; - if (val * len > JS_STRING_LEN_MAX) { - JS_ThrowInternalError(ctx, "string too long"); - goto fail; - } - if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) - goto fail; - if (len == 1) { - string_buffer_fill(b, string_get(p, 0), n); - } else { - while (n-- > 0) { - string_buffer_concat(b, p, 0, len); - } - } - JS_FreeValue(ctx, str); - return string_buffer_end(b); - -fail: - JS_FreeValue(ctx, str); - return JS_EXCEPTION; -} - -JSValue js_string_trim(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - JSValue str, ret; - int a, b, len; - JSString* p; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return str; - p = JS_VALUE_GET_STRING(str); - a = 0; - b = len = p->len; - if (magic & 1) { - while (a < len && lre_is_space(string_get(p, a))) - a++; - } - if (magic & 2) { - while (b > a && lre_is_space(string_get(p, b - 1))) - b--; - } - ret = js_sub_string(ctx, p, a, b); - JS_FreeValue(ctx, str); - return ret; -} - -JSValue js_string___quote(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_ToQuotedString(ctx, this_val); -} - -/* return 0 if before the first char */ -int string_prevc(JSString* p, int* pidx) { - int idx, c, c1; - - idx = *pidx; - if (idx <= 0) - return 0; - idx--; - if (p->is_wide_char) { - c = p->u.str16[idx]; - if (c >= 0xdc00 && c < 0xe000 && idx > 0) { - c1 = p->u.str16[idx - 1]; - if (c1 >= 0xd800 && c1 <= 0xdc00) { - c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; - idx--; - } - } - } else { - c = p->u.str8[idx]; - } - *pidx = idx; - return c; -} - -BOOL test_final_sigma(JSString* p, int sigma_pos) { - int k, c1; - - /* before C: skip case ignorable chars and check there is - a cased letter */ - k = sigma_pos; - for (;;) { - c1 = string_prevc(p, &k); - if (!lre_is_case_ignorable(c1)) - break; - } - if (!lre_is_cased(c1)) - return FALSE; - - /* after C: skip case ignorable chars and check there is - no cased letter */ - k = sigma_pos + 1; - for (;;) { - if (k >= p->len) - return TRUE; - c1 = string_getc(p, &k); - if (!lre_is_case_ignorable(c1)) - break; - } - return !lre_is_cased(c1); -} - -JSValue js_string_localeCompare(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue a, b; - int cmp; - - a = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(a)) - return JS_EXCEPTION; - b = JS_ToString(ctx, argv[0]); - if (JS_IsException(b)) { - JS_FreeValue(ctx, a); - return JS_EXCEPTION; - } - cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); - JS_FreeValue(ctx, a); - JS_FreeValue(ctx, b); - return JS_NewInt32(ctx, cmp); -} - -JSValue js_string_toLowerCase(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int to_lower) { - JSValue val; - StringBuffer b_s, *b = &b_s; - JSString* p; - int i, c, j, l; - uint32_t res[LRE_CC_RES_LEN_MAX]; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - if (p->len == 0) - return val; - if (string_buffer_init(ctx, b, p->len)) - goto fail; - for (i = 0; i < p->len;) { - c = string_getc(p, &i); - if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) { - res[0] = 0x3c2; /* final sigma */ - l = 1; - } else { - l = lre_case_conv(res, c, to_lower); - } - for (j = 0; j < l; j++) { - if (string_buffer_putc(b, res[j])) - goto fail; - } - } - JS_FreeValue(ctx, val); - return string_buffer_end(b); -fail: - JS_FreeValue(ctx, val); - string_buffer_free(b); - return JS_EXCEPTION; -} - -#ifdef CONFIG_ALL_UNICODE - -/* return (-1, NULL) if exception, otherwise (len, buf) */ -int JS_ToUTF32String(JSContext* ctx, uint32_t** pbuf, JSValueConst val1) { - JSValue val; - JSString* p; - uint32_t* buf; - int i, j, len; - - val = JS_ToString(ctx, val1); - if (JS_IsException(val)) - return -1; - p = JS_VALUE_GET_STRING(val); - len = p->len; - /* UTF32 buffer length is len minus the number of correct surrogates pairs */ - buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1)); - if (!buf) { - JS_FreeValue(ctx, val); - goto fail; - } - for (i = j = 0; i < len;) - buf[j++] = string_getc(p, &i); - JS_FreeValue(ctx, val); - *pbuf = buf; - return j; -fail: - *pbuf = NULL; - return -1; -} - -JSValue JS_NewUTF32String(JSContext* ctx, const uint32_t* buf, int len) { - int i; - StringBuffer b_s, *b = &b_s; - if (string_buffer_init(ctx, b, len)) - return JS_EXCEPTION; - for (i = 0; i < len; i++) { - if (string_buffer_putc(b, buf[i])) - goto fail; - } - return string_buffer_end(b); -fail: - string_buffer_free(b); - return JS_EXCEPTION; -} - -JSValue js_string_normalize(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - const char *form, *p; - size_t form_len; - int is_compat, buf_len, out_len; - UnicodeNormalizationEnum n_type; - JSValue val; - uint32_t *buf, *out_buf; - - val = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(val)) - return val; - buf_len = JS_ToUTF32String(ctx, &buf, val); - JS_FreeValue(ctx, val); - if (buf_len < 0) - return JS_EXCEPTION; - - if (argc == 0 || JS_IsUndefined(argv[0])) { - n_type = UNICODE_NFC; - } else { - form = JS_ToCStringLen(ctx, &form_len, argv[0]); - if (!form) - goto fail1; - p = form; - if (p[0] != 'N' || p[1] != 'F') - goto bad_form; - p += 2; - is_compat = FALSE; - if (*p == 'K') { - is_compat = TRUE; - p++; - } - if (*p == 'C' || *p == 'D') { - n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C'); - if ((p + 1 - form) != form_len) - goto bad_form; - } else { - bad_form: - JS_FreeCString(ctx, form); - JS_ThrowRangeError(ctx, "bad normalization form"); - fail1: - js_free(ctx, buf); - return JS_EXCEPTION; - } - JS_FreeCString(ctx, form); - } - - out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, ctx->rt, (DynBufReallocFunc*)js_realloc_rt); - js_free(ctx, buf); - if (out_len < 0) - return JS_EXCEPTION; - val = JS_NewUTF32String(ctx, out_buf, out_len); - js_free(ctx, out_buf); - return val; -} -#endif /* CONFIG_ALL_UNICODE */ - -/* also used for String.prototype.valueOf */ -JSValue js_string_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return js_thisStringValue(ctx, this_val); -} - -#if 0 -JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToStringCheckObject(ctx, argv[0]); -} - -JSValue js_string___toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return JS_ToString(ctx, argv[0]); -} - -JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst - this_val, - int argc, JSValueConst *argv) -{ - JSValue str; - int idx; - BOOL is_unicode; - JSString *p; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return str; - if (JS_ToInt32Sat(ctx, &idx, argv[1])) { - JS_FreeValue(ctx, str); - return JS_EXCEPTION; - } - is_unicode = JS_ToBool(ctx, argv[2]); - p = JS_VALUE_GET_STRING(str); - if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) { - idx++; - } else { - string_getc(p, &idx); - } - JS_FreeValue(ctx, str); - return JS_NewInt32(ctx, idx); -} -#endif - -/* String Iterator */ - -JSValue js_string_iterator_next(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, BOOL* pdone, int magic) { - JSArrayIteratorData* it; - uint32_t idx, c, start; - JSString* p; - - it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR); - if (!it) { - *pdone = FALSE; - return JS_EXCEPTION; - } - if (JS_IsUndefined(it->obj)) - goto done; - p = JS_VALUE_GET_STRING(it->obj); - idx = it->idx; - if (idx >= p->len) { - JS_FreeValue(ctx, it->obj); - it->obj = JS_UNDEFINED; - done: - *pdone = TRUE; - return JS_UNDEFINED; - } - - start = idx; - c = string_getc(p, (int*)&idx); - it->idx = idx; - *pdone = FALSE; - if (c <= 0xffff) { - return js_new_string_char(ctx, c); - } else { - return js_new_string16(ctx, p->u.str16 + start, 2); - } -} - -JSValue js_string_CreateHTML(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - JSValue str; - const JSString* p; - StringBuffer b_s, *b = &b_s; - struct { - const char *tag, *attr; - } const defs[] = { - {"a", "name"}, {"big", NULL}, {"blink", NULL}, {"b", NULL}, {"tt", NULL}, {"font", "color"}, {"font", "size"}, - {"i", NULL}, {"a", "href"}, {"small", NULL}, {"strike", NULL}, {"sub", NULL}, {"sup", NULL}, - }; - - str = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(str)) - return JS_EXCEPTION; - string_buffer_init(ctx, b, 7); - string_buffer_putc8(b, '<'); - string_buffer_puts8(b, defs[magic].tag); - if (defs[magic].attr) { - // r += " " + attr + "=\"" + value + "\""; - JSValue value; - int i; - - string_buffer_putc8(b, ' '); - string_buffer_puts8(b, defs[magic].attr); - string_buffer_puts8(b, "=\""); - value = JS_ToStringCheckObject(ctx, argv[0]); - if (JS_IsException(value)) { - JS_FreeValue(ctx, str); - string_buffer_free(b); - return JS_EXCEPTION; - } - p = JS_VALUE_GET_STRING(value); - for (i = 0; i < p->len; i++) { - int c = string_get(p, i); - if (c == '"') { - string_buffer_puts8(b, """); - } else { - string_buffer_putc16(b, c); - } - } - JS_FreeValue(ctx, value); - string_buffer_putc8(b, '\"'); - } - // return r + ">" + str + ""; - string_buffer_putc8(b, '>'); - string_buffer_concat_value_free(b, str); - string_buffer_puts8(b, "'); - return string_buffer_end(b); -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-string.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../string.h" +#include "../types.h" +#include "js-function.h" +#include "js-object.h" +#include "js-array.h" +#include "quickjs/libregexp.h" + +/* String */ + +int js_string_get_own_property(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop) { + JSObject* p; + JSString* p1; + uint32_t idx, ch; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + if (__JS_AtomIsTaggedInt(prop)) { + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + if (desc) { + if (p1->is_wide_char) + ch = p1->u.str16[idx]; + else + ch = p1->u.str8[idx]; + desc->flags = JS_PROP_ENUMERABLE; + desc->value = js_new_string_char(ctx, ch); + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + } + return TRUE; + } + } + } + return FALSE; +} + +int js_string_define_own_property(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags) { + uint32_t idx; + JSObject* p; + JSString *p1, *p2; + + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + p = JS_VALUE_GET_OBJ(this_obj); + if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) + goto def; + p1 = JS_VALUE_GET_STRING(p->u.object_data); + if (idx >= p1->len) + goto def; + if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags)) + goto fail; + /* check that the same value is configured */ + if (flags & JS_PROP_HAS_VALUE) { + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + goto fail; + p2 = JS_VALUE_GET_STRING(val); + if (p2->len != 1) + goto fail; + if (string_get(p1, idx) != string_get(p2, 0)) { + fail: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + } + return TRUE; + } else { + def: + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, flags | JS_PROP_NO_EXOTIC); + } +} + +int js_string_delete_property(JSContext* ctx, JSValueConst obj, JSAtom prop) { + uint32_t idx; + + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < js_string_obj_get_length(ctx, obj)) { + return FALSE; + } + } + return TRUE; +} + +static const JSClassExoticMethods js_string_exotic_methods = { + .get_own_property = js_string_get_own_property, + .define_own_property = js_string_define_own_property, + .delete_property = js_string_delete_property, +}; + +JSValue js_string_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { + JSValue val, obj; + if (argc == 0) { + val = JS_AtomToString(ctx, JS_ATOM_empty_string); + } else { + if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) { + JSAtomStruct* p = JS_VALUE_GET_PTR(argv[0]); + val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")"); + } else { + val = JS_ToString(ctx, argv[0]); + } + if (JS_IsException(val)) + return val; + } + if (!JS_IsUndefined(new_target)) { + JSString* p1 = JS_VALUE_GET_STRING(val); + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); + if (!JS_IsException(obj)) { + JS_SetObjectData(ctx, obj, val); + JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); + } + return obj; + } else { + return val; + } +} + +JSValue js_thisStringValue(JSContext* ctx, JSValueConst this_val) { + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject* p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_STRING) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a string"); +} + +JSValue js_string_fromCharCode(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + int i; + StringBuffer b_s, *b = &b_s; + + string_buffer_init(ctx, b, argc); + + for (i = 0; i < argc; i++) { + int32_t c; + if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) { + string_buffer_free(b); + return JS_EXCEPTION; + } + } + return string_buffer_end(b); +} + +JSValue js_string_fromCodePoint(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + double d; + int i, c; + StringBuffer b_s, *b = &b_s; + + /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */ + + if (string_buffer_init(ctx, b, argc)) + goto fail; + for (i = 0; i < argc; i++) { + if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) { + c = JS_VALUE_GET_INT(argv[i]); + if (c < 0 || c > 0x10ffff) + goto range_error; + } else { + if (JS_ToFloat64(ctx, &d, argv[i])) + goto fail; + if (d < 0 || d > 0x10ffff || (c = (int)d) != d) + goto range_error; + } + if (string_buffer_putc(b, c)) + goto fail; + } + return string_buffer_end(b); + +range_error: + JS_ThrowRangeError(ctx, "invalid code point"); +fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +JSValue js_string_raw(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // raw(temp,...a) + JSValue cooked, val, raw; + StringBuffer b_s, *b = &b_s; + int64_t i, n; + + string_buffer_init(ctx, b, 0); + raw = JS_UNDEFINED; + cooked = JS_ToObject(ctx, argv[0]); + if (JS_IsException(cooked)) + goto exception; + raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw)); + if (JS_IsException(raw)) + goto exception; + if (js_get_length64(ctx, &n, raw) < 0) + goto exception; + + for (i = 0; i < n; i++) { + val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i)); + if (JS_IsException(val)) + goto exception; + string_buffer_concat_value_free(b, val); + if (i < n - 1 && i + 1 < argc) { + if (string_buffer_concat_value(b, argv[i + 1])) + goto exception; + } + } + JS_FreeValue(ctx, cooked); + JS_FreeValue(ctx, raw); + return string_buffer_end(b); + +exception: + JS_FreeValue(ctx, cooked); + JS_FreeValue(ctx, raw); + string_buffer_free(b); + return JS_EXCEPTION; +} + +/* only used in test262 */ +JSValue js_string_codePointRange(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + uint32_t start, end, i, n; + StringBuffer b_s, *b = &b_s; + + if (JS_ToUint32(ctx, &start, argv[0]) || JS_ToUint32(ctx, &end, argv[1])) + return JS_EXCEPTION; + end = min_uint32(end, 0x10ffff + 1); + + if (start > end) { + start = end; + } + n = end - start; + if (end > 0x10000) { + n += end - max_uint32(start, 0x10000); + } + if (string_buffer_init2(ctx, b, n, end >= 0x100)) + return JS_EXCEPTION; + for (i = start; i < end; i++) { + string_buffer_putc(b, i); + } + return string_buffer_end(b); +} + +#if 0 +JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int c; + if (JS_ToInt32(ctx, &c, argv[0])) + return JS_EXCEPTION; + return JS_NewBool(ctx, lre_is_space(c)); +} +#endif + +JSValue js_string_charCodeAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue val, ret; + JSString* p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = JS_NAN; + } else { + if (p->is_wide_char) + c = p->u.str16[idx]; + else + c = p->u.str8[idx]; + ret = JS_NewInt32(ctx, c); + } + JS_FreeValue(ctx, val); + return ret; +} + +JSValue js_string_charAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue val, ret; + JSString* p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = js_new_string8(ctx, NULL, 0); + } else { + if (p->is_wide_char) + c = p->u.str16[idx]; + else + c = p->u.str8[idx]; + ret = js_new_string_char(ctx, c); + } + JS_FreeValue(ctx, val); + return ret; +} + +JSValue js_string_codePointAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue val, ret; + JSString* p; + int idx, c; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (JS_ToInt32Sat(ctx, &idx, argv[0])) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (idx < 0 || idx >= p->len) { + ret = JS_UNDEFINED; + } else { + c = string_getc(p, &idx); + ret = JS_NewInt32(ctx, c); + } + JS_FreeValue(ctx, val); + return ret; +} + +JSValue js_string_concat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue r; + int i; + + /* XXX: Use more efficient method */ + /* XXX: This method is OK if r has a single refcount */ + /* XXX: should use string_buffer? */ + r = JS_ToStringCheckObject(ctx, this_val); + for (i = 0; i < argc; i++) { + if (JS_IsException(r)) + break; + r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i])); + } + return r; +} + +int string_cmp(JSString* p1, JSString* p2, int x1, int x2, int len) { + int i, c1, c2; + for (i = 0; i < len; i++) { + if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i))) + return c1 - c2; + } + return 0; +} + +int string_indexof_char(JSString* p, int c, int from) { + /* assuming 0 <= from <= p->len */ + int i, len = p->len; + if (p->is_wide_char) { + for (i = from; i < len; i++) { + if (p->u.str16[i] == c) + return i; + } + } else { + if ((c & ~0xff) == 0) { + for (i = from; i < len; i++) { + if (p->u.str8[i] == (uint8_t)c) + return i; + } + } + } + return -1; +} + +int string_indexof(JSString* p1, JSString* p2, int from) { + /* assuming 0 <= from <= p1->len */ + int c, i, j, len1 = p1->len, len2 = p2->len; + if (len2 == 0) + return from; + for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) { + j = string_indexof_char(p1, c, i); + if (j < 0 || j + len2 > len1) + break; + if (!string_cmp(p1, p2, j + 1, 1, len2 - 1)) + return j; + } + return -1; +} + +int64_t string_advance_index(JSString* p, int64_t index, BOOL unicode) { + if (!unicode || index >= p->len || !p->is_wide_char) { + index++; + } else { + int index32 = (int)index; + string_getc(p, &index32); + index = index32; + } + return index; +} + +JSValue js_string_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int lastIndexOf) { + JSValue str, v; + int i, len, v_len, pos, start, stop, ret, inc; + JSString* p; + JSString* p1; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + v = JS_ToString(ctx, argv[0]); + if (JS_IsException(v)) + goto fail; + p = JS_VALUE_GET_STRING(str); + p1 = JS_VALUE_GET_STRING(v); + len = p->len; + v_len = p1->len; + if (lastIndexOf) { + pos = len - v_len; + if (argc > 1) { + double d; + if (JS_ToFloat64(ctx, &d, argv[1])) + goto fail; + if (!isnan(d)) { + if (d <= 0) + pos = 0; + else if (d < pos) + pos = d; + } + } + start = pos; + stop = 0; + inc = -1; + } else { + pos = 0; + if (argc > 1) { + if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) + goto fail; + } + start = pos; + stop = len - v_len; + inc = 1; + } + ret = -1; + if (len >= v_len && inc * (stop - start) >= 0) { + for (i = start;; i += inc) { + if (!string_cmp(p, p1, i, 0, v_len)) { + ret = i; + break; + } + if (i == stop) + break; + } + } + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_NewInt32(ctx, ret); + +fail: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_EXCEPTION; +} + +/* return < 0 if exception or TRUE/FALSE */ +int js_is_regexp(JSContext* ctx, JSValueConst obj); + +JSValue js_string_includes(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + JSValue str, v = JS_UNDEFINED; + int i, len, v_len, pos, start, stop, ret; + JSString* p; + JSString* p1; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + ret = js_is_regexp(ctx, argv[0]); + if (ret) { + if (ret > 0) + JS_ThrowTypeError(ctx, "regex not supported"); + goto fail; + } + v = JS_ToString(ctx, argv[0]); + if (JS_IsException(v)) + goto fail; + p = JS_VALUE_GET_STRING(str); + p1 = JS_VALUE_GET_STRING(v); + len = p->len; + v_len = p1->len; + pos = (magic == 2) ? len : 0; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) + goto fail; + } + len -= v_len; + ret = 0; + if (magic == 0) { + start = pos; + stop = len; + } else { + if (magic == 1) { + if (pos > len) + goto done; + } else { + pos -= v_len; + } + start = stop = pos; + } + if (start >= 0 && start <= stop) { + for (i = start;; i++) { + if (!string_cmp(p, p1, i, 0, v_len)) { + ret = 1; + break; + } + if (i == stop) + break; + } + } +done: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_NewBool(ctx, ret); + +fail: + JS_FreeValue(ctx, str); + JS_FreeValue(ctx, v); + return JS_EXCEPTION; +} + +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; +} + +JSValue js_string_match(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int atom) { + // match(rx), search(rx), matchAll(rx) + // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll + JSValueConst O = this_val, regexp = argv[0], args[2]; + JSValue matcher, S, rx, result, str; + int args_len; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) { + matcher = JS_GetProperty(ctx, regexp, atom); + if (JS_IsException(matcher)) + return JS_EXCEPTION; + if (atom == JS_ATOM_Symbol_matchAll) { + if (check_regexp_g_flag(ctx, regexp) < 0) { + JS_FreeValue(ctx, matcher); + return JS_EXCEPTION; + } + } + if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) { + return JS_CallFree(ctx, matcher, regexp, 1, &O); + } + } + S = JS_ToString(ctx, O); + if (JS_IsException(S)) + return JS_EXCEPTION; + args_len = 1; + args[0] = regexp; + str = JS_UNDEFINED; + if (atom == JS_ATOM_Symbol_matchAll) { + str = JS_NewString(ctx, "g"); + if (JS_IsException(str)) + goto fail; + args[args_len++] = (JSValueConst)str; + } + rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); + JS_FreeValue(ctx, str); + if (JS_IsException(rx)) { + fail: + JS_FreeValue(ctx, S); + return JS_EXCEPTION; + } + result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst*)&S); + JS_FreeValue(ctx, S); + return result; +} + +JSValue js_string___GetSubstitution(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // GetSubstitution(matched, str, position, captures, namedCaptures, rep) + JSValueConst matched, str, captures, namedCaptures, rep; + JSValue capture, name, s; + uint32_t position, len, matched_len, captures_len; + int i, j, j0, k, k1; + int c, c1; + StringBuffer b_s, *b = &b_s; + JSString *sp, *rp; + + matched = argv[0]; + str = argv[1]; + captures = argv[3]; + namedCaptures = argv[4]; + rep = argv[5]; + + if (!JS_IsString(rep) || !JS_IsString(str)) + return JS_ThrowTypeError(ctx, "not a string"); + + sp = JS_VALUE_GET_STRING(str); + rp = JS_VALUE_GET_STRING(rep); + + string_buffer_init(ctx, b, 0); + + captures_len = 0; + if (!JS_IsUndefined(captures)) { + if (js_get_length32(ctx, &captures_len, captures)) + goto exception; + } + if (js_get_length32(ctx, &matched_len, matched)) + goto exception; + if (JS_ToUint32(ctx, &position, argv[2]) < 0) + goto exception; + + len = rp->len; + i = 0; + for (;;) { + j = string_indexof_char(rp, '$', i); + if (j < 0 || j + 1 >= len) + break; + string_buffer_concat(b, rp, i, j); + j0 = j++; + c = string_get(rp, j++); + if (c == '$') { + string_buffer_putc8(b, '$'); + } else if (c == '&') { + if (string_buffer_concat_value(b, matched)) + goto exception; + } else if (c == '`') { + string_buffer_concat(b, sp, 0, position); + } else if (c == '\'') { + string_buffer_concat(b, sp, position + matched_len, sp->len); + } else if (c >= '0' && c <= '9') { + k = c - '0'; + if (j < len) { + c1 = string_get(rp, j); + if (c1 >= '0' && c1 <= '9') { + /* This behavior is specified in ES6 and refined in ECMA 2019 */ + /* ECMA 2019 does not have the extra test, but + Test262 S15.5.4.11_A3_T1..3 require this behavior */ + k1 = k * 10 + c1 - '0'; + if (k1 >= 1 && k1 < captures_len) { + k = k1; + j++; + } + } + } + if (k >= 1 && k < captures_len) { + s = JS_GetPropertyInt64(ctx, captures, k); + if (JS_IsException(s)) + goto exception; + if (!JS_IsUndefined(s)) { + if (string_buffer_concat_value_free(b, s)) + goto exception; + } + } else { + goto norep; + } + } else if (c == '<' && !JS_IsUndefined(namedCaptures)) { + k = string_indexof_char(rp, '>', j); + if (k < 0) + goto norep; + name = js_sub_string(ctx, rp, j, k); + if (JS_IsException(name)) + goto exception; + capture = JS_GetPropertyValue(ctx, namedCaptures, name); + if (JS_IsException(capture)) + goto exception; + if (!JS_IsUndefined(capture)) { + if (string_buffer_concat_value_free(b, capture)) + goto exception; + } + j = k + 1; + } else { + norep: + string_buffer_concat(b, rp, j0, j); + } + i = j; + } + string_buffer_concat(b, rp, i, rp->len); + return string_buffer_end(b); +exception: + string_buffer_free(b); + return JS_EXCEPTION; +} + +JSValue js_string_replace(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int is_replaceAll) { + // replace(rx, rep) + JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1]; + JSValueConst args[6]; + JSValue str, search_str, replaceValue_str, repl_str; + JSString *sp, *searchp; + StringBuffer b_s, *b = &b_s; + int pos, functionalReplace, endOfLastMatch; + BOOL is_first; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + search_str = JS_UNDEFINED; + replaceValue_str = JS_UNDEFINED; + repl_str = JS_UNDEFINED; + + if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) { + JSValue replacer; + if (is_replaceAll) { + if (check_regexp_g_flag(ctx, searchValue) < 0) + return JS_EXCEPTION; + } + replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace); + if (JS_IsException(replacer)) + return JS_EXCEPTION; + if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) { + args[0] = O; + args[1] = replaceValue; + return JS_CallFree(ctx, replacer, searchValue, 2, args); + } + } + string_buffer_init(ctx, b, 0); + + str = JS_ToString(ctx, O); + if (JS_IsException(str)) + goto exception; + search_str = JS_ToString(ctx, searchValue); + if (JS_IsException(search_str)) + goto exception; + functionalReplace = JS_IsFunction(ctx, replaceValue); + if (!functionalReplace) { + replaceValue_str = JS_ToString(ctx, replaceValue); + if (JS_IsException(replaceValue_str)) + goto exception; + } + + sp = JS_VALUE_GET_STRING(str); + searchp = JS_VALUE_GET_STRING(search_str); + endOfLastMatch = 0; + is_first = TRUE; + for (;;) { + if (unlikely(searchp->len == 0)) { + if (is_first) + pos = 0; + else if (endOfLastMatch >= sp->len) + pos = -1; + else + pos = endOfLastMatch + 1; + } else { + pos = string_indexof(sp, searchp, endOfLastMatch); + } + if (pos < 0) { + if (is_first) { + string_buffer_free(b); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + return str; + } else { + break; + } + } + if (functionalReplace) { + args[0] = search_str; + args[1] = JS_NewInt32(ctx, pos); + args[2] = str; + repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args)); + } else { + args[0] = search_str; + args[1] = str; + args[2] = JS_NewInt32(ctx, pos); + args[3] = JS_UNDEFINED; + args[4] = JS_UNDEFINED; + args[5] = replaceValue_str; + repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); + } + if (JS_IsException(repl_str)) + goto exception; + + string_buffer_concat(b, sp, endOfLastMatch, pos); + string_buffer_concat_value_free(b, repl_str); + endOfLastMatch = pos + searchp->len; + is_first = FALSE; + if (!is_replaceAll) + break; + } + string_buffer_concat(b, sp, endOfLastMatch, sp->len); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +exception: + string_buffer_free(b); + JS_FreeValue(ctx, search_str); + JS_FreeValue(ctx, replaceValue_str); + JS_FreeValue(ctx, str); + return JS_EXCEPTION; +} + +JSValue js_string_split(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // split(sep, limit) + JSValueConst O = this_val, separator = argv[0], limit = argv[1]; + JSValueConst args[2]; + JSValue S, A, R, T; + uint32_t lim, lengthA; + int64_t p, q, s, r, e; + JSString *sp, *rp; + + if (JS_IsUndefined(O) || JS_IsNull(O)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + + S = JS_UNDEFINED; + A = JS_UNDEFINED; + R = JS_UNDEFINED; + + if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) { + JSValue splitter; + splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split); + if (JS_IsException(splitter)) + return JS_EXCEPTION; + if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) { + args[0] = O; + args[1] = limit; + return JS_CallFree(ctx, splitter, separator, 2, args); + } + } + S = JS_ToString(ctx, O); + if (JS_IsException(S)) + goto exception; + A = JS_NewArray(ctx); + if (JS_IsException(A)) + goto exception; + lengthA = 0; + if (JS_IsUndefined(limit)) { + lim = 0xffffffff; + } else { + if (JS_ToUint32(ctx, &lim, limit) < 0) + goto exception; + } + sp = JS_VALUE_GET_STRING(S); + s = sp->len; + R = JS_ToString(ctx, separator); + if (JS_IsException(R)) + goto exception; + rp = JS_VALUE_GET_STRING(R); + r = rp->len; + p = 0; + if (lim == 0) + goto done; + if (JS_IsUndefined(separator)) + goto add_tail; + if (s == 0) { + if (r != 0) + goto add_tail; + goto done; + } + q = p; + for (q = p; (q += !r) <= s - r - !r; q = p = e + r) { + e = string_indexof(sp, rp, q); + if (e < 0) + break; + T = js_sub_string(ctx, sp, p, e); + if (JS_IsException(T)) + goto exception; + if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) + goto exception; + if (lengthA == lim) + goto done; + } +add_tail: + T = js_sub_string(ctx, sp, p, s); + if (JS_IsException(T)) + goto exception; + if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) + goto exception; +done: + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, R); + return A; + +exception: + JS_FreeValue(ctx, A); + JS_FreeValue(ctx, S); + JS_FreeValue(ctx, R); + return JS_EXCEPTION; +} + +JSValue js_string_substring(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue str, ret; + int a, b, start, end; + JSString* p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + b = p->len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + if (a < b) { + start = a; + end = b; + } else { + start = b; + end = a; + } + ret = js_sub_string(ctx, p, start, end); + JS_FreeValue(ctx, str); + return ret; +} + +JSValue js_string_substr(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue str, ret; + int a, len, n; + JSString* p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + n = len - a; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + ret = js_sub_string(ctx, p, a, a + n); + JS_FreeValue(ctx, str); + return ret; +} + +JSValue js_string_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue str, ret; + int len, start, end; + JSString* p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + end = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + } + ret = js_sub_string(ctx, p, start, max_int(end, start)); + JS_FreeValue(ctx, str); + return ret; +} + +JSValue js_string_pad(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int padEnd) { + JSValue str, v = JS_UNDEFINED; + StringBuffer b_s, *b = &b_s; + JSString *p, *p1 = NULL; + int n, len, c = ' '; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + goto fail1; + if (JS_ToInt32Sat(ctx, &n, argv[0])) + goto fail2; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (len >= n) + return str; + if (argc > 1 && !JS_IsUndefined(argv[1])) { + v = JS_ToString(ctx, argv[1]); + if (JS_IsException(v)) + goto fail2; + p1 = JS_VALUE_GET_STRING(v); + if (p1->len == 0) { + JS_FreeValue(ctx, v); + return str; + } + if (p1->len == 1) { + c = string_get(p1, 0); + p1 = NULL; + } + } + if (n > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + goto fail2; + } + if (string_buffer_init(ctx, b, n)) + goto fail3; + n -= len; + if (padEnd) { + if (string_buffer_concat(b, p, 0, len)) + goto fail; + } + if (p1) { + while (n > 0) { + int chunk = min_int(n, p1->len); + if (string_buffer_concat(b, p1, 0, chunk)) + goto fail; + n -= chunk; + } + } else { + if (string_buffer_fill(b, c, n)) + goto fail; + } + if (!padEnd) { + if (string_buffer_concat(b, p, 0, len)) + goto fail; + } + JS_FreeValue(ctx, v); + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + string_buffer_free(b); +fail3: + JS_FreeValue(ctx, v); +fail2: + JS_FreeValue(ctx, str); +fail1: + return JS_EXCEPTION; +} + +JSValue js_string_repeat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue str; + StringBuffer b_s, *b = &b_s; + JSString* p; + int64_t val; + int n, len; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + goto fail; + if (JS_ToInt64Sat(ctx, &val, argv[0])) + goto fail; + if (val < 0 || val > 2147483647) { + JS_ThrowRangeError(ctx, "invalid repeat count"); + goto fail; + } + n = val; + p = JS_VALUE_GET_STRING(str); + len = p->len; + if (len == 0 || n == 1) + return str; + if (val * len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + goto fail; + } + if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) + goto fail; + if (len == 1) { + string_buffer_fill(b, string_get(p, 0), n); + } else { + while (n-- > 0) { + string_buffer_concat(b, p, 0, len); + } + } + JS_FreeValue(ctx, str); + return string_buffer_end(b); + +fail: + JS_FreeValue(ctx, str); + return JS_EXCEPTION; +} + +JSValue js_string_trim(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + JSValue str, ret; + int a, b, len; + JSString* p; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return str; + p = JS_VALUE_GET_STRING(str); + a = 0; + b = len = p->len; + if (magic & 1) { + while (a < len && lre_is_space(string_get(p, a))) + a++; + } + if (magic & 2) { + while (b > a && lre_is_space(string_get(p, b - 1))) + b--; + } + ret = js_sub_string(ctx, p, a, b); + JS_FreeValue(ctx, str); + return ret; +} + +JSValue js_string___quote(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_ToQuotedString(ctx, this_val); +} + +/* return 0 if before the first char */ +int string_prevc(JSString* p, int* pidx) { + int idx, c, c1; + + idx = *pidx; + if (idx <= 0) + return 0; + idx--; + if (p->is_wide_char) { + c = p->u.str16[idx]; + if (c >= 0xdc00 && c < 0xe000 && idx > 0) { + c1 = p->u.str16[idx - 1]; + if (c1 >= 0xd800 && c1 <= 0xdc00) { + c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; + idx--; + } + } + } else { + c = p->u.str8[idx]; + } + *pidx = idx; + return c; +} + +BOOL test_final_sigma(JSString* p, int sigma_pos) { + int k, c1; + + /* before C: skip case ignorable chars and check there is + a cased letter */ + k = sigma_pos; + for (;;) { + c1 = string_prevc(p, &k); + if (!lre_is_case_ignorable(c1)) + break; + } + if (!lre_is_cased(c1)) + return FALSE; + + /* after C: skip case ignorable chars and check there is + no cased letter */ + k = sigma_pos + 1; + for (;;) { + if (k >= p->len) + return TRUE; + c1 = string_getc(p, &k); + if (!lre_is_case_ignorable(c1)) + break; + } + return !lre_is_cased(c1); +} + +JSValue js_string_localeCompare(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue a, b; + int cmp; + + a = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(a)) + return JS_EXCEPTION; + b = JS_ToString(ctx, argv[0]); + if (JS_IsException(b)) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); + JS_FreeValue(ctx, a); + JS_FreeValue(ctx, b); + return JS_NewInt32(ctx, cmp); +} + +JSValue js_string_toLowerCase(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int to_lower) { + JSValue val; + StringBuffer b_s, *b = &b_s; + JSString* p; + int i, c, j, l; + uint32_t res[LRE_CC_RES_LEN_MAX]; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + if (p->len == 0) + return val; + if (string_buffer_init(ctx, b, p->len)) + goto fail; + for (i = 0; i < p->len;) { + c = string_getc(p, &i); + if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) { + res[0] = 0x3c2; /* final sigma */ + l = 1; + } else { + l = lre_case_conv(res, c, to_lower); + } + for (j = 0; j < l; j++) { + if (string_buffer_putc(b, res[j])) + goto fail; + } + } + JS_FreeValue(ctx, val); + return string_buffer_end(b); +fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} + +#ifdef CONFIG_ALL_UNICODE + +/* return (-1, NULL) if exception, otherwise (len, buf) */ +int JS_ToUTF32String(JSContext* ctx, uint32_t** pbuf, JSValueConst val1) { + JSValue val; + JSString* p; + uint32_t* buf; + int i, j, len; + + val = JS_ToString(ctx, val1); + if (JS_IsException(val)) + return -1; + p = JS_VALUE_GET_STRING(val); + len = p->len; + /* UTF32 buffer length is len minus the number of correct surrogates pairs */ + buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1)); + if (!buf) { + JS_FreeValue(ctx, val); + goto fail; + } + for (i = j = 0; i < len;) + buf[j++] = string_getc(p, &i); + JS_FreeValue(ctx, val); + *pbuf = buf; + return j; +fail: + *pbuf = NULL; + return -1; +} + +JSValue JS_NewUTF32String(JSContext* ctx, const uint32_t* buf, int len) { + int i; + StringBuffer b_s, *b = &b_s; + if (string_buffer_init(ctx, b, len)) + return JS_EXCEPTION; + for (i = 0; i < len; i++) { + if (string_buffer_putc(b, buf[i])) + goto fail; + } + return string_buffer_end(b); +fail: + string_buffer_free(b); + return JS_EXCEPTION; +} + +JSValue js_string_normalize(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + const char *form, *p; + size_t form_len; + int is_compat, buf_len, out_len; + UnicodeNormalizationEnum n_type; + JSValue val; + uint32_t *buf, *out_buf; + + val = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(val)) + return val; + buf_len = JS_ToUTF32String(ctx, &buf, val); + JS_FreeValue(ctx, val); + if (buf_len < 0) + return JS_EXCEPTION; + + if (argc == 0 || JS_IsUndefined(argv[0])) { + n_type = UNICODE_NFC; + } else { + form = JS_ToCStringLen(ctx, &form_len, argv[0]); + if (!form) + goto fail1; + p = form; + if (p[0] != 'N' || p[1] != 'F') + goto bad_form; + p += 2; + is_compat = FALSE; + if (*p == 'K') { + is_compat = TRUE; + p++; + } + if (*p == 'C' || *p == 'D') { + n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C'); + if ((p + 1 - form) != form_len) + goto bad_form; + } else { + bad_form: + JS_FreeCString(ctx, form); + JS_ThrowRangeError(ctx, "bad normalization form"); + fail1: + js_free(ctx, buf); + return JS_EXCEPTION; + } + JS_FreeCString(ctx, form); + } + + out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, ctx->rt, (DynBufReallocFunc*)js_realloc_rt); + js_free(ctx, buf); + if (out_len < 0) + return JS_EXCEPTION; + val = JS_NewUTF32String(ctx, out_buf, out_len); + js_free(ctx, out_buf); + return val; +} +#endif /* CONFIG_ALL_UNICODE */ + +/* also used for String.prototype.valueOf */ +JSValue js_string_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return js_thisStringValue(ctx, this_val); +} + +#if 0 +JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToStringCheckObject(ctx, argv[0]); +} + +JSValue js_string___toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return JS_ToString(ctx, argv[0]); +} + +JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst + this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + int idx; + BOOL is_unicode; + JSString *p; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return str; + if (JS_ToInt32Sat(ctx, &idx, argv[1])) { + JS_FreeValue(ctx, str); + return JS_EXCEPTION; + } + is_unicode = JS_ToBool(ctx, argv[2]); + p = JS_VALUE_GET_STRING(str); + if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) { + idx++; + } else { + string_getc(p, &idx); + } + JS_FreeValue(ctx, str); + return JS_NewInt32(ctx, idx); +} +#endif + +/* String Iterator */ + +JSValue js_string_iterator_next(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, BOOL* pdone, int magic) { + JSArrayIteratorData* it; + uint32_t idx, c, start; + JSString* p; + + it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR); + if (!it) { + *pdone = FALSE; + return JS_EXCEPTION; + } + if (JS_IsUndefined(it->obj)) + goto done; + p = JS_VALUE_GET_STRING(it->obj); + idx = it->idx; + if (idx >= p->len) { + JS_FreeValue(ctx, it->obj); + it->obj = JS_UNDEFINED; + done: + *pdone = TRUE; + return JS_UNDEFINED; + } + + start = idx; + c = string_getc(p, (int*)&idx); + it->idx = idx; + *pdone = FALSE; + if (c <= 0xffff) { + return js_new_string_char(ctx, c); + } else { + return js_new_string16(ctx, p->u.str16 + start, 2); + } +} + +JSValue js_string_CreateHTML(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + JSValue str; + const JSString* p; + StringBuffer b_s, *b = &b_s; + struct { + const char *tag, *attr; + } const defs[] = { + {"a", "name"}, {"big", NULL}, {"blink", NULL}, {"b", NULL}, {"tt", NULL}, {"font", "color"}, {"font", "size"}, + {"i", NULL}, {"a", "href"}, {"small", NULL}, {"strike", NULL}, {"sub", NULL}, {"sup", NULL}, + }; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + string_buffer_init(ctx, b, 7); + string_buffer_putc8(b, '<'); + string_buffer_puts8(b, defs[magic].tag); + if (defs[magic].attr) { + // r += " " + attr + "=\"" + value + "\""; + JSValue value; + int i; + + string_buffer_putc8(b, ' '); + string_buffer_puts8(b, defs[magic].attr); + string_buffer_puts8(b, "=\""); + value = JS_ToStringCheckObject(ctx, argv[0]); + if (JS_IsException(value)) { + JS_FreeValue(ctx, str); + string_buffer_free(b); + return JS_EXCEPTION; + } + p = JS_VALUE_GET_STRING(value); + for (i = 0; i < p->len; i++) { + int c = string_get(p, i); + if (c == '"') { + string_buffer_puts8(b, """); + } else { + string_buffer_putc16(b, c); + } + } + JS_FreeValue(ctx, value); + string_buffer_putc8(b, '\"'); + } + // return r + ">" + str + ""; + string_buffer_putc8(b, '>'); + string_buffer_concat_value_free(b, str); + string_buffer_puts8(b, "'); + return string_buffer_end(b); +} diff --git a/src/core/builtins/js-string.h b/src/core/builtins/js-string.h index 2f6142e39..f2d0f4150 100644 --- a/src/core/builtins/js-string.h +++ b/src/core/builtins/js-string.h @@ -1,91 +1,91 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_STRING_H -#define QUICKJS_JS_STRING_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "../types.h" - -/* ES6 Annex B 2.3.2 etc. */ -enum { - magic_string_anchor, - magic_string_big, - magic_string_blink, - magic_string_bold, - magic_string_fixed, - magic_string_fontcolor, - magic_string_fontsize, - magic_string_italics, - magic_string_link, - magic_string_small, - magic_string_strike, - magic_string_sub, - magic_string_sup, -}; - -int js_string_get_own_property(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop); -int js_string_define_own_property(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); -int js_string_delete_property(JSContext* ctx, JSValueConst obj, JSAtom prop); -JSValue js_string_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); -JSValue js_thisStringValue(JSContext* ctx, JSValueConst this_val) ; -JSValue js_string_fromCharCode(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_fromCodePoint(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_raw(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -/* only used in test262 */ -JSValue js_string_codePointRange(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_charCodeAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_charAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_codePointAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_concat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -int string_cmp(JSString* p1, JSString* p2, int x1, int x2, int len); -int string_indexof_char(JSString* p, int c, int from); -int string_indexof(JSString* p1, JSString* p2, int from); -int64_t string_advance_index(JSString* p, int64_t index, BOOL unicode); -JSValue js_string_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int lastIndexOf) ; -int js_is_regexp(JSContext* ctx, JSValueConst obj);; -JSValue js_string_includes(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -int check_regexp_g_flag(JSContext* ctx, JSValueConst regexp); -JSValue js_string_match(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int atom); -JSValue js_string___GetSubstitution(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_replace(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int is_replaceAll); -JSValue js_string_split(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_substring(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_substr(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_pad(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int padEnd); -JSValue js_string_repeat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_trim(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); -JSValue js_string___quote(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -int string_prevc(JSString* p, int* pidx); -BOOL test_final_sigma(JSString* p, int sigma_pos); -JSValue js_string_localeCompare(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_toLowerCase(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int to_lower); -JSValue js_string_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); -JSValue js_string_iterator_next(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, BOOL* pdone, int magic); -JSValue js_string_CreateHTML(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_STRING_H +#define QUICKJS_JS_STRING_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "../types.h" + +/* ES6 Annex B 2.3.2 etc. */ +enum { + magic_string_anchor, + magic_string_big, + magic_string_blink, + magic_string_bold, + magic_string_fixed, + magic_string_fontcolor, + magic_string_fontsize, + magic_string_italics, + magic_string_link, + magic_string_small, + magic_string_strike, + magic_string_sub, + magic_string_sup, +}; + +int js_string_get_own_property(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop); +int js_string_define_own_property(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); +int js_string_delete_property(JSContext* ctx, JSValueConst obj, JSAtom prop); +JSValue js_string_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv); +JSValue js_thisStringValue(JSContext* ctx, JSValueConst this_val) ; +JSValue js_string_fromCharCode(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_fromCodePoint(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_raw(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +/* only used in test262 */ +JSValue js_string_codePointRange(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_charCodeAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_charAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_codePointAt(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_concat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +int string_cmp(JSString* p1, JSString* p2, int x1, int x2, int len); +int string_indexof_char(JSString* p, int c, int from); +int string_indexof(JSString* p1, JSString* p2, int from); +int64_t string_advance_index(JSString* p, int64_t index, BOOL unicode); +JSValue js_string_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int lastIndexOf) ; +int js_is_regexp(JSContext* ctx, JSValueConst obj);; +JSValue js_string_includes(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +int check_regexp_g_flag(JSContext* ctx, JSValueConst regexp); +JSValue js_string_match(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int atom); +JSValue js_string___GetSubstitution(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_replace(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int is_replaceAll); +JSValue js_string_split(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_substring(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_substr(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_pad(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int padEnd); +JSValue js_string_repeat(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_trim(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); +JSValue js_string___quote(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +int string_prevc(JSString* p, int* pidx); +BOOL test_final_sigma(JSString* p, int sigma_pos); +JSValue js_string_localeCompare(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_toLowerCase(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int to_lower); +JSValue js_string_toString(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv); +JSValue js_string_iterator_next(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, BOOL* pdone, int magic); +JSValue js_string_CreateHTML(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic); + #endif \ No newline at end of file diff --git a/src/core/builtins/js-symbol.c b/src/core/builtins/js-symbol.c index 547e83006..32d21edea 100644 --- a/src/core/builtins/js-symbol.c +++ b/src/core/builtins/js-symbol.c @@ -1,127 +1,127 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-symbol.h" -#include "../exception.h" -#include "../string.h" -#include "../types.h" -#include "js-string.h" - -/* Symbol */ - -JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv) -{ - JSValue str; - JSString *p; - - if (!JS_IsUndefined(new_target)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (argc == 0 || JS_IsUndefined(argv[0])) { - p = NULL; - } else { - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return JS_EXCEPTION; - p = JS_VALUE_GET_STRING(str); - } - return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL); -} - -JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val) -{ - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL) - return JS_DupValue(ctx, this_val); - - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); - if (p->class_id == JS_CLASS_SYMBOL) { - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL) - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_ThrowTypeError(ctx, "not a symbol"); -} - -JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue val, ret; - val = js_thisSymbolValue(ctx, this_val); - if (JS_IsException(val)) - return val; - /* XXX: use JS_ToStringInternal() with a flags */ - ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val); - JS_FreeValue(ctx, val); - return ret; -} - -JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_thisSymbolValue(ctx, this_val); -} - -JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val) -{ - JSValue val, ret; - JSAtomStruct *p; - - val = js_thisSymbolValue(ctx, this_val); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_PTR(val); - if (p->len == 0 && p->is_wide_char != 0) { - ret = JS_UNDEFINED; - } else { - ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)); - } - JS_FreeValue(ctx, val); - return ret; -} - -JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue str; - - str = JS_ToString(ctx, argv[0]); - if (JS_IsException(str)) - return JS_EXCEPTION; - return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL); -} - -JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSAtomStruct *p; - - if (!JS_IsSymbol(argv[0])) - return JS_ThrowTypeError(ctx, "not a symbol"); - p = JS_VALUE_GET_PTR(argv[0]); - if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL) - return JS_UNDEFINED; - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-symbol.h" +#include "../exception.h" +#include "../string.h" +#include "../types.h" +#include "js-string.h" + +/* Symbol */ + +JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv) +{ + JSValue str; + JSString *p; + + if (!JS_IsUndefined(new_target)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (argc == 0 || JS_IsUndefined(argv[0])) { + p = NULL; + } else { + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return JS_EXCEPTION; + p = JS_VALUE_GET_STRING(str); + } + return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL); +} + +JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val) +{ + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL) + return JS_DupValue(ctx, this_val); + + if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id == JS_CLASS_SYMBOL) { + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL) + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue val, ret; + val = js_thisSymbolValue(ctx, this_val); + if (JS_IsException(val)) + return val; + /* XXX: use JS_ToStringInternal() with a flags */ + ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val); + JS_FreeValue(ctx, val); + return ret; +} + +JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_thisSymbolValue(ctx, this_val); +} + +JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val) +{ + JSValue val, ret; + JSAtomStruct *p; + + val = js_thisSymbolValue(ctx, this_val); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_PTR(val); + if (p->len == 0 && p->is_wide_char != 0) { + ret = JS_UNDEFINED; + } else { + ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)); + } + JS_FreeValue(ctx, val); + return ret; +} + +JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + + str = JS_ToString(ctx, argv[0]); + if (JS_IsException(str)) + return JS_EXCEPTION; + return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL); +} + +JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSAtomStruct *p; + + if (!JS_IsSymbol(argv[0])) + return JS_ThrowTypeError(ctx, "not a symbol"); + p = JS_VALUE_GET_PTR(argv[0]); + if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL) + return JS_UNDEFINED; + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); } \ No newline at end of file diff --git a/src/core/builtins/js-symbol.h b/src/core/builtins/js-symbol.h index f3db3081f..b9f8814a7 100644 --- a/src/core/builtins/js-symbol.h +++ b/src/core/builtins/js-symbol.h @@ -1,44 +1,44 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_SYMBOL_H -#define QUICKJS_JS_SYMBOL_H - -#include "quickjs/quickjs.h" - -JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, - int argc, JSValueConst *argv); -JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val); -JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val); -JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); -JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - -#endif +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_SYMBOL_H +#define QUICKJS_JS_SYMBOL_H + +#include "quickjs/quickjs.h" + +JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, + int argc, JSValueConst *argv); +JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val); +JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val); +JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +#endif diff --git a/src/core/builtins/js-typed-array.c b/src/core/builtins/js-typed-array.c index 8a4c0c603..e1a65ce94 100644 --- a/src/core/builtins/js-typed-array.c +++ b/src/core/builtins/js-typed-array.c @@ -1,2269 +1,2269 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "js-typed-array.h" -#include "../convertion.h" -#include "../exception.h" -#include "../function.h" -#include "../object.h" -#include "../runtime.h" -#include "../string.h" -#include "js-array.h" -#include "js-atomics.h" -#include "js-function.h" -#include "js-object.h" -#include "js-operator.h" - -/* Typed Arrays */ - -static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {0, 0, 0, 1, 1, 2, 2, -#ifdef CONFIG_BIGNUM - 3, 3, /* BigInt64Array, BigUint64Array */ -#endif - 2, 3}; - -JSValue -js_array_buffer_constructor3(JSContext* ctx, JSValueConst new_target, uint64_t len, JSClassID class_id, uint8_t* buf, JSFreeArrayBufferDataFunc* free_func, void* opaque, BOOL alloc_flag) { - JSRuntime* rt = ctx->rt; - JSValue obj; - JSArrayBuffer* abuf = NULL; - - obj = js_create_from_ctor(ctx, new_target, class_id); - if (JS_IsException(obj)) - return obj; - /* XXX: we are currently limited to 2 GB */ - if (len > INT32_MAX) { - JS_ThrowRangeError(ctx, "invalid array buffer length"); - goto fail; - } - abuf = js_malloc(ctx, sizeof(*abuf)); - if (!abuf) - goto fail; - abuf->byte_length = len; - if (alloc_flag) { - if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && rt->sab_funcs.sab_alloc) { - abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, max_int(len, 1)); - if (!abuf->data) - goto fail; - memset(abuf->data, 0, len); - } else { - /* the allocation must be done after the object creation */ - abuf->data = js_mallocz(ctx, max_int(len, 1)); - if (!abuf->data) - goto fail; - } - } else { - if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && rt->sab_funcs.sab_dup) { - rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf); - } - abuf->data = buf; - } - init_list_head(&abuf->array_list); - abuf->detached = FALSE; - abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER); - abuf->opaque = opaque; - abuf->free_func = free_func; - if (alloc_flag && buf) - memcpy(abuf->data, buf, len); - JS_SetOpaque(obj, abuf); - return obj; -fail: - JS_FreeValue(ctx, obj); - js_free(ctx, abuf); - return JS_EXCEPTION; -} - -void js_array_buffer_free(JSRuntime* rt, void* opaque, void* ptr) { - js_free_rt(rt, ptr); -} - -JSValue js_array_buffer_constructor2(JSContext* ctx, JSValueConst new_target, uint64_t len, JSClassID class_id) { - return js_array_buffer_constructor3(ctx, new_target, len, class_id, NULL, js_array_buffer_free, NULL, TRUE); -} - -JSValue js_array_buffer_constructor1(JSContext* ctx, JSValueConst new_target, uint64_t len) { - return js_array_buffer_constructor2(ctx, new_target, len, JS_CLASS_ARRAY_BUFFER); -} - -JSValue JS_NewArrayBuffer(JSContext* ctx, uint8_t* buf, size_t len, JSFreeArrayBufferDataFunc* free_func, void* opaque, BOOL is_shared) { - return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER, buf, free_func, opaque, FALSE); -} - -/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */ -JSValue JS_NewArrayBufferCopy(JSContext* ctx, const uint8_t* buf, size_t len) { - return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, JS_CLASS_ARRAY_BUFFER, (uint8_t*)buf, js_array_buffer_free, NULL, TRUE); -} - -JSValue js_array_buffer_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { - uint64_t len; - if (JS_ToIndex(ctx, &len, argv[0])) - return JS_EXCEPTION; - return js_array_buffer_constructor1(ctx, new_target, len); -} - -JSValue js_shared_array_buffer_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { - uint64_t len; - if (JS_ToIndex(ctx, &len, argv[0])) - return JS_EXCEPTION; - return js_array_buffer_constructor2(ctx, new_target, len, JS_CLASS_SHARED_ARRAY_BUFFER); -} - -/* also used for SharedArrayBuffer */ -void js_array_buffer_finalizer(JSRuntime* rt, JSValue val) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSArrayBuffer* abuf = p->u.array_buffer; - if (abuf) { - /* The ArrayBuffer finalizer may be called before the typed - array finalizers using it, so abuf->array_list is not - necessarily empty. */ - // assert(list_empty(&abuf->array_list)); - if (abuf->shared && rt->sab_funcs.sab_free) { - rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); - } else { - if (abuf->free_func) - abuf->free_func(rt, abuf->opaque, abuf->data); - } - js_free_rt(rt, abuf); - } -} - -JSValue js_array_buffer_isView(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSObject* p; - BOOL res; - res = FALSE; - if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(argv[0]); - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_DATAVIEW) { - res = TRUE; - } - } - return JS_NewBool(ctx, res); -} - -const JSCFunctionListEntry js_array_buffer_funcs[] = { - JS_CFUNC_DEF("isView", 1, js_array_buffer_isView), - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), -}; - -JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext* ctx) { - return JS_ThrowTypeError(ctx, "ArrayBuffer is detached"); -} - -JSValue js_array_buffer_get_byteLength(JSContext* ctx, JSValueConst this_val, int class_id) { - JSArrayBuffer* abuf = JS_GetOpaque2(ctx, this_val, class_id); - if (!abuf) - return JS_EXCEPTION; - /* return 0 if detached */ - return JS_NewUint32(ctx, abuf->byte_length); -} - -void JS_DetachArrayBuffer(JSContext* ctx, JSValueConst obj) { - JSArrayBuffer* abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); - struct list_head* el; - - if (!abuf || abuf->detached) - return; - if (abuf->free_func) - abuf->free_func(ctx->rt, abuf->opaque, abuf->data); - abuf->data = NULL; - abuf->byte_length = 0; - abuf->detached = TRUE; - - list_for_each(el, &abuf->array_list) { - JSTypedArray* ta; - JSObject* p; - - ta = list_entry(el, JSTypedArray, link); - p = ta->obj; - /* Note: the typed array length and offset fields are not modified */ - if (p->class_id != JS_CLASS_DATAVIEW) { - p->u.array.count = 0; - p->u.array.u.ptr = NULL; - } - } -} - -/* get an ArrayBuffer or SharedArrayBuffer */ -JSArrayBuffer* js_get_array_buffer(JSContext* ctx, JSValueConst obj) { - JSObject* p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - goto fail; - p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ARRAY_BUFFER && p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { - fail: - JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER); - return NULL; - } - return p->u.array_buffer; -} - -/* return NULL if exception. WARNING: any JS call can detach the - buffer and render the returned pointer invalid */ -uint8_t* JS_GetArrayBuffer(JSContext* ctx, size_t* psize, JSValueConst obj) { - JSArrayBuffer* abuf = js_get_array_buffer(ctx, obj); - if (!abuf) - goto fail; - if (abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - *psize = abuf->byte_length; - return abuf->data; -fail: - *psize = 0; - return NULL; -} - -JSValue js_array_buffer_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int class_id) { - JSArrayBuffer *abuf, *new_abuf; - int64_t len, start, end, new_len; - JSValue ctor, new_obj; - - abuf = JS_GetOpaque2(ctx, this_val, class_id); - if (!abuf) - return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - len = abuf->byte_length; - - if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) - return JS_EXCEPTION; - - end = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len)) - return JS_EXCEPTION; - } - new_len = max_int64(end - start, 0); - ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); - if (JS_IsException(ctor)) - return ctor; - if (JS_IsUndefined(ctor)) { - new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, class_id); - } else { - JSValue args[1]; - args[0] = JS_NewInt64(ctx, new_len); - new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst*)args); - JS_FreeValue(ctx, ctor); - JS_FreeValue(ctx, args[0]); - } - if (JS_IsException(new_obj)) - return new_obj; - new_abuf = JS_GetOpaque2(ctx, new_obj, class_id); - if (!new_abuf) - goto fail; - if (js_same_value(ctx, new_obj, this_val)) { - JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer"); - goto fail; - } - if (new_abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - if (new_abuf->byte_length < new_len) { - JS_ThrowTypeError(ctx, "new ArrayBuffer is too small"); - goto fail; - } - /* must test again because of side effects */ - if (abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - memcpy(new_abuf->data, abuf->data + start, new_len); - return new_obj; -fail: - JS_FreeValue(ctx, new_obj); - return JS_EXCEPTION; -} - -const JSCFunctionListEntry js_array_buffer_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER), - JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE), -}; - -/* SharedArrayBuffer */ - -const JSCFunctionListEntry js_shared_array_buffer_funcs[] = { - JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), -}; - -const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER), - JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE), -}; - -JSObject* get_typed_array(JSContext* ctx, JSValueConst this_val, int is_dataview) { - JSObject* p; - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - goto fail; - p = JS_VALUE_GET_OBJ(this_val); - if (is_dataview) { - if (p->class_id != JS_CLASS_DATAVIEW) - goto fail; - } else { - if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { - fail: - JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray"); - return NULL; - } - } - return p; -} - -/* WARNING: 'p' must be a typed array */ -BOOL typed_array_is_detached(JSContext* ctx, JSObject* p) { - JSTypedArray* ta = p->u.typed_array; - JSArrayBuffer* abuf = ta->buffer->u.array_buffer; - /* XXX: could simplify test by ensuring that - p->u.array.u.ptr is NULL iff it is detached */ - return abuf->detached; -} - - -int validate_typed_array(JSContext* ctx, JSValueConst this_val) { - JSObject* p; - p = get_typed_array(ctx, this_val, 0); - if (!p) - return -1; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } - return 0; -} - -JSValue js_typed_array_get_length(JSContext* ctx, JSValueConst this_val) { - JSObject* p; - p = get_typed_array(ctx, this_val, 0); - if (!p) - return JS_EXCEPTION; - return JS_NewInt32(ctx, p->u.array.count); -} - -JSValue js_typed_array_get_buffer(JSContext* ctx, JSValueConst this_val, int is_dataview) { - JSObject* p; - JSTypedArray* ta; - p = get_typed_array(ctx, this_val, is_dataview); - if (!p) - return JS_EXCEPTION; - ta = p->u.typed_array; - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); -} - -JSValue js_typed_array_get_byteLength(JSContext* ctx, JSValueConst this_val, int is_dataview) { - JSObject* p; - JSTypedArray* ta; - p = get_typed_array(ctx, this_val, is_dataview); - if (!p) - return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) { - if (is_dataview) { - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - } else { - return JS_NewInt32(ctx, 0); - } - } - ta = p->u.typed_array; - return JS_NewInt32(ctx, ta->length); -} - -JSValue js_typed_array_get_byteOffset(JSContext* ctx, JSValueConst this_val, int is_dataview) { - JSObject* p; - JSTypedArray* ta; - p = get_typed_array(ctx, this_val, is_dataview); - if (!p) - return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) { - if (is_dataview) { - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - } else { - return JS_NewInt32(ctx, 0); - } - } - ta = p->u.typed_array; - return JS_NewInt32(ctx, ta->offset); -} - -/* Return the buffer associated to the typed array or an exception if - it is not a typed array or if the buffer is detached. pbyte_offset, - pbyte_length or pbytes_per_element can be NULL. */ -JSValue JS_GetTypedArrayBuffer(JSContext* ctx, JSValueConst obj, size_t* pbyte_offset, size_t* pbyte_length, size_t* pbytes_per_element) { - JSObject* p; - JSTypedArray* ta; - p = get_typed_array(ctx, obj, FALSE); - if (!p) - return JS_EXCEPTION; - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - ta = p->u.typed_array; - if (pbyte_offset) - *pbyte_offset = ta->offset; - if (pbyte_length) - *pbyte_length = ta->length; - if (pbytes_per_element) { - *pbytes_per_element = 1 << typed_array_size_log2(p->class_id); - } - return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); -} - -JSValue js_typed_array_get_toStringTag(JSContext* ctx, JSValueConst this_val) { - JSObject* p; - if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) - return JS_UNDEFINED; - p = JS_VALUE_GET_OBJ(this_val); - if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY)) - return JS_UNDEFINED; - return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name); -} - -JSValue js_typed_array_set_internal(JSContext* ctx, JSValueConst dst, JSValueConst src, JSValueConst off) { - JSObject* p; - JSObject* src_p; - uint32_t i; - int64_t src_len, offset; - JSValue val, src_obj = JS_UNDEFINED; - - p = get_typed_array(ctx, dst, 0); - if (!p) - goto fail; - if (JS_ToInt64Sat(ctx, &offset, off)) - goto fail; - if (offset < 0) - goto range_error; - if (typed_array_is_detached(ctx, p)) { - detached: - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - src_obj = JS_ToObject(ctx, src); - if (JS_IsException(src_obj)) - goto fail; - src_p = JS_VALUE_GET_OBJ(src_obj); - if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY && src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - JSTypedArray* dest_ta = p->u.typed_array; - JSArrayBuffer* dest_abuf = dest_ta->buffer->u.array_buffer; - JSTypedArray* src_ta = src_p->u.typed_array; - JSArrayBuffer* src_abuf = src_ta->buffer->u.array_buffer; - int shift = typed_array_size_log2(p->class_id); - - if (src_abuf->detached) - goto detached; - - src_len = src_p->u.array.count; - if (offset > (int64_t)(p->u.array.count - src_len)) - goto range_error; - - /* copying between typed objects */ - if (src_p->class_id == p->class_id) { - /* same type, use memmove */ - memmove(dest_abuf->data + dest_ta->offset + (offset << shift), src_abuf->data + src_ta->offset, src_len << shift); - goto done; - } - if (dest_abuf->data == src_abuf->data) { - /* copying between the same buffer using different types of mappings - would require a temporary buffer */ - } - /* otherwise, default behavior is slow but correct */ - } else { - if (js_get_length64(ctx, &src_len, src_obj)) - goto fail; - if (offset > (int64_t)(p->u.array.count - src_len)) { - range_error: - JS_ThrowRangeError(ctx, "invalid array length"); - goto fail; - } - } - for (i = 0; i < src_len; i++) { - val = JS_GetPropertyUint32(ctx, src_obj, i); - if (JS_IsException(val)) - goto fail; - if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0) - goto fail; - } -done: - JS_FreeValue(ctx, src_obj); - return JS_UNDEFINED; -fail: - JS_FreeValue(ctx, src_obj); - return JS_EXCEPTION; -} - -JSValue js_typed_array_set(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValueConst offset = JS_UNDEFINED; - if (argc > 1) { - offset = argv[1]; - } - return js_typed_array_set_internal(ctx, this_val, argv[0], offset); -} - -JSValue js_create_typed_array_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { - if (validate_typed_array(ctx, this_val)) - return JS_EXCEPTION; - return js_create_array_iterator(ctx, this_val, argc, argv, magic); -} - -/* return < 0 if exception */ -int js_typed_array_get_length_internal(JSContext* ctx, JSValueConst obj) { - JSObject* p; - p = get_typed_array(ctx, obj, 0); - if (!p) - return -1; - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - return -1; - } - return p->u.array.count; -} - -#if 0 -/* validate a typed array and return its length */ -JSValue js_typed_array___getLength(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - BOOL ignore_detached = JS_ToBool(ctx, argv[1]); - - if (ignore_detached) { - return js_typed_array_get_length(ctx, argv[0]); - } else { - int len; - len = js_typed_array_get_length_internal(ctx, argv[0]); - if (len < 0) - return JS_EXCEPTION; - return JS_NewInt32(ctx, len); - } -} -#endif - -JSValue js_typed_array_create(JSContext* ctx, JSValueConst ctor, int argc, JSValueConst* argv) { - JSValue ret; - int new_len; - int64_t len; - - ret = JS_CallConstructor(ctx, ctor, argc, argv); - if (JS_IsException(ret)) - return ret; - /* validate the typed array */ - new_len = js_typed_array_get_length_internal(ctx, ret); - if (new_len < 0) - goto fail; - if (argc == 1) { - /* ensure that it is large enough */ - if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) - goto fail; - if (new_len < len) { - JS_ThrowTypeError(ctx, "TypedArray length is too small"); - fail: - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; - } - } - return ret; -} - -#if 0 -JSValue js_typed_array___create(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) -{ - return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1); -} -#endif - -JSValue js_typed_array___speciesCreate(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValueConst obj; - JSObject* p; - JSValue ctor, ret; - int argc1; - - obj = argv[0]; - p = get_typed_array(ctx, obj, 0); - if (!p) - return JS_EXCEPTION; - ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED); - if (JS_IsException(ctor)) - return ctor; - argc1 = max_int(argc - 1, 0); - if (JS_IsUndefined(ctor)) { - ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1, p->class_id); - } else { - ret = js_typed_array_create(ctx, ctor, argc1, argv + 1); - JS_FreeValue(ctx, ctor); - } - return ret; -} - -JSValue js_typed_array_from(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - // from(items, mapfn = void 0, this_arg = void 0) - JSValueConst items = argv[0], mapfn, this_arg; - JSValueConst args[2]; - JSValue stack[2]; - JSValue iter, arr, r, v, v2; - int64_t k, len; - int done, mapping; - - mapping = FALSE; - mapfn = JS_UNDEFINED; - this_arg = JS_UNDEFINED; - r = JS_UNDEFINED; - arr = JS_UNDEFINED; - stack[0] = JS_UNDEFINED; - stack[1] = JS_UNDEFINED; - - if (argc > 1) { - mapfn = argv[1]; - if (!JS_IsUndefined(mapfn)) { - if (check_function(ctx, mapfn)) - goto exception; - mapping = 1; - if (argc > 2) - this_arg = argv[2]; - } - } - iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); - if (JS_IsException(iter)) - goto exception; - if (!JS_IsUndefined(iter)) { - JS_FreeValue(ctx, iter); - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - goto exception; - stack[0] = JS_DupValue(ctx, items); - if (js_for_of_start(ctx, &stack[1], FALSE)) - goto exception; - for (k = 0;; k++) { - v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); - if (JS_IsException(v)) - goto exception_close; - if (done) - break; - if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) - goto exception_close; - } - } else { - arr = JS_ToObject(ctx, items); - if (JS_IsException(arr)) - goto exception; - } - if (js_get_length64(ctx, &len, arr) < 0) - goto exception; - v = JS_NewInt64(ctx, len); - args[0] = v; - r = js_typed_array_create(ctx, this_val, 1, args); - JS_FreeValue(ctx, v); - if (JS_IsException(r)) - goto exception; - for (k = 0; k < len; k++) { - v = JS_GetPropertyInt64(ctx, arr, k); - if (JS_IsException(v)) - goto exception; - if (mapping) { - args[0] = v; - args[1] = JS_NewInt32(ctx, k); - v2 = JS_Call(ctx, mapfn, this_arg, 2, args); - JS_FreeValue(ctx, v); - v = v2; - if (JS_IsException(v)) - goto exception; - } - if (JS_SetPropertyInt64(ctx, r, k, v) < 0) - goto exception; - } - goto done; - -exception_close: - if (!JS_IsUndefined(stack[0])) - JS_IteratorClose(ctx, stack[0], TRUE); -exception: - JS_FreeValue(ctx, r); - r = JS_EXCEPTION; -done: - JS_FreeValue(ctx, arr); - JS_FreeValue(ctx, stack[0]); - JS_FreeValue(ctx, stack[1]); - return r; -} - -JSValue js_typed_array_of(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValue obj; - JSValueConst args[1]; - int i; - - args[0] = JS_NewInt32(ctx, argc); - obj = js_typed_array_create(ctx, this_val, 1, args); - if (JS_IsException(obj)) - return obj; - - for (i = 0; i < argc; i++) { - if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - } - return obj; -} - -JSValue js_typed_array_copyWithin(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSObject* p; - int len, to, from, final, count, shift; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - return JS_EXCEPTION; - - if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len)) - return JS_EXCEPTION; - - if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len)) - return JS_EXCEPTION; - - final = len; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) - return JS_EXCEPTION; - } - - count = min_int(final - from, len - to); - if (count > 0) { - p = JS_VALUE_GET_OBJ(this_val); - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - shift = typed_array_size_log2(p->class_id); - memmove(p->u.array.u.uint8_ptr + (to << shift), p->u.array.u.uint8_ptr + (from << shift), count << shift); - } - return JS_DupValue(ctx, this_val); -} - -JSValue js_typed_array_fill(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSObject* p; - int len, k, final, shift; - uint64_t v64; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - return JS_EXCEPTION; - p = JS_VALUE_GET_OBJ(this_val); - - if (p->class_id == JS_CLASS_UINT8C_ARRAY) { - int32_t v; - if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0]))) - return JS_EXCEPTION; - v64 = v; - } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) { - uint32_t v; - if (JS_ToUint32(ctx, &v, argv[0])) - return JS_EXCEPTION; - v64 = v; - } else -#ifdef CONFIG_BIGNUM - if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { - if (JS_ToBigInt64(ctx, (int64_t*)&v64, argv[0])) - return JS_EXCEPTION; - } else -#endif - { - double d; - if (JS_ToFloat64(ctx, &d, argv[0])) - return JS_EXCEPTION; - if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { - union { - float f; - uint32_t u32; - } u; - u.f = d; - v64 = u.u32; - } else { - JSFloat64Union u; - u.d = d; - v64 = u.u64; - } - } - - k = 0; - if (argc > 1) { - if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) - return JS_EXCEPTION; - } - - final = len; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) - return JS_EXCEPTION; - } - - if (typed_array_is_detached(ctx, p)) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - - shift = typed_array_size_log2(p->class_id); - switch (shift) { - case 0: - if (k < final) { - memset(p->u.array.u.uint8_ptr + k, v64, final - k); - } - break; - case 1: - for (; k < final; k++) { - p->u.array.u.uint16_ptr[k] = v64; - } - break; - case 2: - for (; k < final; k++) { - p->u.array.u.uint32_ptr[k] = v64; - } - break; - case 3: - for (; k < final; k++) { - p->u.array.u.uint64_ptr[k] = v64; - } - break; - default: - abort(); - } - return JS_DupValue(ctx, this_val); -} - -JSValue js_typed_array_find(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int findIndex) { - JSValueConst func, this_arg; - JSValueConst args[3]; - JSValue val, index_val, res; - int len, k; - - val = JS_UNDEFINED; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - goto exception; - - func = argv[0]; - if (check_function(ctx, func)) - goto exception; - - this_arg = JS_UNDEFINED; - if (argc > 1) - this_arg = argv[1]; - - for (k = 0; k < len; k++) { - index_val = JS_NewInt32(ctx, k); - val = JS_GetPropertyValue(ctx, this_val, index_val); - if (JS_IsException(val)) - goto exception; - args[0] = val; - args[1] = index_val; - args[2] = this_val; - res = JS_Call(ctx, func, this_arg, 3, args); - if (JS_IsException(res)) - goto exception; - if (JS_ToBoolFree(ctx, res)) { - if (findIndex) { - JS_FreeValue(ctx, val); - return index_val; - } else { - return val; - } - } - JS_FreeValue(ctx, val); - } - if (findIndex) - return JS_NewInt32(ctx, -1); - else - return JS_UNDEFINED; - -exception: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -#define special_indexOf 0 -#define special_lastIndexOf 1 -#define special_includes -1 - -JSValue js_typed_array_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special) { - JSObject* p; - int len, tag, is_int, is_bigint, k, stop, inc, res = -1; - int64_t v64; - double d; - float f; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - goto exception; - if (len == 0) - goto done; - - if (special == special_lastIndexOf) { - k = len - 1; - if (argc > 1) { - if (JS_ToFloat64(ctx, &d, argv[1])) - goto exception; - if (isnan(d)) { - k = 0; - } else { - if (d >= 0) { - if (d < k) { - k = d; - } - } else { - d += len; - if (d < 0) - goto done; - k = d; - } - } - } - stop = -1; - inc = -1; - } else { - k = 0; - if (argc > 1) { - if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) - goto exception; - } - stop = len; - inc = 1; - } - - p = JS_VALUE_GET_OBJ(this_val); - /* if the array was detached, no need to go further (but no - exception is raised) */ - if (typed_array_is_detached(ctx, p)) { - /* "includes" scans all the properties, so "undefined" can match */ - if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0) - res = 0; - goto done; - } - - is_bigint = 0; - is_int = 0; /* avoid warning */ - v64 = 0; /* avoid warning */ - tag = JS_VALUE_GET_NORM_TAG(argv[0]); - if (tag == JS_TAG_INT) { - is_int = 1; - v64 = JS_VALUE_GET_INT(argv[0]); - d = v64; - } else if (tag == JS_TAG_FLOAT64) { - d = JS_VALUE_GET_FLOAT64(argv[0]); - v64 = d; - is_int = (v64 == d); - } else -#ifdef CONFIG_BIGNUM - 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 - { - goto done; - } - - switch (p->class_id) { - case JS_CLASS_INT8_ARRAY: - if (is_int && (int8_t)v64 == v64) - goto scan8; - break; - case JS_CLASS_UINT8C_ARRAY: - case JS_CLASS_UINT8_ARRAY: - if (is_int && (uint8_t)v64 == v64) { - const uint8_t *pv, *pp; - uint16_t v; - scan8: - pv = p->u.array.u.uint8_ptr; - v = v64; - if (inc > 0) { - pp = memchr(pv + k, v, len - k); - if (pp) - res = pp - pv; - } else { - for (; k != stop; k += inc) { - if (pv[k] == v) { - res = k; - break; - } - } - } - } - break; - case JS_CLASS_INT16_ARRAY: - if (is_int && (int16_t)v64 == v64) - goto scan16; - break; - case JS_CLASS_UINT16_ARRAY: - if (is_int && (uint16_t)v64 == v64) { - const uint16_t* pv; - uint16_t v; - scan16: - pv = p->u.array.u.uint16_ptr; - v = v64; - for (; k != stop; k += inc) { - if (pv[k] == v) { - res = k; - break; - } - } - } - break; - case JS_CLASS_INT32_ARRAY: - if (is_int && (int32_t)v64 == v64) - goto scan32; - break; - case JS_CLASS_UINT32_ARRAY: - if (is_int && (uint32_t)v64 == v64) { - const uint32_t* pv; - uint32_t v; - scan32: - pv = p->u.array.u.uint32_ptr; - v = v64; - for (; k != stop; k += inc) { - if (pv[k] == v) { - res = k; - break; - } - } - } - break; - case JS_CLASS_FLOAT32_ARRAY: - if (is_bigint) - break; - if (isnan(d)) { - const float* pv = p->u.array.u.float_ptr; - /* special case: indexOf returns -1, includes finds NaN */ - if (special != special_includes) - goto done; - for (; k != stop; k += inc) { - if (isnan(pv[k])) { - res = k; - break; - } - } - } else if ((f = (float)d) == d) { - const float* pv = p->u.array.u.float_ptr; - for (; k != stop; k += inc) { - if (pv[k] == f) { - res = k; - break; - } - } - } - break; - case JS_CLASS_FLOAT64_ARRAY: - if (is_bigint) - break; - if (isnan(d)) { - const double* pv = p->u.array.u.double_ptr; - /* special case: indexOf returns -1, includes finds NaN */ - if (special != special_includes) - goto done; - for (; k != stop; k += inc) { - if (isnan(pv[k])) { - res = k; - break; - } - } - } else { - const double* pv = p->u.array.u.double_ptr; - for (; k != stop; k += inc) { - if (pv[k] == d) { - res = k; - break; - } - } - } - break; -#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_bigint || (is_math_mode(ctx) && is_int && v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) { - const uint64_t* pv; - uint64_t v; - scan64: - pv = p->u.array.u.uint64_ptr; - v = v64; - for (; k != stop; k += inc) { - if (pv[k] == v) { - res = k; - break; - } - } - } - break; -#endif - } - -done: - if (special == special_includes) - return JS_NewBool(ctx, res >= 0); - else - return JS_NewInt32(ctx, res); - -exception: - return JS_EXCEPTION; -} - -JSValue js_typed_array_join(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int toLocaleString) { - JSValue sep = JS_UNDEFINED, el; - StringBuffer b_s, *b = &b_s; - JSString* p = NULL; - int i, n; - int c; - - n = js_typed_array_get_length_internal(ctx, this_val); - if (n < 0) - goto exception; - - c = ','; /* default separator */ - if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { - sep = JS_ToString(ctx, argv[0]); - if (JS_IsException(sep)) - goto exception; - p = JS_VALUE_GET_STRING(sep); - if (p->len == 1 && !p->is_wide_char) - c = p->u.str8[0]; - else - c = -1; - } - string_buffer_init(ctx, b, 0); - - /* XXX: optimize with direct access */ - for (i = 0; i < n; i++) { - if (i > 0) { - if (c >= 0) { - if (string_buffer_putc8(b, c)) - goto fail; - } else { - if (string_buffer_concat(b, p, 0, p->len)) - goto fail; - } - } - el = JS_GetPropertyUint32(ctx, this_val, i); - /* Can return undefined for example if the typed array is detached */ - if (!JS_IsNull(el) && !JS_IsUndefined(el)) { - if (JS_IsException(el)) - goto fail; - if (toLocaleString) { - el = JS_ToLocaleStringFree(ctx, el); - } - if (string_buffer_concat_value_free(b, el)) - goto fail; - } - } - JS_FreeValue(ctx, sep); - return string_buffer_end(b); - -fail: - string_buffer_free(b); - JS_FreeValue(ctx, sep); -exception: - return JS_EXCEPTION; -} - -JSValue js_typed_array_reverse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSObject* p; - int len; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - return JS_EXCEPTION; - if (len > 0) { - p = JS_VALUE_GET_OBJ(this_val); - switch (typed_array_size_log2(p->class_id)) { - case 0: { - uint8_t* p1 = p->u.array.u.uint8_ptr; - uint8_t* p2 = p1 + len - 1; - while (p1 < p2) { - uint8_t v = *p1; - *p1++ = *p2; - *p2-- = v; - } - } break; - case 1: { - uint16_t* p1 = p->u.array.u.uint16_ptr; - uint16_t* p2 = p1 + len - 1; - while (p1 < p2) { - uint16_t v = *p1; - *p1++ = *p2; - *p2-- = v; - } - } break; - case 2: { - uint32_t* p1 = p->u.array.u.uint32_ptr; - uint32_t* p2 = p1 + len - 1; - while (p1 < p2) { - uint32_t v = *p1; - *p1++ = *p2; - *p2-- = v; - } - } break; - case 3: { - uint64_t* p1 = p->u.array.u.uint64_ptr; - uint64_t* p2 = p1 + len - 1; - while (p1 < p2) { - uint64_t v = *p1; - *p1++ = *p2; - *p2-- = v; - } - } break; - default: - abort(); - } - } - return JS_DupValue(ctx, this_val); -} - -JSValue js_typed_array_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValueConst args[2]; - JSValue arr, val; - JSObject *p, *p1; - int n, len, start, final, count, shift; - - arr = JS_UNDEFINED; - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - goto exception; - - if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) - goto exception; - final = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) - goto exception; - } - count = max_int(final - start, 0); - - p = get_typed_array(ctx, this_val, 0); - if (p == NULL) - goto exception; - shift = typed_array_size_log2(p->class_id); - - args[0] = this_val; - args[1] = JS_NewInt32(ctx, count); - arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); - if (JS_IsException(arr)) - goto exception; - - if (count > 0) { - if (validate_typed_array(ctx, this_val) || validate_typed_array(ctx, arr)) - goto exception; - - p1 = get_typed_array(ctx, arr, 0); - if (p1 != NULL && p->class_id == p1->class_id && typed_array_get_length(ctx, p1) >= count && typed_array_get_length(ctx, p) >= start + count) { - memcpy(p1->u.array.u.uint8_ptr, p->u.array.u.uint8_ptr + (start << shift), count << shift); - } else { - for (n = 0; n < count; n++) { - val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n)); - if (JS_IsException(val)) - goto exception; - if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val, JS_PROP_THROW) < 0) - goto exception; - } - } - } - return arr; - -exception: - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -JSValue js_typed_array_subarray(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSValueConst args[4]; - JSValue arr, byteOffset, ta_buffer; - JSObject* p; - int len, start, final, count, shift, offset; - - p = get_typed_array(ctx, this_val, 0); - if (!p) - goto exception; - len = p->u.array.count; - if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) - goto exception; - - final = len; - if (!JS_IsUndefined(argv[1])) { - if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) - goto exception; - } - count = max_int(final - start, 0); - byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0); - if (JS_IsException(byteOffset)) - goto exception; - shift = typed_array_size_log2(p->class_id); - offset = JS_VALUE_GET_INT(byteOffset) + (start << shift); - JS_FreeValue(ctx, byteOffset); - ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0); - if (JS_IsException(ta_buffer)) - goto exception; - args[0] = this_val; - args[1] = ta_buffer; - args[2] = JS_NewInt32(ctx, offset); - args[3] = JS_NewInt32(ctx, count); - arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args); - JS_FreeValue(ctx, ta_buffer); - return arr; - -exception: - return JS_EXCEPTION; -} - -/* TypedArray.prototype.sort */ - -int js_cmp_doubles(double x, double y) { - if (isnan(x)) - return isnan(y) ? 0 : +1; - if (isnan(y)) - return -1; - if (x < y) - return -1; - if (x > y) - return 1; - if (x != 0) - return 0; - if (signbit(x)) - return signbit(y) ? 0 : -1; - else - return signbit(y) ? 1 : 0; -} - -int js_TA_cmp_int8(const void* a, const void* b, void* opaque) { - return *(const int8_t*)a - *(const int8_t*)b; -} - -int js_TA_cmp_uint8(const void* a, const void* b, void* opaque) { - return *(const uint8_t*)a - *(const uint8_t*)b; -} - -int js_TA_cmp_int16(const void* a, const void* b, void* opaque) { - return *(const int16_t*)a - *(const int16_t*)b; -} - -int js_TA_cmp_uint16(const void* a, const void* b, void* opaque) { - return *(const uint16_t*)a - *(const uint16_t*)b; -} - -int js_TA_cmp_int32(const void* a, const void* b, void* opaque) { - int32_t x = *(const int32_t*)a; - int32_t y = *(const int32_t*)b; - return (y < x) - (y > x); -} - -int js_TA_cmp_uint32(const void* a, const void* b, void* opaque) { - uint32_t x = *(const uint32_t*)a; - uint32_t y = *(const uint32_t*)b; - return (y < x) - (y > x); -} - -#ifdef CONFIG_BIGNUM -int js_TA_cmp_int64(const void* a, const void* b, void* opaque) { - int64_t x = *(const int64_t*)a; - int64_t y = *(const int64_t*)b; - return (y < x) - (y > x); -} - -int js_TA_cmp_uint64(const void* a, const void* b, void* opaque) { - uint64_t x = *(const uint64_t*)a; - uint64_t y = *(const uint64_t*)b; - return (y < x) - (y > x); -} -#endif - -int js_TA_cmp_float32(const void* a, const void* b, void* opaque) { - return js_cmp_doubles(*(const float*)a, *(const float*)b); -} - -int js_TA_cmp_float64(const void* a, const void* b, void* opaque) { - return js_cmp_doubles(*(const double*)a, *(const double*)b); -} - -JSValue js_TA_get_int8(JSContext* ctx, const void* a) { - return JS_NewInt32(ctx, *(const int8_t*)a); -} - -JSValue js_TA_get_uint8(JSContext* ctx, const void* a) { - return JS_NewInt32(ctx, *(const uint8_t*)a); -} - -JSValue js_TA_get_int16(JSContext* ctx, const void* a) { - return JS_NewInt32(ctx, *(const int16_t*)a); -} - -JSValue js_TA_get_uint16(JSContext* ctx, const void* a) { - return JS_NewInt32(ctx, *(const uint16_t*)a); -} - -JSValue js_TA_get_int32(JSContext* ctx, const void* a) { - return JS_NewInt32(ctx, *(const int32_t*)a); -} - -JSValue js_TA_get_uint32(JSContext* ctx, const void* a) { - return JS_NewUint32(ctx, *(const uint32_t*)a); -} - -#ifdef CONFIG_BIGNUM -JSValue js_TA_get_int64(JSContext* ctx, const void* a) { - return JS_NewBigInt64(ctx, *(int64_t*)a); -} - -JSValue js_TA_get_uint64(JSContext* ctx, const void* a) { - return JS_NewBigUint64(ctx, *(uint64_t*)a); -} -#endif - -JSValue js_TA_get_float32(JSContext* ctx, const void* a) { - return __JS_NewFloat64(ctx, *(const float*)a); -} - -JSValue js_TA_get_float64(JSContext* ctx, const void* a) { - return __JS_NewFloat64(ctx, *(const double*)a); -} - -struct TA_sort_context { - JSContext* ctx; - int exception; - JSValueConst arr; - JSValueConst cmp; - JSValue (*getfun)(JSContext* ctx, const void* a); - uint8_t* array_ptr; /* cannot change unless the array is detached */ - int elt_size; -}; - -int js_TA_cmp_generic(const void* a, const void* b, void* opaque) { - struct TA_sort_context* psc = opaque; - JSContext* ctx = psc->ctx; - uint32_t a_idx, b_idx; - JSValueConst argv[2]; - JSValue res; - int cmp; - - cmp = 0; - if (!psc->exception) { - a_idx = *(uint32_t*)a; - b_idx = *(uint32_t*)b; - argv[0] = psc->getfun(ctx, psc->array_ptr + a_idx * (size_t)psc->elt_size); - argv[1] = psc->getfun(ctx, psc->array_ptr + b_idx * (size_t)(psc->elt_size)); - res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); - if (JS_IsException(res)) { - psc->exception = 1; - goto done; - } - if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { - int val = JS_VALUE_GET_INT(res); - cmp = (val > 0) - (val < 0); - } else { - double val; - if (JS_ToFloat64Free(ctx, &val, res) < 0) { - psc->exception = 1; - goto done; - } else { - cmp = (val > 0) - (val < 0); - } - } - if (cmp == 0) { - /* make sort stable: compare array offsets */ - cmp = (a_idx > b_idx) - (a_idx < b_idx); - } - if (validate_typed_array(ctx, psc->arr) < 0) { - psc->exception = 1; - } - done: - JS_FreeValue(ctx, (JSValue)argv[0]); - JS_FreeValue(ctx, (JSValue)argv[1]); - } - return cmp; -} - -JSValue js_typed_array_sort(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - JSObject* p; - int len; - size_t elt_size; - struct TA_sort_context tsc; - void* array_ptr; - int (*cmpfun)(const void* a, const void* b, void* opaque); - - tsc.ctx = ctx; - tsc.exception = 0; - tsc.arr = this_val; - tsc.cmp = argv[0]; - - len = js_typed_array_get_length_internal(ctx, this_val); - if (len < 0) - return JS_EXCEPTION; - if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) - return JS_EXCEPTION; - - if (len > 1) { - p = JS_VALUE_GET_OBJ(this_val); - switch (p->class_id) { - case JS_CLASS_INT8_ARRAY: - tsc.getfun = js_TA_get_int8; - cmpfun = js_TA_cmp_int8; - break; - case JS_CLASS_UINT8C_ARRAY: - case JS_CLASS_UINT8_ARRAY: - tsc.getfun = js_TA_get_uint8; - cmpfun = js_TA_cmp_uint8; - break; - case JS_CLASS_INT16_ARRAY: - tsc.getfun = js_TA_get_int16; - cmpfun = js_TA_cmp_int16; - break; - case JS_CLASS_UINT16_ARRAY: - tsc.getfun = js_TA_get_uint16; - cmpfun = js_TA_cmp_uint16; - break; - case JS_CLASS_INT32_ARRAY: - tsc.getfun = js_TA_get_int32; - cmpfun = js_TA_cmp_int32; - break; - case JS_CLASS_UINT32_ARRAY: - tsc.getfun = js_TA_get_uint32; - cmpfun = js_TA_cmp_uint32; - break; -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT64_ARRAY: - tsc.getfun = js_TA_get_int64; - cmpfun = js_TA_cmp_int64; - break; - case JS_CLASS_BIG_UINT64_ARRAY: - tsc.getfun = js_TA_get_uint64; - cmpfun = js_TA_cmp_uint64; - break; -#endif - case JS_CLASS_FLOAT32_ARRAY: - tsc.getfun = js_TA_get_float32; - cmpfun = js_TA_cmp_float32; - break; - case JS_CLASS_FLOAT64_ARRAY: - tsc.getfun = js_TA_get_float64; - cmpfun = js_TA_cmp_float64; - break; - default: - abort(); - } - array_ptr = p->u.array.u.ptr; - elt_size = 1 << typed_array_size_log2(p->class_id); - if (!JS_IsUndefined(tsc.cmp)) { - uint32_t* array_idx; - void* array_tmp; - size_t i, j; - - /* XXX: a stable sort would use less memory */ - array_idx = js_malloc(ctx, len * sizeof(array_idx[0])); - if (!array_idx) - return JS_EXCEPTION; - for (i = 0; i < len; i++) - array_idx[i] = i; - tsc.array_ptr = array_ptr; - tsc.elt_size = elt_size; - rqsort(array_idx, len, sizeof(array_idx[0]), js_TA_cmp_generic, &tsc); - if (tsc.exception) - goto fail; - array_tmp = js_malloc(ctx, len * elt_size); - if (!array_tmp) { - fail: - js_free(ctx, array_idx); - return JS_EXCEPTION; - } - memcpy(array_tmp, array_ptr, len * elt_size); - switch (elt_size) { - case 1: - for (i = 0; i < len; i++) { - j = array_idx[i]; - ((uint8_t*)array_ptr)[i] = ((uint8_t*)array_tmp)[j]; - } - break; - case 2: - for (i = 0; i < len; i++) { - j = array_idx[i]; - ((uint16_t*)array_ptr)[i] = ((uint16_t*)array_tmp)[j]; - } - break; - case 4: - for (i = 0; i < len; i++) { - j = array_idx[i]; - ((uint32_t*)array_ptr)[i] = ((uint32_t*)array_tmp)[j]; - } - break; - case 8: - for (i = 0; i < len; i++) { - j = array_idx[i]; - ((uint64_t*)array_ptr)[i] = ((uint64_t*)array_tmp)[j]; - } - break; - default: - abort(); - } - js_free(ctx, array_tmp); - js_free(ctx, array_idx); - } else { - rqsort(array_ptr, len, elt_size, cmpfun, &tsc); - if (tsc.exception) - return JS_EXCEPTION; - } - } - return JS_DupValue(ctx, this_val); -} - -const JSCFunctionListEntry js_typed_array_base_funcs[] = { - JS_CFUNC_DEF("from", 1, js_typed_array_from), JS_CFUNC_DEF("of", 0, js_typed_array_of), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), - // JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ), - // JS_CFUNC_DEF("__create", 2, js_typed_array___create ), - // JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ), -}; - -const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { - JS_CGETSET_DEF("length", js_typed_array_get_length, NULL), - JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0), - JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0), - JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0), - JS_CFUNC_DEF("set", 1, js_typed_array_set), - JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE), - JS_ALIAS_DEF("[Symbol.iterator]", "values"), - JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY), - JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE), - JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL), - JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin), - JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA), - JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA), - JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA), - JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA), - JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA), - JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA), - JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA), - JS_CFUNC_DEF("fill", 1, js_typed_array_fill), - JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0), - JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1), - JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse), - JS_CFUNC_DEF("slice", 2, js_typed_array_slice), - JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray), - JS_CFUNC_DEF("sort", 1, js_typed_array_sort), - JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0), - JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1), - JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf), - JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf), - JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes), - // JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@ -}; - -JSValue js_typed_array_base_constructor(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { - return JS_ThrowTypeError(ctx, "cannot be called"); -} - -/* 'obj' must be an allocated typed array object */ -int typed_array_init(JSContext* ctx, JSValueConst obj, JSValue buffer, uint64_t offset, uint64_t len) { - JSTypedArray* ta; - JSObject *p, *pbuffer; - JSArrayBuffer* abuf; - int size_log2; - - p = JS_VALUE_GET_OBJ(obj); - size_log2 = typed_array_size_log2(p->class_id); - ta = js_malloc(ctx, sizeof(*ta)); - if (!ta) { - JS_FreeValue(ctx, buffer); - return -1; - } - pbuffer = JS_VALUE_GET_OBJ(buffer); - abuf = pbuffer->u.array_buffer; - ta->obj = p; - ta->buffer = pbuffer; - ta->offset = offset; - ta->length = len << size_log2; - list_add_tail(&ta->link, &abuf->array_list); - p->u.typed_array = ta; - p->u.array.count = len; - p->u.array.u.ptr = abuf->data + offset; - return 0; -} - -JSValue js_array_from_iterator(JSContext* ctx, uint32_t* plen, JSValueConst obj, JSValueConst method) { - JSValue arr, iter, next_method = JS_UNDEFINED, val; - BOOL done; - uint32_t k; - - *plen = 0; - arr = JS_NewArray(ctx); - if (JS_IsException(arr)) - return arr; - iter = JS_GetIterator2(ctx, obj, method); - if (JS_IsException(iter)) - goto fail; - next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); - if (JS_IsException(next_method)) - goto fail; - k = 0; - for (;;) { - val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); - if (JS_IsException(val)) - goto fail; - if (done) { - JS_FreeValue(ctx, val); - break; - } - if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) - goto fail; - k++; - } - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - *plen = k; - return arr; -fail: - JS_FreeValue(ctx, next_method); - JS_FreeValue(ctx, iter); - JS_FreeValue(ctx, arr); - return JS_EXCEPTION; -} - -JSValue js_typed_array_constructor_obj(JSContext* ctx, JSValueConst new_target, JSValueConst obj, int classid) { - JSValue iter, ret, arr = JS_UNDEFINED, val, buffer; - uint32_t i; - int size_log2; - int64_t len; - - size_log2 = typed_array_size_log2(classid); - ret = js_create_from_ctor(ctx, new_target, classid); - if (JS_IsException(ret)) - return JS_EXCEPTION; - - iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); - if (JS_IsException(iter)) - goto fail; - if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { - uint32_t len1; - arr = js_array_from_iterator(ctx, &len1, obj, iter); - JS_FreeValue(ctx, iter); - if (JS_IsException(arr)) - goto fail; - len = len1; - } else { - if (js_get_length64(ctx, &len, obj)) - goto fail; - arr = JS_DupValue(ctx, obj); - } - - buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, len << size_log2); - if (JS_IsException(buffer)) - goto fail; - if (typed_array_init(ctx, ret, buffer, 0, len)) - goto fail; - - for (i = 0; i < len; i++) { - val = JS_GetPropertyUint32(ctx, arr, i); - if (JS_IsException(val)) - goto fail; - if (JS_SetPropertyUint32(ctx, ret, i, val) < 0) - goto fail; - } - JS_FreeValue(ctx, arr); - return ret; -fail: - JS_FreeValue(ctx, arr); - JS_FreeValue(ctx, ret); - return JS_EXCEPTION; -} - -JSValue js_typed_array_constructor_ta(JSContext* ctx, JSValueConst new_target, JSValueConst src_obj, int classid) { - JSObject *p, *src_buffer; - JSTypedArray* ta; - JSValue ctor, obj, buffer; - uint32_t len, i; - int size_log2; - JSArrayBuffer *src_abuf, *abuf; - - obj = js_create_from_ctor(ctx, new_target, classid); - if (JS_IsException(obj)) - return obj; - p = JS_VALUE_GET_OBJ(src_obj); - if (typed_array_is_detached(ctx, p)) { - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - ta = p->u.typed_array; - len = p->u.array.count; - src_buffer = ta->buffer; - src_abuf = src_buffer->u.array_buffer; - if (!src_abuf->shared) { - ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer), JS_UNDEFINED); - if (JS_IsException(ctor)) - goto fail; - } else { - /* force ArrayBuffer default constructor */ - ctor = JS_UNDEFINED; - } - size_log2 = typed_array_size_log2(classid); - buffer = js_array_buffer_constructor1(ctx, ctor, (uint64_t)len << size_log2); - JS_FreeValue(ctx, ctor); - if (JS_IsException(buffer)) - goto fail; - /* necessary because it could have been detached */ - if (typed_array_is_detached(ctx, p)) { - JS_FreeValue(ctx, buffer); - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER); - if (typed_array_init(ctx, obj, buffer, 0, len)) - goto fail; - if (p->class_id == classid) { - /* same type: copy the content */ - memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length); - } else { - for (i = 0; i < len; i++) { - JSValue val; - val = JS_GetPropertyUint32(ctx, src_obj, i); - if (JS_IsException(val)) - goto fail; - if (JS_SetPropertyUint32(ctx, obj, i, val) < 0) - goto fail; - } - } - return obj; -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -JSValue js_typed_array_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv, int classid) { - JSValue buffer, obj; - JSArrayBuffer* abuf; - int size_log2; - uint64_t len, offset; - - size_log2 = typed_array_size_log2(classid); - if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) { - if (JS_ToIndex(ctx, &len, argv[0])) - return JS_EXCEPTION; - buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, len << size_log2); - if (JS_IsException(buffer)) - return JS_EXCEPTION; - offset = 0; - } else { - JSObject* p = JS_VALUE_GET_OBJ(argv[0]); - if (p->class_id == JS_CLASS_ARRAY_BUFFER || p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { - abuf = p->u.array_buffer; - if (JS_ToIndex(ctx, &offset, argv[1])) - return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - if ((offset & ((1 << size_log2) - 1)) != 0 || offset > abuf->byte_length) - return JS_ThrowRangeError(ctx, "invalid offset"); - if (JS_IsUndefined(argv[2])) { - if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) - goto invalid_length; - len = (abuf->byte_length - offset) >> size_log2; - } else { - if (JS_ToIndex(ctx, &len, argv[2])) - return JS_EXCEPTION; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - if ((offset + (len << size_log2)) > abuf->byte_length) { - invalid_length: - return JS_ThrowRangeError(ctx, "invalid length"); - } - } - buffer = JS_DupValue(ctx, argv[0]); - } else { - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid); - } else { - return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid); - } - } - } - - obj = js_create_from_ctor(ctx, new_target, classid); - if (JS_IsException(obj)) { - JS_FreeValue(ctx, buffer); - return JS_EXCEPTION; - } - if (typed_array_init(ctx, obj, buffer, offset, len)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - return obj; -} - -void js_typed_array_finalizer(JSRuntime* rt, JSValue val) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSTypedArray* ta = p->u.typed_array; - if (ta) { - /* during the GC the finalizers are called in an arbitrary - order so the ArrayBuffer finalizer may have been called */ - if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) { - list_del(&ta->link); - } - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); - js_free_rt(rt, ta); - } -} - -void js_typed_array_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSTypedArray* ta = p->u.typed_array; - if (ta) { - JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func); - } -} - -JSValue js_dataview_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { - JSArrayBuffer* abuf; - uint64_t offset; - uint32_t len; - JSValueConst buffer; - JSValue obj; - JSTypedArray* ta; - JSObject* p; - - buffer = argv[0]; - abuf = js_get_array_buffer(ctx, buffer); - if (!abuf) - return JS_EXCEPTION; - offset = 0; - if (argc > 1) { - if (JS_ToIndex(ctx, &offset, argv[1])) - return JS_EXCEPTION; - } - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - if (offset > abuf->byte_length) - return JS_ThrowRangeError(ctx, "invalid byteOffset"); - len = abuf->byte_length - offset; - if (argc > 2 && !JS_IsUndefined(argv[2])) { - uint64_t l; - if (JS_ToIndex(ctx, &l, argv[2])) - return JS_EXCEPTION; - if (l > len) - return JS_ThrowRangeError(ctx, "invalid byteLength"); - len = l; - } - - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW); - if (JS_IsException(obj)) - return JS_EXCEPTION; - if (abuf->detached) { - /* could have been detached in js_create_from_ctor() */ - JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - goto fail; - } - ta = js_malloc(ctx, sizeof(*ta)); - if (!ta) { - fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - p = JS_VALUE_GET_OBJ(obj); - ta->obj = p; - ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer)); - ta->offset = offset; - ta->length = len; - list_add_tail(&ta->link, &abuf->array_list); - p->u.typed_array = ta; - return obj; -} - -JSValue js_dataview_getValue(JSContext* ctx, JSValueConst this_obj, int argc, JSValueConst* argv, int class_id) { - JSTypedArray* ta; - JSArrayBuffer* abuf; - int is_swap, size; - uint8_t* ptr; - uint32_t v; - uint64_t pos; - - ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); - if (!ta) - return JS_EXCEPTION; - size = 1 << typed_array_size_log2(class_id); - if (JS_ToIndex(ctx, &pos, argv[0])) - return JS_EXCEPTION; - is_swap = FALSE; - if (argc > 1) - is_swap = JS_ToBool(ctx, argv[1]); -#ifndef WORDS_BIGENDIAN - is_swap ^= 1; -#endif - abuf = ta->buffer->u.array_buffer; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - if ((pos + size) > ta->length) - return JS_ThrowRangeError(ctx, "out of bound"); - ptr = abuf->data + ta->offset + pos; - - switch (class_id) { - case JS_CLASS_INT8_ARRAY: - return JS_NewInt32(ctx, *(int8_t*)ptr); - case JS_CLASS_UINT8_ARRAY: - return JS_NewInt32(ctx, *(uint8_t*)ptr); - case JS_CLASS_INT16_ARRAY: - v = get_u16(ptr); - if (is_swap) - v = bswap16(v); - return JS_NewInt32(ctx, (int16_t)v); - case JS_CLASS_UINT16_ARRAY: - v = get_u16(ptr); - if (is_swap) - v = bswap16(v); - return JS_NewInt32(ctx, v); - case JS_CLASS_INT32_ARRAY: - v = get_u32(ptr); - if (is_swap) - v = bswap32(v); - return JS_NewInt32(ctx, v); - case JS_CLASS_UINT32_ARRAY: - v = get_u32(ptr); - if (is_swap) - v = bswap32(v); - return JS_NewUint32(ctx, v); -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT64_ARRAY: { - uint64_t v; - v = get_u64(ptr); - if (is_swap) - v = bswap64(v); - return JS_NewBigInt64(ctx, v); - } break; - case JS_CLASS_BIG_UINT64_ARRAY: { - uint64_t v; - v = get_u64(ptr); - if (is_swap) - v = bswap64(v); - return JS_NewBigUint64(ctx, v); - } break; -#endif - case JS_CLASS_FLOAT32_ARRAY: { - union { - float f; - uint32_t i; - } u; - v = get_u32(ptr); - if (is_swap) - v = bswap32(v); - u.i = v; - return __JS_NewFloat64(ctx, u.f); - } - case JS_CLASS_FLOAT64_ARRAY: { - union { - double f; - uint64_t i; - } u; - u.i = get_u64(ptr); - if (is_swap) - u.i = bswap64(u.i); - return __JS_NewFloat64(ctx, u.f); - } - default: - abort(); - } -} - -JSValue js_dataview_setValue(JSContext* ctx, JSValueConst this_obj, int argc, JSValueConst* argv, int class_id) { - JSTypedArray* ta; - JSArrayBuffer* abuf; - int is_swap, size; - uint8_t* ptr; - uint64_t v64; - uint32_t v; - uint64_t pos; - JSValueConst val; - - ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); - if (!ta) - return JS_EXCEPTION; - size = 1 << typed_array_size_log2(class_id); - if (JS_ToIndex(ctx, &pos, argv[0])) - return JS_EXCEPTION; - val = argv[1]; - v = 0; /* avoid warning */ - v64 = 0; /* avoid warning */ - if (class_id <= JS_CLASS_UINT32_ARRAY) { - if (JS_ToUint32(ctx, &v, val)) - return JS_EXCEPTION; - } else -#ifdef CONFIG_BIGNUM - if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { - if (JS_ToBigInt64(ctx, (int64_t*)&v64, val)) - return JS_EXCEPTION; - } else -#endif - { - double d; - if (JS_ToFloat64(ctx, &d, val)) - return JS_EXCEPTION; - if (class_id == JS_CLASS_FLOAT32_ARRAY) { - union { - float f; - uint32_t i; - } u; - u.f = d; - v = u.i; - } else { - JSFloat64Union u; - u.d = d; - v64 = u.u64; - } - } - is_swap = FALSE; - if (argc > 2) - is_swap = JS_ToBool(ctx, argv[2]); -#ifndef WORDS_BIGENDIAN - is_swap ^= 1; -#endif - abuf = ta->buffer->u.array_buffer; - if (abuf->detached) - return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); - if ((pos + size) > ta->length) - return JS_ThrowRangeError(ctx, "out of bound"); - ptr = abuf->data + ta->offset + pos; - - switch (class_id) { - case JS_CLASS_INT8_ARRAY: - case JS_CLASS_UINT8_ARRAY: - *ptr = v; - break; - case JS_CLASS_INT16_ARRAY: - case JS_CLASS_UINT16_ARRAY: - if (is_swap) - v = bswap16(v); - put_u16(ptr, v); - break; - case JS_CLASS_INT32_ARRAY: - case JS_CLASS_UINT32_ARRAY: - case JS_CLASS_FLOAT32_ARRAY: - if (is_swap) - v = bswap32(v); - put_u32(ptr, v); - break; -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT64_ARRAY: - case JS_CLASS_BIG_UINT64_ARRAY: -#endif - case JS_CLASS_FLOAT64_ARRAY: - if (is_swap) - v64 = bswap64(v64); - put_u64(ptr, v64); - break; - default: - abort(); - } - return JS_UNDEFINED; -} - -const JSCFunctionListEntry js_dataview_proto_funcs[] = { - JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1), - JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1), - JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1), - JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY), - JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY), - JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY), - JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY), - JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY), - JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY), -#ifdef CONFIG_BIGNUM - JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY), - JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY), -#endif - JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY), - JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY), - JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY), - JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY), - JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY), - JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY), - JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY), - JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY), -#ifdef CONFIG_BIGNUM - JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY), - JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY), -#endif - JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY), - JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY), - JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE), -}; - -void JS_AddIntrinsicTypedArrays(JSContext* ctx) { - JSValue typed_array_base_proto, typed_array_base_func; - JSValueConst array_buffer_func, shared_array_buffer_func; - int i; - - ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER], js_array_buffer_proto_funcs, countof(js_array_buffer_proto_funcs)); - - array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer", js_array_buffer_constructor, 1, ctx->class_proto[JS_CLASS_ARRAY_BUFFER]); - JS_SetPropertyFunctionList(ctx, array_buffer_func, js_array_buffer_funcs, countof(js_array_buffer_funcs)); - - ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER], js_shared_array_buffer_proto_funcs, countof(js_shared_array_buffer_proto_funcs)); - - shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer", js_shared_array_buffer_constructor, 1, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]); - JS_SetPropertyFunctionList(ctx, shared_array_buffer_func, js_shared_array_buffer_funcs, countof(js_shared_array_buffer_funcs)); - - typed_array_base_proto = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, typed_array_base_proto, js_typed_array_base_proto_funcs, countof(js_typed_array_base_proto_funcs)); - - /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */ - JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString); - /* XXX: should use alias method in JSCFunctionListEntry */ //@@@ - JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - - typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor, "TypedArray", 0); - JS_SetPropertyFunctionList(ctx, typed_array_base_func, js_typed_array_base_funcs, countof(js_typed_array_base_funcs)); - JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); - - for (i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) { - JSValue func_obj; - char buf[ATOM_GET_STR_BUF_SIZE]; - const char* name; - - ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto); - JS_DefinePropertyValueStr(ctx, ctx->class_proto[i], "BYTES_PER_ELEMENT", JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), 0); - name = JS_AtomGetStr(ctx, buf, sizeof(buf), JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); - func_obj = JS_NewCFunction3(ctx, (JSCFunction*)js_typed_array_constructor, name, 3, JS_CFUNC_constructor_magic, i, typed_array_base_func); - JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]); - JS_DefinePropertyValueStr(ctx, func_obj, "BYTES_PER_ELEMENT", JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), 0); - } - JS_FreeValue(ctx, typed_array_base_proto); - JS_FreeValue(ctx, typed_array_base_func); - - /* DataView */ - ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx); - JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW], js_dataview_proto_funcs, countof(js_dataview_proto_funcs)); - JS_NewGlobalCConstructorOnly(ctx, "DataView", js_dataview_constructor, 1, ctx->class_proto[JS_CLASS_DATAVIEW]); - /* Atomics */ -#ifdef CONFIG_ATOMICS - JS_AddIntrinsicAtomics(ctx); -#endif +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "js-typed-array.h" +#include "../convertion.h" +#include "../exception.h" +#include "../function.h" +#include "../object.h" +#include "../runtime.h" +#include "../string.h" +#include "js-array.h" +#include "js-atomics.h" +#include "js-function.h" +#include "js-object.h" +#include "js-operator.h" + +/* Typed Arrays */ + +static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {0, 0, 0, 1, 1, 2, 2, +#ifdef CONFIG_BIGNUM + 3, 3, /* BigInt64Array, BigUint64Array */ +#endif + 2, 3}; + +JSValue +js_array_buffer_constructor3(JSContext* ctx, JSValueConst new_target, uint64_t len, JSClassID class_id, uint8_t* buf, JSFreeArrayBufferDataFunc* free_func, void* opaque, BOOL alloc_flag) { + JSRuntime* rt = ctx->rt; + JSValue obj; + JSArrayBuffer* abuf = NULL; + + obj = js_create_from_ctor(ctx, new_target, class_id); + if (JS_IsException(obj)) + return obj; + /* XXX: we are currently limited to 2 GB */ + if (len > INT32_MAX) { + JS_ThrowRangeError(ctx, "invalid array buffer length"); + goto fail; + } + abuf = js_malloc(ctx, sizeof(*abuf)); + if (!abuf) + goto fail; + abuf->byte_length = len; + if (alloc_flag) { + if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && rt->sab_funcs.sab_alloc) { + abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, max_int(len, 1)); + if (!abuf->data) + goto fail; + memset(abuf->data, 0, len); + } else { + /* the allocation must be done after the object creation */ + abuf->data = js_mallocz(ctx, max_int(len, 1)); + if (!abuf->data) + goto fail; + } + } else { + if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && rt->sab_funcs.sab_dup) { + rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf); + } + abuf->data = buf; + } + init_list_head(&abuf->array_list); + abuf->detached = FALSE; + abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER); + abuf->opaque = opaque; + abuf->free_func = free_func; + if (alloc_flag && buf) + memcpy(abuf->data, buf, len); + JS_SetOpaque(obj, abuf); + return obj; +fail: + JS_FreeValue(ctx, obj); + js_free(ctx, abuf); + return JS_EXCEPTION; +} + +void js_array_buffer_free(JSRuntime* rt, void* opaque, void* ptr) { + js_free_rt(rt, ptr); +} + +JSValue js_array_buffer_constructor2(JSContext* ctx, JSValueConst new_target, uint64_t len, JSClassID class_id) { + return js_array_buffer_constructor3(ctx, new_target, len, class_id, NULL, js_array_buffer_free, NULL, TRUE); +} + +JSValue js_array_buffer_constructor1(JSContext* ctx, JSValueConst new_target, uint64_t len) { + return js_array_buffer_constructor2(ctx, new_target, len, JS_CLASS_ARRAY_BUFFER); +} + +JSValue JS_NewArrayBuffer(JSContext* ctx, uint8_t* buf, size_t len, JSFreeArrayBufferDataFunc* free_func, void* opaque, BOOL is_shared) { + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER, buf, free_func, opaque, FALSE); +} + +/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */ +JSValue JS_NewArrayBufferCopy(JSContext* ctx, const uint8_t* buf, size_t len) { + return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, JS_CLASS_ARRAY_BUFFER, (uint8_t*)buf, js_array_buffer_free, NULL, TRUE); +} + +JSValue js_array_buffer_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { + uint64_t len; + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + return js_array_buffer_constructor1(ctx, new_target, len); +} + +JSValue js_shared_array_buffer_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { + uint64_t len; + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + return js_array_buffer_constructor2(ctx, new_target, len, JS_CLASS_SHARED_ARRAY_BUFFER); +} + +/* also used for SharedArrayBuffer */ +void js_array_buffer_finalizer(JSRuntime* rt, JSValue val) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSArrayBuffer* abuf = p->u.array_buffer; + if (abuf) { + /* The ArrayBuffer finalizer may be called before the typed + array finalizers using it, so abuf->array_list is not + necessarily empty. */ + // assert(list_empty(&abuf->array_list)); + if (abuf->shared && rt->sab_funcs.sab_free) { + rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); + } else { + if (abuf->free_func) + abuf->free_func(rt, abuf->opaque, abuf->data); + } + js_free_rt(rt, abuf); + } +} + +JSValue js_array_buffer_isView(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSObject* p; + BOOL res; + res = FALSE; + if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_DATAVIEW) { + res = TRUE; + } + } + return JS_NewBool(ctx, res); +} + +const JSCFunctionListEntry js_array_buffer_funcs[] = { + JS_CFUNC_DEF("isView", 1, js_array_buffer_isView), + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), +}; + +JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext* ctx) { + return JS_ThrowTypeError(ctx, "ArrayBuffer is detached"); +} + +JSValue js_array_buffer_get_byteLength(JSContext* ctx, JSValueConst this_val, int class_id) { + JSArrayBuffer* abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + /* return 0 if detached */ + return JS_NewUint32(ctx, abuf->byte_length); +} + +void JS_DetachArrayBuffer(JSContext* ctx, JSValueConst obj) { + JSArrayBuffer* abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); + struct list_head* el; + + if (!abuf || abuf->detached) + return; + if (abuf->free_func) + abuf->free_func(ctx->rt, abuf->opaque, abuf->data); + abuf->data = NULL; + abuf->byte_length = 0; + abuf->detached = TRUE; + + list_for_each(el, &abuf->array_list) { + JSTypedArray* ta; + JSObject* p; + + ta = list_entry(el, JSTypedArray, link); + p = ta->obj; + /* Note: the typed array length and offset fields are not modified */ + if (p->class_id != JS_CLASS_DATAVIEW) { + p->u.array.count = 0; + p->u.array.u.ptr = NULL; + } + } +} + +/* get an ArrayBuffer or SharedArrayBuffer */ +JSArrayBuffer* js_get_array_buffer(JSContext* ctx, JSValueConst obj) { + JSObject* p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ARRAY_BUFFER && p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { + fail: + JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER); + return NULL; + } + return p->u.array_buffer; +} + +/* return NULL if exception. WARNING: any JS call can detach the + buffer and render the returned pointer invalid */ +uint8_t* JS_GetArrayBuffer(JSContext* ctx, size_t* psize, JSValueConst obj) { + JSArrayBuffer* abuf = js_get_array_buffer(ctx, obj); + if (!abuf) + goto fail; + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + *psize = abuf->byte_length; + return abuf->data; +fail: + *psize = 0; + return NULL; +} + +JSValue js_array_buffer_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int class_id) { + JSArrayBuffer *abuf, *new_abuf; + int64_t len, start, end, new_len; + JSValue ctor, new_obj; + + abuf = JS_GetOpaque2(ctx, this_val, class_id); + if (!abuf) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + len = abuf->byte_length; + + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + return JS_EXCEPTION; + + end = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + new_len = max_int64(end - start, 0); + ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + if (JS_IsUndefined(ctor)) { + new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, class_id); + } else { + JSValue args[1]; + args[0] = JS_NewInt64(ctx, new_len); + new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst*)args); + JS_FreeValue(ctx, ctor); + JS_FreeValue(ctx, args[0]); + } + if (JS_IsException(new_obj)) + return new_obj; + new_abuf = JS_GetOpaque2(ctx, new_obj, class_id); + if (!new_abuf) + goto fail; + if (js_same_value(ctx, new_obj, this_val)) { + JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer"); + goto fail; + } + if (new_abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + if (new_abuf->byte_length < new_len) { + JS_ThrowTypeError(ctx, "new ArrayBuffer is too small"); + goto fail; + } + /* must test again because of side effects */ + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + memcpy(new_abuf->data, abuf->data + start, new_len); + return new_obj; +fail: + JS_FreeValue(ctx, new_obj); + return JS_EXCEPTION; +} + +const JSCFunctionListEntry js_array_buffer_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE), +}; + +/* SharedArrayBuffer */ + +const JSCFunctionListEntry js_shared_array_buffer_funcs[] = { + JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), +}; + +const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER), + JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE), +}; + +JSObject* get_typed_array(JSContext* ctx, JSValueConst this_val, int is_dataview) { + JSObject* p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + goto fail; + p = JS_VALUE_GET_OBJ(this_val); + if (is_dataview) { + if (p->class_id != JS_CLASS_DATAVIEW) + goto fail; + } else { + if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { + fail: + JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray"); + return NULL; + } + } + return p; +} + +/* WARNING: 'p' must be a typed array */ +BOOL typed_array_is_detached(JSContext* ctx, JSObject* p) { + JSTypedArray* ta = p->u.typed_array; + JSArrayBuffer* abuf = ta->buffer->u.array_buffer; + /* XXX: could simplify test by ensuring that + p->u.array.u.ptr is NULL iff it is detached */ + return abuf->detached; +} + + +int validate_typed_array(JSContext* ctx, JSValueConst this_val) { + JSObject* p; + p = get_typed_array(ctx, this_val, 0); + if (!p) + return -1; + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return -1; + } + return 0; +} + +JSValue js_typed_array_get_length(JSContext* ctx, JSValueConst this_val) { + JSObject* p; + p = get_typed_array(ctx, this_val, 0); + if (!p) + return JS_EXCEPTION; + return JS_NewInt32(ctx, p->u.array.count); +} + +JSValue js_typed_array_get_buffer(JSContext* ctx, JSValueConst this_val, int is_dataview) { + JSObject* p; + JSTypedArray* ta; + p = get_typed_array(ctx, this_val, is_dataview); + if (!p) + return JS_EXCEPTION; + ta = p->u.typed_array; + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); +} + +JSValue js_typed_array_get_byteLength(JSContext* ctx, JSValueConst this_val, int is_dataview) { + JSObject* p; + JSTypedArray* ta; + p = get_typed_array(ctx, this_val, is_dataview); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_detached(ctx, p)) { + if (is_dataview) { + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + } else { + return JS_NewInt32(ctx, 0); + } + } + ta = p->u.typed_array; + return JS_NewInt32(ctx, ta->length); +} + +JSValue js_typed_array_get_byteOffset(JSContext* ctx, JSValueConst this_val, int is_dataview) { + JSObject* p; + JSTypedArray* ta; + p = get_typed_array(ctx, this_val, is_dataview); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_detached(ctx, p)) { + if (is_dataview) { + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + } else { + return JS_NewInt32(ctx, 0); + } + } + ta = p->u.typed_array; + return JS_NewInt32(ctx, ta->offset); +} + +/* Return the buffer associated to the typed array or an exception if + it is not a typed array or if the buffer is detached. pbyte_offset, + pbyte_length or pbytes_per_element can be NULL. */ +JSValue JS_GetTypedArrayBuffer(JSContext* ctx, JSValueConst obj, size_t* pbyte_offset, size_t* pbyte_length, size_t* pbytes_per_element) { + JSObject* p; + JSTypedArray* ta; + p = get_typed_array(ctx, obj, FALSE); + if (!p) + return JS_EXCEPTION; + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + ta = p->u.typed_array; + if (pbyte_offset) + *pbyte_offset = ta->offset; + if (pbyte_length) + *pbyte_length = ta->length; + if (pbytes_per_element) { + *pbytes_per_element = 1 << typed_array_size_log2(p->class_id); + } + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); +} + +JSValue js_typed_array_get_toStringTag(JSContext* ctx, JSValueConst this_val) { + JSObject* p; + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_UNDEFINED; + p = JS_VALUE_GET_OBJ(this_val); + if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY)) + return JS_UNDEFINED; + return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name); +} + +JSValue js_typed_array_set_internal(JSContext* ctx, JSValueConst dst, JSValueConst src, JSValueConst off) { + JSObject* p; + JSObject* src_p; + uint32_t i; + int64_t src_len, offset; + JSValue val, src_obj = JS_UNDEFINED; + + p = get_typed_array(ctx, dst, 0); + if (!p) + goto fail; + if (JS_ToInt64Sat(ctx, &offset, off)) + goto fail; + if (offset < 0) + goto range_error; + if (typed_array_is_detached(ctx, p)) { + detached: + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + src_obj = JS_ToObject(ctx, src); + if (JS_IsException(src_obj)) + goto fail; + src_p = JS_VALUE_GET_OBJ(src_obj); + if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY && src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + JSTypedArray* dest_ta = p->u.typed_array; + JSArrayBuffer* dest_abuf = dest_ta->buffer->u.array_buffer; + JSTypedArray* src_ta = src_p->u.typed_array; + JSArrayBuffer* src_abuf = src_ta->buffer->u.array_buffer; + int shift = typed_array_size_log2(p->class_id); + + if (src_abuf->detached) + goto detached; + + src_len = src_p->u.array.count; + if (offset > (int64_t)(p->u.array.count - src_len)) + goto range_error; + + /* copying between typed objects */ + if (src_p->class_id == p->class_id) { + /* same type, use memmove */ + memmove(dest_abuf->data + dest_ta->offset + (offset << shift), src_abuf->data + src_ta->offset, src_len << shift); + goto done; + } + if (dest_abuf->data == src_abuf->data) { + /* copying between the same buffer using different types of mappings + would require a temporary buffer */ + } + /* otherwise, default behavior is slow but correct */ + } else { + if (js_get_length64(ctx, &src_len, src_obj)) + goto fail; + if (offset > (int64_t)(p->u.array.count - src_len)) { + range_error: + JS_ThrowRangeError(ctx, "invalid array length"); + goto fail; + } + } + for (i = 0; i < src_len; i++) { + val = JS_GetPropertyUint32(ctx, src_obj, i); + if (JS_IsException(val)) + goto fail; + if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0) + goto fail; + } +done: + JS_FreeValue(ctx, src_obj); + return JS_UNDEFINED; +fail: + JS_FreeValue(ctx, src_obj); + return JS_EXCEPTION; +} + +JSValue js_typed_array_set(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValueConst offset = JS_UNDEFINED; + if (argc > 1) { + offset = argv[1]; + } + return js_typed_array_set_internal(ctx, this_val, argv[0], offset); +} + +JSValue js_create_typed_array_iterator(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic) { + if (validate_typed_array(ctx, this_val)) + return JS_EXCEPTION; + return js_create_array_iterator(ctx, this_val, argc, argv, magic); +} + +/* return < 0 if exception */ +int js_typed_array_get_length_internal(JSContext* ctx, JSValueConst obj) { + JSObject* p; + p = get_typed_array(ctx, obj, 0); + if (!p) + return -1; + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return -1; + } + return p->u.array.count; +} + +#if 0 +/* validate a typed array and return its length */ +JSValue js_typed_array___getLength(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + BOOL ignore_detached = JS_ToBool(ctx, argv[1]); + + if (ignore_detached) { + return js_typed_array_get_length(ctx, argv[0]); + } else { + int len; + len = js_typed_array_get_length_internal(ctx, argv[0]); + if (len < 0) + return JS_EXCEPTION; + return JS_NewInt32(ctx, len); + } +} +#endif + +JSValue js_typed_array_create(JSContext* ctx, JSValueConst ctor, int argc, JSValueConst* argv) { + JSValue ret; + int new_len; + int64_t len; + + ret = JS_CallConstructor(ctx, ctor, argc, argv); + if (JS_IsException(ret)) + return ret; + /* validate the typed array */ + new_len = js_typed_array_get_length_internal(ctx, ret); + if (new_len < 0) + goto fail; + if (argc == 1) { + /* ensure that it is large enough */ + if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) + goto fail; + if (new_len < len) { + JS_ThrowTypeError(ctx, "TypedArray length is too small"); + fail: + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + } + return ret; +} + +#if 0 +JSValue js_typed_array___create(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) +{ + return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1); +} +#endif + +JSValue js_typed_array___speciesCreate(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValueConst obj; + JSObject* p; + JSValue ctor, ret; + int argc1; + + obj = argv[0]; + p = get_typed_array(ctx, obj, 0); + if (!p) + return JS_EXCEPTION; + ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED); + if (JS_IsException(ctor)) + return ctor; + argc1 = max_int(argc - 1, 0); + if (JS_IsUndefined(ctor)) { + ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1, p->class_id); + } else { + ret = js_typed_array_create(ctx, ctor, argc1, argv + 1); + JS_FreeValue(ctx, ctor); + } + return ret; +} + +JSValue js_typed_array_from(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + // from(items, mapfn = void 0, this_arg = void 0) + JSValueConst items = argv[0], mapfn, this_arg; + JSValueConst args[2]; + JSValue stack[2]; + JSValue iter, arr, r, v, v2; + int64_t k, len; + int done, mapping; + + mapping = FALSE; + mapfn = JS_UNDEFINED; + this_arg = JS_UNDEFINED; + r = JS_UNDEFINED; + arr = JS_UNDEFINED; + stack[0] = JS_UNDEFINED; + stack[1] = JS_UNDEFINED; + + if (argc > 1) { + mapfn = argv[1]; + if (!JS_IsUndefined(mapfn)) { + if (check_function(ctx, mapfn)) + goto exception; + mapping = 1; + if (argc > 2) + this_arg = argv[2]; + } + } + iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto exception; + if (!JS_IsUndefined(iter)) { + JS_FreeValue(ctx, iter); + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + goto exception; + stack[0] = JS_DupValue(ctx, items); + if (js_for_of_start(ctx, &stack[1], FALSE)) + goto exception; + for (k = 0;; k++) { + v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); + if (JS_IsException(v)) + goto exception_close; + if (done) + break; + if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) + goto exception_close; + } + } else { + arr = JS_ToObject(ctx, items); + if (JS_IsException(arr)) + goto exception; + } + if (js_get_length64(ctx, &len, arr) < 0) + goto exception; + v = JS_NewInt64(ctx, len); + args[0] = v; + r = js_typed_array_create(ctx, this_val, 1, args); + JS_FreeValue(ctx, v); + if (JS_IsException(r)) + goto exception; + for (k = 0; k < len; k++) { + v = JS_GetPropertyInt64(ctx, arr, k); + if (JS_IsException(v)) + goto exception; + if (mapping) { + args[0] = v; + args[1] = JS_NewInt32(ctx, k); + v2 = JS_Call(ctx, mapfn, this_arg, 2, args); + JS_FreeValue(ctx, v); + v = v2; + if (JS_IsException(v)) + goto exception; + } + if (JS_SetPropertyInt64(ctx, r, k, v) < 0) + goto exception; + } + goto done; + +exception_close: + if (!JS_IsUndefined(stack[0])) + JS_IteratorClose(ctx, stack[0], TRUE); +exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; +done: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, stack[0]); + JS_FreeValue(ctx, stack[1]); + return r; +} + +JSValue js_typed_array_of(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValue obj; + JSValueConst args[1]; + int i; + + args[0] = JS_NewInt32(ctx, argc); + obj = js_typed_array_create(ctx, this_val, 1, args); + if (JS_IsException(obj)) + return obj; + + for (i = 0; i < argc; i++) { + if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + } + return obj; +} + +JSValue js_typed_array_copyWithin(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSObject* p; + int len, to, from, final, count, shift; + + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + return JS_EXCEPTION; + + if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len)) + return JS_EXCEPTION; + + if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len)) + return JS_EXCEPTION; + + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) + return JS_EXCEPTION; + } + + count = min_int(final - from, len - to); + if (count > 0) { + p = JS_VALUE_GET_OBJ(this_val); + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + shift = typed_array_size_log2(p->class_id); + memmove(p->u.array.u.uint8_ptr + (to << shift), p->u.array.u.uint8_ptr + (from << shift), count << shift); + } + return JS_DupValue(ctx, this_val); +} + +JSValue js_typed_array_fill(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSObject* p; + int len, k, final, shift; + uint64_t v64; + + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(this_val); + + if (p->class_id == JS_CLASS_UINT8C_ARRAY) { + int32_t v; + if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0]))) + return JS_EXCEPTION; + v64 = v; + } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) { + uint32_t v; + if (JS_ToUint32(ctx, &v, argv[0])) + return JS_EXCEPTION; + v64 = v; + } else +#ifdef CONFIG_BIGNUM + if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + if (JS_ToBigInt64(ctx, (int64_t*)&v64, argv[0])) + return JS_EXCEPTION; + } else +#endif + { + double d; + if (JS_ToFloat64(ctx, &d, argv[0])) + return JS_EXCEPTION; + if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { + union { + float f; + uint32_t u32; + } u; + u.f = d; + v64 = u.u32; + } else { + JSFloat64Union u; + u.d = d; + v64 = u.u64; + } + } + + k = 0; + if (argc > 1) { + if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + + final = len; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) + return JS_EXCEPTION; + } + + if (typed_array_is_detached(ctx, p)) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + + shift = typed_array_size_log2(p->class_id); + switch (shift) { + case 0: + if (k < final) { + memset(p->u.array.u.uint8_ptr + k, v64, final - k); + } + break; + case 1: + for (; k < final; k++) { + p->u.array.u.uint16_ptr[k] = v64; + } + break; + case 2: + for (; k < final; k++) { + p->u.array.u.uint32_ptr[k] = v64; + } + break; + case 3: + for (; k < final; k++) { + p->u.array.u.uint64_ptr[k] = v64; + } + break; + default: + abort(); + } + return JS_DupValue(ctx, this_val); +} + +JSValue js_typed_array_find(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int findIndex) { + JSValueConst func, this_arg; + JSValueConst args[3]; + JSValue val, index_val, res; + int len, k; + + val = JS_UNDEFINED; + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + goto exception; + + func = argv[0]; + if (check_function(ctx, func)) + goto exception; + + this_arg = JS_UNDEFINED; + if (argc > 1) + this_arg = argv[1]; + + for (k = 0; k < len; k++) { + index_val = JS_NewInt32(ctx, k); + val = JS_GetPropertyValue(ctx, this_val, index_val); + if (JS_IsException(val)) + goto exception; + args[0] = val; + args[1] = index_val; + args[2] = this_val; + res = JS_Call(ctx, func, this_arg, 3, args); + if (JS_IsException(res)) + goto exception; + if (JS_ToBoolFree(ctx, res)) { + if (findIndex) { + JS_FreeValue(ctx, val); + return index_val; + } else { + return val; + } + } + JS_FreeValue(ctx, val); + } + if (findIndex) + return JS_NewInt32(ctx, -1); + else + return JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +#define special_indexOf 0 +#define special_lastIndexOf 1 +#define special_includes -1 + +JSValue js_typed_array_indexOf(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int special) { + JSObject* p; + int len, tag, is_int, is_bigint, k, stop, inc, res = -1; + int64_t v64; + double d; + float f; + + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + goto exception; + if (len == 0) + goto done; + + if (special == special_lastIndexOf) { + k = len - 1; + if (argc > 1) { + if (JS_ToFloat64(ctx, &d, argv[1])) + goto exception; + if (isnan(d)) { + k = 0; + } else { + if (d >= 0) { + if (d < k) { + k = d; + } + } else { + d += len; + if (d < 0) + goto done; + k = d; + } + } + } + stop = -1; + inc = -1; + } else { + k = 0; + if (argc > 1) { + if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) + goto exception; + } + stop = len; + inc = 1; + } + + p = JS_VALUE_GET_OBJ(this_val); + /* if the array was detached, no need to go further (but no + exception is raised) */ + if (typed_array_is_detached(ctx, p)) { + /* "includes" scans all the properties, so "undefined" can match */ + if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0) + res = 0; + goto done; + } + + is_bigint = 0; + is_int = 0; /* avoid warning */ + v64 = 0; /* avoid warning */ + tag = JS_VALUE_GET_NORM_TAG(argv[0]); + if (tag == JS_TAG_INT) { + is_int = 1; + v64 = JS_VALUE_GET_INT(argv[0]); + d = v64; + } else if (tag == JS_TAG_FLOAT64) { + d = JS_VALUE_GET_FLOAT64(argv[0]); + v64 = d; + is_int = (v64 == d); + } else +#ifdef CONFIG_BIGNUM + 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 + { + goto done; + } + + switch (p->class_id) { + case JS_CLASS_INT8_ARRAY: + if (is_int && (int8_t)v64 == v64) + goto scan8; + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (is_int && (uint8_t)v64 == v64) { + const uint8_t *pv, *pp; + uint16_t v; + scan8: + pv = p->u.array.u.uint8_ptr; + v = v64; + if (inc > 0) { + pp = memchr(pv + k, v, len - k); + if (pp) + res = pp - pv; + } else { + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + } + break; + case JS_CLASS_INT16_ARRAY: + if (is_int && (int16_t)v64 == v64) + goto scan16; + break; + case JS_CLASS_UINT16_ARRAY: + if (is_int && (uint16_t)v64 == v64) { + const uint16_t* pv; + uint16_t v; + scan16: + pv = p->u.array.u.uint16_ptr; + v = v64; + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + break; + case JS_CLASS_INT32_ARRAY: + if (is_int && (int32_t)v64 == v64) + goto scan32; + break; + case JS_CLASS_UINT32_ARRAY: + if (is_int && (uint32_t)v64 == v64) { + const uint32_t* pv; + uint32_t v; + scan32: + pv = p->u.array.u.uint32_ptr; + v = v64; + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + break; + case JS_CLASS_FLOAT32_ARRAY: + if (is_bigint) + break; + if (isnan(d)) { + const float* pv = p->u.array.u.float_ptr; + /* special case: indexOf returns -1, includes finds NaN */ + if (special != special_includes) + goto done; + for (; k != stop; k += inc) { + if (isnan(pv[k])) { + res = k; + break; + } + } + } else if ((f = (float)d) == d) { + const float* pv = p->u.array.u.float_ptr; + for (; k != stop; k += inc) { + if (pv[k] == f) { + res = k; + break; + } + } + } + break; + case JS_CLASS_FLOAT64_ARRAY: + if (is_bigint) + break; + if (isnan(d)) { + const double* pv = p->u.array.u.double_ptr; + /* special case: indexOf returns -1, includes finds NaN */ + if (special != special_includes) + goto done; + for (; k != stop; k += inc) { + if (isnan(pv[k])) { + res = k; + break; + } + } + } else { + const double* pv = p->u.array.u.double_ptr; + for (; k != stop; k += inc) { + if (pv[k] == d) { + res = k; + break; + } + } + } + break; +#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_bigint || (is_math_mode(ctx) && is_int && v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) { + const uint64_t* pv; + uint64_t v; + scan64: + pv = p->u.array.u.uint64_ptr; + v = v64; + for (; k != stop; k += inc) { + if (pv[k] == v) { + res = k; + break; + } + } + } + break; +#endif + } + +done: + if (special == special_includes) + return JS_NewBool(ctx, res >= 0); + else + return JS_NewInt32(ctx, res); + +exception: + return JS_EXCEPTION; +} + +JSValue js_typed_array_join(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int toLocaleString) { + JSValue sep = JS_UNDEFINED, el; + StringBuffer b_s, *b = &b_s; + JSString* p = NULL; + int i, n; + int c; + + n = js_typed_array_get_length_internal(ctx, this_val); + if (n < 0) + goto exception; + + c = ','; /* default separator */ + if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { + sep = JS_ToString(ctx, argv[0]); + if (JS_IsException(sep)) + goto exception; + p = JS_VALUE_GET_STRING(sep); + if (p->len == 1 && !p->is_wide_char) + c = p->u.str8[0]; + else + c = -1; + } + string_buffer_init(ctx, b, 0); + + /* XXX: optimize with direct access */ + for (i = 0; i < n; i++) { + if (i > 0) { + if (c >= 0) { + if (string_buffer_putc8(b, c)) + goto fail; + } else { + if (string_buffer_concat(b, p, 0, p->len)) + goto fail; + } + } + el = JS_GetPropertyUint32(ctx, this_val, i); + /* Can return undefined for example if the typed array is detached */ + if (!JS_IsNull(el) && !JS_IsUndefined(el)) { + if (JS_IsException(el)) + goto fail; + if (toLocaleString) { + el = JS_ToLocaleStringFree(ctx, el); + } + if (string_buffer_concat_value_free(b, el)) + goto fail; + } + } + JS_FreeValue(ctx, sep); + return string_buffer_end(b); + +fail: + string_buffer_free(b); + JS_FreeValue(ctx, sep); +exception: + return JS_EXCEPTION; +} + +JSValue js_typed_array_reverse(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSObject* p; + int len; + + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + return JS_EXCEPTION; + if (len > 0) { + p = JS_VALUE_GET_OBJ(this_val); + switch (typed_array_size_log2(p->class_id)) { + case 0: { + uint8_t* p1 = p->u.array.u.uint8_ptr; + uint8_t* p2 = p1 + len - 1; + while (p1 < p2) { + uint8_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } break; + case 1: { + uint16_t* p1 = p->u.array.u.uint16_ptr; + uint16_t* p2 = p1 + len - 1; + while (p1 < p2) { + uint16_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } break; + case 2: { + uint32_t* p1 = p->u.array.u.uint32_ptr; + uint32_t* p2 = p1 + len - 1; + while (p1 < p2) { + uint32_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } break; + case 3: { + uint64_t* p1 = p->u.array.u.uint64_ptr; + uint64_t* p2 = p1 + len - 1; + while (p1 < p2) { + uint64_t v = *p1; + *p1++ = *p2; + *p2-- = v; + } + } break; + default: + abort(); + } + } + return JS_DupValue(ctx, this_val); +} + +JSValue js_typed_array_slice(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValueConst args[2]; + JSValue arr, val; + JSObject *p, *p1; + int n, len, start, final, count, shift; + + arr = JS_UNDEFINED; + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + goto exception; + + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int(final - start, 0); + + p = get_typed_array(ctx, this_val, 0); + if (p == NULL) + goto exception; + shift = typed_array_size_log2(p->class_id); + + args[0] = this_val; + args[1] = JS_NewInt32(ctx, count); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); + if (JS_IsException(arr)) + goto exception; + + if (count > 0) { + if (validate_typed_array(ctx, this_val) || validate_typed_array(ctx, arr)) + goto exception; + + p1 = get_typed_array(ctx, arr, 0); + if (p1 != NULL && p->class_id == p1->class_id && typed_array_get_length(ctx, p1) >= count && typed_array_get_length(ctx, p) >= start + count) { + memcpy(p1->u.array.u.uint8_ptr, p->u.array.u.uint8_ptr + (start << shift), count << shift); + } else { + for (n = 0; n < count; n++) { + val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n)); + if (JS_IsException(val)) + goto exception; + if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val, JS_PROP_THROW) < 0) + goto exception; + } + } + } + return arr; + +exception: + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +JSValue js_typed_array_subarray(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSValueConst args[4]; + JSValue arr, byteOffset, ta_buffer; + JSObject* p; + int len, start, final, count, shift, offset; + + p = get_typed_array(ctx, this_val, 0); + if (!p) + goto exception; + len = p->u.array.count; + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) + goto exception; + } + count = max_int(final - start, 0); + byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0); + if (JS_IsException(byteOffset)) + goto exception; + shift = typed_array_size_log2(p->class_id); + offset = JS_VALUE_GET_INT(byteOffset) + (start << shift); + JS_FreeValue(ctx, byteOffset); + ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0); + if (JS_IsException(ta_buffer)) + goto exception; + args[0] = this_val; + args[1] = ta_buffer; + args[2] = JS_NewInt32(ctx, offset); + args[3] = JS_NewInt32(ctx, count); + arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args); + JS_FreeValue(ctx, ta_buffer); + return arr; + +exception: + return JS_EXCEPTION; +} + +/* TypedArray.prototype.sort */ + +int js_cmp_doubles(double x, double y) { + if (isnan(x)) + return isnan(y) ? 0 : +1; + if (isnan(y)) + return -1; + if (x < y) + return -1; + if (x > y) + return 1; + if (x != 0) + return 0; + if (signbit(x)) + return signbit(y) ? 0 : -1; + else + return signbit(y) ? 1 : 0; +} + +int js_TA_cmp_int8(const void* a, const void* b, void* opaque) { + return *(const int8_t*)a - *(const int8_t*)b; +} + +int js_TA_cmp_uint8(const void* a, const void* b, void* opaque) { + return *(const uint8_t*)a - *(const uint8_t*)b; +} + +int js_TA_cmp_int16(const void* a, const void* b, void* opaque) { + return *(const int16_t*)a - *(const int16_t*)b; +} + +int js_TA_cmp_uint16(const void* a, const void* b, void* opaque) { + return *(const uint16_t*)a - *(const uint16_t*)b; +} + +int js_TA_cmp_int32(const void* a, const void* b, void* opaque) { + int32_t x = *(const int32_t*)a; + int32_t y = *(const int32_t*)b; + return (y < x) - (y > x); +} + +int js_TA_cmp_uint32(const void* a, const void* b, void* opaque) { + uint32_t x = *(const uint32_t*)a; + uint32_t y = *(const uint32_t*)b; + return (y < x) - (y > x); +} + +#ifdef CONFIG_BIGNUM +int js_TA_cmp_int64(const void* a, const void* b, void* opaque) { + int64_t x = *(const int64_t*)a; + int64_t y = *(const int64_t*)b; + return (y < x) - (y > x); +} + +int js_TA_cmp_uint64(const void* a, const void* b, void* opaque) { + uint64_t x = *(const uint64_t*)a; + uint64_t y = *(const uint64_t*)b; + return (y < x) - (y > x); +} +#endif + +int js_TA_cmp_float32(const void* a, const void* b, void* opaque) { + return js_cmp_doubles(*(const float*)a, *(const float*)b); +} + +int js_TA_cmp_float64(const void* a, const void* b, void* opaque) { + return js_cmp_doubles(*(const double*)a, *(const double*)b); +} + +JSValue js_TA_get_int8(JSContext* ctx, const void* a) { + return JS_NewInt32(ctx, *(const int8_t*)a); +} + +JSValue js_TA_get_uint8(JSContext* ctx, const void* a) { + return JS_NewInt32(ctx, *(const uint8_t*)a); +} + +JSValue js_TA_get_int16(JSContext* ctx, const void* a) { + return JS_NewInt32(ctx, *(const int16_t*)a); +} + +JSValue js_TA_get_uint16(JSContext* ctx, const void* a) { + return JS_NewInt32(ctx, *(const uint16_t*)a); +} + +JSValue js_TA_get_int32(JSContext* ctx, const void* a) { + return JS_NewInt32(ctx, *(const int32_t*)a); +} + +JSValue js_TA_get_uint32(JSContext* ctx, const void* a) { + return JS_NewUint32(ctx, *(const uint32_t*)a); +} + +#ifdef CONFIG_BIGNUM +JSValue js_TA_get_int64(JSContext* ctx, const void* a) { + return JS_NewBigInt64(ctx, *(int64_t*)a); +} + +JSValue js_TA_get_uint64(JSContext* ctx, const void* a) { + return JS_NewBigUint64(ctx, *(uint64_t*)a); +} +#endif + +JSValue js_TA_get_float32(JSContext* ctx, const void* a) { + return __JS_NewFloat64(ctx, *(const float*)a); +} + +JSValue js_TA_get_float64(JSContext* ctx, const void* a) { + return __JS_NewFloat64(ctx, *(const double*)a); +} + +struct TA_sort_context { + JSContext* ctx; + int exception; + JSValueConst arr; + JSValueConst cmp; + JSValue (*getfun)(JSContext* ctx, const void* a); + uint8_t* array_ptr; /* cannot change unless the array is detached */ + int elt_size; +}; + +int js_TA_cmp_generic(const void* a, const void* b, void* opaque) { + struct TA_sort_context* psc = opaque; + JSContext* ctx = psc->ctx; + uint32_t a_idx, b_idx; + JSValueConst argv[2]; + JSValue res; + int cmp; + + cmp = 0; + if (!psc->exception) { + a_idx = *(uint32_t*)a; + b_idx = *(uint32_t*)b; + argv[0] = psc->getfun(ctx, psc->array_ptr + a_idx * (size_t)psc->elt_size); + argv[1] = psc->getfun(ctx, psc->array_ptr + b_idx * (size_t)(psc->elt_size)); + res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); + if (JS_IsException(res)) { + psc->exception = 1; + goto done; + } + if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { + int val = JS_VALUE_GET_INT(res); + cmp = (val > 0) - (val < 0); + } else { + double val; + if (JS_ToFloat64Free(ctx, &val, res) < 0) { + psc->exception = 1; + goto done; + } else { + cmp = (val > 0) - (val < 0); + } + } + if (cmp == 0) { + /* make sort stable: compare array offsets */ + cmp = (a_idx > b_idx) - (a_idx < b_idx); + } + if (validate_typed_array(ctx, psc->arr) < 0) { + psc->exception = 1; + } + done: + JS_FreeValue(ctx, (JSValue)argv[0]); + JS_FreeValue(ctx, (JSValue)argv[1]); + } + return cmp; +} + +JSValue js_typed_array_sort(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + JSObject* p; + int len; + size_t elt_size; + struct TA_sort_context tsc; + void* array_ptr; + int (*cmpfun)(const void* a, const void* b, void* opaque); + + tsc.ctx = ctx; + tsc.exception = 0; + tsc.arr = this_val; + tsc.cmp = argv[0]; + + len = js_typed_array_get_length_internal(ctx, this_val); + if (len < 0) + return JS_EXCEPTION; + if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) + return JS_EXCEPTION; + + if (len > 1) { + p = JS_VALUE_GET_OBJ(this_val); + switch (p->class_id) { + case JS_CLASS_INT8_ARRAY: + tsc.getfun = js_TA_get_int8; + cmpfun = js_TA_cmp_int8; + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + tsc.getfun = js_TA_get_uint8; + cmpfun = js_TA_cmp_uint8; + break; + case JS_CLASS_INT16_ARRAY: + tsc.getfun = js_TA_get_int16; + cmpfun = js_TA_cmp_int16; + break; + case JS_CLASS_UINT16_ARRAY: + tsc.getfun = js_TA_get_uint16; + cmpfun = js_TA_cmp_uint16; + break; + case JS_CLASS_INT32_ARRAY: + tsc.getfun = js_TA_get_int32; + cmpfun = js_TA_cmp_int32; + break; + case JS_CLASS_UINT32_ARRAY: + tsc.getfun = js_TA_get_uint32; + cmpfun = js_TA_cmp_uint32; + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + tsc.getfun = js_TA_get_int64; + cmpfun = js_TA_cmp_int64; + break; + case JS_CLASS_BIG_UINT64_ARRAY: + tsc.getfun = js_TA_get_uint64; + cmpfun = js_TA_cmp_uint64; + break; +#endif + case JS_CLASS_FLOAT32_ARRAY: + tsc.getfun = js_TA_get_float32; + cmpfun = js_TA_cmp_float32; + break; + case JS_CLASS_FLOAT64_ARRAY: + tsc.getfun = js_TA_get_float64; + cmpfun = js_TA_cmp_float64; + break; + default: + abort(); + } + array_ptr = p->u.array.u.ptr; + elt_size = 1 << typed_array_size_log2(p->class_id); + if (!JS_IsUndefined(tsc.cmp)) { + uint32_t* array_idx; + void* array_tmp; + size_t i, j; + + /* XXX: a stable sort would use less memory */ + array_idx = js_malloc(ctx, len * sizeof(array_idx[0])); + if (!array_idx) + return JS_EXCEPTION; + for (i = 0; i < len; i++) + array_idx[i] = i; + tsc.array_ptr = array_ptr; + tsc.elt_size = elt_size; + rqsort(array_idx, len, sizeof(array_idx[0]), js_TA_cmp_generic, &tsc); + if (tsc.exception) + goto fail; + array_tmp = js_malloc(ctx, len * elt_size); + if (!array_tmp) { + fail: + js_free(ctx, array_idx); + return JS_EXCEPTION; + } + memcpy(array_tmp, array_ptr, len * elt_size); + switch (elt_size) { + case 1: + for (i = 0; i < len; i++) { + j = array_idx[i]; + ((uint8_t*)array_ptr)[i] = ((uint8_t*)array_tmp)[j]; + } + break; + case 2: + for (i = 0; i < len; i++) { + j = array_idx[i]; + ((uint16_t*)array_ptr)[i] = ((uint16_t*)array_tmp)[j]; + } + break; + case 4: + for (i = 0; i < len; i++) { + j = array_idx[i]; + ((uint32_t*)array_ptr)[i] = ((uint32_t*)array_tmp)[j]; + } + break; + case 8: + for (i = 0; i < len; i++) { + j = array_idx[i]; + ((uint64_t*)array_ptr)[i] = ((uint64_t*)array_tmp)[j]; + } + break; + default: + abort(); + } + js_free(ctx, array_tmp); + js_free(ctx, array_idx); + } else { + rqsort(array_ptr, len, elt_size, cmpfun, &tsc); + if (tsc.exception) + return JS_EXCEPTION; + } + } + return JS_DupValue(ctx, this_val); +} + +const JSCFunctionListEntry js_typed_array_base_funcs[] = { + JS_CFUNC_DEF("from", 1, js_typed_array_from), JS_CFUNC_DEF("of", 0, js_typed_array_of), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), + // JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ), + // JS_CFUNC_DEF("__create", 2, js_typed_array___create ), + // JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ), +}; + +const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { + JS_CGETSET_DEF("length", js_typed_array_get_length, NULL), + JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0), + JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0), + JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0), + JS_CFUNC_DEF("set", 1, js_typed_array_set), + JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE), + JS_ALIAS_DEF("[Symbol.iterator]", "values"), + JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY), + JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE), + JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL), + JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin), + JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA), + JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA), + JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA), + JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA), + JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA), + JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA), + JS_CFUNC_DEF("fill", 1, js_typed_array_fill), + JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1), + JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse), + JS_CFUNC_DEF("slice", 2, js_typed_array_slice), + JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray), + JS_CFUNC_DEF("sort", 1, js_typed_array_sort), + JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0), + JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1), + JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf), + JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf), + JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes), + // JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@ +}; + +JSValue js_typed_array_base_constructor(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + return JS_ThrowTypeError(ctx, "cannot be called"); +} + +/* 'obj' must be an allocated typed array object */ +int typed_array_init(JSContext* ctx, JSValueConst obj, JSValue buffer, uint64_t offset, uint64_t len) { + JSTypedArray* ta; + JSObject *p, *pbuffer; + JSArrayBuffer* abuf; + int size_log2; + + p = JS_VALUE_GET_OBJ(obj); + size_log2 = typed_array_size_log2(p->class_id); + ta = js_malloc(ctx, sizeof(*ta)); + if (!ta) { + JS_FreeValue(ctx, buffer); + return -1; + } + pbuffer = JS_VALUE_GET_OBJ(buffer); + abuf = pbuffer->u.array_buffer; + ta->obj = p; + ta->buffer = pbuffer; + ta->offset = offset; + ta->length = len << size_log2; + list_add_tail(&ta->link, &abuf->array_list); + p->u.typed_array = ta; + p->u.array.count = len; + p->u.array.u.ptr = abuf->data + offset; + return 0; +} + +JSValue js_array_from_iterator(JSContext* ctx, uint32_t* plen, JSValueConst obj, JSValueConst method) { + JSValue arr, iter, next_method = JS_UNDEFINED, val; + BOOL done; + uint32_t k; + + *plen = 0; + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return arr; + iter = JS_GetIterator2(ctx, obj, method); + if (JS_IsException(iter)) + goto fail; + next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next_method)) + goto fail; + k = 0; + for (;;) { + val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); + if (JS_IsException(val)) + goto fail; + if (done) { + JS_FreeValue(ctx, val); + break; + } + if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) + goto fail; + k++; + } + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + *plen = k; + return arr; +fail: + JS_FreeValue(ctx, next_method); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; +} + +JSValue js_typed_array_constructor_obj(JSContext* ctx, JSValueConst new_target, JSValueConst obj, int classid) { + JSValue iter, ret, arr = JS_UNDEFINED, val, buffer; + uint32_t i; + int size_log2; + int64_t len; + + size_log2 = typed_array_size_log2(classid); + ret = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(ret)) + return JS_EXCEPTION; + + iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(iter)) + goto fail; + if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { + uint32_t len1; + arr = js_array_from_iterator(ctx, &len1, obj, iter); + JS_FreeValue(ctx, iter); + if (JS_IsException(arr)) + goto fail; + len = len1; + } else { + if (js_get_length64(ctx, &len, obj)) + goto fail; + arr = JS_DupValue(ctx, obj); + } + + buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, len << size_log2); + if (JS_IsException(buffer)) + goto fail; + if (typed_array_init(ctx, ret, buffer, 0, len)) + goto fail; + + for (i = 0; i < len; i++) { + val = JS_GetPropertyUint32(ctx, arr, i); + if (JS_IsException(val)) + goto fail; + if (JS_SetPropertyUint32(ctx, ret, i, val) < 0) + goto fail; + } + JS_FreeValue(ctx, arr); + return ret; +fail: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; +} + +JSValue js_typed_array_constructor_ta(JSContext* ctx, JSValueConst new_target, JSValueConst src_obj, int classid) { + JSObject *p, *src_buffer; + JSTypedArray* ta; + JSValue ctor, obj, buffer; + uint32_t len, i; + int size_log2; + JSArrayBuffer *src_abuf, *abuf; + + obj = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_GET_OBJ(src_obj); + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + ta = p->u.typed_array; + len = p->u.array.count; + src_buffer = ta->buffer; + src_abuf = src_buffer->u.array_buffer; + if (!src_abuf->shared) { + ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer), JS_UNDEFINED); + if (JS_IsException(ctor)) + goto fail; + } else { + /* force ArrayBuffer default constructor */ + ctor = JS_UNDEFINED; + } + size_log2 = typed_array_size_log2(classid); + buffer = js_array_buffer_constructor1(ctx, ctor, (uint64_t)len << size_log2); + JS_FreeValue(ctx, ctor); + if (JS_IsException(buffer)) + goto fail; + /* necessary because it could have been detached */ + if (typed_array_is_detached(ctx, p)) { + JS_FreeValue(ctx, buffer); + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER); + if (typed_array_init(ctx, obj, buffer, 0, len)) + goto fail; + if (p->class_id == classid) { + /* same type: copy the content */ + memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length); + } else { + for (i = 0; i < len; i++) { + JSValue val; + val = JS_GetPropertyUint32(ctx, src_obj, i); + if (JS_IsException(val)) + goto fail; + if (JS_SetPropertyUint32(ctx, obj, i, val) < 0) + goto fail; + } + } + return obj; +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +JSValue js_typed_array_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv, int classid) { + JSValue buffer, obj; + JSArrayBuffer* abuf; + int size_log2; + uint64_t len, offset; + + size_log2 = typed_array_size_log2(classid); + if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) { + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, len << size_log2); + if (JS_IsException(buffer)) + return JS_EXCEPTION; + offset = 0; + } else { + JSObject* p = JS_VALUE_GET_OBJ(argv[0]); + if (p->class_id == JS_CLASS_ARRAY_BUFFER || p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { + abuf = p->u.array_buffer; + if (JS_ToIndex(ctx, &offset, argv[1])) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((offset & ((1 << size_log2) - 1)) != 0 || offset > abuf->byte_length) + return JS_ThrowRangeError(ctx, "invalid offset"); + if (JS_IsUndefined(argv[2])) { + if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) + goto invalid_length; + len = (abuf->byte_length - offset) >> size_log2; + } else { + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((offset + (len << size_log2)) > abuf->byte_length) { + invalid_length: + return JS_ThrowRangeError(ctx, "invalid length"); + } + } + buffer = JS_DupValue(ctx, argv[0]); + } else { + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid); + } else { + return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid); + } + } + } + + obj = js_create_from_ctor(ctx, new_target, classid); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, buffer); + return JS_EXCEPTION; + } + if (typed_array_init(ctx, obj, buffer, offset, len)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +void js_typed_array_finalizer(JSRuntime* rt, JSValue val) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSTypedArray* ta = p->u.typed_array; + if (ta) { + /* during the GC the finalizers are called in an arbitrary + order so the ArrayBuffer finalizer may have been called */ + if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) { + list_del(&ta->link); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); + js_free_rt(rt, ta); + } +} + +void js_typed_array_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSTypedArray* ta = p->u.typed_array; + if (ta) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func); + } +} + +JSValue js_dataview_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { + JSArrayBuffer* abuf; + uint64_t offset; + uint32_t len; + JSValueConst buffer; + JSValue obj; + JSTypedArray* ta; + JSObject* p; + + buffer = argv[0]; + abuf = js_get_array_buffer(ctx, buffer); + if (!abuf) + return JS_EXCEPTION; + offset = 0; + if (argc > 1) { + if (JS_ToIndex(ctx, &offset, argv[1])) + return JS_EXCEPTION; + } + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if (offset > abuf->byte_length) + return JS_ThrowRangeError(ctx, "invalid byteOffset"); + len = abuf->byte_length - offset; + if (argc > 2 && !JS_IsUndefined(argv[2])) { + uint64_t l; + if (JS_ToIndex(ctx, &l, argv[2])) + return JS_EXCEPTION; + if (l > len) + return JS_ThrowRangeError(ctx, "invalid byteLength"); + len = l; + } + + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW); + if (JS_IsException(obj)) + return JS_EXCEPTION; + if (abuf->detached) { + /* could have been detached in js_create_from_ctor() */ + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + goto fail; + } + ta = js_malloc(ctx, sizeof(*ta)); + if (!ta) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p = JS_VALUE_GET_OBJ(obj); + ta->obj = p; + ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer)); + ta->offset = offset; + ta->length = len; + list_add_tail(&ta->link, &abuf->array_list); + p->u.typed_array = ta; + return obj; +} + +JSValue js_dataview_getValue(JSContext* ctx, JSValueConst this_obj, int argc, JSValueConst* argv, int class_id) { + JSTypedArray* ta; + JSArrayBuffer* abuf; + int is_swap, size; + uint8_t* ptr; + uint32_t v; + uint64_t pos; + + ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); + if (!ta) + return JS_EXCEPTION; + size = 1 << typed_array_size_log2(class_id); + if (JS_ToIndex(ctx, &pos, argv[0])) + return JS_EXCEPTION; + is_swap = FALSE; + if (argc > 1) + is_swap = JS_ToBool(ctx, argv[1]); +#ifndef WORDS_BIGENDIAN + is_swap ^= 1; +#endif + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((pos + size) > ta->length) + return JS_ThrowRangeError(ctx, "out of bound"); + ptr = abuf->data + ta->offset + pos; + + switch (class_id) { + case JS_CLASS_INT8_ARRAY: + return JS_NewInt32(ctx, *(int8_t*)ptr); + case JS_CLASS_UINT8_ARRAY: + return JS_NewInt32(ctx, *(uint8_t*)ptr); + case JS_CLASS_INT16_ARRAY: + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return JS_NewInt32(ctx, (int16_t)v); + case JS_CLASS_UINT16_ARRAY: + v = get_u16(ptr); + if (is_swap) + v = bswap16(v); + return JS_NewInt32(ctx, v); + case JS_CLASS_INT32_ARRAY: + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + return JS_NewInt32(ctx, v); + case JS_CLASS_UINT32_ARRAY: + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + return JS_NewUint32(ctx, v); +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: { + uint64_t v; + v = get_u64(ptr); + if (is_swap) + v = bswap64(v); + return JS_NewBigInt64(ctx, v); + } break; + case JS_CLASS_BIG_UINT64_ARRAY: { + uint64_t v; + v = get_u64(ptr); + if (is_swap) + v = bswap64(v); + return JS_NewBigUint64(ctx, v); + } break; +#endif + case JS_CLASS_FLOAT32_ARRAY: { + union { + float f; + uint32_t i; + } u; + v = get_u32(ptr); + if (is_swap) + v = bswap32(v); + u.i = v; + return __JS_NewFloat64(ctx, u.f); + } + case JS_CLASS_FLOAT64_ARRAY: { + union { + double f; + uint64_t i; + } u; + u.i = get_u64(ptr); + if (is_swap) + u.i = bswap64(u.i); + return __JS_NewFloat64(ctx, u.f); + } + default: + abort(); + } +} + +JSValue js_dataview_setValue(JSContext* ctx, JSValueConst this_obj, int argc, JSValueConst* argv, int class_id) { + JSTypedArray* ta; + JSArrayBuffer* abuf; + int is_swap, size; + uint8_t* ptr; + uint64_t v64; + uint32_t v; + uint64_t pos; + JSValueConst val; + + ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); + if (!ta) + return JS_EXCEPTION; + size = 1 << typed_array_size_log2(class_id); + if (JS_ToIndex(ctx, &pos, argv[0])) + return JS_EXCEPTION; + val = argv[1]; + v = 0; /* avoid warning */ + v64 = 0; /* avoid warning */ + if (class_id <= JS_CLASS_UINT32_ARRAY) { + if (JS_ToUint32(ctx, &v, val)) + return JS_EXCEPTION; + } else +#ifdef CONFIG_BIGNUM + if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + if (JS_ToBigInt64(ctx, (int64_t*)&v64, val)) + return JS_EXCEPTION; + } else +#endif + { + double d; + if (JS_ToFloat64(ctx, &d, val)) + return JS_EXCEPTION; + if (class_id == JS_CLASS_FLOAT32_ARRAY) { + union { + float f; + uint32_t i; + } u; + u.f = d; + v = u.i; + } else { + JSFloat64Union u; + u.d = d; + v64 = u.u64; + } + } + is_swap = FALSE; + if (argc > 2) + is_swap = JS_ToBool(ctx, argv[2]); +#ifndef WORDS_BIGENDIAN + is_swap ^= 1; +#endif + abuf = ta->buffer->u.array_buffer; + if (abuf->detached) + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + if ((pos + size) > ta->length) + return JS_ThrowRangeError(ctx, "out of bound"); + ptr = abuf->data + ta->offset + pos; + + switch (class_id) { + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + *ptr = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + if (is_swap) + v = bswap16(v); + put_u16(ptr, v); + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + if (is_swap) + v = bswap32(v); + put_u32(ptr, v); + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: +#endif + case JS_CLASS_FLOAT64_ARRAY: + if (is_swap) + v64 = bswap64(v64); + put_u64(ptr, v64); + break; + default: + abort(); + } + return JS_UNDEFINED; +} + +const JSCFunctionListEntry js_dataview_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1), + JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1), + JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1), + JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY), + JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY), + JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY), + JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY), + JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY), + JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY), +#ifdef CONFIG_BIGNUM + JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY), + JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY), +#endif + JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY), + JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY), + JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY), + JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY), + JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY), + JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY), + JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY), + JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY), +#ifdef CONFIG_BIGNUM + JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY), + JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY), +#endif + JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY), + JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE), +}; + +void JS_AddIntrinsicTypedArrays(JSContext* ctx) { + JSValue typed_array_base_proto, typed_array_base_func; + JSValueConst array_buffer_func, shared_array_buffer_func; + int i; + + ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER], js_array_buffer_proto_funcs, countof(js_array_buffer_proto_funcs)); + + array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer", js_array_buffer_constructor, 1, ctx->class_proto[JS_CLASS_ARRAY_BUFFER]); + JS_SetPropertyFunctionList(ctx, array_buffer_func, js_array_buffer_funcs, countof(js_array_buffer_funcs)); + + ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER], js_shared_array_buffer_proto_funcs, countof(js_shared_array_buffer_proto_funcs)); + + shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer", js_shared_array_buffer_constructor, 1, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]); + JS_SetPropertyFunctionList(ctx, shared_array_buffer_func, js_shared_array_buffer_funcs, countof(js_shared_array_buffer_funcs)); + + typed_array_base_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, typed_array_base_proto, js_typed_array_base_proto_funcs, countof(js_typed_array_base_proto_funcs)); + + /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */ + JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString); + /* XXX: should use alias method in JSCFunctionListEntry */ //@@@ + JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + + typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor, "TypedArray", 0); + JS_SetPropertyFunctionList(ctx, typed_array_base_func, js_typed_array_base_funcs, countof(js_typed_array_base_funcs)); + JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); + + for (i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) { + JSValue func_obj; + char buf[ATOM_GET_STR_BUF_SIZE]; + const char* name; + + ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto); + JS_DefinePropertyValueStr(ctx, ctx->class_proto[i], "BYTES_PER_ELEMENT", JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), 0); + name = JS_AtomGetStr(ctx, buf, sizeof(buf), JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); + func_obj = JS_NewCFunction3(ctx, (JSCFunction*)js_typed_array_constructor, name, 3, JS_CFUNC_constructor_magic, i, typed_array_base_func); + JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]); + JS_DefinePropertyValueStr(ctx, func_obj, "BYTES_PER_ELEMENT", JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), 0); + } + JS_FreeValue(ctx, typed_array_base_proto); + JS_FreeValue(ctx, typed_array_base_func); + + /* DataView */ + ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW], js_dataview_proto_funcs, countof(js_dataview_proto_funcs)); + JS_NewGlobalCConstructorOnly(ctx, "DataView", js_dataview_constructor, 1, ctx->class_proto[JS_CLASS_DATAVIEW]); + /* Atomics */ +#ifdef CONFIG_ATOMICS + JS_AddIntrinsicAtomics(ctx); +#endif } \ No newline at end of file diff --git a/src/core/builtins/js-typed-array.h b/src/core/builtins/js-typed-array.h index 9569a9484..68a96e02f 100644 --- a/src/core/builtins/js-typed-array.h +++ b/src/core/builtins/js-typed-array.h @@ -1,40 +1,40 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_JS_TYPED_ARRAY_H -#define QUICKJS_JS_TYPED_ARRAY_H - -#include "../types.h" -#include "quickjs/cutils.h" -#include "quickjs/quickjs.h" - -JSValue js_array_buffer_constructor3(JSContext* ctx, JSValueConst new_target, uint64_t len, JSClassID class_id, uint8_t* buf, JSFreeArrayBufferDataFunc* free_func, void* opaque, BOOL alloc_flag); -JSValue js_typed_array_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv, int classid); - -JSArrayBuffer* js_get_array_buffer(JSContext* ctx, JSValueConst obj); -BOOL typed_array_is_detached(JSContext* ctx, JSObject* p); -JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext* ctx); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_JS_TYPED_ARRAY_H +#define QUICKJS_JS_TYPED_ARRAY_H + +#include "../types.h" +#include "quickjs/cutils.h" +#include "quickjs/quickjs.h" + +JSValue js_array_buffer_constructor3(JSContext* ctx, JSValueConst new_target, uint64_t len, JSClassID class_id, uint8_t* buf, JSFreeArrayBufferDataFunc* free_func, void* opaque, BOOL alloc_flag); +JSValue js_typed_array_constructor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv, int classid); + +JSArrayBuffer* js_get_array_buffer(JSContext* ctx, JSValueConst obj); +BOOL typed_array_is_detached(JSContext* ctx, JSObject* p); +JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext* ctx); + #endif \ No newline at end of file diff --git a/src/core/bytecode.c b/src/core/bytecode.c index a39c35e5f..473084fc1 100644 --- a/src/core/bytecode.c +++ b/src/core/bytecode.c @@ -1,2093 +1,2093 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "bytecode.h" -#include "builtins/js-function.h" -#include "builtins/js-object.h" -#include "builtins/js-typed-array.h" -#include "exception.h" -#include "function.h" -#include "gc.h" -#include "malloc.h" -#include "module.h" -#include "object.h" -#include "parser.h" -#include "runtime.h" -#include "shape.h" -#include "string.h" - -void free_function_bytecode(JSRuntime* rt, JSFunctionBytecode* b) { - int i; - -#if 0 - { - char buf[ATOM_GET_STR_BUF_SIZE]; - printf("freeing %s\n", - JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); - } -#endif - free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); - - if (b->vardefs) { - for (i = 0; i < b->arg_count + b->var_count; i++) { - JS_FreeAtomRT(rt, b->vardefs[i].var_name); - } - } - for (i = 0; i < b->cpool_count; i++) - JS_FreeValueRT(rt, b->cpool[i]); - - for (i = 0; i < b->closure_var_count; i++) { - JSClosureVar* cv = &b->closure_var[i]; - JS_FreeAtomRT(rt, cv->var_name); - } - if (b->realm) - JS_FreeContext(b->realm); - - JS_FreeAtomRT(rt, b->func_name); - if (b->has_debug) { - JS_FreeAtomRT(rt, b->debug.filename); - js_free_rt(rt, b->debug.pc2line_buf); - js_free_rt(rt, b->debug.source); - } - - remove_gc_object(&b->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { - list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); - } else { - js_free_rt(rt, b); - } -} - -void free_bytecode_atoms(JSRuntime* rt, const uint8_t* bc_buf, int bc_len, BOOL use_short_opcodes) { - int pos, len, op; - JSAtom atom; - const JSOpCode* oi; - - pos = 0; - while (pos < bc_len) { - op = bc_buf[pos]; - if (use_short_opcodes) - oi = &short_opcode_info(op); - else - oi = &opcode_info[op]; - - len = oi->size; - switch (oi->fmt) { - case OP_FMT_atom: - case OP_FMT_atom_u8: - case OP_FMT_atom_u16: - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - atom = get_u32(bc_buf + pos + 1); - JS_FreeAtomRT(rt, atom); - break; - default: - break; - } - pos += len; - } -} - -/*******************************************************************/ -/* binary object writer & reader */ - -typedef enum BCTagEnum { - BC_TAG_NULL = 1, - BC_TAG_UNDEFINED, - BC_TAG_BOOL_FALSE, - BC_TAG_BOOL_TRUE, - BC_TAG_INT32, - BC_TAG_FLOAT64, - BC_TAG_STRING, - BC_TAG_OBJECT, - BC_TAG_ARRAY, - BC_TAG_BIG_INT, - BC_TAG_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 -#define BC_BASE_VERSION 2 -#else -#define BC_BASE_VERSION 1 -#endif -#define BC_BE_VERSION 0x40 -#ifdef WORDS_BIGENDIAN -#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION) -#else -#define BC_VERSION BC_BASE_VERSION -#endif - -typedef struct BCWriterState { - JSContext* ctx; - DynBuf dbuf; - 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 -static const char* const bc_tag_str[] = { - "invalid", "null", "undefined", "false", "true", "int32", - "float64", "string", "object", "array", "bigint", "bigfloat", - "bigdecimal", "template", "function", "module", "TypedArray", "ArrayBuffer", - "SharedArrayBuffer", "Date", "ObjectValue", "ObjectReference", -}; -#endif - -static void bc_put_u8(BCWriterState* s, uint8_t v) { - dbuf_putc(&s->dbuf, v); -} - -static void bc_put_u16(BCWriterState* s, uint16_t v) { - if (s->byte_swap) - v = bswap16(v); - dbuf_put_u16(&s->dbuf, v); -} - -static __maybe_unused void bc_put_u32(BCWriterState* s, uint32_t v) { - if (s->byte_swap) - v = bswap32(v); - dbuf_put_u32(&s->dbuf, v); -} - -static void bc_put_u64(BCWriterState* s, uint64_t v) { - if (s->byte_swap) - v = bswap64(v); - dbuf_put(&s->dbuf, (uint8_t*)&v, sizeof(v)); -} - -static void bc_put_leb128(BCWriterState* s, uint32_t v) { - dbuf_put_leb128(&s->dbuf, v); -} - -static void bc_put_sleb128(BCWriterState* s, int32_t v) { - dbuf_put_sleb128(&s->dbuf, v); -} - -static void bc_set_flags(uint32_t* pflags, int* pidx, uint32_t val, int n) { - *pflags = *pflags | (val << *pidx); - *pidx += n; -} - -static int bc_atom_to_idx(BCWriterState* s, uint32_t* pres, JSAtom atom) { - uint32_t v; - - if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { - *pres = atom; - return 0; - } - atom -= s->first_atom; - if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) { - *pres = s->atom_to_idx[atom]; - return 0; - } - if (atom >= s->atom_to_idx_size) { - int old_size, i; - old_size = s->atom_to_idx_size; - if (js_resize_array(s->ctx, (void**)&s->atom_to_idx, sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, atom + 1)) - return -1; - /* XXX: could add a specific js_resize_array() function to do it */ - for (i = old_size; i < s->atom_to_idx_size; i++) - s->atom_to_idx[i] = 0; - } - if (js_resize_array(s->ctx, (void**)&s->idx_to_atom, sizeof(s->idx_to_atom[0]), &s->idx_to_atom_size, - s->idx_to_atom_count + 1)) - goto fail; - - v = s->idx_to_atom_count++; - s->idx_to_atom[v] = atom + s->first_atom; - v += s->first_atom; - s->atom_to_idx[atom] = v; - *pres = v; - return 0; -fail: - *pres = 0; - return -1; -} - -static int bc_put_atom(BCWriterState* s, JSAtom atom) { - uint32_t v; - - if (__JS_AtomIsTaggedInt(atom)) { - v = (__JS_AtomToUInt32(atom) << 1) | 1; - } else { - if (bc_atom_to_idx(s, &v, atom)) - return -1; - v <<= 1; - } - bc_put_leb128(s, v); - return 0; -} - -static void bc_byte_swap(uint8_t* bc_buf, int bc_len) { - int pos, len, op, fmt; - - pos = 0; - while (pos < bc_len) { - op = bc_buf[pos]; - len = short_opcode_info(op).size; - fmt = short_opcode_info(op).fmt; - switch (fmt) { - case OP_FMT_u16: - case OP_FMT_i16: - case OP_FMT_label16: - case OP_FMT_npop: - case OP_FMT_loc: - case OP_FMT_arg: - case OP_FMT_var_ref: - put_u16(bc_buf + pos + 1, bswap16(get_u16(bc_buf + pos + 1))); - break; - case OP_FMT_i32: - case OP_FMT_u32: - case OP_FMT_const: - case OP_FMT_label: - case OP_FMT_atom: - case OP_FMT_atom_u8: - put_u32(bc_buf + pos + 1, bswap32(get_u32(bc_buf + pos + 1))); - break; - case OP_FMT_atom_u16: - case OP_FMT_label_u16: - put_u32(bc_buf + pos + 1, bswap32(get_u32(bc_buf + pos + 1))); - put_u16(bc_buf + pos + 1 + 4, bswap16(get_u16(bc_buf + pos + 1 + 4))); - break; - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - put_u32(bc_buf + pos + 1, bswap32(get_u32(bc_buf + pos + 1))); - put_u32(bc_buf + pos + 1 + 4, bswap32(get_u32(bc_buf + pos + 1 + 4))); - if (fmt == OP_FMT_atom_label_u16) { - put_u16(bc_buf + pos + 1 + 4 + 4, bswap16(get_u16(bc_buf + pos + 1 + 4 + 4))); - } - break; - case OP_FMT_npop_u16: - put_u16(bc_buf + pos + 1, bswap16(get_u16(bc_buf + pos + 1))); - put_u16(bc_buf + pos + 1 + 2, bswap16(get_u16(bc_buf + pos + 1 + 2))); - break; - default: - break; - } - pos += len; - } -} - -static int JS_WriteFunctionBytecode(BCWriterState* s, const uint8_t* bc_buf1, int bc_len) { - int pos, len, op; - JSAtom atom; - uint8_t* bc_buf; - uint32_t val; - - bc_buf = js_malloc(s->ctx, bc_len); - if (!bc_buf) - return -1; - memcpy(bc_buf, bc_buf1, bc_len); - - pos = 0; - while (pos < bc_len) { - op = bc_buf[pos]; - len = short_opcode_info(op).size; - switch (short_opcode_info(op).fmt) { - case OP_FMT_atom: - case OP_FMT_atom_u8: - case OP_FMT_atom_u16: - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - atom = get_u32(bc_buf + pos + 1); - if (bc_atom_to_idx(s, &val, atom)) - goto fail; - put_u32(bc_buf + pos + 1, val); - break; - default: - break; - } - pos += len; - } - - if (s->byte_swap) - bc_byte_swap(bc_buf, bc_len); - - dbuf_put(&s->dbuf, bc_buf, bc_len); - - js_free(s->ctx, bc_buf); - return 0; -fail: - js_free(s->ctx, bc_buf); - return -1; -} - -static void JS_WriteString(BCWriterState* s, JSString* p) { - int i; - bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); - if (p->is_wide_char) { - for (i = 0; i < p->len; i++) - bc_put_u16(s, p->u.str16[i]); - } else { - dbuf_put(&s->dbuf, p->u.str8, p->len); - } -} - -#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) { - /* XXX: this field is redundant */ - bc_put_leb128(s, b->arg_count + b->var_count); - for (i = 0; i < b->arg_count + b->var_count; i++) { - JSVarDef* vd = &b->vardefs[i]; - bc_put_atom(s, vd->var_name); - bc_put_leb128(s, vd->scope_level); - bc_put_leb128(s, vd->scope_next + 1); - flags = idx = 0; - bc_set_flags(&flags, &idx, vd->var_kind, 4); - bc_set_flags(&flags, &idx, vd->is_const, 1); - bc_set_flags(&flags, &idx, vd->is_lexical, 1); - bc_set_flags(&flags, &idx, vd->is_captured, 1); - assert(idx <= 8); - bc_put_u8(s, flags); - } - } else { - bc_put_leb128(s, 0); - } - - for (i = 0; i < b->closure_var_count; i++) { - JSClosureVar* cv = &b->closure_var[i]; - bc_put_atom(s, cv->var_name); - bc_put_leb128(s, cv->var_idx); - flags = idx = 0; - bc_set_flags(&flags, &idx, cv->is_local, 1); - bc_set_flags(&flags, &idx, cv->is_arg, 1); - bc_set_flags(&flags, &idx, cv->is_const, 1); - bc_set_flags(&flags, &idx, cv->is_lexical, 1); - bc_set_flags(&flags, &idx, cv->var_kind, 4); - assert(idx <= 8); - bc_put_u8(s, flags); - } - - if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) - goto fail; - - if (b->has_debug) { - bc_put_atom(s, b->debug.filename); - bc_put_leb128(s, b->debug.line_num); - bc_put_leb128(s, b->debug.pc2line_len); - dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); - } - - for (i = 0; i < b->cpool_count; i++) { - if (JS_WriteObjectRec(s, b->cpool[i])) - goto fail; - } - return 0; -fail: - return -1; -} - -static int JS_WriteModule(BCWriterState* s, JSValueConst obj) { - JSModuleDef* m = JS_VALUE_GET_PTR(obj); - int i; - - bc_put_u8(s, BC_TAG_MODULE); - bc_put_atom(s, m->module_name); - - bc_put_leb128(s, m->req_module_entries_count); - for (i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry* rme = &m->req_module_entries[i]; - bc_put_atom(s, rme->module_name); - } - - bc_put_leb128(s, m->export_entries_count); - for (i = 0; i < m->export_entries_count; i++) { - JSExportEntry* me = &m->export_entries[i]; - bc_put_u8(s, me->export_type); - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - bc_put_leb128(s, me->u.local.var_idx); - } else { - bc_put_leb128(s, me->u.req_module_idx); - bc_put_atom(s, me->local_name); - } - bc_put_atom(s, me->export_name); - } - - bc_put_leb128(s, m->star_export_entries_count); - for (i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry* se = &m->star_export_entries[i]; - bc_put_leb128(s, se->req_module_idx); - } - - bc_put_leb128(s, m->import_entries_count); - for (i = 0; i < m->import_entries_count; i++) { - JSImportEntry* mi = &m->import_entries[i]; - bc_put_leb128(s, mi->var_idx); - bc_put_atom(s, mi->import_name); - bc_put_leb128(s, mi->req_module_idx); - } - - if (JS_WriteObjectRec(s, m->func_obj)) - goto fail; - return 0; -fail: - return -1; -} - -static int JS_WriteArray(BCWriterState* s, JSValueConst obj) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - uint32_t i, len; - JSValue val; - int ret; - BOOL is_template; - - if (s->allow_bytecode && !p->extensible) { - /* not extensible array: we consider it is a - template when we are saving bytecode */ - bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); - is_template = TRUE; - } else { - bc_put_u8(s, BC_TAG_ARRAY); - is_template = FALSE; - } - if (js_get_length32(s->ctx, &len, obj)) - goto fail1; - bc_put_leb128(s, len); - for (i = 0; i < len; i++) { - val = JS_GetPropertyUint32(s->ctx, obj, i); - if (JS_IsException(val)) - goto fail1; - ret = JS_WriteObjectRec(s, val); - JS_FreeValue(s->ctx, val); - if (ret) - goto fail1; - } - if (is_template) { - val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); - if (JS_IsException(val)) - goto fail1; - ret = JS_WriteObjectRec(s, val); - JS_FreeValue(s->ctx, val); - if (ret) - goto fail1; - } - return 0; -fail1: - return -1; -} - -static int JS_WriteObjectTag(BCWriterState* s, JSValueConst obj) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - uint32_t i, prop_count; - JSShape* sh; - JSShapeProperty* pr; - int pass; - JSAtom atom; - - bc_put_u8(s, BC_TAG_OBJECT); - prop_count = 0; - sh = p->shape; - for (pass = 0; pass < 2; pass++) { - if (pass == 1) - bc_put_leb128(s, prop_count); - for (i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { - atom = pr->atom; - if (atom != JS_ATOM_NULL && JS_AtomIsString(s->ctx, atom) && (pr->flags & JS_PROP_ENUMERABLE)) { - if (pr->flags & JS_PROP_TMASK) { - JS_ThrowTypeError(s->ctx, "only value properties are supported"); - goto fail; - } - if (pass == 0) { - prop_count++; - } else { - bc_put_atom(s, atom); - if (JS_WriteObjectRec(s, p->prop[i].u.value)) - goto fail; - } - } - } - } - return 0; -fail: - return -1; -} - -static int JS_WriteTypedArray(BCWriterState* s, JSValueConst obj) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - JSTypedArray* ta = p->u.typed_array; - - bc_put_u8(s, BC_TAG_TYPED_ARRAY); - bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); - bc_put_leb128(s, p->u.array.count); - bc_put_leb128(s, ta->offset); - if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) - return -1; - return 0; -} - -static int JS_WriteArrayBuffer(BCWriterState* s, JSValueConst obj) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - JSArrayBuffer* abuf = p->u.array_buffer; - if (abuf->detached) { - JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); - return -1; - } - bc_put_u8(s, BC_TAG_ARRAY_BUFFER); - bc_put_leb128(s, abuf->byte_length); - dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); - return 0; -} - -static int JS_WriteSharedArrayBuffer(BCWriterState* s, JSValueConst obj) { - JSObject* p = JS_VALUE_GET_OBJ(obj); - JSArrayBuffer* abuf = p->u.array_buffer; - assert(!abuf->detached); /* SharedArrayBuffer are never detached */ - bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); - bc_put_leb128(s, abuf->byte_length); - bc_put_u64(s, (uintptr_t)abuf->data); - if (js_resize_array(s->ctx, (void**)&s->sab_tab, sizeof(s->sab_tab[0]), &s->sab_tab_size, s->sab_tab_len + 1)) - return -1; - /* keep the SAB pointer so that the user can clone it or free it */ - s->sab_tab[s->sab_tab_len++] = abuf->data; - return 0; -} - -static int JS_WriteObjectRec(BCWriterState* s, JSValueConst obj) { - uint32_t tag; - - if (js_check_stack_overflow(s->ctx->rt, 0)) { - JS_ThrowStackOverflow(s->ctx); - return -1; - } - - tag = JS_VALUE_GET_NORM_TAG(obj); - switch (tag) { - case JS_TAG_NULL: - bc_put_u8(s, BC_TAG_NULL); - break; - case JS_TAG_UNDEFINED: - bc_put_u8(s, BC_TAG_UNDEFINED); - break; - case JS_TAG_BOOL: - bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj)); - break; - case JS_TAG_INT: - bc_put_u8(s, BC_TAG_INT32); - bc_put_sleb128(s, JS_VALUE_GET_INT(obj)); - break; - case JS_TAG_FLOAT64: { - JSFloat64Union u; - bc_put_u8(s, BC_TAG_FLOAT64); - u.d = JS_VALUE_GET_FLOAT64(obj); - bc_put_u64(s, u.u64); - } break; - case JS_TAG_STRING: { - JSString* p = JS_VALUE_GET_STRING(obj); - bc_put_u8(s, BC_TAG_STRING); - JS_WriteString(s, p); - } break; - case JS_TAG_FUNCTION_BYTECODE: - if (!s->allow_bytecode) - goto invalid_tag; - if (JS_WriteFunctionTag(s, obj)) - goto fail; - break; - case JS_TAG_MODULE: - if (!s->allow_bytecode) - goto invalid_tag; - if (JS_WriteModule(s, obj)) - goto fail; - break; - case JS_TAG_OBJECT: { - JSObject* p = JS_VALUE_GET_OBJ(obj); - int ret, idx; - - if (s->allow_reference) { - idx = js_object_list_find(s->ctx, &s->object_list, p); - if (idx >= 0) { - bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); - bc_put_leb128(s, idx); - break; - } else { - if (js_object_list_add(s->ctx, &s->object_list, p)) - goto fail; - } - } else { - if (p->tmp_mark) { - JS_ThrowTypeError(s->ctx, "circular reference"); - goto fail; - } - p->tmp_mark = 1; - } - switch (p->class_id) { - case JS_CLASS_ARRAY: - ret = JS_WriteArray(s, obj); - break; - case JS_CLASS_OBJECT: - ret = JS_WriteObjectTag(s, obj); - break; - case JS_CLASS_ARRAY_BUFFER: - ret = JS_WriteArrayBuffer(s, obj); - break; - case JS_CLASS_SHARED_ARRAY_BUFFER: - if (!s->allow_sab) - goto invalid_tag; - ret = JS_WriteSharedArrayBuffer(s, obj); - break; - case JS_CLASS_DATE: - bc_put_u8(s, BC_TAG_DATE); - ret = JS_WriteObjectRec(s, p->u.object_data); - break; - case JS_CLASS_NUMBER: - case JS_CLASS_STRING: - case JS_CLASS_BOOLEAN: -#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: - case JS_TAG_BIG_DECIMAL: - if (JS_WriteBigNum(s, obj)) - goto fail; - break; -#endif - default: - invalid_tag: - JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag); - goto fail; - } - return 0; - -fail: - return -1; -} - -/* create the atom table */ -static int JS_WriteObjectAtoms(BCWriterState* s) { - JSRuntime* rt = s->ctx->rt; - DynBuf dbuf1; - int i, atoms_size; - uint8_t version; - - dbuf1 = s->dbuf; - js_dbuf_init(s->ctx, &s->dbuf); - - version = BC_VERSION; - if (s->byte_swap) - version ^= BC_BE_VERSION; - bc_put_u8(s, version); - - bc_put_leb128(s, s->idx_to_atom_count); - for (i = 0; i < s->idx_to_atom_count; i++) { - JSAtomStruct* p = rt->atom_array[s->idx_to_atom[i]]; - JS_WriteString(s, p); - } - /* XXX: should check for OOM in above phase */ - - /* move the atoms at the start */ - /* XXX: could just append dbuf1 data, but it uses more memory if - dbuf1 is larger than dbuf */ - atoms_size = s->dbuf.size; - if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size)) - goto fail; - memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size); - memcpy(dbuf1.buf, s->dbuf.buf, atoms_size); - dbuf1.size += atoms_size; - dbuf_free(&s->dbuf); - s->dbuf = dbuf1; - return 0; -fail: - dbuf_free(&dbuf1); - return -1; -} - -uint8_t* JS_WriteObject2(JSContext* ctx, - size_t* psize, - JSValueConst obj, - int flags, - uint8_t*** psab_tab, - size_t* psab_tab_len) { - BCWriterState ss, *s = &ss; - - memset(s, 0, sizeof(*s)); - s->ctx = ctx; - /* 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; - uint32_t first_atom; - uint32_t idx_to_atom_count; - JSAtom* idx_to_atom; - int error_state; - BOOL allow_sab : 8; - BOOL allow_bytecode : 8; - BOOL is_rom_data : 8; - BOOL allow_reference : 8; - /* object references */ - JSObject** objects; - int objects_count; - int objects_size; - -#ifdef DUMP_READ_OBJECT - const uint8_t* ptr_last; - int level; -#endif -} BCReaderState; - -#ifdef DUMP_READ_OBJECT -static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState* s, const char* fmt, ...) { - va_list ap; - int i, n, n0; - - if (!s->ptr_last) - s->ptr_last = s->buf_start; - - n = n0 = 0; - if (s->ptr > s->ptr_last || s->ptr == s->buf_start) { - n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start)); - n += n0; - } - for (i = 0; s->ptr_last < s->ptr; i++) { - if ((i & 7) == 0 && i > 0) { - printf("\n%*s", n0, ""); - n = n0; - } - n += printf(" %02x", *s->ptr_last++); - } - if (*fmt == '}') - s->level--; - if (n < 32 + s->level * 2) { - printf("%*s", 32 + s->level * 2 - n, ""); - } - va_start(ap, fmt); - vfprintf(stdout, fmt, ap); - va_end(ap); - if (strchr(fmt, '{')) - s->level++; -} -#else -#define bc_read_trace(...) -#endif - -static int bc_read_error_end(BCReaderState* s) { - if (!s->error_state) { - JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer"); - } - return s->error_state = -1; -} - -static int bc_get_u8(BCReaderState* s, uint8_t* pval) { - if (unlikely(s->buf_end - s->ptr < 1)) { - *pval = 0; /* avoid warning */ - return bc_read_error_end(s); - } - *pval = *s->ptr++; - return 0; -} - -static int bc_get_u16(BCReaderState* s, uint16_t* pval) { - if (unlikely(s->buf_end - s->ptr < 2)) { - *pval = 0; /* avoid warning */ - return bc_read_error_end(s); - } - *pval = get_u16(s->ptr); - s->ptr += 2; - return 0; -} - -static __maybe_unused int bc_get_u32(BCReaderState* s, uint32_t* pval) { - if (unlikely(s->buf_end - s->ptr < 4)) { - *pval = 0; /* avoid warning */ - return bc_read_error_end(s); - } - *pval = get_u32(s->ptr); - s->ptr += 4; - return 0; -} - -static int bc_get_u64(BCReaderState* s, uint64_t* pval) { - if (unlikely(s->buf_end - s->ptr < 8)) { - *pval = 0; /* avoid warning */ - return bc_read_error_end(s); - } - *pval = get_u64(s->ptr); - s->ptr += 8; - return 0; -} - -static int bc_get_leb128(BCReaderState* s, uint32_t* pval) { - int ret; - ret = get_leb128(pval, s->ptr, s->buf_end); - if (unlikely(ret < 0)) - return bc_read_error_end(s); - s->ptr += ret; - return 0; -} - -static int bc_get_sleb128(BCReaderState* s, int32_t* pval) { - int ret; - ret = get_sleb128(pval, s->ptr, s->buf_end); - if (unlikely(ret < 0)) - return bc_read_error_end(s); - s->ptr += ret; - return 0; -} - -/* XXX: used to read an `int` with a positive value */ -static int bc_get_leb128_int(BCReaderState* s, int* pval) { - return bc_get_leb128(s, (uint32_t*)pval); -} - -static int bc_get_leb128_u16(BCReaderState* s, uint16_t* pval) { - uint32_t val; - if (bc_get_leb128(s, &val)) { - *pval = 0; - return -1; - } - *pval = val; - return 0; -} - -static int bc_get_buf(BCReaderState* s, uint8_t* buf, uint32_t buf_len) { - if (buf_len != 0) { - if (unlikely(!buf || s->buf_end - s->ptr < buf_len)) - return bc_read_error_end(s); - memcpy(buf, s->ptr, buf_len); - s->ptr += buf_len; - } - return 0; -} - -static int bc_idx_to_atom(BCReaderState* s, JSAtom* patom, uint32_t idx) { - JSAtom atom; - - if (__JS_AtomIsTaggedInt(idx)) { - atom = idx; - } else if (idx < s->first_atom) { - atom = JS_DupAtom(s->ctx, idx); - } else { - idx -= s->first_atom; - if (idx >= s->idx_to_atom_count) { - JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)", (unsigned int)(s->ptr - s->buf_start)); - *patom = JS_ATOM_NULL; - return s->error_state = -1; - } - atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]); - } - *patom = atom; - return 0; -} - -static int bc_get_atom(BCReaderState* s, JSAtom* patom) { - uint32_t v; - if (bc_get_leb128(s, &v)) - return -1; - if (v & 1) { - *patom = __JS_AtomFromUInt32(v >> 1); - return 0; - } else { - return bc_idx_to_atom(s, patom, v >> 1); - } -} - -static JSString* JS_ReadString(BCReaderState* s) { - uint32_t len; - size_t size; - BOOL is_wide_char; - JSString* p; - - if (bc_get_leb128(s, &len)) - return NULL; - is_wide_char = len & 1; - len >>= 1; - p = js_alloc_string(s->ctx, len, is_wide_char); - if (!p) { - s->error_state = -1; - return NULL; - } - size = (size_t)len << is_wide_char; - if ((s->buf_end - s->ptr) < size) { - bc_read_error_end(s); - js_free_string(s->ctx->rt, p); - return NULL; - } - memcpy(p->u.str8, s->ptr, size); - s->ptr += size; - if (!is_wide_char) { - p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ - } -#ifdef DUMP_READ_OBJECT - JS_DumpString(s->ctx->rt, p); - printf("\n"); -#endif - return p; -} - -static uint32_t bc_get_flags(uint32_t flags, int* pidx, int n) { - uint32_t val; - /* XXX: this does not work for n == 32 */ - val = (flags >> *pidx) & ((1U << n) - 1); - *pidx += n; - return val; -} - -static int JS_ReadFunctionBytecode(BCReaderState* s, JSFunctionBytecode* b, int byte_code_offset, uint32_t bc_len) { - uint8_t* bc_buf; - int pos, len, op; - JSAtom atom; - uint32_t idx; - - if (s->is_rom_data) { - /* directly use the input buffer */ - if (unlikely(s->buf_end - s->ptr < bc_len)) - return bc_read_error_end(s); - bc_buf = (uint8_t*)s->ptr; - s->ptr += bc_len; - } else { - bc_buf = (void*)((uint8_t*)b + byte_code_offset); - if (bc_get_buf(s, bc_buf, bc_len)) - return -1; - } - b->byte_code_buf = bc_buf; - - pos = 0; - while (pos < bc_len) { - op = bc_buf[pos]; - len = short_opcode_info(op).size; - switch (short_opcode_info(op).fmt) { - case OP_FMT_atom: - case OP_FMT_atom_u8: - case OP_FMT_atom_u16: - case OP_FMT_atom_label_u8: - case OP_FMT_atom_label_u16: - idx = get_u32(bc_buf + pos + 1); - if (s->is_rom_data) { - /* just increment the reference count of the atom */ - JS_DupAtom(s->ctx, (JSAtom)idx); - } else { - if (bc_idx_to_atom(s, &atom, idx)) { - /* Note: the atoms will be freed up to this position */ - b->byte_code_len = pos; - return -1; - } - put_u32(bc_buf + pos + 1, atom); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "at %d, fixup atom: ", pos + 1); - print_atom(s->ctx, atom); - printf("\n"); -#endif - } - break; - default: - break; - } - pos += len; - } - return 0; -} - -#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; - if (local_count != 0) { - b->vardefs = (void*)((uint8_t*)b + vardefs_offset); - } - if (b->closure_var_count != 0) { - b->closure_var = (void*)((uint8_t*)b + closure_var_offset); - } - if (b->cpool_count != 0) { - b->cpool = (void*)((uint8_t*)b + cpool_offset); - } - - add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); - - obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); - -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); - print_atom(s->ctx, b->func_name); - printf("\n"); -#endif - bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", b->arg_count, b->var_count, - b->defined_arg_count, b->closure_var_count, b->cpool_count); - bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", b->stack_size, b->byte_code_len, local_count); - - if (local_count != 0) { - bc_read_trace(s, "vars {\n"); - for (i = 0; i < local_count; i++) { - JSVarDef* vd = &b->vardefs[i]; - if (bc_get_atom(s, &vd->var_name)) - goto fail; - if (bc_get_leb128_int(s, &vd->scope_level)) - goto fail; - if (bc_get_leb128_int(s, &vd->scope_next)) - goto fail; - vd->scope_next--; - if (bc_get_u8(s, &v8)) - goto fail; - idx = 0; - vd->var_kind = bc_get_flags(v8, &idx, 4); - vd->is_const = bc_get_flags(v8, &idx, 1); - vd->is_lexical = bc_get_flags(v8, &idx, 1); - vd->is_captured = bc_get_flags(v8, &idx, 1); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); - print_atom(s->ctx, vd->var_name); - printf("\n"); -#endif - } - bc_read_trace(s, "}\n"); - } - if (b->closure_var_count != 0) { - bc_read_trace(s, "closure vars {\n"); - for (i = 0; i < b->closure_var_count; i++) { - JSClosureVar* cv = &b->closure_var[i]; - int var_idx; - if (bc_get_atom(s, &cv->var_name)) - goto fail; - if (bc_get_leb128_int(s, &var_idx)) - goto fail; - cv->var_idx = var_idx; - if (bc_get_u8(s, &v8)) - goto fail; - idx = 0; - cv->is_local = bc_get_flags(v8, &idx, 1); - cv->is_arg = bc_get_flags(v8, &idx, 1); - cv->is_const = bc_get_flags(v8, &idx, 1); - cv->is_lexical = bc_get_flags(v8, &idx, 1); - cv->var_kind = bc_get_flags(v8, &idx, 4); -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); - print_atom(s->ctx, cv->var_name); - printf("\n"); -#endif - } - bc_read_trace(s, "}\n"); - } - { - bc_read_trace(s, "bytecode {\n"); - if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) - goto fail; - bc_read_trace(s, "}\n"); - } - if (b->has_debug) { - /* read optional debug information */ - bc_read_trace(s, "debug {\n"); - if (bc_get_atom(s, &b->debug.filename)) - goto fail; - if (bc_get_leb128_int(s, &b->debug.line_num)) - goto fail; - if (bc_get_leb128_int(s, &b->debug.pc2line_len)) - goto fail; - if (b->debug.pc2line_len) { - b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); - if (!b->debug.pc2line_buf) - goto fail; - if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) - goto fail; - } -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "filename: "); - print_atom(s->ctx, b->debug.filename); - printf("\n"); -#endif - bc_read_trace(s, "}\n"); - } - if (b->cpool_count != 0) { - bc_read_trace(s, "cpool {\n"); - for (i = 0; i < b->cpool_count; i++) { - JSValue val; - val = JS_ReadObjectRec(s); - if (JS_IsException(val)) - goto fail; - b->cpool[i] = val; - } - bc_read_trace(s, "}\n"); - } - b->realm = JS_DupContext(ctx); - return obj; -fail: - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue JS_ReadModule(BCReaderState* s) { - JSContext* ctx = s->ctx; - JSValue obj; - JSModuleDef* m = NULL; - JSAtom module_name; - int i; - uint8_t v8; - - if (bc_get_atom(s, &module_name)) - goto fail; -#ifdef DUMP_READ_OBJECT - bc_read_trace(s, "name: "); - print_atom(s->ctx, module_name); - printf("\n"); -#endif - m = js_new_module_def(ctx, module_name); - if (!m) - goto fail; - obj = JS_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; - - if (js_check_stack_overflow(ctx->rt, 0)) - return JS_ThrowStackOverflow(ctx); - - if (bc_get_u8(s, &tag)) - return JS_EXCEPTION; - - bc_read_trace(s, "%s {\n", bc_tag_str[tag]); - - switch (tag) { - case BC_TAG_NULL: - obj = JS_NULL; - break; - case BC_TAG_UNDEFINED: - obj = JS_UNDEFINED; - break; - case BC_TAG_BOOL_FALSE: - case BC_TAG_BOOL_TRUE: - obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE); - break; - case BC_TAG_INT32: { - int32_t val; - if (bc_get_sleb128(s, &val)) - return JS_EXCEPTION; - bc_read_trace(s, "%d\n", val); - obj = JS_NewInt32(ctx, val); - } break; - case BC_TAG_FLOAT64: { - JSFloat64Union u; - if (bc_get_u64(s, &u.u64)) - return JS_EXCEPTION; - bc_read_trace(s, "%g\n", u.d); - obj = __JS_NewFloat64(ctx, u.d); - } break; - case BC_TAG_STRING: { - JSString* p; - p = JS_ReadString(s); - if (!p) - return JS_EXCEPTION; - obj = JS_MKPTR(JS_TAG_STRING, p); - } break; - case BC_TAG_FUNCTION_BYTECODE: - if (!s->allow_bytecode) - goto invalid_tag; - obj = JS_ReadFunctionTag(s); - break; - case BC_TAG_MODULE: - if (!s->allow_bytecode) - goto invalid_tag; - obj = JS_ReadModule(s); - break; - case BC_TAG_OBJECT: - obj = JS_ReadObjectTag(s); - break; - case BC_TAG_ARRAY: - case BC_TAG_TEMPLATE_OBJECT: - obj = JS_ReadArray(s, tag); - break; - case BC_TAG_TYPED_ARRAY: - obj = JS_ReadTypedArray(s); - break; - case BC_TAG_ARRAY_BUFFER: - obj = JS_ReadArrayBuffer(s); - break; - case BC_TAG_SHARED_ARRAY_BUFFER: - if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) - goto invalid_tag; - obj = JS_ReadSharedArrayBuffer(s); - break; - case BC_TAG_DATE: - obj = JS_ReadDate(s); - break; - case BC_TAG_OBJECT_VALUE: - obj = JS_ReadObjectValue(s); - break; -#ifdef CONFIG_BIGNUM - case BC_TAG_BIG_INT: - case BC_TAG_BIG_FLOAT: - 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; -} - -static int JS_ReadObjectAtoms(BCReaderState* s) { - uint8_t v8; - JSString* p; - int i; - JSAtom atom; - - if (bc_get_u8(s, &v8)) - return -1; - /* XXX: could support byte swapped input */ - if (v8 != BC_VERSION) { - JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", v8, BC_VERSION); - return -1; - } - if (bc_get_leb128(s, &s->idx_to_atom_count)) - return -1; - - bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count); - - if (s->idx_to_atom_count != 0) { - s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count * sizeof(s->idx_to_atom[0])); - if (!s->idx_to_atom) - return s->error_state = -1; - } - for (i = 0; i < s->idx_to_atom_count; i++) { - p = JS_ReadString(s); - if (!p) - return -1; - atom = JS_NewAtomStr(s->ctx, p); - if (atom == JS_ATOM_NULL) - return s->error_state = -1; - s->idx_to_atom[i] = atom; - if (s->is_rom_data && (atom != (i + s->first_atom))) - s->is_rom_data = FALSE; /* atoms must be relocated */ - } - bc_read_trace(s, "}\n"); - return 0; -} - -static void bc_reader_free(BCReaderState* s) { - int i; - if (s->idx_to_atom) { - for (i = 0; i < s->idx_to_atom_count; i++) { - JS_FreeAtom(s->ctx, s->idx_to_atom[i]); - } - js_free(s->ctx, s->idx_to_atom); - } - js_free(s->ctx, s->objects); -} - -JSValue JS_ReadObject(JSContext* ctx, const uint8_t* buf, size_t buf_len, int flags) { - BCReaderState ss, *s = &ss; - JSValue obj; - - ctx->binary_object_count += 1; - ctx->binary_object_size += buf_len; - - memset(s, 0, sizeof(*s)); - s->ctx = ctx; - s->buf_start = buf; - s->buf_end = buf + buf_len; - s->ptr = buf; - s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); - s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0); - s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); - s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); - if (s->allow_bytecode) - s->first_atom = JS_ATOM_END; - else - s->first_atom = 1; - if (JS_ReadObjectAtoms(s)) { - obj = JS_EXCEPTION; - } else { - obj = JS_ReadObjectRec(s); - } - bc_reader_free(s); - return obj; +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "bytecode.h" +#include "builtins/js-function.h" +#include "builtins/js-object.h" +#include "builtins/js-typed-array.h" +#include "exception.h" +#include "function.h" +#include "gc.h" +#include "malloc.h" +#include "module.h" +#include "object.h" +#include "parser.h" +#include "runtime.h" +#include "shape.h" +#include "string.h" + +void free_function_bytecode(JSRuntime* rt, JSFunctionBytecode* b) { + int i; + +#if 0 + { + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("freeing %s\n", + JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + } +#endif + free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); + + if (b->vardefs) { + for (i = 0; i < b->arg_count + b->var_count; i++) { + JS_FreeAtomRT(rt, b->vardefs[i].var_name); + } + } + for (i = 0; i < b->cpool_count; i++) + JS_FreeValueRT(rt, b->cpool[i]); + + for (i = 0; i < b->closure_var_count; i++) { + JSClosureVar* cv = &b->closure_var[i]; + JS_FreeAtomRT(rt, cv->var_name); + } + if (b->realm) + JS_FreeContext(b->realm); + + JS_FreeAtomRT(rt, b->func_name); + if (b->has_debug) { + JS_FreeAtomRT(rt, b->debug.filename); + js_free_rt(rt, b->debug.pc2line_buf); + js_free_rt(rt, b->debug.source); + } + + remove_gc_object(&b->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { + list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, b); + } +} + +void free_bytecode_atoms(JSRuntime* rt, const uint8_t* bc_buf, int bc_len, BOOL use_short_opcodes) { + int pos, len, op; + JSAtom atom; + const JSOpCode* oi; + + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + if (use_short_opcodes) + oi = &short_opcode_info(op); + else + oi = &opcode_info[op]; + + len = oi->size; + switch (oi->fmt) { + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + atom = get_u32(bc_buf + pos + 1); + JS_FreeAtomRT(rt, atom); + break; + default: + break; + } + pos += len; + } +} + +/*******************************************************************/ +/* binary object writer & reader */ + +typedef enum BCTagEnum { + BC_TAG_NULL = 1, + BC_TAG_UNDEFINED, + BC_TAG_BOOL_FALSE, + BC_TAG_BOOL_TRUE, + BC_TAG_INT32, + BC_TAG_FLOAT64, + BC_TAG_STRING, + BC_TAG_OBJECT, + BC_TAG_ARRAY, + BC_TAG_BIG_INT, + BC_TAG_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 +#define BC_BASE_VERSION 2 +#else +#define BC_BASE_VERSION 1 +#endif +#define BC_BE_VERSION 0x40 +#ifdef WORDS_BIGENDIAN +#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION) +#else +#define BC_VERSION BC_BASE_VERSION +#endif + +typedef struct BCWriterState { + JSContext* ctx; + DynBuf dbuf; + 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 +static const char* const bc_tag_str[] = { + "invalid", "null", "undefined", "false", "true", "int32", + "float64", "string", "object", "array", "bigint", "bigfloat", + "bigdecimal", "template", "function", "module", "TypedArray", "ArrayBuffer", + "SharedArrayBuffer", "Date", "ObjectValue", "ObjectReference", +}; +#endif + +static void bc_put_u8(BCWriterState* s, uint8_t v) { + dbuf_putc(&s->dbuf, v); +} + +static void bc_put_u16(BCWriterState* s, uint16_t v) { + if (s->byte_swap) + v = bswap16(v); + dbuf_put_u16(&s->dbuf, v); +} + +static __maybe_unused void bc_put_u32(BCWriterState* s, uint32_t v) { + if (s->byte_swap) + v = bswap32(v); + dbuf_put_u32(&s->dbuf, v); +} + +static void bc_put_u64(BCWriterState* s, uint64_t v) { + if (s->byte_swap) + v = bswap64(v); + dbuf_put(&s->dbuf, (uint8_t*)&v, sizeof(v)); +} + +static void bc_put_leb128(BCWriterState* s, uint32_t v) { + dbuf_put_leb128(&s->dbuf, v); +} + +static void bc_put_sleb128(BCWriterState* s, int32_t v) { + dbuf_put_sleb128(&s->dbuf, v); +} + +static void bc_set_flags(uint32_t* pflags, int* pidx, uint32_t val, int n) { + *pflags = *pflags | (val << *pidx); + *pidx += n; +} + +static int bc_atom_to_idx(BCWriterState* s, uint32_t* pres, JSAtom atom) { + uint32_t v; + + if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { + *pres = atom; + return 0; + } + atom -= s->first_atom; + if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) { + *pres = s->atom_to_idx[atom]; + return 0; + } + if (atom >= s->atom_to_idx_size) { + int old_size, i; + old_size = s->atom_to_idx_size; + if (js_resize_array(s->ctx, (void**)&s->atom_to_idx, sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, atom + 1)) + return -1; + /* XXX: could add a specific js_resize_array() function to do it */ + for (i = old_size; i < s->atom_to_idx_size; i++) + s->atom_to_idx[i] = 0; + } + if (js_resize_array(s->ctx, (void**)&s->idx_to_atom, sizeof(s->idx_to_atom[0]), &s->idx_to_atom_size, + s->idx_to_atom_count + 1)) + goto fail; + + v = s->idx_to_atom_count++; + s->idx_to_atom[v] = atom + s->first_atom; + v += s->first_atom; + s->atom_to_idx[atom] = v; + *pres = v; + return 0; +fail: + *pres = 0; + return -1; +} + +static int bc_put_atom(BCWriterState* s, JSAtom atom) { + uint32_t v; + + if (__JS_AtomIsTaggedInt(atom)) { + v = (__JS_AtomToUInt32(atom) << 1) | 1; + } else { + if (bc_atom_to_idx(s, &v, atom)) + return -1; + v <<= 1; + } + bc_put_leb128(s, v); + return 0; +} + +static void bc_byte_swap(uint8_t* bc_buf, int bc_len) { + int pos, len, op, fmt; + + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + fmt = short_opcode_info(op).fmt; + switch (fmt) { + case OP_FMT_u16: + case OP_FMT_i16: + case OP_FMT_label16: + case OP_FMT_npop: + case OP_FMT_loc: + case OP_FMT_arg: + case OP_FMT_var_ref: + put_u16(bc_buf + pos + 1, bswap16(get_u16(bc_buf + pos + 1))); + break; + case OP_FMT_i32: + case OP_FMT_u32: + case OP_FMT_const: + case OP_FMT_label: + case OP_FMT_atom: + case OP_FMT_atom_u8: + put_u32(bc_buf + pos + 1, bswap32(get_u32(bc_buf + pos + 1))); + break; + case OP_FMT_atom_u16: + case OP_FMT_label_u16: + put_u32(bc_buf + pos + 1, bswap32(get_u32(bc_buf + pos + 1))); + put_u16(bc_buf + pos + 1 + 4, bswap16(get_u16(bc_buf + pos + 1 + 4))); + break; + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + put_u32(bc_buf + pos + 1, bswap32(get_u32(bc_buf + pos + 1))); + put_u32(bc_buf + pos + 1 + 4, bswap32(get_u32(bc_buf + pos + 1 + 4))); + if (fmt == OP_FMT_atom_label_u16) { + put_u16(bc_buf + pos + 1 + 4 + 4, bswap16(get_u16(bc_buf + pos + 1 + 4 + 4))); + } + break; + case OP_FMT_npop_u16: + put_u16(bc_buf + pos + 1, bswap16(get_u16(bc_buf + pos + 1))); + put_u16(bc_buf + pos + 1 + 2, bswap16(get_u16(bc_buf + pos + 1 + 2))); + break; + default: + break; + } + pos += len; + } +} + +static int JS_WriteFunctionBytecode(BCWriterState* s, const uint8_t* bc_buf1, int bc_len) { + int pos, len, op; + JSAtom atom; + uint8_t* bc_buf; + uint32_t val; + + bc_buf = js_malloc(s->ctx, bc_len); + if (!bc_buf) + return -1; + memcpy(bc_buf, bc_buf1, bc_len); + + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + switch (short_opcode_info(op).fmt) { + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + atom = get_u32(bc_buf + pos + 1); + if (bc_atom_to_idx(s, &val, atom)) + goto fail; + put_u32(bc_buf + pos + 1, val); + break; + default: + break; + } + pos += len; + } + + if (s->byte_swap) + bc_byte_swap(bc_buf, bc_len); + + dbuf_put(&s->dbuf, bc_buf, bc_len); + + js_free(s->ctx, bc_buf); + return 0; +fail: + js_free(s->ctx, bc_buf); + return -1; +} + +static void JS_WriteString(BCWriterState* s, JSString* p) { + int i; + bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); + if (p->is_wide_char) { + for (i = 0; i < p->len; i++) + bc_put_u16(s, p->u.str16[i]); + } else { + dbuf_put(&s->dbuf, p->u.str8, p->len); + } +} + +#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) { + /* XXX: this field is redundant */ + bc_put_leb128(s, b->arg_count + b->var_count); + for (i = 0; i < b->arg_count + b->var_count; i++) { + JSVarDef* vd = &b->vardefs[i]; + bc_put_atom(s, vd->var_name); + bc_put_leb128(s, vd->scope_level); + bc_put_leb128(s, vd->scope_next + 1); + flags = idx = 0; + bc_set_flags(&flags, &idx, vd->var_kind, 4); + bc_set_flags(&flags, &idx, vd->is_const, 1); + bc_set_flags(&flags, &idx, vd->is_lexical, 1); + bc_set_flags(&flags, &idx, vd->is_captured, 1); + assert(idx <= 8); + bc_put_u8(s, flags); + } + } else { + bc_put_leb128(s, 0); + } + + for (i = 0; i < b->closure_var_count; i++) { + JSClosureVar* cv = &b->closure_var[i]; + bc_put_atom(s, cv->var_name); + bc_put_leb128(s, cv->var_idx); + flags = idx = 0; + bc_set_flags(&flags, &idx, cv->is_local, 1); + bc_set_flags(&flags, &idx, cv->is_arg, 1); + bc_set_flags(&flags, &idx, cv->is_const, 1); + bc_set_flags(&flags, &idx, cv->is_lexical, 1); + bc_set_flags(&flags, &idx, cv->var_kind, 4); + assert(idx <= 8); + bc_put_u8(s, flags); + } + + if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) + goto fail; + + if (b->has_debug) { + bc_put_atom(s, b->debug.filename); + bc_put_leb128(s, b->debug.line_num); + bc_put_leb128(s, b->debug.pc2line_len); + dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); + } + + for (i = 0; i < b->cpool_count; i++) { + if (JS_WriteObjectRec(s, b->cpool[i])) + goto fail; + } + return 0; +fail: + return -1; +} + +static int JS_WriteModule(BCWriterState* s, JSValueConst obj) { + JSModuleDef* m = JS_VALUE_GET_PTR(obj); + int i; + + bc_put_u8(s, BC_TAG_MODULE); + bc_put_atom(s, m->module_name); + + bc_put_leb128(s, m->req_module_entries_count); + for (i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry* rme = &m->req_module_entries[i]; + bc_put_atom(s, rme->module_name); + } + + bc_put_leb128(s, m->export_entries_count); + for (i = 0; i < m->export_entries_count; i++) { + JSExportEntry* me = &m->export_entries[i]; + bc_put_u8(s, me->export_type); + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + bc_put_leb128(s, me->u.local.var_idx); + } else { + bc_put_leb128(s, me->u.req_module_idx); + bc_put_atom(s, me->local_name); + } + bc_put_atom(s, me->export_name); + } + + bc_put_leb128(s, m->star_export_entries_count); + for (i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry* se = &m->star_export_entries[i]; + bc_put_leb128(s, se->req_module_idx); + } + + bc_put_leb128(s, m->import_entries_count); + for (i = 0; i < m->import_entries_count; i++) { + JSImportEntry* mi = &m->import_entries[i]; + bc_put_leb128(s, mi->var_idx); + bc_put_atom(s, mi->import_name); + bc_put_leb128(s, mi->req_module_idx); + } + + if (JS_WriteObjectRec(s, m->func_obj)) + goto fail; + return 0; +fail: + return -1; +} + +static int JS_WriteArray(BCWriterState* s, JSValueConst obj) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + uint32_t i, len; + JSValue val; + int ret; + BOOL is_template; + + if (s->allow_bytecode && !p->extensible) { + /* not extensible array: we consider it is a + template when we are saving bytecode */ + bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); + is_template = TRUE; + } else { + bc_put_u8(s, BC_TAG_ARRAY); + is_template = FALSE; + } + if (js_get_length32(s->ctx, &len, obj)) + goto fail1; + bc_put_leb128(s, len); + for (i = 0; i < len; i++) { + val = JS_GetPropertyUint32(s->ctx, obj, i); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + if (is_template) { + val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); + if (JS_IsException(val)) + goto fail1; + ret = JS_WriteObjectRec(s, val); + JS_FreeValue(s->ctx, val); + if (ret) + goto fail1; + } + return 0; +fail1: + return -1; +} + +static int JS_WriteObjectTag(BCWriterState* s, JSValueConst obj) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + uint32_t i, prop_count; + JSShape* sh; + JSShapeProperty* pr; + int pass; + JSAtom atom; + + bc_put_u8(s, BC_TAG_OBJECT); + prop_count = 0; + sh = p->shape; + for (pass = 0; pass < 2; pass++) { + if (pass == 1) + bc_put_leb128(s, prop_count); + for (i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + atom = pr->atom; + if (atom != JS_ATOM_NULL && JS_AtomIsString(s->ctx, atom) && (pr->flags & JS_PROP_ENUMERABLE)) { + if (pr->flags & JS_PROP_TMASK) { + JS_ThrowTypeError(s->ctx, "only value properties are supported"); + goto fail; + } + if (pass == 0) { + prop_count++; + } else { + bc_put_atom(s, atom); + if (JS_WriteObjectRec(s, p->prop[i].u.value)) + goto fail; + } + } + } + } + return 0; +fail: + return -1; +} + +static int JS_WriteTypedArray(BCWriterState* s, JSValueConst obj) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + JSTypedArray* ta = p->u.typed_array; + + bc_put_u8(s, BC_TAG_TYPED_ARRAY); + bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); + bc_put_leb128(s, p->u.array.count); + bc_put_leb128(s, ta->offset); + if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) + return -1; + return 0; +} + +static int JS_WriteArrayBuffer(BCWriterState* s, JSValueConst obj) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer* abuf = p->u.array_buffer; + if (abuf->detached) { + JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); + return -1; + } + bc_put_u8(s, BC_TAG_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); + return 0; +} + +static int JS_WriteSharedArrayBuffer(BCWriterState* s, JSValueConst obj) { + JSObject* p = JS_VALUE_GET_OBJ(obj); + JSArrayBuffer* abuf = p->u.array_buffer; + assert(!abuf->detached); /* SharedArrayBuffer are never detached */ + bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); + bc_put_leb128(s, abuf->byte_length); + bc_put_u64(s, (uintptr_t)abuf->data); + if (js_resize_array(s->ctx, (void**)&s->sab_tab, sizeof(s->sab_tab[0]), &s->sab_tab_size, s->sab_tab_len + 1)) + return -1; + /* keep the SAB pointer so that the user can clone it or free it */ + s->sab_tab[s->sab_tab_len++] = abuf->data; + return 0; +} + +static int JS_WriteObjectRec(BCWriterState* s, JSValueConst obj) { + uint32_t tag; + + if (js_check_stack_overflow(s->ctx->rt, 0)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + + tag = JS_VALUE_GET_NORM_TAG(obj); + switch (tag) { + case JS_TAG_NULL: + bc_put_u8(s, BC_TAG_NULL); + break; + case JS_TAG_UNDEFINED: + bc_put_u8(s, BC_TAG_UNDEFINED); + break; + case JS_TAG_BOOL: + bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj)); + break; + case JS_TAG_INT: + bc_put_u8(s, BC_TAG_INT32); + bc_put_sleb128(s, JS_VALUE_GET_INT(obj)); + break; + case JS_TAG_FLOAT64: { + JSFloat64Union u; + bc_put_u8(s, BC_TAG_FLOAT64); + u.d = JS_VALUE_GET_FLOAT64(obj); + bc_put_u64(s, u.u64); + } break; + case JS_TAG_STRING: { + JSString* p = JS_VALUE_GET_STRING(obj); + bc_put_u8(s, BC_TAG_STRING); + JS_WriteString(s, p); + } break; + case JS_TAG_FUNCTION_BYTECODE: + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteFunctionTag(s, obj)) + goto fail; + break; + case JS_TAG_MODULE: + if (!s->allow_bytecode) + goto invalid_tag; + if (JS_WriteModule(s, obj)) + goto fail; + break; + case JS_TAG_OBJECT: { + JSObject* p = JS_VALUE_GET_OBJ(obj); + int ret, idx; + + if (s->allow_reference) { + idx = js_object_list_find(s->ctx, &s->object_list, p); + if (idx >= 0) { + bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); + bc_put_leb128(s, idx); + break; + } else { + if (js_object_list_add(s->ctx, &s->object_list, p)) + goto fail; + } + } else { + if (p->tmp_mark) { + JS_ThrowTypeError(s->ctx, "circular reference"); + goto fail; + } + p->tmp_mark = 1; + } + switch (p->class_id) { + case JS_CLASS_ARRAY: + ret = JS_WriteArray(s, obj); + break; + case JS_CLASS_OBJECT: + ret = JS_WriteObjectTag(s, obj); + break; + case JS_CLASS_ARRAY_BUFFER: + ret = JS_WriteArrayBuffer(s, obj); + break; + case JS_CLASS_SHARED_ARRAY_BUFFER: + if (!s->allow_sab) + goto invalid_tag; + ret = JS_WriteSharedArrayBuffer(s, obj); + break; + case JS_CLASS_DATE: + bc_put_u8(s, BC_TAG_DATE); + ret = JS_WriteObjectRec(s, p->u.object_data); + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: +#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: + case JS_TAG_BIG_DECIMAL: + if (JS_WriteBigNum(s, obj)) + goto fail; + break; +#endif + default: + invalid_tag: + JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag); + goto fail; + } + return 0; + +fail: + return -1; +} + +/* create the atom table */ +static int JS_WriteObjectAtoms(BCWriterState* s) { + JSRuntime* rt = s->ctx->rt; + DynBuf dbuf1; + int i, atoms_size; + uint8_t version; + + dbuf1 = s->dbuf; + js_dbuf_init(s->ctx, &s->dbuf); + + version = BC_VERSION; + if (s->byte_swap) + version ^= BC_BE_VERSION; + bc_put_u8(s, version); + + bc_put_leb128(s, s->idx_to_atom_count); + for (i = 0; i < s->idx_to_atom_count; i++) { + JSAtomStruct* p = rt->atom_array[s->idx_to_atom[i]]; + JS_WriteString(s, p); + } + /* XXX: should check for OOM in above phase */ + + /* move the atoms at the start */ + /* XXX: could just append dbuf1 data, but it uses more memory if + dbuf1 is larger than dbuf */ + atoms_size = s->dbuf.size; + if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size)) + goto fail; + memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size); + memcpy(dbuf1.buf, s->dbuf.buf, atoms_size); + dbuf1.size += atoms_size; + dbuf_free(&s->dbuf); + s->dbuf = dbuf1; + return 0; +fail: + dbuf_free(&dbuf1); + return -1; +} + +uint8_t* JS_WriteObject2(JSContext* ctx, + size_t* psize, + JSValueConst obj, + int flags, + uint8_t*** psab_tab, + size_t* psab_tab_len) { + BCWriterState ss, *s = &ss; + + memset(s, 0, sizeof(*s)); + s->ctx = ctx; + /* 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; + uint32_t first_atom; + uint32_t idx_to_atom_count; + JSAtom* idx_to_atom; + int error_state; + BOOL allow_sab : 8; + BOOL allow_bytecode : 8; + BOOL is_rom_data : 8; + BOOL allow_reference : 8; + /* object references */ + JSObject** objects; + int objects_count; + int objects_size; + +#ifdef DUMP_READ_OBJECT + const uint8_t* ptr_last; + int level; +#endif +} BCReaderState; + +#ifdef DUMP_READ_OBJECT +static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState* s, const char* fmt, ...) { + va_list ap; + int i, n, n0; + + if (!s->ptr_last) + s->ptr_last = s->buf_start; + + n = n0 = 0; + if (s->ptr > s->ptr_last || s->ptr == s->buf_start) { + n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start)); + n += n0; + } + for (i = 0; s->ptr_last < s->ptr; i++) { + if ((i & 7) == 0 && i > 0) { + printf("\n%*s", n0, ""); + n = n0; + } + n += printf(" %02x", *s->ptr_last++); + } + if (*fmt == '}') + s->level--; + if (n < 32 + s->level * 2) { + printf("%*s", 32 + s->level * 2 - n, ""); + } + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + if (strchr(fmt, '{')) + s->level++; +} +#else +#define bc_read_trace(...) +#endif + +static int bc_read_error_end(BCReaderState* s) { + if (!s->error_state) { + JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer"); + } + return s->error_state = -1; +} + +static int bc_get_u8(BCReaderState* s, uint8_t* pval) { + if (unlikely(s->buf_end - s->ptr < 1)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = *s->ptr++; + return 0; +} + +static int bc_get_u16(BCReaderState* s, uint16_t* pval) { + if (unlikely(s->buf_end - s->ptr < 2)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = get_u16(s->ptr); + s->ptr += 2; + return 0; +} + +static __maybe_unused int bc_get_u32(BCReaderState* s, uint32_t* pval) { + if (unlikely(s->buf_end - s->ptr < 4)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = get_u32(s->ptr); + s->ptr += 4; + return 0; +} + +static int bc_get_u64(BCReaderState* s, uint64_t* pval) { + if (unlikely(s->buf_end - s->ptr < 8)) { + *pval = 0; /* avoid warning */ + return bc_read_error_end(s); + } + *pval = get_u64(s->ptr); + s->ptr += 8; + return 0; +} + +static int bc_get_leb128(BCReaderState* s, uint32_t* pval) { + int ret; + ret = get_leb128(pval, s->ptr, s->buf_end); + if (unlikely(ret < 0)) + return bc_read_error_end(s); + s->ptr += ret; + return 0; +} + +static int bc_get_sleb128(BCReaderState* s, int32_t* pval) { + int ret; + ret = get_sleb128(pval, s->ptr, s->buf_end); + if (unlikely(ret < 0)) + return bc_read_error_end(s); + s->ptr += ret; + return 0; +} + +/* XXX: used to read an `int` with a positive value */ +static int bc_get_leb128_int(BCReaderState* s, int* pval) { + return bc_get_leb128(s, (uint32_t*)pval); +} + +static int bc_get_leb128_u16(BCReaderState* s, uint16_t* pval) { + uint32_t val; + if (bc_get_leb128(s, &val)) { + *pval = 0; + return -1; + } + *pval = val; + return 0; +} + +static int bc_get_buf(BCReaderState* s, uint8_t* buf, uint32_t buf_len) { + if (buf_len != 0) { + if (unlikely(!buf || s->buf_end - s->ptr < buf_len)) + return bc_read_error_end(s); + memcpy(buf, s->ptr, buf_len); + s->ptr += buf_len; + } + return 0; +} + +static int bc_idx_to_atom(BCReaderState* s, JSAtom* patom, uint32_t idx) { + JSAtom atom; + + if (__JS_AtomIsTaggedInt(idx)) { + atom = idx; + } else if (idx < s->first_atom) { + atom = JS_DupAtom(s->ctx, idx); + } else { + idx -= s->first_atom; + if (idx >= s->idx_to_atom_count) { + JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)", (unsigned int)(s->ptr - s->buf_start)); + *patom = JS_ATOM_NULL; + return s->error_state = -1; + } + atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]); + } + *patom = atom; + return 0; +} + +static int bc_get_atom(BCReaderState* s, JSAtom* patom) { + uint32_t v; + if (bc_get_leb128(s, &v)) + return -1; + if (v & 1) { + *patom = __JS_AtomFromUInt32(v >> 1); + return 0; + } else { + return bc_idx_to_atom(s, patom, v >> 1); + } +} + +static JSString* JS_ReadString(BCReaderState* s) { + uint32_t len; + size_t size; + BOOL is_wide_char; + JSString* p; + + if (bc_get_leb128(s, &len)) + return NULL; + is_wide_char = len & 1; + len >>= 1; + p = js_alloc_string(s->ctx, len, is_wide_char); + if (!p) { + s->error_state = -1; + return NULL; + } + size = (size_t)len << is_wide_char; + if ((s->buf_end - s->ptr) < size) { + bc_read_error_end(s); + js_free_string(s->ctx->rt, p); + return NULL; + } + memcpy(p->u.str8, s->ptr, size); + s->ptr += size; + if (!is_wide_char) { + p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ + } +#ifdef DUMP_READ_OBJECT + JS_DumpString(s->ctx->rt, p); + printf("\n"); +#endif + return p; +} + +static uint32_t bc_get_flags(uint32_t flags, int* pidx, int n) { + uint32_t val; + /* XXX: this does not work for n == 32 */ + val = (flags >> *pidx) & ((1U << n) - 1); + *pidx += n; + return val; +} + +static int JS_ReadFunctionBytecode(BCReaderState* s, JSFunctionBytecode* b, int byte_code_offset, uint32_t bc_len) { + uint8_t* bc_buf; + int pos, len, op; + JSAtom atom; + uint32_t idx; + + if (s->is_rom_data) { + /* directly use the input buffer */ + if (unlikely(s->buf_end - s->ptr < bc_len)) + return bc_read_error_end(s); + bc_buf = (uint8_t*)s->ptr; + s->ptr += bc_len; + } else { + bc_buf = (void*)((uint8_t*)b + byte_code_offset); + if (bc_get_buf(s, bc_buf, bc_len)) + return -1; + } + b->byte_code_buf = bc_buf; + + pos = 0; + while (pos < bc_len) { + op = bc_buf[pos]; + len = short_opcode_info(op).size; + switch (short_opcode_info(op).fmt) { + case OP_FMT_atom: + case OP_FMT_atom_u8: + case OP_FMT_atom_u16: + case OP_FMT_atom_label_u8: + case OP_FMT_atom_label_u16: + idx = get_u32(bc_buf + pos + 1); + if (s->is_rom_data) { + /* just increment the reference count of the atom */ + JS_DupAtom(s->ctx, (JSAtom)idx); + } else { + if (bc_idx_to_atom(s, &atom, idx)) { + /* Note: the atoms will be freed up to this position */ + b->byte_code_len = pos; + return -1; + } + put_u32(bc_buf + pos + 1, atom); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "at %d, fixup atom: ", pos + 1); + print_atom(s->ctx, atom); + printf("\n"); +#endif + } + break; + default: + break; + } + pos += len; + } + return 0; +} + +#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; + if (local_count != 0) { + b->vardefs = (void*)((uint8_t*)b + vardefs_offset); + } + if (b->closure_var_count != 0) { + b->closure_var = (void*)((uint8_t*)b + closure_var_offset); + } + if (b->cpool_count != 0) { + b->cpool = (void*)((uint8_t*)b + cpool_offset); + } + + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + + obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); + +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); + print_atom(s->ctx, b->func_name); + printf("\n"); +#endif + bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", b->arg_count, b->var_count, + b->defined_arg_count, b->closure_var_count, b->cpool_count); + bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", b->stack_size, b->byte_code_len, local_count); + + if (local_count != 0) { + bc_read_trace(s, "vars {\n"); + for (i = 0; i < local_count; i++) { + JSVarDef* vd = &b->vardefs[i]; + if (bc_get_atom(s, &vd->var_name)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_level)) + goto fail; + if (bc_get_leb128_int(s, &vd->scope_next)) + goto fail; + vd->scope_next--; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + vd->var_kind = bc_get_flags(v8, &idx, 4); + vd->is_const = bc_get_flags(v8, &idx, 1); + vd->is_lexical = bc_get_flags(v8, &idx, 1); + vd->is_captured = bc_get_flags(v8, &idx, 1); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); + print_atom(s->ctx, vd->var_name); + printf("\n"); +#endif + } + bc_read_trace(s, "}\n"); + } + if (b->closure_var_count != 0) { + bc_read_trace(s, "closure vars {\n"); + for (i = 0; i < b->closure_var_count; i++) { + JSClosureVar* cv = &b->closure_var[i]; + int var_idx; + if (bc_get_atom(s, &cv->var_name)) + goto fail; + if (bc_get_leb128_int(s, &var_idx)) + goto fail; + cv->var_idx = var_idx; + if (bc_get_u8(s, &v8)) + goto fail; + idx = 0; + cv->is_local = bc_get_flags(v8, &idx, 1); + cv->is_arg = bc_get_flags(v8, &idx, 1); + cv->is_const = bc_get_flags(v8, &idx, 1); + cv->is_lexical = bc_get_flags(v8, &idx, 1); + cv->var_kind = bc_get_flags(v8, &idx, 4); +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); + print_atom(s->ctx, cv->var_name); + printf("\n"); +#endif + } + bc_read_trace(s, "}\n"); + } + { + bc_read_trace(s, "bytecode {\n"); + if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) + goto fail; + bc_read_trace(s, "}\n"); + } + if (b->has_debug) { + /* read optional debug information */ + bc_read_trace(s, "debug {\n"); + if (bc_get_atom(s, &b->debug.filename)) + goto fail; + if (bc_get_leb128_int(s, &b->debug.line_num)) + goto fail; + if (bc_get_leb128_int(s, &b->debug.pc2line_len)) + goto fail; + if (b->debug.pc2line_len) { + b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); + if (!b->debug.pc2line_buf) + goto fail; + if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) + goto fail; + } +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "filename: "); + print_atom(s->ctx, b->debug.filename); + printf("\n"); +#endif + bc_read_trace(s, "}\n"); + } + if (b->cpool_count != 0) { + bc_read_trace(s, "cpool {\n"); + for (i = 0; i < b->cpool_count; i++) { + JSValue val; + val = JS_ReadObjectRec(s); + if (JS_IsException(val)) + goto fail; + b->cpool[i] = val; + } + bc_read_trace(s, "}\n"); + } + b->realm = JS_DupContext(ctx); + return obj; +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue JS_ReadModule(BCReaderState* s) { + JSContext* ctx = s->ctx; + JSValue obj; + JSModuleDef* m = NULL; + JSAtom module_name; + int i; + uint8_t v8; + + if (bc_get_atom(s, &module_name)) + goto fail; +#ifdef DUMP_READ_OBJECT + bc_read_trace(s, "name: "); + print_atom(s->ctx, module_name); + printf("\n"); +#endif + m = js_new_module_def(ctx, module_name); + if (!m) + goto fail; + obj = JS_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; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + if (bc_get_u8(s, &tag)) + return JS_EXCEPTION; + + bc_read_trace(s, "%s {\n", bc_tag_str[tag]); + + switch (tag) { + case BC_TAG_NULL: + obj = JS_NULL; + break; + case BC_TAG_UNDEFINED: + obj = JS_UNDEFINED; + break; + case BC_TAG_BOOL_FALSE: + case BC_TAG_BOOL_TRUE: + obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE); + break; + case BC_TAG_INT32: { + int32_t val; + if (bc_get_sleb128(s, &val)) + return JS_EXCEPTION; + bc_read_trace(s, "%d\n", val); + obj = JS_NewInt32(ctx, val); + } break; + case BC_TAG_FLOAT64: { + JSFloat64Union u; + if (bc_get_u64(s, &u.u64)) + return JS_EXCEPTION; + bc_read_trace(s, "%g\n", u.d); + obj = __JS_NewFloat64(ctx, u.d); + } break; + case BC_TAG_STRING: { + JSString* p; + p = JS_ReadString(s); + if (!p) + return JS_EXCEPTION; + obj = JS_MKPTR(JS_TAG_STRING, p); + } break; + case BC_TAG_FUNCTION_BYTECODE: + if (!s->allow_bytecode) + goto invalid_tag; + obj = JS_ReadFunctionTag(s); + break; + case BC_TAG_MODULE: + if (!s->allow_bytecode) + goto invalid_tag; + obj = JS_ReadModule(s); + break; + case BC_TAG_OBJECT: + obj = JS_ReadObjectTag(s); + break; + case BC_TAG_ARRAY: + case BC_TAG_TEMPLATE_OBJECT: + obj = JS_ReadArray(s, tag); + break; + case BC_TAG_TYPED_ARRAY: + obj = JS_ReadTypedArray(s); + break; + case BC_TAG_ARRAY_BUFFER: + obj = JS_ReadArrayBuffer(s); + break; + case BC_TAG_SHARED_ARRAY_BUFFER: + if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) + goto invalid_tag; + obj = JS_ReadSharedArrayBuffer(s); + break; + case BC_TAG_DATE: + obj = JS_ReadDate(s); + break; + case BC_TAG_OBJECT_VALUE: + obj = JS_ReadObjectValue(s); + break; +#ifdef CONFIG_BIGNUM + case BC_TAG_BIG_INT: + case BC_TAG_BIG_FLOAT: + 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; +} + +static int JS_ReadObjectAtoms(BCReaderState* s) { + uint8_t v8; + JSString* p; + int i; + JSAtom atom; + + if (bc_get_u8(s, &v8)) + return -1; + /* XXX: could support byte swapped input */ + if (v8 != BC_VERSION) { + JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", v8, BC_VERSION); + return -1; + } + if (bc_get_leb128(s, &s->idx_to_atom_count)) + return -1; + + bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count); + + if (s->idx_to_atom_count != 0) { + s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count * sizeof(s->idx_to_atom[0])); + if (!s->idx_to_atom) + return s->error_state = -1; + } + for (i = 0; i < s->idx_to_atom_count; i++) { + p = JS_ReadString(s); + if (!p) + return -1; + atom = JS_NewAtomStr(s->ctx, p); + if (atom == JS_ATOM_NULL) + return s->error_state = -1; + s->idx_to_atom[i] = atom; + if (s->is_rom_data && (atom != (i + s->first_atom))) + s->is_rom_data = FALSE; /* atoms must be relocated */ + } + bc_read_trace(s, "}\n"); + return 0; +} + +static void bc_reader_free(BCReaderState* s) { + int i; + if (s->idx_to_atom) { + for (i = 0; i < s->idx_to_atom_count; i++) { + JS_FreeAtom(s->ctx, s->idx_to_atom[i]); + } + js_free(s->ctx, s->idx_to_atom); + } + js_free(s->ctx, s->objects); +} + +JSValue JS_ReadObject(JSContext* ctx, const uint8_t* buf, size_t buf_len, int flags) { + BCReaderState ss, *s = &ss; + JSValue obj; + + ctx->binary_object_count += 1; + ctx->binary_object_size += buf_len; + + memset(s, 0, sizeof(*s)); + s->ctx = ctx; + s->buf_start = buf; + s->buf_end = buf + buf_len; + s->ptr = buf; + s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); + s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0); + s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); + s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); + if (s->allow_bytecode) + s->first_atom = JS_ATOM_END; + else + s->first_atom = 1; + if (JS_ReadObjectAtoms(s)) { + obj = JS_EXCEPTION; + } else { + obj = JS_ReadObjectRec(s); + } + bc_reader_free(s); + return obj; } \ No newline at end of file diff --git a/src/core/bytecode.h b/src/core/bytecode.h index c7bfbe16b..c7272cc0e 100644 --- a/src/core/bytecode.h +++ b/src/core/bytecode.h @@ -1,38 +1,38 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_BYTECODE_H -#define QUICKJS_BYTECODE_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "types.h" - -void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); -void free_bytecode_atoms(JSRuntime *rt, - const uint8_t *bc_buf, int bc_len, - BOOL use_short_opcodes);; - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_BYTECODE_H +#define QUICKJS_BYTECODE_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "types.h" + +void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); +void free_bytecode_atoms(JSRuntime *rt, + const uint8_t *bc_buf, int bc_len, + BOOL use_short_opcodes);; + #endif \ No newline at end of file diff --git a/src/core/convertion.c b/src/core/convertion.c index 440a8692c..b49f799b0 100644 --- a/src/core/convertion.c +++ b/src/core/convertion.c @@ -1,1674 +1,1674 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "convertion.h" -#include "builtins/js-big-num.h" -#include "exception.h" -#include "function.h" -#include "quickjs/libregexp.h" -#include "string.h" - -static JSValue JS_ToNumberHintFree(JSContext* ctx, JSValue val, JSToNumberHintEnum flag); - -int skip_spaces(const char* pc) { - const uint8_t *p, *p_next, *p_start; - uint32_t c; - - p = p_start = (const uint8_t*)pc; - for (;;) { - c = *p; - if (c < 128) { - if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) - break; - p++; - } else { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (!lre_is_space(c)) - break; - p = p_next; - } - } - return p - p_start; -} - -JSValue JS_ToPrimitiveFree(JSContext* ctx, JSValue val, int hint) { - int i; - BOOL force_ordinary; - - JSAtom method_name; - JSValue method, ret; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return val; - force_ordinary = hint & HINT_FORCE_ORDINARY; - hint &= ~HINT_FORCE_ORDINARY; - if (!force_ordinary) { - method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); - if (JS_IsException(method)) - goto exception; - /* ECMA says *If exoticToPrim is not undefined* but tests in - test262 use null as a non callable converter */ - if (!JS_IsUndefined(method) && !JS_IsNull(method)) { - JSAtom atom; - JSValue arg; - switch (hint) { - case HINT_STRING: - atom = JS_ATOM_string; - break; - case HINT_NUMBER: - atom = JS_ATOM_number; - break; - default: - case HINT_NONE: - atom = JS_ATOM_default; - break; - } - arg = JS_AtomToString(ctx, atom); - ret = JS_CallFree(ctx, method, val, 1, (JSValueConst*)&arg); - JS_FreeValue(ctx, arg); - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, val); - if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) - return ret; - JS_FreeValue(ctx, ret); - return JS_ThrowTypeError(ctx, "toPrimitive"); - } - } - if (hint != HINT_STRING) - hint = HINT_NUMBER; - for (i = 0; i < 2; i++) { - if ((i ^ hint) == 0) { - method_name = JS_ATOM_toString; - } else { - method_name = JS_ATOM_valueOf; - } - method = JS_GetProperty(ctx, val, method_name); - if (JS_IsException(method)) - goto exception; - if (JS_IsFunction(ctx, method)) { - ret = JS_CallFree(ctx, method, val, 0, NULL); - if (JS_IsException(ret)) - goto exception; - if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { - JS_FreeValue(ctx, val); - return ret; - } - JS_FreeValue(ctx, ret); - } else { - JS_FreeValue(ctx, method); - } - } - JS_ThrowTypeError(ctx, "toPrimitive"); -exception: - JS_FreeValue(ctx, val); - return JS_EXCEPTION; -} - -JSValue JS_ToPrimitive(JSContext* ctx, JSValueConst val, int hint) { - return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); -} - -__exception int JS_ToArrayLengthFree(JSContext* ctx, uint32_t* plen, JSValue val, BOOL is_array_ctor) { - uint32_t tag, len; - - tag = JS_VALUE_GET_TAG(val); - switch (tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: { - int v; - v = JS_VALUE_GET_INT(val); - if (v < 0) - goto fail; - len = v; - } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - bf_t a; - BOOL res; - bf_get_int32((int32_t*)&len, &p->num, BF_GET_INT_MOD); - bf_init(ctx->bf_ctx, &a); - bf_set_ui(&a, len); - res = bf_cmp_eq(&a, &p->num); - bf_delete(&a); - JS_FreeValue(ctx, val); - if (!res) - goto fail; - } break; -#endif - default: - if (JS_TAG_IS_FLOAT64(tag)) { - double d; - d = JS_VALUE_GET_FLOAT64(val); - len = (uint32_t)d; - if (len != d) - goto fail; - } else { - uint32_t len1; - - if (is_array_ctor) { - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - /* cannot recurse because val is a number */ - if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) - return -1; - } else { - /* legacy behavior: must do the conversion twice and compare */ - if (JS_ToUint32(ctx, &len, val)) { - JS_FreeValue(ctx, val); - return -1; - } - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return -1; - /* cannot recurse because val is a number */ - if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) - return -1; - if (len1 != len) { - fail: - JS_ThrowRangeError(ctx, "invalid array length"); - return -1; - } - } - } - break; - } - *plen = len; - return 0; -} - -JSValue JS_ToNumber(JSContext* ctx, JSValueConst val) { - return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); -} - -JSValue JS_ToNumberFree(JSContext* ctx, JSValue val) { - return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); -} - -JSValue JS_ToNumericFree(JSContext* ctx, JSValue val) { - return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); -} - -JSValue JS_ToNumeric(JSContext* ctx, JSValueConst val) { - return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); -} - -static JSValue JS_ToNumberHintFree(JSContext* ctx, JSValue val, JSToNumberHintEnum flag) { - uint32_t tag; - JSValue ret; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { -#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_NUMERIC) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); - } - ret = val; - break; - case JS_TAG_BIG_FLOAT: - if (flag != TON_FLAG_NUMERIC) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number"); - } - ret = val; - break; -#endif - case JS_TAG_FLOAT64: - case JS_TAG_INT: - case JS_TAG_EXCEPTION: - ret = val; - break; - case JS_TAG_BOOL: - case JS_TAG_NULL: - ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_UNDEFINED: - ret = JS_NAN; - break; - case JS_TAG_OBJECT: - val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); - if (JS_IsException(val)) - return JS_EXCEPTION; - goto redo; - case JS_TAG_STRING: { - const char* str; - const char* p; - size_t len; - - str = JS_ToCStringLen(ctx, &len, val); - JS_FreeValue(ctx, val); - if (!str) - return JS_EXCEPTION; - p = str; - p += skip_spaces(p); - if ((p - str) == len) { - ret = JS_NewInt32(ctx, 0); - } else { - int flags = ATOD_ACCEPT_BIN_OCT; - ret = js_atof(ctx, p, &p, 0, flags); - if (!JS_IsException(ret)) { - p += skip_spaces(p); - if ((p - str) != len) { - JS_FreeValue(ctx, ret); - ret = JS_NAN; - } - } - } - JS_FreeCString(ctx, str); - } break; - case JS_TAG_SYMBOL: - JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); - default: - JS_FreeValue(ctx, val); - ret = JS_NAN; - break; - } - return ret; -} - -__exception int __JS_ToFloat64Free(JSContext* ctx, double* pres, JSValue val) { - double d; - uint32_t tag; - - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = JS_FLOAT64_NAN; - return -1; - } - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_INT: - d = JS_VALUE_GET_INT(val); - break; - case JS_TAG_FLOAT64: - d = JS_VALUE_GET_FLOAT64(val); - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - /* XXX: there can be a double rounding issue with some - primitives (such as JS_ToUint8ClampFree()), but it is - not critical to fix it. */ - bf_get_float64(&p->num, &d, BF_RNDN); - JS_FreeValue(ctx, val); - } break; -#endif - default: - abort(); - } - *pres = d; - return 0; -} - -int JS_ToFloat64(JSContext* ctx, double* pres, JSValueConst val) { - return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); -} - -/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ -__maybe_unused JSValue JS_ToIntegerFree(JSContext* ctx, JSValue val) { - uint32_t tag; - JSValue ret; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); - break; - case JS_TAG_FLOAT64: { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - ret = JS_NewInt32(ctx, 0); - } else { - /* convert -0 to +0 */ - d = trunc(d) + 0.0; - ret = JS_NewFloat64(ctx, d); - } - } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: { - bf_t a_s, *a, r_s, *r = &r_s; - BOOL is_nan; - - a = JS_ToBigFloat(ctx, &a_s, val); - if (!bf_is_finite(a)) { - is_nan = bf_is_nan(a); - if (is_nan) - ret = JS_NewInt32(ctx, 0); - else - ret = JS_DupValue(ctx, val); - } else { - ret = JS_NewBigInt(ctx); - if (!JS_IsException(ret)) { - r = JS_GetBigInt(ret); - bf_set(r, a); - bf_rint(r, BF_RNDZ); - ret = JS_CompactBigInt(ctx, ret); - } - } - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, val); - } break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) - return val; - goto redo; - } - return ret; -} - -/* Note: the integer value is satured to 32 bits */ -int JS_ToInt32SatFree(JSContext* ctx, int* pres, JSValue val) { - uint32_t tag; - int ret; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); - break; - case JS_TAG_EXCEPTION: - *pres = 0; - return -1; - case JS_TAG_FLOAT64: { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - ret = 0; - } else { - if (d < INT32_MIN) - ret = INT32_MIN; - else if (d > INT32_MAX) - ret = INT32_MAX; - else - ret = (int)d; - } - } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - bf_get_int32(&ret, &p->num, 0); - JS_FreeValue(ctx, val); - } break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } - *pres = ret; - return 0; -} - -int JS_ToInt32Sat(JSContext* ctx, int* pres, JSValueConst val) { - return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); -} - -int JS_ToInt32Clamp(JSContext* ctx, int* pres, JSValueConst val, int min, int max, int min_offset) { - int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); - if (res == 0) { - if (*pres < min) { - *pres += min_offset; - if (*pres < min) - *pres = min; - } else { - if (*pres > max) - *pres = max; - } - } - return res; -} - -int JS_ToInt64SatFree(JSContext* ctx, int64_t* pres, JSValue val) { - uint32_t tag; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - *pres = JS_VALUE_GET_INT(val); - return 0; - case JS_TAG_EXCEPTION: - *pres = 0; - return -1; - case JS_TAG_FLOAT64: { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - *pres = 0; - } else { - if (d < INT64_MIN) - *pres = INT64_MIN; - else if (d > INT64_MAX) - *pres = INT64_MAX; - else - *pres = (int64_t)d; - } - } - return 0; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - bf_get_int64(pres, &p->num, 0); - JS_FreeValue(ctx, val); - } - return 0; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } -} - -int JS_ToInt64Sat(JSContext* ctx, int64_t* pres, JSValueConst val) { - return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); -} - -int JS_ToInt64Clamp(JSContext* ctx, int64_t* pres, JSValueConst val, int64_t min, int64_t max, int64_t neg_offset) { - int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); - if (res == 0) { - if (*pres < 0) - *pres += neg_offset; - if (*pres < min) - *pres = min; - else if (*pres > max) - *pres = max; - } - return res; -} - -/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) - in case of exception */ -int JS_ToInt64Free(JSContext* ctx, int64_t* pres, JSValue val) { - uint32_t tag; - int64_t ret; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); - break; - case JS_TAG_FLOAT64: { - JSFloat64Union u; - double d; - int e; - d = JS_VALUE_GET_FLOAT64(val); - u.d = d; - /* we avoid doing fmod(x, 2^64) */ - e = (u.u64 >> 52) & 0x7ff; - if (likely(e <= (1023 + 62))) { - /* fast case */ - ret = (int64_t)d; - } else if (e <= (1023 + 62 + 53)) { - uint64_t v; - /* remainder modulo 2^64 */ - v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); - ret = v << ((e - 1023) - 52); - /* take the sign into account */ - if (u.u64 >> 63) - ret = -ret; - } else { - ret = 0; /* also handles NaN and +inf */ - } - } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - bf_get_int64(&ret, &p->num, BF_GET_INT_MOD); - JS_FreeValue(ctx, val); - } break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } - *pres = ret; - return 0; -} - -int JS_ToInt64(JSContext* ctx, int64_t* pres, JSValueConst val) { - return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val)); -} - -int JS_ToInt64Ext(JSContext* ctx, int64_t* pres, JSValueConst val) { - if (JS_IsBigInt(ctx, val)) - return JS_ToBigInt64(ctx, pres, val); - else - return JS_ToInt64(ctx, pres, val); -} - -/* return (<0, 0) in case of exception */ -int JS_ToInt32Free(JSContext* ctx, int32_t* pres, JSValue val) { - uint32_t tag; - int32_t ret; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - ret = JS_VALUE_GET_INT(val); - break; - case JS_TAG_FLOAT64: { - JSFloat64Union u; - double d; - int e; - d = JS_VALUE_GET_FLOAT64(val); - u.d = d; - /* we avoid doing fmod(x, 2^32) */ - e = (u.u64 >> 52) & 0x7ff; - if (likely(e <= (1023 + 30))) { - /* fast case */ - ret = (int32_t)d; - } else if (e <= (1023 + 30 + 53)) { - uint64_t v; - /* remainder modulo 2^32 */ - v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); - v = v << ((e - 1023) - 52 + 32); - ret = v >> 32; - /* take the sign into account */ - if (u.u64 >> 63) - ret = -ret; - } else { - ret = 0; /* also handles NaN and +inf */ - } - } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - bf_get_int32(&ret, &p->num, BF_GET_INT_MOD); - JS_FreeValue(ctx, val); - } break; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } - *pres = ret; - return 0; -} - -int JS_ToInt32(JSContext* ctx, int32_t* pres, JSValueConst val) { - return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val)); -} - -int JS_ToUint8ClampFree(JSContext* ctx, int32_t* pres, JSValue val) { - uint32_t tag; - int res; - -redo: - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_INT: - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - res = JS_VALUE_GET_INT(val); -#ifdef CONFIG_BIGNUM - int_clamp: -#endif - res = max_int(0, min_int(255, res)); - break; - case JS_TAG_FLOAT64: { - double d = JS_VALUE_GET_FLOAT64(val); - if (isnan(d)) { - res = 0; - } else { - if (d < 0) - res = 0; - else if (d > 255) - res = 255; - else - res = lrint(d); - } - } break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - bf_t r_s, *r = &r_s; - bf_init(ctx->bf_ctx, r); - bf_set(r, &p->num); - bf_rint(r, BF_RNDN); - bf_get_int32(&res, r, 0); - bf_delete(r); - JS_FreeValue(ctx, val); - } - goto int_clamp; -#endif - default: - val = JS_ToNumberFree(ctx, val); - if (JS_IsException(val)) { - *pres = 0; - return -1; - } - goto redo; - } - *pres = res; - return 0; -} - -int JS_ToBoolFree(JSContext* ctx, JSValue val) { - uint32_t tag = JS_VALUE_GET_TAG(val); - switch (tag) { - case JS_TAG_INT: - return JS_VALUE_GET_INT(val) != 0; - case JS_TAG_BOOL: - case JS_TAG_NULL: - case JS_TAG_UNDEFINED: - return JS_VALUE_GET_INT(val); - case JS_TAG_EXCEPTION: - return -1; - case JS_TAG_STRING: { - BOOL ret = JS_VALUE_GET_STRING(val)->len != 0; - JS_FreeValue(ctx, val); - return ret; - } -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - BOOL ret; - ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; - JS_FreeValue(ctx, val); - return ret; - } - case JS_TAG_BIG_DECIMAL: { - JSBigDecimal* p = JS_VALUE_GET_PTR(val); - BOOL ret; - ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; - JS_FreeValue(ctx, val); - return ret; - } -#endif - case JS_TAG_OBJECT: { - JSObject* p = JS_VALUE_GET_OBJ(val); - BOOL ret; - ret = !p->is_HTMLDDA; - JS_FreeValue(ctx, val); - return ret; - } break; - default: - if (JS_TAG_IS_FLOAT64(tag)) { - double d = JS_VALUE_GET_FLOAT64(val); - return !isnan(d) && d != 0; - } else { - JS_FreeValue(ctx, val); - return TRUE; - } - } -} - -int JS_ToBool(JSContext* ctx, JSValueConst val) { - return JS_ToBoolFree(ctx, JS_DupValue(ctx, val)); -} - -/* XXX: remove */ -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; -} - -#ifdef CONFIG_BIGNUM -JSValue js_string_to_bigint(JSContext* ctx, const char* buf, int radix, int flags, slimb_t* pexponent) { - bf_t a_s, *a = &a_s; - int ret; - JSValue val; - val = JS_NewBigInt(ctx); - if (JS_IsException(val)) - return val; - a = JS_GetBigInt(val); - ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); - if (ret & BF_ST_MEM_ERROR) { - JS_FreeValue(ctx, val); - return JS_ThrowOutOfMemory(ctx); - } - val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); - return val; -} - -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; -} - -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 -JSValue js_atof2(JSContext* ctx, const char* str, const char** pp, int radix, int flags, slimb_t* pexponent) -#else -JSValue js_atof(JSContext* ctx, const char* str, const char** pp, int radix, int flags) -#endif -{ - const char *p, *p_start; - int sep, is_neg; - BOOL is_float, has_legacy_octal; - int atod_type = flags & ATOD_TYPE_MASK; - char buf1[64], *buf; - int i, j, len; - BOOL buf_allocated = FALSE; - JSValue val; - - /* optional separator between digits */ - sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; - has_legacy_octal = FALSE; - - p = str; - p_start = p; - is_neg = 0; - if (p[0] == '+') { - p++; - p_start++; - if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) - goto no_radix_prefix; - } else if (p[0] == '-') { - p++; - p_start++; - is_neg = 1; - if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) - goto no_radix_prefix; - } - if (p[0] == '0') { - if ((p[1] == 'x' || p[1] == 'X') && (radix == 0 || radix == 16)) { - p += 2; - radix = 16; - } else if ((p[1] == 'o' || p[1] == 'O') && radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { - p += 2; - radix = 8; - } else if ((p[1] == 'b' || p[1] == 'B') && radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { - p += 2; - radix = 2; - } else if ((p[1] >= '0' && p[1] <= '9') && radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { - int i; - has_legacy_octal = TRUE; - sep = 256; - for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) - continue; - if (p[i] == '8' || p[i] == '9') - goto no_prefix; - p += 1; - radix = 8; - } else { - goto no_prefix; - } - /* there must be a digit after the prefix */ - if (to_digit((uint8_t)*p) >= radix) - goto fail; - no_prefix:; - } else { - no_radix_prefix: - if (!(flags & ATOD_INT_ONLY) && (atod_type == ATOD_TYPE_FLOAT64 || atod_type == ATOD_TYPE_BIG_FLOAT) && strstart(p, "Infinity", &p)) { -#ifdef CONFIG_BIGNUM - if (atod_type == ATOD_TYPE_BIG_FLOAT) { - bf_t* a; - val = JS_NewBigFloat(ctx); - if (JS_IsException(val)) - goto done; - a = JS_GetBigFloat(val); - bf_set_inf(a, is_neg); - } else -#endif - { - double d = 1.0 / 0.0; - if (is_neg) - d = -d; - val = JS_NewFloat64(ctx, d); - } - goto done; - } - } - if (radix == 0) - radix = 10; - is_float = FALSE; - p_start = p; - while (to_digit((uint8_t)*p) < radix || (*p == sep && (radix != 10 || p != p_start + 1 || p[-1] != '0') && to_digit((uint8_t)p[1]) < radix)) { - p++; - } - if (!(flags & ATOD_INT_ONLY)) { - if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) { - is_float = TRUE; - p++; - if (*p == sep) - goto fail; - while (to_digit((uint8_t)*p) < radix || (*p == sep && to_digit((uint8_t)p[1]) < radix)) - p++; - } - if (p > p_start && (((*p == 'e' || *p == 'E') && radix == 10) || ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { - const char* p1 = p + 1; - is_float = TRUE; - if (*p1 == '+') { - p1++; - } else if (*p1 == '-') { - p1++; - } - if (is_digit((uint8_t)*p1)) { - p = p1 + 1; - while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) - p++; - } - } - } - if (p == p_start) - goto fail; - - buf = buf1; - buf_allocated = FALSE; - len = p - p_start; - if (unlikely((len + 2) > sizeof(buf1))) { - buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ - if (!buf) - goto mem_error; - buf_allocated = TRUE; - } - /* remove the separators and the radix prefixes */ - j = 0; - if (is_neg) - buf[j++] = '-'; - for (i = 0; i < len; i++) { - if (p_start[i] != '_') - buf[j++] = p_start[i]; - } - buf[j] = '\0'; - -#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; - } - } - } else { - if (atod_type == ATOD_TYPE_FLOAT64) { - if (flags & ATOD_MODE_BIGINT) { - if (!is_float) - atod_type = ATOD_TYPE_BIG_INT; - if (has_legacy_octal) - goto fail; - } else { - if (is_float && radix != 10) - goto fail; - } - } - } - - switch (atod_type) { - case ATOD_TYPE_FLOAT64: { - double d; - d = js_strtod(buf, radix, is_float); - /* return int or float64 */ - val = JS_NewFloat64(ctx, d); - } break; - case ATOD_TYPE_BIG_INT: - if (has_legacy_octal || is_float) - goto fail; - val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL); - break; - case ATOD_TYPE_BIG_FLOAT: - if (has_legacy_octal) - goto fail; - val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags, pexponent); - break; - case ATOD_TYPE_BIG_DECIMAL: - if (radix != 10) - goto fail; - val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL); - break; - default: - abort(); - } -#else - { - double d; - (void)has_legacy_octal; - if (is_float && radix != 10) - goto fail; - d = js_strtod(buf, radix, is_float); - val = JS_NewFloat64(ctx, d); - } -#endif - -done: - if (buf_allocated) - js_free_rt(ctx->rt, buf); - if (pp) - *pp = p; - return val; -fail: - val = JS_NAN; - goto done; -mem_error: - val = JS_ThrowOutOfMemory(ctx); - goto done; -} - -#ifdef CONFIG_BIGNUM -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 - -BOOL is_safe_integer(double d) { - return isfinite(d) && floor(d) == d && fabs(d) <= (double)MAX_SAFE_INTEGER; -} - -int JS_ToIndex(JSContext* ctx, uint64_t* plen, JSValueConst val) { - int64_t v; - if (JS_ToInt64Sat(ctx, &v, val)) - return -1; - if (v < 0 || v > MAX_SAFE_INTEGER) { - JS_ThrowRangeError(ctx, "invalid array index"); - *plen = 0; - return -1; - } - *plen = v; - return 0; -} - -/* convert a value to a length between 0 and MAX_SAFE_INTEGER. - return -1 for exception */ -__exception int JS_ToLengthFree(JSContext* ctx, int64_t* plen, JSValue val) { - int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); - JS_FreeValue(ctx, val); - return res; -} - -/* Note: can return an exception */ -int JS_NumberIsInteger(JSContext* ctx, JSValueConst val) { - double d; - if (!JS_IsNumber(val)) - return FALSE; - if (unlikely(JS_ToFloat64(ctx, &d, val))) - return -1; - return isfinite(d) && floor(d) == d; -} - -BOOL JS_NumberIsNegativeOrMinusZero(JSContext* ctx, JSValueConst val) { - uint32_t tag; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_INT: { - int v; - v = JS_VALUE_GET_INT(val); - return (v < 0); - } - case JS_TAG_FLOAT64: { - JSFloat64Union u; - u.d = JS_VALUE_GET_FLOAT64(val); - return (u.u64 >> 63); - } -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - /* Note: integer zeros are not necessarily positive */ - return p->num.sign && !bf_is_zero(&p->num); - } - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - return p->num.sign; - } break; - case JS_TAG_BIG_DECIMAL: { - JSBigDecimal* p = JS_VALUE_GET_PTR(val); - return p->num.sign; - } break; -#endif - default: - return FALSE; - } -} - -#ifdef CONFIG_BIGNUM - -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; -} - -JSValue js_bigint_to_string(JSContext* ctx, JSValueConst val) { - return js_bigint_to_string1(ctx, val, 10); -} - -JSValue js_ftoa(JSContext* ctx, JSValueConst val1, int radix, limb_t prec, bf_flags_t flags) { - JSValue val, ret; - bf_t a_s, *a; - char* str; - int saved_sign; - - val = JS_ToNumeric(ctx, val1); - if (JS_IsException(val)) - return val; - a = JS_ToBigFloat(ctx, &a_s, val); - saved_sign = a->sign; - if (a->expn == BF_EXP_ZERO) - a->sign = 0; - flags |= BF_FTOA_JS_QUIRKS; - if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) { - /* Note: for floating point numbers with a radix which is not - a power of two, the current precision is used to compute - the number of digits. */ - if ((radix & (radix - 1)) != 0) { - bf_t r_s, *r = &r_s; - int prec, flags1; - /* must round first */ - if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { - prec = ctx->fp_env.prec; - flags1 = ctx->fp_env.flags & (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)); - } else { - prec = 53; - flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL; - } - bf_init(ctx->bf_ctx, r); - bf_set(r, a); - bf_round(r, prec, flags1 | BF_RNDN); - str = bf_ftoa(NULL, r, radix, prec, flags1 | flags); - bf_delete(r); - } else { - str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags); - } - } else { - str = bf_ftoa(NULL, a, radix, prec, flags); - } - a->sign = saved_sign; - if (a == &a_s) - bf_delete(a); - JS_FreeValue(ctx, val); - if (!str) - return JS_ThrowOutOfMemory(ctx); - ret = JS_NewString(ctx, str); - bf_free(ctx->bf_ctx, str); - return ret; -} - -JSValue js_bigfloat_to_string(JSContext* ctx, JSValueConst val) { - return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); -} - -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; -} - -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 */ -char* i64toa(char* buf_end, int64_t n, unsigned int base) { - char* q = buf_end; - int digit, is_neg; - - is_neg = 0; - if (n < 0) { - is_neg = 1; - n = -n; - } - *--q = '\0'; - do { - digit = (uint64_t)n % base; - n = (uint64_t)n / base; - if (digit < 10) - digit += '0'; - else - digit += 'a' - 10; - *--q = digit; - } while (n != 0); - if (is_neg) - *--q = '-'; - return q; -} - -/* buf1 contains the printf result */ -void js_ecvt1(double d, int n_digits, int* decpt, int* sign, char* buf, int rounding_mode, char* buf1, int buf1_size) { - if (rounding_mode != FE_TONEAREST) - fesetround(rounding_mode); - snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d); - if (rounding_mode != FE_TONEAREST) - fesetround(FE_TONEAREST); - *sign = (buf1[0] == '-'); - /* mantissa */ - buf[0] = buf1[1]; - if (n_digits > 1) - memcpy(buf + 1, buf1 + 3, n_digits - 1); - buf[n_digits] = '\0'; - /* exponent */ - *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1; -} - -/* needed because ecvt usually limits the number of digits to - 17. Return the number of digits. */ -int js_ecvt(double d, int n_digits, int* decpt, int* sign, char* buf, BOOL is_fixed) { - int rounding_mode; - char buf_tmp[JS_DTOA_BUF_SIZE]; - - if (!is_fixed) { - unsigned int n_digits_min, n_digits_max; - /* find the minimum amount of digits (XXX: inefficient but simple) */ - n_digits_min = 1; - n_digits_max = 17; - while (n_digits_min < n_digits_max) { - n_digits = (n_digits_min + n_digits_max) / 2; - js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST, buf_tmp, sizeof(buf_tmp)); - if (strtod(buf_tmp, NULL) == d) { - /* no need to keep the trailing zeros */ - while (n_digits >= 2 && buf[n_digits - 1] == '0') - n_digits--; - n_digits_max = n_digits; - } else { - n_digits_min = n_digits + 1; - } - } - n_digits = n_digits_max; - rounding_mode = FE_TONEAREST; - } else { - rounding_mode = FE_TONEAREST; -#ifdef CONFIG_PRINTF_RNDN - { - char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE]; - int decpt1, sign1, decpt2, sign2; - /* The JS rounding is specified as round to nearest ties away - from zero (RNDNA), but in printf the "ties" case is not - specified (for example it is RNDN for glibc, RNDNA for - Windows), so we must round manually. */ - js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST, buf_tmp, sizeof(buf_tmp)); - /* XXX: could use 2 digits to reduce the average running time */ - if (buf1[n_digits] == '5') { - js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD, buf_tmp, sizeof(buf_tmp)); - js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD, buf_tmp, sizeof(buf_tmp)); - if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) { - /* exact result: round away from zero */ - if (sign1) - rounding_mode = FE_DOWNWARD; - else - rounding_mode = FE_UPWARD; - } - } - } -#endif /* CONFIG_PRINTF_RNDN */ - } - js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode, buf_tmp, sizeof(buf_tmp)); - return n_digits; -} - -int js_fcvt1(char* buf, int buf_size, double d, int n_digits, int rounding_mode) { - int n; - if (rounding_mode != FE_TONEAREST) - fesetround(rounding_mode); - n = snprintf(buf, buf_size, "%.*f", n_digits, d); - if (rounding_mode != FE_TONEAREST) - fesetround(FE_TONEAREST); - assert(n < buf_size); - return n; -} - -void js_fcvt(char* buf, int buf_size, double d, int n_digits) { - int rounding_mode; - rounding_mode = FE_TONEAREST; -#ifdef CONFIG_PRINTF_RNDN - { - int n1, n2; - char buf1[JS_DTOA_BUF_SIZE]; - char buf2[JS_DTOA_BUF_SIZE]; - - /* The JS rounding is specified as round to nearest ties away from - zero (RNDNA), but in printf the "ties" case is not specified - (for example it is RNDN for glibc, RNDNA for Windows), so we - must round manually. */ - n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST); - rounding_mode = FE_TONEAREST; - /* XXX: could use 2 digits to reduce the average running time */ - if (buf1[n1 - 1] == '5') { - n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD); - n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD); - if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) { - /* exact result: round away from zero */ - if (buf1[0] == '-') - rounding_mode = FE_DOWNWARD; - else - rounding_mode = FE_UPWARD; - } - } - } -#endif /* CONFIG_PRINTF_RNDN */ - js_fcvt1(buf, buf_size, d, n_digits, rounding_mode); -} - -/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. - XXX: radix != 10 is only supported for small integers -*/ -void js_dtoa1(char* buf, double d, int radix, int n_digits, int flags) { - char* q; - - if (!isfinite(d)) { - if (isnan(d)) { - strcpy(buf, "NaN"); - } else { - q = buf; - if (d < 0) - *q++ = '-'; - strcpy(q, "Infinity"); - } - } else if (flags == JS_DTOA_VAR_FORMAT) { - int64_t i64; - char buf1[70], *ptr; - i64 = (int64_t)d; - if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER) - goto generic_conv; - /* fast path for integers */ - ptr = i64toa(buf1 + sizeof(buf1), i64, radix); - strcpy(buf, ptr); - } else { - if (d == 0.0) - d = 0.0; /* convert -0 to 0 */ - if (flags == JS_DTOA_FRAC_FORMAT) { - js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits); - } else { - char buf1[JS_DTOA_BUF_SIZE]; - int sign, decpt, k, n, i, p, n_max; - BOOL is_fixed; - generic_conv: - is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT); - if (is_fixed) { - n_max = n_digits; - } else { - n_max = 21; - } - /* the number has k digits (k >= 1) */ - k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed); - n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ - q = buf; - if (sign) - *q++ = '-'; - if (flags & JS_DTOA_FORCE_EXP) - goto force_exp; - if (n >= 1 && n <= n_max) { - if (k <= n) { - memcpy(q, buf1, k); - q += k; - for (i = 0; i < (n - k); i++) - *q++ = '0'; - *q = '\0'; - } else { - /* k > n */ - memcpy(q, buf1, n); - q += n; - *q++ = '.'; - for (i = 0; i < (k - n); i++) - *q++ = buf1[n + i]; - *q = '\0'; - } - } else if (n >= -5 && n <= 0) { - *q++ = '0'; - *q++ = '.'; - for (i = 0; i < -n; i++) - *q++ = '0'; - memcpy(q, buf1, k); - q += k; - *q = '\0'; - } else { - force_exp: - /* exponential notation */ - *q++ = buf1[0]; - if (k > 1) { - *q++ = '.'; - for (i = 1; i < k; i++) - *q++ = buf1[i]; - } - *q++ = 'e'; - p = n - 1; - if (p >= 0) - *q++ = '+'; - sprintf(q, "%d", p); - } - } - } -} - -JSValue js_dtoa(JSContext* ctx, double d, int radix, int n_digits, int flags) { - char buf[JS_DTOA_BUF_SIZE]; - js_dtoa1(buf, d, radix, n_digits, flags); - return JS_NewString(ctx, buf); -} - -JSValue JS_ToStringInternal(JSContext* ctx, JSValueConst val, BOOL is_ToPropertyKey) { - uint32_t tag; - const char* str; - char buf[32]; - - tag = JS_VALUE_GET_NORM_TAG(val); - switch (tag) { - case JS_TAG_STRING: - return JS_DupValue(ctx, val); - case JS_TAG_INT: - snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val)); - str = buf; - goto new_string; - case JS_TAG_BOOL: - return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? JS_ATOM_true : JS_ATOM_false); - case JS_TAG_NULL: - return JS_AtomToString(ctx, JS_ATOM_null); - case JS_TAG_UNDEFINED: - return JS_AtomToString(ctx, JS_ATOM_undefined); - case JS_TAG_EXCEPTION: - return JS_EXCEPTION; - case JS_TAG_OBJECT: { - JSValue val1, ret; - val1 = JS_ToPrimitive(ctx, val, HINT_STRING); - if (JS_IsException(val1)) - return val1; - ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); - JS_FreeValue(ctx, val1); - return ret; - } break; - case JS_TAG_FUNCTION_BYTECODE: - str = "[function bytecode]"; - goto new_string; - case JS_TAG_SYMBOL: - if (is_ToPropertyKey) { - return JS_DupValue(ctx, val); - } else { - return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); - } - case JS_TAG_FLOAT64: - return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, JS_DTOA_VAR_FORMAT); -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - return ctx->rt->bigint_ops.to_string(ctx, val); - case JS_TAG_BIG_FLOAT: - return ctx->rt->bigfloat_ops.to_string(ctx, val); - case JS_TAG_BIG_DECIMAL: - return ctx->rt->bigdecimal_ops.to_string(ctx, val); -#endif - default: - str = "[unsupported type]"; - new_string: - return JS_NewString(ctx, str); - } -} - -JSValue JS_ToString(JSContext* ctx, JSValueConst val) { - return JS_ToStringInternal(ctx, val, FALSE); -} - -JSValue JS_ToStringFree(JSContext* ctx, JSValue val) { - JSValue ret; - ret = JS_ToString(ctx, val); - JS_FreeValue(ctx, val); - return ret; -} - -JSValue JS_ToLocaleStringFree(JSContext* ctx, JSValue val) { - if (JS_IsUndefined(val) || JS_IsNull(val)) - return JS_ToStringFree(ctx, val); - return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); -} - -JSValue JS_ToPropertyKey(JSContext* ctx, JSValueConst val) { - return JS_ToStringInternal(ctx, val, TRUE); -} - -JSValue JS_ToStringCheckObject(JSContext* ctx, JSValueConst val) { - uint32_t tag = JS_VALUE_GET_TAG(val); - if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) - return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); - return JS_ToString(ctx, val); -} - -JSValue JS_ToQuotedString(JSContext* ctx, JSValueConst val1) { - JSValue val; - JSString* p; - int i; - uint32_t c; - StringBuffer b_s, *b = &b_s; - char buf[16]; - - val = JS_ToStringCheckObject(ctx, val1); - if (JS_IsException(val)) - return val; - p = JS_VALUE_GET_STRING(val); - - if (string_buffer_init(ctx, b, p->len + 2)) - goto fail; - - if (string_buffer_putc8(b, '\"')) - goto fail; - for (i = 0; i < p->len;) { - c = string_getc(p, &i); - switch (c) { - case '\t': - c = 't'; - goto quote; - case '\r': - c = 'r'; - goto quote; - case '\n': - c = 'n'; - goto quote; - case '\b': - c = 'b'; - goto quote; - case '\f': - c = 'f'; - goto quote; - case '\"': - case '\\': - quote: - if (string_buffer_putc8(b, '\\')) - goto fail; - if (string_buffer_putc8(b, c)) - goto fail; - break; - default: - if (c < 32 || (c >= 0xd800 && c < 0xe000)) { - snprintf(buf, sizeof(buf), "\\u%04x", c); - if (string_buffer_puts8(b, buf)) - goto fail; - } else { - if (string_buffer_putc(b, c)) - goto fail; - } - break; - } - } - if (string_buffer_putc8(b, '\"')) - goto fail; - JS_FreeValue(ctx, val); - return string_buffer_end(b); -fail: - JS_FreeValue(ctx, val); - string_buffer_free(b); - return JS_EXCEPTION; -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "convertion.h" +#include "builtins/js-big-num.h" +#include "exception.h" +#include "function.h" +#include "quickjs/libregexp.h" +#include "string.h" + +static JSValue JS_ToNumberHintFree(JSContext* ctx, JSValue val, JSToNumberHintEnum flag); + +int skip_spaces(const char* pc) { + const uint8_t *p, *p_next, *p_start; + uint32_t c; + + p = p_start = (const uint8_t*)pc; + for (;;) { + c = *p; + if (c < 128) { + if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) + break; + p++; + } else { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); + if (!lre_is_space(c)) + break; + p = p_next; + } + } + return p - p_start; +} + +JSValue JS_ToPrimitiveFree(JSContext* ctx, JSValue val, int hint) { + int i; + BOOL force_ordinary; + + JSAtom method_name; + JSValue method, ret; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return val; + force_ordinary = hint & HINT_FORCE_ORDINARY; + hint &= ~HINT_FORCE_ORDINARY; + if (!force_ordinary) { + method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); + if (JS_IsException(method)) + goto exception; + /* ECMA says *If exoticToPrim is not undefined* but tests in + test262 use null as a non callable converter */ + if (!JS_IsUndefined(method) && !JS_IsNull(method)) { + JSAtom atom; + JSValue arg; + switch (hint) { + case HINT_STRING: + atom = JS_ATOM_string; + break; + case HINT_NUMBER: + atom = JS_ATOM_number; + break; + default: + case HINT_NONE: + atom = JS_ATOM_default; + break; + } + arg = JS_AtomToString(ctx, atom); + ret = JS_CallFree(ctx, method, val, 1, (JSValueConst*)&arg); + JS_FreeValue(ctx, arg); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) + return ret; + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "toPrimitive"); + } + } + if (hint != HINT_STRING) + hint = HINT_NUMBER; + for (i = 0; i < 2; i++) { + if ((i ^ hint) == 0) { + method_name = JS_ATOM_toString; + } else { + method_name = JS_ATOM_valueOf; + } + method = JS_GetProperty(ctx, val, method_name); + if (JS_IsException(method)) + goto exception; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, val, 0, NULL); + if (JS_IsException(ret)) + goto exception; + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, val); + return ret; + } + JS_FreeValue(ctx, ret); + } else { + JS_FreeValue(ctx, method); + } + } + JS_ThrowTypeError(ctx, "toPrimitive"); +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +JSValue JS_ToPrimitive(JSContext* ctx, JSValueConst val, int hint) { + return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); +} + +__exception int JS_ToArrayLengthFree(JSContext* ctx, uint32_t* plen, JSValue val, BOOL is_array_ctor) { + uint32_t tag, len; + + tag = JS_VALUE_GET_TAG(val); + switch (tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: { + int v; + v = JS_VALUE_GET_INT(val); + if (v < 0) + goto fail; + len = v; + } break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + bf_t a; + BOOL res; + bf_get_int32((int32_t*)&len, &p->num, BF_GET_INT_MOD); + bf_init(ctx->bf_ctx, &a); + bf_set_ui(&a, len); + res = bf_cmp_eq(&a, &p->num); + bf_delete(&a); + JS_FreeValue(ctx, val); + if (!res) + goto fail; + } break; +#endif + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d; + d = JS_VALUE_GET_FLOAT64(val); + len = (uint32_t)d; + if (len != d) + goto fail; + } else { + uint32_t len1; + + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } + } + break; + } + *plen = len; + return 0; +} + +JSValue JS_ToNumber(JSContext* ctx, JSValueConst val) { + return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); +} + +JSValue JS_ToNumberFree(JSContext* ctx, JSValue val) { + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); +} + +JSValue JS_ToNumericFree(JSContext* ctx, JSValue val) { + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); +} + +JSValue JS_ToNumeric(JSContext* ctx, JSValueConst val) { + return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); +} + +static JSValue JS_ToNumberHintFree(JSContext* ctx, JSValue val, JSToNumberHintEnum flag) { + uint32_t tag; + JSValue ret; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { +#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_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); + } + ret = val; + break; + case JS_TAG_BIG_FLOAT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number"); + } + ret = val; + break; +#endif + case JS_TAG_FLOAT64: + case JS_TAG_INT: + case JS_TAG_EXCEPTION: + ret = val; + break; + case JS_TAG_BOOL: + case JS_TAG_NULL: + ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_UNDEFINED: + ret = JS_NAN; + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + goto redo; + case JS_TAG_STRING: { + const char* str; + const char* p; + size_t len; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + p = str; + p += skip_spaces(p); + if ((p - str) == len) { + ret = JS_NewInt32(ctx, 0); + } else { + int flags = ATOD_ACCEPT_BIN_OCT; + ret = js_atof(ctx, p, &p, 0, flags); + if (!JS_IsException(ret)) { + p += skip_spaces(p); + if ((p - str) != len) { + JS_FreeValue(ctx, ret); + ret = JS_NAN; + } + } + } + JS_FreeCString(ctx, str); + } break; + case JS_TAG_SYMBOL: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); + default: + JS_FreeValue(ctx, val); + ret = JS_NAN; + break; + } + return ret; +} + +__exception int __JS_ToFloat64Free(JSContext* ctx, double* pres, JSValue val) { + double d; + uint32_t tag; + + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = JS_FLOAT64_NAN; + return -1; + } + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_INT: + d = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(val); + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + /* XXX: there can be a double rounding issue with some + primitives (such as JS_ToUint8ClampFree()), but it is + not critical to fix it. */ + bf_get_float64(&p->num, &d, BF_RNDN); + JS_FreeValue(ctx, val); + } break; +#endif + default: + abort(); + } + *pres = d; + return 0; +} + +int JS_ToFloat64(JSContext* ctx, double* pres, JSValueConst val) { + return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); +} + +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ +__maybe_unused JSValue JS_ToIntegerFree(JSContext* ctx, JSValue val) { + uint32_t tag; + JSValue ret; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_FLOAT64: { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = JS_NewInt32(ctx, 0); + } else { + /* convert -0 to +0 */ + d = trunc(d) + 0.0; + ret = JS_NewFloat64(ctx, d); + } + } break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: { + bf_t a_s, *a, r_s, *r = &r_s; + BOOL is_nan; + + a = JS_ToBigFloat(ctx, &a_s, val); + if (!bf_is_finite(a)) { + is_nan = bf_is_nan(a); + if (is_nan) + ret = JS_NewInt32(ctx, 0); + else + ret = JS_DupValue(ctx, val); + } else { + ret = JS_NewBigInt(ctx); + if (!JS_IsException(ret)) { + r = JS_GetBigInt(ret); + bf_set(r, a); + bf_rint(r, BF_RNDZ); + ret = JS_CompactBigInt(ctx, ret); + } + } + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, val); + } break; +#endif + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; + goto redo; + } + return ret; +} + +/* Note: the integer value is satured to 32 bits */ +int JS_ToInt32SatFree(JSContext* ctx, int* pres, JSValue val) { + uint32_t tag; + int ret; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = 0; + } else { + if (d < INT32_MIN) + ret = INT32_MIN; + else if (d > INT32_MAX) + ret = INT32_MAX; + else + ret = (int)d; + } + } break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + bf_get_int32(&ret, &p->num, 0); + JS_FreeValue(ctx, val); + } break; +#endif + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32Sat(JSContext* ctx, int* pres, JSValueConst val) { + return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); +} + +int JS_ToInt32Clamp(JSContext* ctx, int* pres, JSValueConst val, int min, int max, int min_offset) { + int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); + if (res == 0) { + if (*pres < min) { + *pres += min_offset; + if (*pres < min) + *pres = min; + } else { + if (*pres > max) + *pres = max; + } + } + return res; +} + +int JS_ToInt64SatFree(JSContext* ctx, int64_t* pres, JSValue val) { + uint32_t tag; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + *pres = JS_VALUE_GET_INT(val); + return 0; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + *pres = 0; + } else { + if (d < INT64_MIN) + *pres = INT64_MIN; + else if (d > INT64_MAX) + *pres = INT64_MAX; + else + *pres = (int64_t)d; + } + } + return 0; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + bf_get_int64(pres, &p->num, 0); + JS_FreeValue(ctx, val); + } + return 0; +#endif + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } +} + +int JS_ToInt64Sat(JSContext* ctx, int64_t* pres, JSValueConst val) { + return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); +} + +int JS_ToInt64Clamp(JSContext* ctx, int64_t* pres, JSValueConst val, int64_t min, int64_t max, int64_t neg_offset) { + int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); + if (res == 0) { + if (*pres < 0) + *pres += neg_offset; + if (*pres < min) + *pres = min; + else if (*pres > max) + *pres = max; + } + return res; +} + +/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) + in case of exception */ +int JS_ToInt64Free(JSContext* ctx, int64_t* pres, JSValue val) { + uint32_t tag; + int64_t ret; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^64) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 62))) { + /* fast case */ + ret = (int64_t)d; + } else if (e <= (1023 + 62 + 53)) { + uint64_t v; + /* remainder modulo 2^64 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + ret = v << ((e - 1023) - 52); + /* take the sign into account */ + if (u.u64 >> 63) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + bf_get_int64(&ret, &p->num, BF_GET_INT_MOD); + JS_FreeValue(ctx, val); + } break; +#endif + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt64(JSContext* ctx, int64_t* pres, JSValueConst val) { + return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val)); +} + +int JS_ToInt64Ext(JSContext* ctx, int64_t* pres, JSValueConst val) { + if (JS_IsBigInt(ctx, val)) + return JS_ToBigInt64(ctx, pres, val); + else + return JS_ToInt64(ctx, pres, val); +} + +/* return (<0, 0) in case of exception */ +int JS_ToInt32Free(JSContext* ctx, int32_t* pres, JSValue val) { + uint32_t tag; + int32_t ret; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^32) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 30))) { + /* fast case */ + ret = (int32_t)d; + } else if (e <= (1023 + 30 + 53)) { + uint64_t v; + /* remainder modulo 2^32 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + v = v << ((e - 1023) - 52 + 32); + ret = v >> 32; + /* take the sign into account */ + if (u.u64 >> 63) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + bf_get_int32(&ret, &p->num, BF_GET_INT_MOD); + JS_FreeValue(ctx, val); + } break; +#endif + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32(JSContext* ctx, int32_t* pres, JSValueConst val) { + return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val)); +} + +int JS_ToUint8ClampFree(JSContext* ctx, int32_t* pres, JSValue val) { + uint32_t tag; + int res; + +redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = JS_VALUE_GET_INT(val); +#ifdef CONFIG_BIGNUM + int_clamp: +#endif + res = max_int(0, min_int(255, res)); + break; + case JS_TAG_FLOAT64: { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + res = 0; + } else { + if (d < 0) + res = 0; + else if (d > 255) + res = 255; + else + res = lrint(d); + } + } break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + bf_t r_s, *r = &r_s; + bf_init(ctx->bf_ctx, r); + bf_set(r, &p->num); + bf_rint(r, BF_RNDN); + bf_get_int32(&res, r, 0); + bf_delete(r); + JS_FreeValue(ctx, val); + } + goto int_clamp; +#endif + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = res; + return 0; +} + +int JS_ToBoolFree(JSContext* ctx, JSValue val) { + uint32_t tag = JS_VALUE_GET_TAG(val); + switch (tag) { + case JS_TAG_INT: + return JS_VALUE_GET_INT(val) != 0; + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_VALUE_GET_INT(val); + case JS_TAG_EXCEPTION: + return -1; + case JS_TAG_STRING: { + BOOL ret = JS_VALUE_GET_STRING(val)->len != 0; + JS_FreeValue(ctx, val); + return ret; + } +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + BOOL ret; + ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_BIG_DECIMAL: { + JSBigDecimal* p = JS_VALUE_GET_PTR(val); + BOOL ret; + ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + JS_FreeValue(ctx, val); + return ret; + } +#endif + case JS_TAG_OBJECT: { + JSObject* p = JS_VALUE_GET_OBJ(val); + BOOL ret; + ret = !p->is_HTMLDDA; + JS_FreeValue(ctx, val); + return ret; + } break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d = JS_VALUE_GET_FLOAT64(val); + return !isnan(d) && d != 0; + } else { + JS_FreeValue(ctx, val); + return TRUE; + } + } +} + +int JS_ToBool(JSContext* ctx, JSValueConst val) { + return JS_ToBoolFree(ctx, JS_DupValue(ctx, val)); +} + +/* XXX: remove */ +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; +} + +#ifdef CONFIG_BIGNUM +JSValue js_string_to_bigint(JSContext* ctx, const char* buf, int radix, int flags, slimb_t* pexponent) { + bf_t a_s, *a = &a_s; + int ret; + JSValue val; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); + return val; +} + +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; +} + +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 +JSValue js_atof2(JSContext* ctx, const char* str, const char** pp, int radix, int flags, slimb_t* pexponent) +#else +JSValue js_atof(JSContext* ctx, const char* str, const char** pp, int radix, int flags) +#endif +{ + const char *p, *p_start; + int sep, is_neg; + BOOL is_float, has_legacy_octal; + int atod_type = flags & ATOD_TYPE_MASK; + char buf1[64], *buf; + int i, j, len; + BOOL buf_allocated = FALSE; + JSValue val; + + /* optional separator between digits */ + sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + has_legacy_octal = FALSE; + + p = str; + p_start = p; + is_neg = 0; + if (p[0] == '+') { + p++; + p_start++; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } else if (p[0] == '-') { + p++; + p_start++; + is_neg = 1; + if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) + goto no_radix_prefix; + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && (radix == 0 || radix == 16)) { + p += 2; + radix = 16; + } else if ((p[1] == 'o' || p[1] == 'O') && radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 2; + } else if ((p[1] >= '0' && p[1] <= '9') && radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { + int i; + has_legacy_octal = TRUE; + sep = 256; + for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) + continue; + if (p[i] == '8' || p[i] == '9') + goto no_prefix; + p += 1; + radix = 8; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (to_digit((uint8_t)*p) >= radix) + goto fail; + no_prefix:; + } else { + no_radix_prefix: + if (!(flags & ATOD_INT_ONLY) && (atod_type == ATOD_TYPE_FLOAT64 || atod_type == ATOD_TYPE_BIG_FLOAT) && strstart(p, "Infinity", &p)) { +#ifdef CONFIG_BIGNUM + if (atod_type == ATOD_TYPE_BIG_FLOAT) { + bf_t* a; + val = JS_NewBigFloat(ctx); + if (JS_IsException(val)) + goto done; + a = JS_GetBigFloat(val); + bf_set_inf(a, is_neg); + } else +#endif + { + double d = 1.0 / 0.0; + if (is_neg) + d = -d; + val = JS_NewFloat64(ctx, d); + } + goto done; + } + } + if (radix == 0) + radix = 10; + is_float = FALSE; + p_start = p; + while (to_digit((uint8_t)*p) < radix || (*p == sep && (radix != 10 || p != p_start + 1 || p[-1] != '0') && to_digit((uint8_t)p[1]) < radix)) { + p++; + } + if (!(flags & ATOD_INT_ONLY)) { + if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) { + is_float = TRUE; + p++; + if (*p == sep) + goto fail; + while (to_digit((uint8_t)*p) < radix || (*p == sep && to_digit((uint8_t)p[1]) < radix)) + p++; + } + if (p > p_start && (((*p == 'e' || *p == 'E') && radix == 10) || ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { + const char* p1 = p + 1; + is_float = TRUE; + if (*p1 == '+') { + p1++; + } else if (*p1 == '-') { + p1++; + } + if (is_digit((uint8_t)*p1)) { + p = p1 + 1; + while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) + p++; + } + } + } + if (p == p_start) + goto fail; + + buf = buf1; + buf_allocated = FALSE; + len = p - p_start; + if (unlikely((len + 2) > sizeof(buf1))) { + buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ + if (!buf) + goto mem_error; + buf_allocated = TRUE; + } + /* remove the separators and the radix prefixes */ + j = 0; + if (is_neg) + buf[j++] = '-'; + for (i = 0; i < len; i++) { + if (p_start[i] != '_') + buf[j++] = p_start[i]; + } + buf[j] = '\0'; + +#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; + } + } + } else { + if (atod_type == ATOD_TYPE_FLOAT64) { + if (flags & ATOD_MODE_BIGINT) { + if (!is_float) + atod_type = ATOD_TYPE_BIG_INT; + if (has_legacy_octal) + goto fail; + } else { + if (is_float && radix != 10) + goto fail; + } + } + } + + switch (atod_type) { + case ATOD_TYPE_FLOAT64: { + double d; + d = js_strtod(buf, radix, is_float); + /* return int or float64 */ + val = JS_NewFloat64(ctx, d); + } break; + case ATOD_TYPE_BIG_INT: + if (has_legacy_octal || is_float) + goto fail; + val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL); + break; + case ATOD_TYPE_BIG_FLOAT: + if (has_legacy_octal) + goto fail; + val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags, pexponent); + break; + case ATOD_TYPE_BIG_DECIMAL: + if (radix != 10) + goto fail; + val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL); + break; + default: + abort(); + } +#else + { + double d; + (void)has_legacy_octal; + if (is_float && radix != 10) + goto fail; + d = js_strtod(buf, radix, is_float); + val = JS_NewFloat64(ctx, d); + } +#endif + +done: + if (buf_allocated) + js_free_rt(ctx->rt, buf); + if (pp) + *pp = p; + return val; +fail: + val = JS_NAN; + goto done; +mem_error: + val = JS_ThrowOutOfMemory(ctx); + goto done; +} + +#ifdef CONFIG_BIGNUM +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 + +BOOL is_safe_integer(double d) { + return isfinite(d) && floor(d) == d && fabs(d) <= (double)MAX_SAFE_INTEGER; +} + +int JS_ToIndex(JSContext* ctx, uint64_t* plen, JSValueConst val) { + int64_t v; + if (JS_ToInt64Sat(ctx, &v, val)) + return -1; + if (v < 0 || v > MAX_SAFE_INTEGER) { + JS_ThrowRangeError(ctx, "invalid array index"); + *plen = 0; + return -1; + } + *plen = v; + return 0; +} + +/* convert a value to a length between 0 and MAX_SAFE_INTEGER. + return -1 for exception */ +__exception int JS_ToLengthFree(JSContext* ctx, int64_t* plen, JSValue val) { + int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); + JS_FreeValue(ctx, val); + return res; +} + +/* Note: can return an exception */ +int JS_NumberIsInteger(JSContext* ctx, JSValueConst val) { + double d; + if (!JS_IsNumber(val)) + return FALSE; + if (unlikely(JS_ToFloat64(ctx, &d, val))) + return -1; + return isfinite(d) && floor(d) == d; +} + +BOOL JS_NumberIsNegativeOrMinusZero(JSContext* ctx, JSValueConst val) { + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_INT: { + int v; + v = JS_VALUE_GET_INT(val); + return (v < 0); + } + case JS_TAG_FLOAT64: { + JSFloat64Union u; + u.d = JS_VALUE_GET_FLOAT64(val); + return (u.u64 >> 63); + } +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + /* Note: integer zeros are not necessarily positive */ + return p->num.sign && !bf_is_zero(&p->num); + } + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + return p->num.sign; + } break; + case JS_TAG_BIG_DECIMAL: { + JSBigDecimal* p = JS_VALUE_GET_PTR(val); + return p->num.sign; + } break; +#endif + default: + return FALSE; + } +} + +#ifdef CONFIG_BIGNUM + +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; +} + +JSValue js_bigint_to_string(JSContext* ctx, JSValueConst val) { + return js_bigint_to_string1(ctx, val, 10); +} + +JSValue js_ftoa(JSContext* ctx, JSValueConst val1, int radix, limb_t prec, bf_flags_t flags) { + JSValue val, ret; + bf_t a_s, *a; + char* str; + int saved_sign; + + val = JS_ToNumeric(ctx, val1); + if (JS_IsException(val)) + return val; + a = JS_ToBigFloat(ctx, &a_s, val); + saved_sign = a->sign; + if (a->expn == BF_EXP_ZERO) + a->sign = 0; + flags |= BF_FTOA_JS_QUIRKS; + if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) { + /* Note: for floating point numbers with a radix which is not + a power of two, the current precision is used to compute + the number of digits. */ + if ((radix & (radix - 1)) != 0) { + bf_t r_s, *r = &r_s; + int prec, flags1; + /* must round first */ + if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { + prec = ctx->fp_env.prec; + flags1 = ctx->fp_env.flags & (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)); + } else { + prec = 53; + flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL; + } + bf_init(ctx->bf_ctx, r); + bf_set(r, a); + bf_round(r, prec, flags1 | BF_RNDN); + str = bf_ftoa(NULL, r, radix, prec, flags1 | flags); + bf_delete(r); + } else { + str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags); + } + } else { + str = bf_ftoa(NULL, a, radix, prec, flags); + } + a->sign = saved_sign; + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, val); + if (!str) + return JS_ThrowOutOfMemory(ctx); + ret = JS_NewString(ctx, str); + bf_free(ctx->bf_ctx, str); + return ret; +} + +JSValue js_bigfloat_to_string(JSContext* ctx, JSValueConst val) { + return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); +} + +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; +} + +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 */ +char* i64toa(char* buf_end, int64_t n, unsigned int base) { + char* q = buf_end; + int digit, is_neg; + + is_neg = 0; + if (n < 0) { + is_neg = 1; + n = -n; + } + *--q = '\0'; + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + *--q = digit; + } while (n != 0); + if (is_neg) + *--q = '-'; + return q; +} + +/* buf1 contains the printf result */ +void js_ecvt1(double d, int n_digits, int* decpt, int* sign, char* buf, int rounding_mode, char* buf1, int buf1_size) { + if (rounding_mode != FE_TONEAREST) + fesetround(rounding_mode); + snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d); + if (rounding_mode != FE_TONEAREST) + fesetround(FE_TONEAREST); + *sign = (buf1[0] == '-'); + /* mantissa */ + buf[0] = buf1[1]; + if (n_digits > 1) + memcpy(buf + 1, buf1 + 3, n_digits - 1); + buf[n_digits] = '\0'; + /* exponent */ + *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1; +} + +/* needed because ecvt usually limits the number of digits to + 17. Return the number of digits. */ +int js_ecvt(double d, int n_digits, int* decpt, int* sign, char* buf, BOOL is_fixed) { + int rounding_mode; + char buf_tmp[JS_DTOA_BUF_SIZE]; + + if (!is_fixed) { + unsigned int n_digits_min, n_digits_max; + /* find the minimum amount of digits (XXX: inefficient but simple) */ + n_digits_min = 1; + n_digits_max = 17; + while (n_digits_min < n_digits_max) { + n_digits = (n_digits_min + n_digits_max) / 2; + js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST, buf_tmp, sizeof(buf_tmp)); + if (strtod(buf_tmp, NULL) == d) { + /* no need to keep the trailing zeros */ + while (n_digits >= 2 && buf[n_digits - 1] == '0') + n_digits--; + n_digits_max = n_digits; + } else { + n_digits_min = n_digits + 1; + } + } + n_digits = n_digits_max; + rounding_mode = FE_TONEAREST; + } else { + rounding_mode = FE_TONEAREST; +#ifdef CONFIG_PRINTF_RNDN + { + char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE]; + int decpt1, sign1, decpt2, sign2; + /* The JS rounding is specified as round to nearest ties away + from zero (RNDNA), but in printf the "ties" case is not + specified (for example it is RNDN for glibc, RNDNA for + Windows), so we must round manually. */ + js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST, buf_tmp, sizeof(buf_tmp)); + /* XXX: could use 2 digits to reduce the average running time */ + if (buf1[n_digits] == '5') { + js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD, buf_tmp, sizeof(buf_tmp)); + js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD, buf_tmp, sizeof(buf_tmp)); + if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) { + /* exact result: round away from zero */ + if (sign1) + rounding_mode = FE_DOWNWARD; + else + rounding_mode = FE_UPWARD; + } + } + } +#endif /* CONFIG_PRINTF_RNDN */ + } + js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode, buf_tmp, sizeof(buf_tmp)); + return n_digits; +} + +int js_fcvt1(char* buf, int buf_size, double d, int n_digits, int rounding_mode) { + int n; + if (rounding_mode != FE_TONEAREST) + fesetround(rounding_mode); + n = snprintf(buf, buf_size, "%.*f", n_digits, d); + if (rounding_mode != FE_TONEAREST) + fesetround(FE_TONEAREST); + assert(n < buf_size); + return n; +} + +void js_fcvt(char* buf, int buf_size, double d, int n_digits) { + int rounding_mode; + rounding_mode = FE_TONEAREST; +#ifdef CONFIG_PRINTF_RNDN + { + int n1, n2; + char buf1[JS_DTOA_BUF_SIZE]; + char buf2[JS_DTOA_BUF_SIZE]; + + /* The JS rounding is specified as round to nearest ties away from + zero (RNDNA), but in printf the "ties" case is not specified + (for example it is RNDN for glibc, RNDNA for Windows), so we + must round manually. */ + n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST); + rounding_mode = FE_TONEAREST; + /* XXX: could use 2 digits to reduce the average running time */ + if (buf1[n1 - 1] == '5') { + n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD); + n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD); + if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) { + /* exact result: round away from zero */ + if (buf1[0] == '-') + rounding_mode = FE_DOWNWARD; + else + rounding_mode = FE_UPWARD; + } + } + } +#endif /* CONFIG_PRINTF_RNDN */ + js_fcvt1(buf, buf_size, d, n_digits, rounding_mode); +} + +/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. + XXX: radix != 10 is only supported for small integers +*/ +void js_dtoa1(char* buf, double d, int radix, int n_digits, int flags) { + char* q; + + if (!isfinite(d)) { + if (isnan(d)) { + strcpy(buf, "NaN"); + } else { + q = buf; + if (d < 0) + *q++ = '-'; + strcpy(q, "Infinity"); + } + } else if (flags == JS_DTOA_VAR_FORMAT) { + int64_t i64; + char buf1[70], *ptr; + i64 = (int64_t)d; + if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER) + goto generic_conv; + /* fast path for integers */ + ptr = i64toa(buf1 + sizeof(buf1), i64, radix); + strcpy(buf, ptr); + } else { + if (d == 0.0) + d = 0.0; /* convert -0 to 0 */ + if (flags == JS_DTOA_FRAC_FORMAT) { + js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits); + } else { + char buf1[JS_DTOA_BUF_SIZE]; + int sign, decpt, k, n, i, p, n_max; + BOOL is_fixed; + generic_conv: + is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT); + if (is_fixed) { + n_max = n_digits; + } else { + n_max = 21; + } + /* the number has k digits (k >= 1) */ + k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed); + n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ + q = buf; + if (sign) + *q++ = '-'; + if (flags & JS_DTOA_FORCE_EXP) + goto force_exp; + if (n >= 1 && n <= n_max) { + if (k <= n) { + memcpy(q, buf1, k); + q += k; + for (i = 0; i < (n - k); i++) + *q++ = '0'; + *q = '\0'; + } else { + /* k > n */ + memcpy(q, buf1, n); + q += n; + *q++ = '.'; + for (i = 0; i < (k - n); i++) + *q++ = buf1[n + i]; + *q = '\0'; + } + } else if (n >= -5 && n <= 0) { + *q++ = '0'; + *q++ = '.'; + for (i = 0; i < -n; i++) + *q++ = '0'; + memcpy(q, buf1, k); + q += k; + *q = '\0'; + } else { + force_exp: + /* exponential notation */ + *q++ = buf1[0]; + if (k > 1) { + *q++ = '.'; + for (i = 1; i < k; i++) + *q++ = buf1[i]; + } + *q++ = 'e'; + p = n - 1; + if (p >= 0) + *q++ = '+'; + sprintf(q, "%d", p); + } + } + } +} + +JSValue js_dtoa(JSContext* ctx, double d, int radix, int n_digits, int flags) { + char buf[JS_DTOA_BUF_SIZE]; + js_dtoa1(buf, d, radix, n_digits, flags); + return JS_NewString(ctx, buf); +} + +JSValue JS_ToStringInternal(JSContext* ctx, JSValueConst val, BOOL is_ToPropertyKey) { + uint32_t tag; + const char* str; + char buf[32]; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch (tag) { + case JS_TAG_STRING: + return JS_DupValue(ctx, val); + case JS_TAG_INT: + snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val)); + str = buf; + goto new_string; + case JS_TAG_BOOL: + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? JS_ATOM_true : JS_ATOM_false); + case JS_TAG_NULL: + return JS_AtomToString(ctx, JS_ATOM_null); + case JS_TAG_UNDEFINED: + return JS_AtomToString(ctx, JS_ATOM_undefined); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_OBJECT: { + JSValue val1, ret; + val1 = JS_ToPrimitive(ctx, val, HINT_STRING); + if (JS_IsException(val1)) + return val1; + ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); + JS_FreeValue(ctx, val1); + return ret; + } break; + case JS_TAG_FUNCTION_BYTECODE: + str = "[function bytecode]"; + goto new_string; + case JS_TAG_SYMBOL: + if (is_ToPropertyKey) { + return JS_DupValue(ctx, val); + } else { + return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); + } + case JS_TAG_FLOAT64: + return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, JS_DTOA_VAR_FORMAT); +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + return ctx->rt->bigint_ops.to_string(ctx, val); + case JS_TAG_BIG_FLOAT: + return ctx->rt->bigfloat_ops.to_string(ctx, val); + case JS_TAG_BIG_DECIMAL: + return ctx->rt->bigdecimal_ops.to_string(ctx, val); +#endif + default: + str = "[unsupported type]"; + new_string: + return JS_NewString(ctx, str); + } +} + +JSValue JS_ToString(JSContext* ctx, JSValueConst val) { + return JS_ToStringInternal(ctx, val, FALSE); +} + +JSValue JS_ToStringFree(JSContext* ctx, JSValue val) { + JSValue ret; + ret = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +JSValue JS_ToLocaleStringFree(JSContext* ctx, JSValue val) { + if (JS_IsUndefined(val) || JS_IsNull(val)) + return JS_ToStringFree(ctx, val); + return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); +} + +JSValue JS_ToPropertyKey(JSContext* ctx, JSValueConst val) { + return JS_ToStringInternal(ctx, val, TRUE); +} + +JSValue JS_ToStringCheckObject(JSContext* ctx, JSValueConst val) { + uint32_t tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); + return JS_ToString(ctx, val); +} + +JSValue JS_ToQuotedString(JSContext* ctx, JSValueConst val1) { + JSValue val; + JSString* p; + int i; + uint32_t c; + StringBuffer b_s, *b = &b_s; + char buf[16]; + + val = JS_ToStringCheckObject(ctx, val1); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + + if (string_buffer_init(ctx, b, p->len + 2)) + goto fail; + + if (string_buffer_putc8(b, '\"')) + goto fail; + for (i = 0; i < p->len;) { + c = string_getc(p, &i); + switch (c) { + case '\t': + c = 't'; + goto quote; + case '\r': + c = 'r'; + goto quote; + case '\n': + c = 'n'; + goto quote; + case '\b': + c = 'b'; + goto quote; + case '\f': + c = 'f'; + goto quote; + case '\"': + case '\\': + quote: + if (string_buffer_putc8(b, '\\')) + goto fail; + if (string_buffer_putc8(b, c)) + goto fail; + break; + default: + if (c < 32 || (c >= 0xd800 && c < 0xe000)) { + snprintf(buf, sizeof(buf), "\\u%04x", c); + if (string_buffer_puts8(b, buf)) + goto fail; + } else { + if (string_buffer_putc(b, c)) + goto fail; + } + break; + } + } + if (string_buffer_putc8(b, '\"')) + goto fail; + JS_FreeValue(ctx, val); + return string_buffer_end(b); +fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} diff --git a/src/core/convertion.h b/src/core/convertion.h index 5e9256eb3..617b7c2a8 100644 --- a/src/core/convertion.h +++ b/src/core/convertion.h @@ -1,168 +1,168 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_CONVERTION_H -#define QUICKJS_CONVERTION_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "types.h" - -#define HINT_STRING 0 -#define HINT_NUMBER 1 -#define HINT_NONE 2 -/* don't try Symbol.toPrimitive */ -#define HINT_FORCE_ORDINARY (1 << 4) - -#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) - -/* maximum buffer size for js_dtoa */ -#define JS_DTOA_BUF_SIZE 128 - -/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ -/* use as many digits as necessary */ -#define JS_DTOA_VAR_FORMAT (0 << 0) -/* use n_digits significant digits (1 <= n_digits <= 101) */ -#define JS_DTOA_FIXED_FORMAT (1 << 0) -/* force fractional format: [-]dd.dd with n_digits fractional digits */ -#define JS_DTOA_FRAC_FORMAT (2 << 0) -/* force exponential notation either in fixed or variable format */ -#define JS_DTOA_FORCE_EXP (1 << 2) - -typedef enum JSToNumberHintEnum { - TON_FLAG_NUMBER, - TON_FLAG_NUMERIC, -} JSToNumberHintEnum; - -int skip_spaces(const char* pc); -JSValue JS_ToPrimitiveFree(JSContext* ctx, JSValue val, int hint); -JSValue JS_ToPrimitive(JSContext* ctx, JSValueConst val, int hint); - -__exception int JS_ToArrayLengthFree(JSContext* ctx, uint32_t* plen, JSValue val, BOOL is_array_ctor); - -JSValue JS_ToNumberFree(JSContext* ctx, JSValue val); -JSValue JS_ToNumericFree(JSContext* ctx, JSValue val); -JSValue JS_ToNumeric(JSContext* ctx, JSValueConst val); -__exception int __JS_ToFloat64Free(JSContext* ctx, double* pres, JSValue val); -/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ -__maybe_unused JSValue JS_ToIntegerFree(JSContext* ctx, JSValue val); -/* Note: the integer value is satured to 32 bits */ -int JS_ToInt32SatFree(JSContext* ctx, int* pres, JSValue val); -int JS_ToInt32Sat(JSContext* ctx, int* pres, JSValueConst val); -int JS_ToInt32Clamp(JSContext* ctx, int* pres, JSValueConst val, int min, int max, int min_offset); -int JS_ToInt64SatFree(JSContext* ctx, int64_t* pres, JSValue val); -int JS_ToInt64Sat(JSContext* ctx, int64_t* pres, JSValueConst val); -int JS_ToInt64Clamp(JSContext* ctx, int64_t* pres, JSValueConst val, int64_t min, int64_t max, int64_t neg_offset); -/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) - in case of exception */ -int JS_ToInt64Free(JSContext* ctx, int64_t* pres, JSValue val); -JSValue JS_ToNumber(JSContext* ctx, JSValueConst val); - -JSValue JS_ToStringFree(JSContext* ctx, JSValue val); -int JS_ToBoolFree(JSContext* ctx, JSValue val); -int JS_ToInt32Free(JSContext* ctx, int32_t* pres, JSValue val); -static inline int JS_ToFloat64Free(JSContext* ctx, double* pres, JSValue val) { - uint32_t tag; - - tag = JS_VALUE_GET_TAG(val); - if (tag <= JS_TAG_NULL) { - *pres = JS_VALUE_GET_INT(val); - return 0; - } else if (JS_TAG_IS_FLOAT64(tag)) { - *pres = JS_VALUE_GET_FLOAT64(val); - return 0; - } else { - return __JS_ToFloat64Free(ctx, pres, val); - } -} - -static inline int JS_ToUint32Free(JSContext* ctx, uint32_t* pres, JSValue val) { - return JS_ToInt32Free(ctx, (int32_t*)pres, val); -} -int JS_ToUint8ClampFree(JSContext* ctx, int32_t* pres, JSValue val); - -static inline int to_digit(int c) { - if (c >= '0' && c <= '9') - return c - '0'; - else if (c >= 'A' && c <= 'Z') - return c - 'A' + 10; - else if (c >= 'a' && c <= 'z') - return c - 'a' + 10; - else - return 36; -} -/* XXX: remove */ -double js_strtod(const char* p, int radix, BOOL is_float); - -#ifdef CONFIG_BIGNUM -JSValue js_string_to_bigint(JSContext* ctx, const char* buf, int radix, int flags, slimb_t* pexponent); -JSValue js_string_to_bigfloat(JSContext* ctx, const char* buf, int radix, int flags, slimb_t* pexponent); -JSValue js_string_to_bigdecimal(JSContext* ctx, const char* buf, int radix, int flags, slimb_t* pexponent); -JSValue js_atof(JSContext* ctx, const char* str, const char** pp, int radix, int flags); -JSValue js_atof2(JSContext* ctx, const char* str, const char** pp, int radix, int flags, slimb_t* pexponent); -#else -JSValue js_atof(JSContext* ctx, const char* str, const char** pp, int radix, int flags); -#endif - -BOOL is_safe_integer(double d); -/* convert a value to a length between 0 and MAX_SAFE_INTEGER. - return -1 for exception */ -__exception int JS_ToLengthFree(JSContext* ctx, int64_t* plen, JSValue val); -/* Note: can return an exception */ -int JS_NumberIsInteger(JSContext* ctx, JSValueConst val); -BOOL JS_NumberIsNegativeOrMinusZero(JSContext* ctx, JSValueConst val); - -#ifdef CONFIG_BIGNUM -JSValue js_bigint_to_string1(JSContext* ctx, JSValueConst val, int radix); -JSValue js_bigint_to_string(JSContext* ctx, JSValueConst val); -JSValue js_ftoa(JSContext* ctx, JSValueConst val1, int radix, limb_t prec, bf_flags_t flags); -JSValue js_bigfloat_to_string(JSContext* ctx, JSValueConst val); -JSValue js_bigdecimal_to_string1(JSContext* ctx, JSValueConst val, limb_t prec, int flags); -JSValue js_bigdecimal_to_string(JSContext* ctx, JSValueConst val); -#endif - -/* 2 <= base <= 36 */ -char* i64toa(char* buf_end, int64_t n, unsigned int base); -/* buf1 contains the printf result */ -void js_ecvt1(double d, int n_digits, int* decpt, int* sign, char* buf, int rounding_mode, char* buf1, int buf1_size); - -/* needed because ecvt usually limits the number of digits to - 17. Return the number of digits. */ -int js_ecvt(double d, int n_digits, int* decpt, int* sign, char* buf, BOOL is_fixed); -int js_fcvt1(char* buf, int buf_size, double d, int n_digits, int rounding_mode); -void js_fcvt(char* buf, int buf_size, double d, int n_digits); - -/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. - XXX: radix != 10 is only supported for small integers -*/ -void js_dtoa1(char* buf, double d, int radix, int n_digits, int flags); -JSValue js_dtoa(JSContext* ctx, double d, int radix, int n_digits, int flags); -JSValue JS_ToStringInternal(JSContext* ctx, JSValueConst val, BOOL is_ToPropertyKey); - -JSValue JS_ToLocaleStringFree(JSContext* ctx, JSValue val); -JSValue JS_ToStringCheckObject(JSContext* ctx, JSValueConst val); -JSValue JS_ToQuotedString(JSContext* ctx, JSValueConst val1); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_CONVERTION_H +#define QUICKJS_CONVERTION_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "types.h" + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +/* don't try Symbol.toPrimitive */ +#define HINT_FORCE_ORDINARY (1 << 4) + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) + +/* maximum buffer size for js_dtoa */ +#define JS_DTOA_BUF_SIZE 128 + +/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ +/* use as many digits as necessary */ +#define JS_DTOA_VAR_FORMAT (0 << 0) +/* use n_digits significant digits (1 <= n_digits <= 101) */ +#define JS_DTOA_FIXED_FORMAT (1 << 0) +/* force fractional format: [-]dd.dd with n_digits fractional digits */ +#define JS_DTOA_FRAC_FORMAT (2 << 0) +/* force exponential notation either in fixed or variable format */ +#define JS_DTOA_FORCE_EXP (1 << 2) + +typedef enum JSToNumberHintEnum { + TON_FLAG_NUMBER, + TON_FLAG_NUMERIC, +} JSToNumberHintEnum; + +int skip_spaces(const char* pc); +JSValue JS_ToPrimitiveFree(JSContext* ctx, JSValue val, int hint); +JSValue JS_ToPrimitive(JSContext* ctx, JSValueConst val, int hint); + +__exception int JS_ToArrayLengthFree(JSContext* ctx, uint32_t* plen, JSValue val, BOOL is_array_ctor); + +JSValue JS_ToNumberFree(JSContext* ctx, JSValue val); +JSValue JS_ToNumericFree(JSContext* ctx, JSValue val); +JSValue JS_ToNumeric(JSContext* ctx, JSValueConst val); +__exception int __JS_ToFloat64Free(JSContext* ctx, double* pres, JSValue val); +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ +__maybe_unused JSValue JS_ToIntegerFree(JSContext* ctx, JSValue val); +/* Note: the integer value is satured to 32 bits */ +int JS_ToInt32SatFree(JSContext* ctx, int* pres, JSValue val); +int JS_ToInt32Sat(JSContext* ctx, int* pres, JSValueConst val); +int JS_ToInt32Clamp(JSContext* ctx, int* pres, JSValueConst val, int min, int max, int min_offset); +int JS_ToInt64SatFree(JSContext* ctx, int64_t* pres, JSValue val); +int JS_ToInt64Sat(JSContext* ctx, int64_t* pres, JSValueConst val); +int JS_ToInt64Clamp(JSContext* ctx, int64_t* pres, JSValueConst val, int64_t min, int64_t max, int64_t neg_offset); +/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) + in case of exception */ +int JS_ToInt64Free(JSContext* ctx, int64_t* pres, JSValue val); +JSValue JS_ToNumber(JSContext* ctx, JSValueConst val); + +JSValue JS_ToStringFree(JSContext* ctx, JSValue val); +int JS_ToBoolFree(JSContext* ctx, JSValue val); +int JS_ToInt32Free(JSContext* ctx, int32_t* pres, JSValue val); +static inline int JS_ToFloat64Free(JSContext* ctx, double* pres, JSValue val) { + uint32_t tag; + + tag = JS_VALUE_GET_TAG(val); + if (tag <= JS_TAG_NULL) { + *pres = JS_VALUE_GET_INT(val); + return 0; + } else if (JS_TAG_IS_FLOAT64(tag)) { + *pres = JS_VALUE_GET_FLOAT64(val); + return 0; + } else { + return __JS_ToFloat64Free(ctx, pres, val); + } +} + +static inline int JS_ToUint32Free(JSContext* ctx, uint32_t* pres, JSValue val) { + return JS_ToInt32Free(ctx, (int32_t*)pres, val); +} +int JS_ToUint8ClampFree(JSContext* ctx, int32_t* pres, JSValue val); + +static inline int to_digit(int c) { + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} +/* XXX: remove */ +double js_strtod(const char* p, int radix, BOOL is_float); + +#ifdef CONFIG_BIGNUM +JSValue js_string_to_bigint(JSContext* ctx, const char* buf, int radix, int flags, slimb_t* pexponent); +JSValue js_string_to_bigfloat(JSContext* ctx, const char* buf, int radix, int flags, slimb_t* pexponent); +JSValue js_string_to_bigdecimal(JSContext* ctx, const char* buf, int radix, int flags, slimb_t* pexponent); +JSValue js_atof(JSContext* ctx, const char* str, const char** pp, int radix, int flags); +JSValue js_atof2(JSContext* ctx, const char* str, const char** pp, int radix, int flags, slimb_t* pexponent); +#else +JSValue js_atof(JSContext* ctx, const char* str, const char** pp, int radix, int flags); +#endif + +BOOL is_safe_integer(double d); +/* convert a value to a length between 0 and MAX_SAFE_INTEGER. + return -1 for exception */ +__exception int JS_ToLengthFree(JSContext* ctx, int64_t* plen, JSValue val); +/* Note: can return an exception */ +int JS_NumberIsInteger(JSContext* ctx, JSValueConst val); +BOOL JS_NumberIsNegativeOrMinusZero(JSContext* ctx, JSValueConst val); + +#ifdef CONFIG_BIGNUM +JSValue js_bigint_to_string1(JSContext* ctx, JSValueConst val, int radix); +JSValue js_bigint_to_string(JSContext* ctx, JSValueConst val); +JSValue js_ftoa(JSContext* ctx, JSValueConst val1, int radix, limb_t prec, bf_flags_t flags); +JSValue js_bigfloat_to_string(JSContext* ctx, JSValueConst val); +JSValue js_bigdecimal_to_string1(JSContext* ctx, JSValueConst val, limb_t prec, int flags); +JSValue js_bigdecimal_to_string(JSContext* ctx, JSValueConst val); +#endif + +/* 2 <= base <= 36 */ +char* i64toa(char* buf_end, int64_t n, unsigned int base); +/* buf1 contains the printf result */ +void js_ecvt1(double d, int n_digits, int* decpt, int* sign, char* buf, int rounding_mode, char* buf1, int buf1_size); + +/* needed because ecvt usually limits the number of digits to + 17. Return the number of digits. */ +int js_ecvt(double d, int n_digits, int* decpt, int* sign, char* buf, BOOL is_fixed); +int js_fcvt1(char* buf, int buf_size, double d, int n_digits, int rounding_mode); +void js_fcvt(char* buf, int buf_size, double d, int n_digits); + +/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. + XXX: radix != 10 is only supported for small integers +*/ +void js_dtoa1(char* buf, double d, int radix, int n_digits, int flags); +JSValue js_dtoa(JSContext* ctx, double d, int radix, int n_digits, int flags); +JSValue JS_ToStringInternal(JSContext* ctx, JSValueConst val, BOOL is_ToPropertyKey); + +JSValue JS_ToLocaleStringFree(JSContext* ctx, JSValue val); +JSValue JS_ToStringCheckObject(JSContext* ctx, JSValueConst val); +JSValue JS_ToQuotedString(JSContext* ctx, JSValueConst val1); + #endif \ No newline at end of file diff --git a/src/core/exception.c b/src/core/exception.c index f25b88134..e58a38210 100644 --- a/src/core/exception.c +++ b/src/core/exception.c @@ -1,257 +1,257 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "exception.h" -#include "builtins/js-function.h" -#include "runtime.h" -#include "string.h" - -JSValue JS_NewError(JSContext* ctx) { - return JS_NewObjectClass(ctx, JS_CLASS_ERROR); -} - -JSValue JS_ThrowError2(JSContext* ctx, JSErrorEnum error_num, const char* fmt, va_list ap, BOOL add_backtrace) { - char buf[256]; - JSValue obj, ret; - - vsnprintf(buf, sizeof(buf), fmt, ap); - obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], JS_CLASS_ERROR); - if (unlikely(JS_IsException(obj))) { - /* out of memory: throw JS_NULL to avoid recursing */ - obj = JS_NULL; - } else { - JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, JS_NewString(ctx, buf), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - } - if (add_backtrace) { - build_backtrace(ctx, obj, NULL, 0, 0); - } - ret = JS_Throw(ctx, obj); - return ret; -} - -JSValue JS_ThrowError(JSContext* ctx, JSErrorEnum error_num, const char* fmt, va_list ap) { - JSRuntime* rt = ctx->rt; - JSStackFrame* sf; - BOOL add_backtrace; - - /* the backtrace is added later if called from a bytecode function */ - sf = rt->current_stack_frame; - add_backtrace = !rt->in_out_of_memory && (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); - return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext* ctx, const char* fmt, ...) { - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext* ctx, const char* fmt, ...) { - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -int __attribute__((format(printf, 3, 4))) -JS_ThrowTypeErrorOrFalse(JSContext* ctx, int flags, const char* fmt, ...) { - va_list ap; - - if ((flags & JS_PROP_THROW) || ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - va_start(ap, fmt); - JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); - va_end(ap); - return -1; - } else { - return FALSE; - } -} - -/* never use it directly */ -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 */ -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)); -} - -/* WARNING: obj is freed */ -JSValue JS_Throw(JSContext *ctx, JSValue obj) -{ - JSRuntime *rt = ctx->rt; - JS_FreeValue(ctx, rt->current_exception); - rt->current_exception = obj; - return JS_EXCEPTION; -} - -/* return the pending exception (cannot be called twice). */ -JSValue JS_GetException(JSContext *ctx) -{ - JSValue val; - JSRuntime *rt = ctx->rt; - val = rt->current_exception; - rt->current_exception = JS_NULL; - return val; -} - -JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) -{ - return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", - atom); -} - -int JS_ThrowTypeErrorReadOnly(JSContext* ctx, int flags, JSAtom atom) { - if ((flags & JS_PROP_THROW) || ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); - return -1; - } else { - return FALSE; - } -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext* ctx, const char* fmt, ...) { - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext* ctx, const char* fmt, ...) { - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext* ctx, const char* fmt, ...) { - JSValue val; - va_list ap; - - va_start(ap, fmt); - val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); - va_end(ap); - return val; -} - -JSValue JS_ThrowOutOfMemory(JSContext* ctx) { - JSRuntime* rt = ctx->rt; - if (!rt->in_out_of_memory) { - rt->in_out_of_memory = TRUE; - JS_ThrowInternalError(ctx, "out of memory"); - rt->in_out_of_memory = FALSE; - } - return JS_EXCEPTION; -} - -JSValue JS_ThrowStackOverflow(JSContext* ctx) { - return JS_ThrowInternalError(ctx, "stack overflow"); -} - -JSValue JS_ThrowTypeErrorNotAnObject(JSContext* ctx) { - return JS_ThrowTypeError(ctx, "not an object"); -} - -JSValue JS_ThrowTypeErrorNotASymbol(JSContext* ctx) { - return JS_ThrowTypeError(ctx, "not a symbol"); -} - -JSValue JS_ThrowReferenceErrorNotDefined(JSContext* ctx, JSAtom name) { - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowReferenceError(ctx, "'%s' is not defined", JS_AtomGetStr(ctx, buf, sizeof(buf), name)); -} - -JSValue JS_ThrowReferenceErrorUninitialized(JSContext* ctx, JSAtom name) { - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowReferenceError(ctx, "%s is not initialized", - name == JS_ATOM_NULL ? "lexical variable" : JS_AtomGetStr(ctx, buf, sizeof(buf), name)); -} - -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); -} - -JSValue JS_ThrowTypeErrorInvalidClass(JSContext* ctx, int class_id) { - JSRuntime* rt = ctx->rt; - JSAtom name; - name = rt->class_array[class_id].class_name; - return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); -} - -BOOL JS_IsError(JSContext* ctx, JSValueConst val) { - JSObject* p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - return (p->class_id == JS_CLASS_ERROR); -} - -/* used to avoid catching interrupt exceptions */ -BOOL JS_IsUncatchableError(JSContext* ctx, JSValueConst val) { - JSObject* p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; -} - -void JS_SetUncatchableError(JSContext* ctx, JSValueConst val, BOOL flag) { - JSObject* p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return; - p = JS_VALUE_GET_OBJ(val); - if (p->class_id == JS_CLASS_ERROR) - p->is_uncatchable_error = flag; -} - -void JS_ResetUncatchableError(JSContext* ctx) { - JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE); +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "exception.h" +#include "builtins/js-function.h" +#include "runtime.h" +#include "string.h" + +JSValue JS_NewError(JSContext* ctx) { + return JS_NewObjectClass(ctx, JS_CLASS_ERROR); +} + +JSValue JS_ThrowError2(JSContext* ctx, JSErrorEnum error_num, const char* fmt, va_list ap, BOOL add_backtrace) { + char buf[256]; + JSValue obj, ret; + + vsnprintf(buf, sizeof(buf), fmt, ap); + obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], JS_CLASS_ERROR); + if (unlikely(JS_IsException(obj))) { + /* out of memory: throw JS_NULL to avoid recursing */ + obj = JS_NULL; + } else { + JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, JS_NewString(ctx, buf), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + if (add_backtrace) { + build_backtrace(ctx, obj, NULL, 0, 0); + } + ret = JS_Throw(ctx, obj); + return ret; +} + +JSValue JS_ThrowError(JSContext* ctx, JSErrorEnum error_num, const char* fmt, va_list ap) { + JSRuntime* rt = ctx->rt; + JSStackFrame* sf; + BOOL add_backtrace; + + /* the backtrace is added later if called from a bytecode function */ + sf = rt->current_stack_frame; + add_backtrace = !rt->in_out_of_memory && (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); + return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext* ctx, const char* fmt, ...) { + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext* ctx, const char* fmt, ...) { + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +int __attribute__((format(printf, 3, 4))) +JS_ThrowTypeErrorOrFalse(JSContext* ctx, int flags, const char* fmt, ...) { + va_list ap; + + if ((flags & JS_PROP_THROW) || ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + va_start(ap, fmt); + JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); + va_end(ap); + return -1; + } else { + return FALSE; + } +} + +/* never use it directly */ +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 */ +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)); +} + +/* WARNING: obj is freed */ +JSValue JS_Throw(JSContext *ctx, JSValue obj) +{ + JSRuntime *rt = ctx->rt; + JS_FreeValue(ctx, rt->current_exception); + rt->current_exception = obj; + return JS_EXCEPTION; +} + +/* return the pending exception (cannot be called twice). */ +JSValue JS_GetException(JSContext *ctx) +{ + JSValue val; + JSRuntime *rt = ctx->rt; + val = rt->current_exception; + rt->current_exception = JS_NULL; + return val; +} + +JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) +{ + return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", + atom); +} + +int JS_ThrowTypeErrorReadOnly(JSContext* ctx, int flags, JSAtom atom) { + if ((flags & JS_PROP_THROW) || ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); + return -1; + } else { + return FALSE; + } +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext* ctx, const char* fmt, ...) { + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext* ctx, const char* fmt, ...) { + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext* ctx, const char* fmt, ...) { + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue JS_ThrowOutOfMemory(JSContext* ctx) { + JSRuntime* rt = ctx->rt; + if (!rt->in_out_of_memory) { + rt->in_out_of_memory = TRUE; + JS_ThrowInternalError(ctx, "out of memory"); + rt->in_out_of_memory = FALSE; + } + return JS_EXCEPTION; +} + +JSValue JS_ThrowStackOverflow(JSContext* ctx) { + return JS_ThrowInternalError(ctx, "stack overflow"); +} + +JSValue JS_ThrowTypeErrorNotAnObject(JSContext* ctx) { + return JS_ThrowTypeError(ctx, "not an object"); +} + +JSValue JS_ThrowTypeErrorNotASymbol(JSContext* ctx) { + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +JSValue JS_ThrowReferenceErrorNotDefined(JSContext* ctx, JSAtom name) { + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "'%s' is not defined", JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +JSValue JS_ThrowReferenceErrorUninitialized(JSContext* ctx, JSAtom name) { + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not initialized", + name == JS_ATOM_NULL ? "lexical variable" : JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +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); +} + +JSValue JS_ThrowTypeErrorInvalidClass(JSContext* ctx, int class_id) { + JSRuntime* rt = ctx->rt; + JSAtom name; + name = rt->class_array[class_id].class_name; + return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); +} + +BOOL JS_IsError(JSContext* ctx, JSValueConst val) { + JSObject* p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return (p->class_id == JS_CLASS_ERROR); +} + +/* used to avoid catching interrupt exceptions */ +BOOL JS_IsUncatchableError(JSContext* ctx, JSValueConst val) { + JSObject* p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; +} + +void JS_SetUncatchableError(JSContext* ctx, JSValueConst val, BOOL flag) { + JSObject* p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_ERROR) + p->is_uncatchable_error = flag; +} + +void JS_ResetUncatchableError(JSContext* ctx) { + JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE); } \ No newline at end of file diff --git a/src/core/exception.h b/src/core/exception.h index 8f2869508..4fb5ad01d 100644 --- a/src/core/exception.h +++ b/src/core/exception.h @@ -1,78 +1,78 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_EXCEPTION_H -#define QUICKJS_EXCEPTION_H - -#include "quickjs/cutils.h" -#include "quickjs/quickjs.h" -#include "types.h" - -#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) - -/* %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, "") - -JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext* ctx, const char* fmt, ...); -JSValue JS_ThrowError2(JSContext* ctx, JSErrorEnum error_num, const char* fmt, va_list ap, BOOL add_backtrace); -JSValue JS_ThrowError(JSContext* ctx, JSErrorEnum error_num, const char* fmt, va_list ap); - -int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext* ctx, int flags, const char* fmt, ...); -JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext* ctx, JSAtom atom, const char* fmt, ...); -JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext* ctx, JSAtom atom, const char* fmt, ...); - -JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext* ctx, JSAtom atom); - -int JS_ThrowTypeErrorReadOnly(JSContext* ctx, int flags, JSAtom atom); -JSValue JS_ThrowOutOfMemory(JSContext* ctx); -JSValue JS_ThrowTypeErrorRevokedProxy(JSContext* ctx); -JSValue JS_ThrowStackOverflow(JSContext* ctx); -JSValue JS_ThrowTypeErrorNotAnObject(JSContext* ctx); -JSValue JS_ThrowTypeErrorNotASymbol(JSContext* ctx); -JSValue JS_ThrowReferenceErrorNotDefined(JSContext* ctx, JSAtom name); -JSValue JS_ThrowReferenceErrorUninitialized(JSContext* ctx, JSAtom name); -JSValue JS_ThrowReferenceErrorUninitialized2(JSContext* ctx, JSFunctionBytecode* b, int idx, BOOL is_ref); -JSValue JS_ThrowTypeErrorInvalidClass(JSContext* ctx, int class_id); - -void JS_SetUncatchableError(JSContext* ctx, JSValueConst val, BOOL flag); - -/* used to avoid catching interrupt exceptions */ -BOOL JS_IsUncatchableError(JSContext* ctx, JSValueConst val); - -JSValue JS_Throw(JSContext* ctx, JSValue obj); -JSValue JS_GetException(JSContext* ctx); -JS_BOOL JS_IsError(JSContext* ctx, JSValueConst val); -void JS_ResetUncatchableError(JSContext* ctx); -JSValue JS_NewError(JSContext* ctx); -JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext* ctx, const char* fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext* ctx, const char* fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext* ctx, const char* fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext* ctx, const char* fmt, ...); -JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext* ctx, const char* fmt, ...); -JSValue JS_ThrowOutOfMemory(JSContext* ctx); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_EXCEPTION_H +#define QUICKJS_EXCEPTION_H + +#include "quickjs/cutils.h" +#include "quickjs/quickjs.h" +#include "types.h" + +#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) + +/* %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, "") + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext* ctx, const char* fmt, ...); +JSValue JS_ThrowError2(JSContext* ctx, JSErrorEnum error_num, const char* fmt, va_list ap, BOOL add_backtrace); +JSValue JS_ThrowError(JSContext* ctx, JSErrorEnum error_num, const char* fmt, va_list ap); + +int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext* ctx, int flags, const char* fmt, ...); +JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext* ctx, JSAtom atom, const char* fmt, ...); +JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext* ctx, JSAtom atom, const char* fmt, ...); + +JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext* ctx, JSAtom atom); + +int JS_ThrowTypeErrorReadOnly(JSContext* ctx, int flags, JSAtom atom); +JSValue JS_ThrowOutOfMemory(JSContext* ctx); +JSValue JS_ThrowTypeErrorRevokedProxy(JSContext* ctx); +JSValue JS_ThrowStackOverflow(JSContext* ctx); +JSValue JS_ThrowTypeErrorNotAnObject(JSContext* ctx); +JSValue JS_ThrowTypeErrorNotASymbol(JSContext* ctx); +JSValue JS_ThrowReferenceErrorNotDefined(JSContext* ctx, JSAtom name); +JSValue JS_ThrowReferenceErrorUninitialized(JSContext* ctx, JSAtom name); +JSValue JS_ThrowReferenceErrorUninitialized2(JSContext* ctx, JSFunctionBytecode* b, int idx, BOOL is_ref); +JSValue JS_ThrowTypeErrorInvalidClass(JSContext* ctx, int class_id); + +void JS_SetUncatchableError(JSContext* ctx, JSValueConst val, BOOL flag); + +/* used to avoid catching interrupt exceptions */ +BOOL JS_IsUncatchableError(JSContext* ctx, JSValueConst val); + +JSValue JS_Throw(JSContext* ctx, JSValue obj); +JSValue JS_GetException(JSContext* ctx); +JS_BOOL JS_IsError(JSContext* ctx, JSValueConst val); +void JS_ResetUncatchableError(JSContext* ctx); +JSValue JS_NewError(JSContext* ctx); +JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext* ctx, const char* fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext* ctx, const char* fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext* ctx, const char* fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext* ctx, const char* fmt, ...); +JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext* ctx, const char* fmt, ...); +JSValue JS_ThrowOutOfMemory(JSContext* ctx); + #endif \ No newline at end of file diff --git a/src/core/function.c b/src/core/function.c index 8cb36c123..e83547403 100644 --- a/src/core/function.c +++ b/src/core/function.c @@ -1,2879 +1,2879 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "function.h" -#include -#include "builtins/js-array.h" -#include "builtins/js-big-num.h" -#include "builtins/js-closures.h" -#include "builtins/js-function.h" -#include "builtins/js-object.h" -#include "builtins/js-operator.h" -#include "builtins/js-regexp.h" -#include "convertion.h" -#include "exception.h" -#include "gc.h" -#include "module.h" -#include "object.h" -#include "parser.h" -#include "runtime.h" -#include "string.h" - -JSValue js_call_c_function(JSContext* ctx, - JSValueConst func_obj, - JSValueConst this_obj, - int argc, - JSValueConst* argv, - int flags) { - JSRuntime* rt = ctx->rt; - JSCFunctionType func; - JSObject* p; - JSStackFrame sf_s, *sf = &sf_s, *prev_sf; - JSValue ret_val; - JSValueConst* arg_buf; - int arg_count, i; - JSCFunctionEnum cproto; - - p = JS_VALUE_GET_OBJ(func_obj); - cproto = p->u.cfunc.cproto; - arg_count = p->u.cfunc.length; - - /* better to always check stack overflow */ - if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) - return JS_ThrowStackOverflow(ctx); - - prev_sf = rt->current_stack_frame; - sf->prev_frame = prev_sf; - rt->current_stack_frame = sf; - ctx = p->u.cfunc.realm; /* change the current realm */ - -#ifdef CONFIG_BIGNUM - /* we only propagate the bignum mode as some runtime functions - test it */ - if (prev_sf) - sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; - else - sf->js_mode = 0; -#else - sf->js_mode = 0; -#endif - sf->cur_func = (JSValue)func_obj; - sf->arg_count = argc; - arg_buf = argv; - - if (unlikely(argc < arg_count)) { - /* ensure that at least argc_count arguments are readable */ - arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); - for (i = 0; i < argc; i++) - arg_buf[i] = argv[i]; - for (i = argc; i < arg_count; i++) - arg_buf[i] = JS_UNDEFINED; - sf->arg_count = arg_count; - } - sf->arg_buf = (JSValue*)arg_buf; - - func = p->u.cfunc.c_function; - switch (cproto) { - case JS_CFUNC_constructor: - case JS_CFUNC_constructor_or_func: - if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { - if (cproto == JS_CFUNC_constructor) { - not_a_constructor: - ret_val = JS_ThrowTypeError(ctx, "must be called with new"); - break; - } else { - this_obj = JS_UNDEFINED; - } - } - /* here this_obj is new_target */ - /* fall thru */ - case JS_CFUNC_generic: - ret_val = func.generic(ctx, this_obj, argc, arg_buf); - break; - case JS_CFUNC_constructor_magic: - case JS_CFUNC_constructor_or_func_magic: - if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { - if (cproto == JS_CFUNC_constructor_magic) { - goto not_a_constructor; - } else { - this_obj = JS_UNDEFINED; - } - } - /* fall thru */ - case JS_CFUNC_generic_magic: - ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, p->u.cfunc.magic); - break; - case JS_CFUNC_getter: - ret_val = func.getter(ctx, this_obj); - break; - case JS_CFUNC_setter: - ret_val = func.setter(ctx, this_obj, arg_buf[0]); - break; - case JS_CFUNC_getter_magic: - ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); - break; - case JS_CFUNC_setter_magic: - ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); - break; - case JS_CFUNC_f_f: { - double d1; - - if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { - ret_val = JS_EXCEPTION; - break; - } - ret_val = JS_NewFloat64(ctx, func.f_f(d1)); - } break; - case JS_CFUNC_f_f_f: { - double d1, d2; - - if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { - ret_val = JS_EXCEPTION; - break; - } - if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { - ret_val = JS_EXCEPTION; - break; - } - ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2)); - } break; - case JS_CFUNC_iterator_next: { - int done; - ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, &done, p->u.cfunc.magic); - if (!JS_IsException(ret_val) && done != 2) { - ret_val = js_create_iterator_result(ctx, ret_val, done); - } - } break; - default: - abort(); - } - - rt->current_stack_frame = sf->prev_frame; - return ret_val; -} - -JSValue js_call_bound_function(JSContext* ctx, - JSValueConst func_obj, - JSValueConst this_obj, - int argc, - JSValueConst* argv, - int flags) { - JSObject* p; - JSBoundFunction* bf; - JSValueConst *arg_buf, new_target; - int arg_count, i; - - p = JS_VALUE_GET_OBJ(func_obj); - bf = p->u.bound_function; - arg_count = bf->argc + argc; - if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) - return JS_ThrowStackOverflow(ctx); - arg_buf = alloca(sizeof(JSValue) * arg_count); - for (i = 0; i < bf->argc; i++) { - arg_buf[i] = bf->argv[i]; - } - for (i = 0; i < argc; i++) { - arg_buf[bf->argc + i] = argv[i]; - } - if (flags & JS_CALL_FLAG_CONSTRUCTOR) { - new_target = this_obj; - if (js_same_value(ctx, func_obj, new_target)) - new_target = bf->func_obj; - return JS_CallConstructor2(ctx, bf->func_obj, new_target, arg_count, arg_buf); - } else { - return JS_Call(ctx, bf->func_obj, bf->this_val, arg_count, arg_buf); - } -} - -/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ -JSValue JS_CallInternal(JSContext* caller_ctx, - JSValueConst func_obj, - JSValueConst this_obj, - JSValueConst new_target, - int argc, - JSValue* argv, - int flags) { - JSRuntime* rt = caller_ctx->rt; - JSContext* ctx; - JSObject* p; - JSFunctionBytecode* b; - JSStackFrame sf_s, *sf = &sf_s; - const uint8_t* pc; - int opcode, arg_allocated_size, i; - JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; - JSVarRef** var_refs; - size_t alloca_size; - -#if !DIRECT_DISPATCH -#define SWITCH(pc) switch (opcode = *pc++) -#define CASE(op) case op -#define DEFAULT default -#define BREAK break -#else - static const void* const dispatch_table[256] = { -#define DEF(id, size, n_pop, n_push, f) &&case_OP_##id, -#if SHORT_OPCODES -#define def(id, size, n_pop, n_push, f) -#else -#define def(id, size, n_pop, n_push, f) &&case_default, -#endif -#include "quickjs/quickjs-opcode.h" - [OP_COUNT... 255] = &&case_default - }; -#define SWITCH(pc) goto* dispatch_table[opcode = *pc++]; -#define CASE(op) case_##op -#define DEFAULT case_default -#define BREAK SWITCH(pc) -#endif - - if (js_poll_interrupts(caller_ctx)) - return JS_EXCEPTION; - if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { - if (flags & JS_CALL_FLAG_GENERATOR) { - JSAsyncFunctionState* s = JS_VALUE_GET_PTR(func_obj); - /* func_obj get contains a pointer to JSFuncAsyncState */ - /* the stack frame is already allocated */ - sf = &s->frame; - p = JS_VALUE_GET_OBJ(sf->cur_func); - b = p->u.func.function_bytecode; - ctx = b->realm; - var_refs = p->u.func.var_refs; - local_buf = arg_buf = sf->arg_buf; - var_buf = sf->var_buf; - stack_buf = sf->var_buf + b->var_count; - sp = sf->cur_sp; - sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ - pc = sf->cur_pc; - sf->prev_frame = rt->current_stack_frame; - rt->current_stack_frame = sf; - if (s->throw_flag) - goto exception; - else - goto restart; - } else { - goto not_a_function; - } - } - p = JS_VALUE_GET_OBJ(func_obj); - if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { - JSClassCall* call_func; - call_func = rt->class_array[p->class_id].call; - if (!call_func) { - not_a_function: - return JS_ThrowTypeError(caller_ctx, "not a function"); - } - return call_func(caller_ctx, func_obj, this_obj, argc, (JSValueConst*)argv, flags); - } - b = p->u.func.function_bytecode; - - if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { - arg_allocated_size = b->arg_count; - } else { - arg_allocated_size = 0; - } - - alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + b->stack_size); - if (js_check_stack_overflow(rt, alloca_size)) - return JS_ThrowStackOverflow(caller_ctx); - - sf->js_mode = b->js_mode; - arg_buf = argv; - sf->arg_count = argc; - sf->cur_func = (JSValue)func_obj; - init_list_head(&sf->var_ref_list); - var_refs = p->u.func.var_refs; - - local_buf = alloca(alloca_size); - if (unlikely(arg_allocated_size)) { - int n = min_int(argc, b->arg_count); - arg_buf = local_buf; - for (i = 0; i < n; i++) - arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); - for (; i < b->arg_count; i++) - arg_buf[i] = JS_UNDEFINED; - sf->arg_count = b->arg_count; - } - var_buf = local_buf + arg_allocated_size; - sf->var_buf = var_buf; - sf->arg_buf = arg_buf; - - for (i = 0; i < b->var_count; i++) - var_buf[i] = JS_UNDEFINED; - - stack_buf = var_buf + b->var_count; - sp = stack_buf; - pc = b->byte_code_buf; - sf->prev_frame = rt->current_stack_frame; - rt->current_stack_frame = sf; - ctx = b->realm; /* set the current realm */ - -restart: - for (;;) { - int call_argc; - JSValue* call_argv; - - SWITCH(pc) { - CASE(OP_push_i32) : * sp++ = JS_NewInt32(ctx, get_u32(pc)); - pc += 4; - BREAK; - CASE(OP_push_const) : * sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]); - pc += 4; - BREAK; -#if SHORT_OPCODES - CASE(OP_push_minus1) - : CASE(OP_push_0) - : CASE(OP_push_1) - : CASE(OP_push_2) - : CASE(OP_push_3) - : CASE(OP_push_4) - : CASE(OP_push_5) : CASE(OP_push_6) : CASE(OP_push_7) : * sp++ = JS_NewInt32(ctx, opcode - OP_push_0); - BREAK; - CASE(OP_push_i8) : * sp++ = JS_NewInt32(ctx, get_i8(pc)); - pc += 1; - BREAK; - CASE(OP_push_i16) : * sp++ = JS_NewInt32(ctx, get_i16(pc)); - pc += 2; - BREAK; - CASE(OP_push_const8) : * sp++ = JS_DupValue(ctx, b->cpool[*pc++]); - BREAK; - CASE(OP_fclosure8) : * sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - BREAK; - CASE(OP_push_empty_string) : * sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); - BREAK; - CASE(OP_get_length) : { - JSValue val; - - val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; -#endif - CASE(OP_push_atom_value) : * sp++ = JS_AtomToValue(ctx, get_u32(pc)); - pc += 4; - BREAK; - CASE(OP_undefined) : * sp++ = JS_UNDEFINED; - BREAK; - CASE(OP_null) : * sp++ = JS_NULL; - BREAK; - CASE(OP_push_this) - : /* OP_push_this is only called at the start of a function */ - { - JSValue val; - if (!(b->js_mode & JS_MODE_STRICT)) { - uint32_t tag = JS_VALUE_GET_TAG(this_obj); - if (likely(tag == JS_TAG_OBJECT)) - goto normal_this; - if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { - val = JS_DupValue(ctx, ctx->global_obj); - } else { - val = JS_ToObject(ctx, this_obj); - if (JS_IsException(val)) - goto exception; - } - } else { - normal_this: - val = JS_DupValue(ctx, this_obj); - } - *sp++ = val; - } - BREAK; - CASE(OP_push_false) : * sp++ = JS_FALSE; - BREAK; - CASE(OP_push_true) : * sp++ = JS_TRUE; - BREAK; - CASE(OP_object) : * sp++ = JS_NewObject(ctx); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - BREAK; - CASE(OP_special_object) : { - int arg = *pc++; - switch (arg) { - case OP_SPECIAL_OBJECT_ARGUMENTS: - *sp++ = js_build_arguments(ctx, argc, (JSValueConst*)argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: - *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst*)argv, sf, min_int(argc, b->arg_count)); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_THIS_FUNC: - *sp++ = JS_DupValue(ctx, sf->cur_func); - break; - case OP_SPECIAL_OBJECT_NEW_TARGET: - *sp++ = JS_DupValue(ctx, new_target); - break; - case OP_SPECIAL_OBJECT_HOME_OBJECT: { - JSObject* p1; - p1 = p->u.func.home_object; - if (unlikely(!p1)) - *sp++ = JS_UNDEFINED; - else - *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - } break; - case OP_SPECIAL_OBJECT_VAR_OBJECT: - *sp++ = JS_NewObjectProto(ctx, JS_NULL); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_IMPORT_META: - *sp++ = js_import_meta(ctx); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - default: - abort(); - } - } - BREAK; - CASE(OP_rest) : { - int first = get_u16(pc); - pc += 2; - *sp++ = js_build_rest(ctx, first, argc, (JSValueConst*)argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - } - BREAK; - - CASE(OP_drop) : JS_FreeValue(ctx, sp[-1]); - sp--; - BREAK; - CASE(OP_nip) : JS_FreeValue(ctx, sp[-2]); - sp[-2] = sp[-1]; - sp--; - BREAK; - CASE(OP_nip1) - : /* a b c -> b c */ - JS_FreeValue(ctx, sp[-3]); - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp--; - BREAK; - CASE(OP_dup) : sp[0] = JS_DupValue(ctx, sp[-1]); - sp++; - BREAK; - CASE(OP_dup2) - : /* a b -> a b a b */ - sp[0] = JS_DupValue(ctx, sp[-2]); - sp[1] = JS_DupValue(ctx, sp[-1]); - sp += 2; - BREAK; - CASE(OP_dup3) - : /* a b c -> a b c a b c */ - sp[0] = JS_DupValue(ctx, sp[-3]); - sp[1] = JS_DupValue(ctx, sp[-2]); - sp[2] = JS_DupValue(ctx, sp[-1]); - sp += 3; - BREAK; - CASE(OP_dup1) - : /* a b -> a a b */ - sp[0] = sp[-1]; - sp[-1] = JS_DupValue(ctx, sp[-2]); - sp++; - BREAK; - CASE(OP_insert2) - : /* obj a -> a obj a (dup_x1) */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_insert3) - : /* obj prop a -> a obj prop a (dup_x2) */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_insert4) - : /* this obj prop a -> a this obj prop a */ - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = JS_DupValue(ctx, sp[0]); - sp++; - BREAK; - CASE(OP_perm3) - : /* obj a b -> a obj b (213) */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = tmp; - } - BREAK; - CASE(OP_rot3l) - : /* x a b -> a b x (231) */ - { - JSValue tmp; - tmp = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot4l) - : /* x a b c -> a b c x */ - { - JSValue tmp; - tmp = sp[-4]; - sp[-4] = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot5l) - : /* x a b c d -> a b c d x */ - { - JSValue tmp; - tmp = sp[-5]; - sp[-5] = sp[-4]; - sp[-4] = sp[-3]; - sp[-3] = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_rot3r) - : /* a b x -> x a b (312) */ - { - JSValue tmp; - tmp = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = tmp; - } - BREAK; - CASE(OP_perm4) - : /* obj prop a b -> a obj prop b */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = tmp; - } - BREAK; - CASE(OP_perm5) - : /* this obj prop a b -> a this obj prop b */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = sp[-4]; - sp[-4] = sp[-5]; - sp[-5] = tmp; - } - BREAK; - CASE(OP_swap) - : /* a b -> b a */ - { - JSValue tmp; - tmp = sp[-2]; - sp[-2] = sp[-1]; - sp[-1] = tmp; - } - BREAK; - CASE(OP_swap2) - : /* a b c d -> c d a b */ - { - JSValue tmp1, tmp2; - tmp1 = sp[-4]; - tmp2 = sp[-3]; - sp[-4] = sp[-2]; - sp[-3] = sp[-1]; - sp[-2] = tmp1; - sp[-1] = tmp2; - } - BREAK; - - CASE(OP_fclosure) : { - JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]); - pc += 4; - *sp++ = js_closure(ctx, bfunc, var_refs, sf); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - } - BREAK; -#if SHORT_OPCODES - CASE(OP_call0) : CASE(OP_call1) : CASE(OP_call2) : CASE(OP_call3) : call_argc = opcode - OP_call0; - goto has_call_argc; -#endif - CASE(OP_call) : CASE(OP_tail_call) : { - call_argc = get_u16(pc); - pc += 2; - goto has_call_argc; - has_call_argc: - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, JS_UNDEFINED, call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - if (opcode == OP_tail_call) - goto done; - for (i = -1; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 1; - *sp++ = ret_val; - } - BREAK; - CASE(OP_call_constructor) : { - call_argc = get_u16(pc); - pc += 2; - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], call_argv[-1], call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - for (i = -2; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 2; - *sp++ = ret_val; - } - BREAK; - CASE(OP_call_method) : CASE(OP_tail_call_method) : { - call_argc = get_u16(pc); - pc += 2; - call_argv = sp - call_argc; - sf->cur_pc = pc; - ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], JS_UNDEFINED, call_argc, call_argv, 0); - if (unlikely(JS_IsException(ret_val))) - goto exception; - if (opcode == OP_tail_call_method) - goto done; - for (i = -2; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 2; - *sp++ = ret_val; - } - BREAK; - CASE(OP_array_from) : { - int i, ret; - - call_argc = get_u16(pc); - pc += 2; - ret_val = JS_NewArray(ctx); - if (unlikely(JS_IsException(ret_val))) - goto exception; - call_argv = sp - call_argc; - for (i = 0; i < call_argc; i++) { - ret = - JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], JS_PROP_C_W_E | JS_PROP_THROW); - call_argv[i] = JS_UNDEFINED; - if (ret < 0) { - JS_FreeValue(ctx, ret_val); - goto exception; - } - } - sp -= call_argc; - *sp++ = ret_val; - } - BREAK; - - CASE(OP_apply) : { - int magic; - magic = get_u16(pc); - pc += 2; - - ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst*)&sp[-2], magic); - if (unlikely(JS_IsException(ret_val))) - goto exception; - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 3; - *sp++ = ret_val; - } - BREAK; - CASE(OP_return) : ret_val = *--sp; - goto done; - CASE(OP_return_undef) : ret_val = JS_UNDEFINED; - goto done; - - CASE(OP_check_ctor_return) - : /* return TRUE if 'this' should be returned */ - if (!JS_IsObject(sp[-1])) { - if (!JS_IsUndefined(sp[-1])) { - JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); - goto exception; - } - sp[0] = JS_TRUE; - } - else { - sp[0] = JS_FALSE; - } - sp++; - BREAK; - CASE(OP_check_ctor) : if (JS_IsUndefined(new_target)) { - JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); - goto exception; - } - BREAK; - CASE(OP_check_brand) : if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0) goto exception; - BREAK; - CASE(OP_add_brand) : if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) goto exception; - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - BREAK; - - CASE(OP_throw) : JS_Throw(ctx, *--sp); - goto exception; - - CASE(OP_throw_error) - : - { - JSAtom atom; - int type; - atom = get_u32(pc); - type = pc[4]; - pc += 5; - if (type == JS_THROW_VAR_RO) - JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); - else if (type == JS_THROW_VAR_REDECL) - JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); - else if (type == JS_THROW_VAR_UNINITIALIZED) - JS_ThrowReferenceErrorUninitialized(ctx, atom); - else if (type == JS_THROW_ERROR_DELETE_SUPER) - JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); - else if (type == JS_THROW_ERROR_ITERATOR_THROW) - JS_ThrowTypeError(ctx, "iterator does not have a throw method"); - else - JS_ThrowInternalError(ctx, "invalid throw var type %d", type); - } - goto exception; - - CASE(OP_eval) : { - JSValueConst obj; - int scope_idx; - call_argc = get_u16(pc); - scope_idx = get_u16(pc + 2) - 1; - pc += 4; - call_argv = sp - call_argc; - sf->cur_pc = pc; - if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { - if (call_argc >= 1) - obj = call_argv[0]; - else - obj = JS_UNDEFINED; - ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, JS_EVAL_TYPE_DIRECT, scope_idx); - } else { - ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, JS_UNDEFINED, call_argc, call_argv, 0); - } - if (unlikely(JS_IsException(ret_val))) - goto exception; - for (i = -1; i < call_argc; i++) - JS_FreeValue(ctx, call_argv[i]); - sp -= call_argc + 1; - *sp++ = ret_val; - } - BREAK; - /* could merge with OP_apply */ - CASE(OP_apply_eval) : { - int scope_idx; - uint32_t len; - JSValue* tab; - JSValueConst obj; - - scope_idx = get_u16(pc) - 1; - pc += 2; - tab = build_arg_list(ctx, &len, sp[-1]); - if (!tab) - goto exception; - if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { - if (len >= 1) - obj = tab[0]; - else - obj = JS_UNDEFINED; - ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, JS_EVAL_TYPE_DIRECT, scope_idx); - } else { - ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, (JSValueConst*)tab); - } - free_arg_list(ctx, tab, len); - if (unlikely(JS_IsException(ret_val))) - goto exception; - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - *sp++ = ret_val; - } - BREAK; - - CASE(OP_regexp) : { - sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, sp[-2], sp[-1]); - sp--; - } - BREAK; - - CASE(OP_get_super) : { - JSValue proto; - proto = JS_GetPrototype(ctx, sp[-1]); - if (JS_IsException(proto)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = proto; - } - BREAK; - - CASE(OP_import) : { - JSValue val; - val = js_dynamic_import(ctx, sp[-1]); - if (JS_IsException(val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; - - CASE(OP_check_var) : { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_CheckGlobalVar(ctx, atom); - if (ret < 0) - goto exception; - *sp++ = JS_NewBool(ctx, ret); - } - BREAK; - - CASE(OP_get_var_undef) : CASE(OP_get_var) : { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_put_var) : CASE(OP_put_var_init) : { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); - sp--; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_var_strict) : { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - /* sp[-2] is JS_TRUE or JS_FALSE */ - if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - goto exception; - } - ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_check_define_var) : { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_CheckDefineGlobalVar(ctx, atom, flags)) - goto exception; - } - BREAK; - CASE(OP_define_var) : { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_DefineGlobalVar(ctx, atom, flags)) - goto exception; - } - BREAK; - CASE(OP_define_func) : { - JSAtom atom; - int flags; - atom = get_u32(pc); - flags = pc[4]; - pc += 5; - if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp--; - } - BREAK; - - CASE(OP_get_loc) : { - int idx; - idx = get_u16(pc); - pc += 2; - sp[0] = JS_DupValue(ctx, var_buf[idx]); - sp++; - } - BREAK; - CASE(OP_put_loc) : { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_loc) : { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); - } - BREAK; - CASE(OP_get_arg) : { - int idx; - idx = get_u16(pc); - pc += 2; - sp[0] = JS_DupValue(ctx, arg_buf[idx]); - sp++; - } - BREAK; - CASE(OP_put_arg) : { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &arg_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_arg) : { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1])); - } - BREAK; - -#if SHORT_OPCODES - CASE(OP_get_loc8) : * sp++ = JS_DupValue(ctx, var_buf[*pc++]); - BREAK; - CASE(OP_put_loc8) : set_value(ctx, &var_buf[*pc++], *--sp); - BREAK; - CASE(OP_set_loc8) : set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); - BREAK; - - CASE(OP_get_loc0) : * sp++ = JS_DupValue(ctx, var_buf[0]); - BREAK; - CASE(OP_get_loc1) : * sp++ = JS_DupValue(ctx, var_buf[1]); - BREAK; - CASE(OP_get_loc2) : * sp++ = JS_DupValue(ctx, var_buf[2]); - BREAK; - CASE(OP_get_loc3) : * sp++ = JS_DupValue(ctx, var_buf[3]); - BREAK; - CASE(OP_put_loc0) : set_value(ctx, &var_buf[0], *--sp); - BREAK; - CASE(OP_put_loc1) : set_value(ctx, &var_buf[1], *--sp); - BREAK; - CASE(OP_put_loc2) : set_value(ctx, &var_buf[2], *--sp); - BREAK; - CASE(OP_put_loc3) : set_value(ctx, &var_buf[3], *--sp); - BREAK; - CASE(OP_set_loc0) : set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_loc1) : set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_loc2) : set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_loc3) : set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_get_arg0) : * sp++ = JS_DupValue(ctx, arg_buf[0]); - BREAK; - CASE(OP_get_arg1) : * sp++ = JS_DupValue(ctx, arg_buf[1]); - BREAK; - CASE(OP_get_arg2) : * sp++ = JS_DupValue(ctx, arg_buf[2]); - BREAK; - CASE(OP_get_arg3) : * sp++ = JS_DupValue(ctx, arg_buf[3]); - BREAK; - CASE(OP_put_arg0) : set_value(ctx, &arg_buf[0], *--sp); - BREAK; - CASE(OP_put_arg1) : set_value(ctx, &arg_buf[1], *--sp); - BREAK; - CASE(OP_put_arg2) : set_value(ctx, &arg_buf[2], *--sp); - BREAK; - CASE(OP_put_arg3) : set_value(ctx, &arg_buf[3], *--sp); - BREAK; - CASE(OP_set_arg0) : set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_arg1) : set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_arg2) : set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_arg3) : set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_get_var_ref0) : * sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); - BREAK; - CASE(OP_get_var_ref1) : * sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); - BREAK; - CASE(OP_get_var_ref2) : * sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); - BREAK; - CASE(OP_get_var_ref3) : * sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); - BREAK; - CASE(OP_put_var_ref0) : set_value(ctx, var_refs[0]->pvalue, *--sp); - BREAK; - CASE(OP_put_var_ref1) : set_value(ctx, var_refs[1]->pvalue, *--sp); - BREAK; - CASE(OP_put_var_ref2) : set_value(ctx, var_refs[2]->pvalue, *--sp); - BREAK; - CASE(OP_put_var_ref3) : set_value(ctx, var_refs[3]->pvalue, *--sp); - BREAK; - CASE(OP_set_var_ref0) : set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_var_ref1) : set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_var_ref2) : set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); - BREAK; - CASE(OP_set_var_ref3) : set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); - BREAK; -#endif - - CASE(OP_get_var_ref) : { - int idx; - JSValue val; - idx = get_u16(pc); - pc += 2; - val = *var_refs[idx]->pvalue; - sp[0] = JS_DupValue(ctx, val); - sp++; - } - BREAK; - CASE(OP_put_var_ref) : { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_var_ref) : { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1])); - } - BREAK; - CASE(OP_get_var_ref_check) : { - int idx; - JSValue val; - idx = get_u16(pc); - pc += 2; - val = *var_refs[idx]->pvalue; - if (unlikely(JS_IsUninitialized(val))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - sp[0] = JS_DupValue(ctx, val); - sp++; - } - BREAK; - CASE(OP_put_var_ref_check) : { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_put_var_ref_check_init) : { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); - goto exception; - } - set_value(ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE(OP_set_loc_uninitialized) : { - int idx; - idx = get_u16(pc); - pc += 2; - set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); - } - BREAK; - CASE(OP_get_loc_check) : { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); - goto exception; - } - sp[0] = JS_DupValue(ctx, var_buf[idx]); - sp++; - } - BREAK; - CASE(OP_put_loc_check) : { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); - goto exception; - } - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_put_loc_check_init) : { - int idx; - idx = get_u16(pc); - pc += 2; - if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { - JS_ThrowReferenceError(ctx, "'this' can be initialized only once"); - goto exception; - } - set_value(ctx, &var_buf[idx], sp[-1]); - sp--; - } - BREAK; - CASE(OP_close_loc) : { - int idx; - idx = get_u16(pc); - pc += 2; - close_lexical_var(ctx, sf, idx, FALSE); - } - BREAK; - - CASE(OP_make_loc_ref) : CASE(OP_make_arg_ref) : CASE(OP_make_var_ref_ref) : { - JSVarRef* var_ref; - JSProperty* pr; - JSAtom atom; - int idx; - atom = get_u32(pc); - idx = get_u16(pc + 4); - pc += 6; - *sp++ = JS_NewObjectProto(ctx, JS_NULL); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - if (opcode == OP_make_var_ref_ref) { - var_ref = var_refs[idx]; - var_ref->header.ref_count++; - } else { - var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); - if (!var_ref) - goto exception; - } - pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, JS_PROP_WRITABLE | JS_PROP_VARREF); - if (!pr) { - free_var_ref(rt, var_ref); - goto exception; - } - pr->u.var_ref = var_ref; - *sp++ = JS_AtomToValue(ctx, atom); - } - BREAK; - CASE(OP_make_var_ref) : { - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - if (JS_GetGlobalVarRef(ctx, atom, sp)) - goto exception; - sp += 2; - } - BREAK; - - CASE(OP_goto) : pc += (int32_t)get_u32(pc); - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; -#if SHORT_OPCODES - CASE(OP_goto16) : pc += (int16_t)get_u16(pc); - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; - CASE(OP_goto8) : pc += (int8_t)pc[0]; - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - BREAK; -#endif - CASE(OP_if_true) : { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 4; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (res) { - pc += (int32_t)get_u32(pc - 4) - 4; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; - CASE(OP_if_false) : { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 4; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (!res) { - pc += (int32_t)get_u32(pc - 4) - 4; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; -#if SHORT_OPCODES - CASE(OP_if_true8) : { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 1; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (res) { - pc += (int8_t)pc[-1] - 1; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; - CASE(OP_if_false8) : { - int res; - JSValue op1; - - op1 = sp[-1]; - pc += 1; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1); - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp--; - if (!res) { - pc += (int8_t)pc[-1] - 1; - } - if (unlikely(js_poll_interrupts(ctx))) - goto exception; - } - BREAK; -#endif - CASE(OP_catch) : { - int32_t diff; - diff = get_u32(pc); - sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); - sp++; - pc += 4; - } - BREAK; - CASE(OP_gosub) : { - int32_t diff; - diff = get_u32(pc); - /* XXX: should have a different tag to avoid security flaw */ - sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf); - sp++; - pc += diff; - } - BREAK; - CASE(OP_ret) : { - JSValue op1; - uint32_t pos; - op1 = sp[-1]; - if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) - goto ret_fail; - pos = JS_VALUE_GET_INT(op1); - if (unlikely(pos >= b->byte_code_len)) { - ret_fail: - JS_ThrowInternalError(ctx, "invalid ret value"); - goto exception; - } - sp--; - pc = b->byte_code_buf + pos; - } - BREAK; - - CASE(OP_for_in_start) : if (js_for_in_start(ctx, sp)) goto exception; - BREAK; - CASE(OP_for_in_next) : if (js_for_in_next(ctx, sp)) goto exception; - sp += 2; - BREAK; - CASE(OP_for_of_start) : if (js_for_of_start(ctx, sp, FALSE)) goto exception; - sp += 1; - *sp++ = JS_NewCatchOffset(ctx, 0); - BREAK; - CASE(OP_for_of_next) : { - int offset = -3 - pc[0]; - pc += 1; - if (js_for_of_next(ctx, sp, offset)) - goto exception; - sp += 2; - } - BREAK; - CASE(OP_for_await_of_start) : if (js_for_of_start(ctx, sp, TRUE)) goto exception; - sp += 1; - *sp++ = JS_NewCatchOffset(ctx, 0); - BREAK; - CASE(OP_iterator_get_value_done) : if (js_iterator_get_value_done(ctx, sp)) goto exception; - sp += 1; - BREAK; - CASE(OP_iterator_check_object) : if (unlikely(!JS_IsObject(sp[-1]))) { - JS_ThrowTypeError(ctx, "iterator must return an object"); - goto exception; - } - BREAK; - - CASE(OP_iterator_close) - : /* iter_obj next catch_offset -> */ - sp--; /* drop the catch offset to avoid getting caught by exception */ - JS_FreeValue(ctx, sp[-1]); /* drop the next method */ - sp--; - if (!JS_IsUndefined(sp[-1])) { - if (JS_IteratorClose(ctx, sp[-1], FALSE)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - } - sp--; - BREAK; - CASE(OP_iterator_close_return) : { - JSValue ret_val; - /* iter_obj next catch_offset ... ret_val -> - ret_eval iter_obj next catch_offset */ - ret_val = *--sp; - while (sp > stack_buf && JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { - JS_FreeValue(ctx, *--sp); - } - if (unlikely(sp < stack_buf + 3)) { - JS_ThrowInternalError(ctx, "iterator_close_return"); - JS_FreeValue(ctx, ret_val); - goto exception; - } - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = ret_val; - sp++; - } - BREAK; - - CASE(OP_iterator_next) - : /* stack: iter_obj next catch_offset val */ - { - JSValue ret; - ret = JS_Call(ctx, sp[-3], sp[-4], 1, (JSValueConst*)(sp - 1)); - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret; - } - BREAK; - - CASE(OP_iterator_call) - : /* stack: iter_obj next catch_offset val */ - { - JSValue method, ret; - BOOL ret_flag; - int flags; - flags = *pc++; - method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? JS_ATOM_throw : JS_ATOM_return); - if (JS_IsException(method)) - goto exception; - if (JS_IsUndefined(method) || JS_IsNull(method)) { - ret_flag = TRUE; - } else { - if (flags & 2) { - /* no argument */ - ret = JS_CallFree(ctx, method, sp[-4], 0, NULL); - } else { - ret = JS_CallFree(ctx, method, sp[-4], 1, (JSValueConst*)(sp - 1)); - } - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret; - ret_flag = FALSE; - } - sp[0] = JS_NewBool(ctx, ret_flag); - sp += 1; - } - BREAK; - - CASE(OP_lnot) : { - int res; - JSValue op1; - - op1 = sp[-1]; - if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { - res = JS_VALUE_GET_INT(op1) != 0; - } else { - res = JS_ToBoolFree(ctx, op1); - } - sp[-1] = JS_NewBool(ctx, !res); - } - BREAK; - - CASE(OP_get_field) : { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetProperty(ctx, sp[-1], atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = val; - } - BREAK; - - CASE(OP_get_field2) : { - JSValue val; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - val = JS_GetProperty(ctx, sp[-1], atom); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_put_field) : { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-2]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_private_symbol) : { - JSAtom atom; - JSValue val; - - atom = get_u32(pc); - pc += 4; - val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); - if (JS_IsException(val)) - goto exception; - *sp++ = val; - } - BREAK; - - CASE(OP_get_private_field) : { - JSValue val; - - val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp[-2] = val; - sp--; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_put_private_field) : { - int ret; - ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-1]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_private_field) : { - int ret; - ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_field) : { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], JS_PROP_C_W_E | JS_PROP_THROW); - sp--; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_set_name) : { - int ret; - JSAtom atom; - atom = get_u32(pc); - pc += 4; - - ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - CASE(OP_set_name_computed) : { - int ret; - ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - CASE(OP_set_proto) : { - JSValue proto; - proto = sp[-1]; - if (JS_IsObject(proto) || JS_IsNull(proto)) { - if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) - goto exception; - } - JS_FreeValue(ctx, proto); - sp--; - } - BREAK; - CASE(OP_set_home_object) : js_method_set_home_object(ctx, sp[-1], sp[-2]); - BREAK; - CASE(OP_define_method) : CASE(OP_define_method_computed) : { - JSValue getter, setter, value; - JSValueConst obj; - JSAtom atom; - int flags, ret, op_flags; - BOOL is_computed; - - is_computed = (opcode == OP_define_method_computed); - if (is_computed) { - atom = JS_ValueToAtom(ctx, sp[-2]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - opcode += OP_define_method - OP_define_method_computed; - } else { - atom = get_u32(pc); - pc += 4; - } - op_flags = *pc++; - - obj = sp[-2 - is_computed]; - flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; - if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) - flags |= JS_PROP_ENUMERABLE; - op_flags &= 3; - value = JS_UNDEFINED; - getter = JS_UNDEFINED; - setter = JS_UNDEFINED; - if (op_flags == OP_DEFINE_METHOD_METHOD) { - value = sp[-1]; - flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; - } else if (op_flags == OP_DEFINE_METHOD_GETTER) { - getter = sp[-1]; - flags |= JS_PROP_HAS_GET; - } else { - setter = sp[-1]; - flags |= JS_PROP_HAS_SET; - } - ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); - if (ret >= 0) { - ret = JS_DefineProperty(ctx, obj, atom, value, getter, setter, flags); - } - JS_FreeValue(ctx, sp[-1]); - if (is_computed) { - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, sp[-2]); - } - sp -= 1 + is_computed; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_define_class) : CASE(OP_define_class_computed) : { - int class_flags; - JSAtom atom; - - atom = get_u32(pc); - class_flags = pc[4]; - pc += 5; - if (js_op_define_class(ctx, sp, atom, class_flags, var_refs, sf, (opcode == OP_define_class_computed)) < 0) - goto exception; - } - BREAK; - - CASE(OP_get_array_el) : { - JSValue val; - - val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); - JS_FreeValue(ctx, sp[-2]); - sp[-2] = val; - sp--; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_get_array_el2) : { - JSValue val; - - val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); - sp[-1] = val; - if (unlikely(JS_IsException(val))) - goto exception; - } - BREAK; - - CASE(OP_get_ref_value) : { - JSValue val; - if (unlikely(JS_IsUndefined(sp[-2]))) { - JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); - if (atom != JS_ATOM_NULL) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - } - goto exception; - } - val = JS_GetPropertyValue(ctx, sp[-2], JS_DupValue(ctx, sp[-1])); - if (unlikely(JS_IsException(val))) - goto exception; - sp[0] = val; - sp++; - } - BREAK; - - CASE(OP_get_super_value) : { - JSValue val; - JSAtom atom; - atom = JS_ValueToAtom(ctx, sp[-1]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE); - JS_FreeAtom(ctx, atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - JS_FreeValue(ctx, sp[-2]); - JS_FreeValue(ctx, sp[-3]); - sp[-3] = val; - sp -= 2; - } - BREAK; - - CASE(OP_put_array_el) : { - int ret; - - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-3]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_ref_value) : { - int ret, flags; - flags = JS_PROP_THROW_STRICT; - if (unlikely(JS_IsUndefined(sp[-3]))) { - if (is_strict_mode(ctx)) { - JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); - if (atom != JS_ATOM_NULL) { - JS_ThrowReferenceErrorNotDefined(ctx, atom); - JS_FreeAtom(ctx, atom); - } - goto exception; - } else { - sp[-3] = JS_DupValue(ctx, ctx->global_obj); - } - } else { - if (is_strict_mode(ctx)) - flags |= JS_PROP_NO_ADD; - } - ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); - JS_FreeValue(ctx, sp[-3]); - sp -= 3; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_put_super_value) : { - int ret; - JSAtom atom; - if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto exception; - } - atom = JS_ValueToAtom(ctx, sp[-2]); - if (unlikely(atom == JS_ATOM_NULL)) - goto exception; - ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4], JS_PROP_THROW_STRICT); - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, sp[-4]); - JS_FreeValue(ctx, sp[-3]); - JS_FreeValue(ctx, sp[-2]); - sp -= 4; - if (ret < 0) - goto exception; - } - BREAK; - - CASE(OP_define_array_el) : { - int ret; - ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1], JS_PROP_C_W_E | JS_PROP_THROW); - sp -= 1; - if (unlikely(ret < 0)) - goto exception; - } - BREAK; - - CASE(OP_append) - : /* array pos enumobj -- array pos */ - { - if (js_append_enumerate(ctx, sp)) - goto exception; - JS_FreeValue(ctx, *--sp); - } - BREAK; - - CASE(OP_copy_data_properties) - : /* target source excludeList */ - { - /* stack offsets (-1 based): - 2 bits for target, - 3 bits for source, - 2 bits for exclusionList */ - int mask; - - mask = *pc++; - if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], sp[-1 - ((mask >> 2) & 7)], sp[-1 - ((mask >> 5) & 7)], 0)) - goto exception; - } - BREAK; - - CASE(OP_add) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int64_t r; - r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); - if (unlikely((int)r != r)) - goto add_slow; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else { - add_slow: - if (js_add_slow(ctx, sp)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_add_loc) : { - JSValue* pv; - int idx; - idx = *pc; - pc += 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(*pv) + JS_VALUE_GET_INT(sp[-1]); - if (unlikely((int)r != r)) - goto add_loc_slow; - *pv = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { - JSValue op1; - op1 = sp[-1]; - sp--; - op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); - if (JS_IsException(op1)) - goto exception; - op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1); - if (JS_IsException(op1)) - goto exception; - set_value(ctx, pv, op1); - } else { - JSValue ops[2]; - add_loc_slow: - /* In case of exception, js_add_slow frees ops[0] - and ops[1], so we must duplicate *pv */ - ops[0] = JS_DupValue(ctx, *pv); - ops[1] = sp[-1]; - sp--; - if (js_add_slow(ctx, ops + 2)) - goto exception; - set_value(ctx, pv, ops[0]); - } - } - BREAK; - CASE(OP_sub) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int64_t r; - r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); - if (unlikely((int)r != r)) - goto binary_arith_slow; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_mul) : { - JSValue op1, op2; - double d; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int32_t v1, v2; - int64_t r; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - r = (int64_t)v1 * v2; - if (unlikely((int)r != r)) { -#ifdef CONFIG_BIGNUM - if (unlikely(sf->js_mode & JS_MODE_MATH) && (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) - goto binary_arith_slow; -#endif - d = (double)r; - goto mul_fp_res; - } - /* need to test zero case for -0 result */ - if (unlikely(r == 0 && (v1 | v2) < 0)) { - d = -0.0; - goto mul_fp_res; - } - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { -#ifdef CONFIG_BIGNUM - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto binary_arith_slow; -#endif - d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); - mul_fp_res: - sp[-2] = __JS_NewFloat64(ctx, d); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_div) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int v1, v2; - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto binary_arith_slow; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_mod) - : -#ifdef CONFIG_BIGNUM - CASE(OP_math_mod) - : -#endif - { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - int v1, v2, r; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); - /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = - -1 and the cases where the result is -0. */ - if (unlikely(v1 < 0 || v2 <= 0)) - goto binary_arith_slow; - r = v1 % v2; - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else { - goto binary_arith_slow; - } - } - BREAK; - CASE(OP_pow) : binary_arith_slow : if (js_binary_arith_slow(ctx, sp, opcode)) goto exception; - sp--; - BREAK; - - CASE(OP_plus) : { - JSValue op1; - uint32_t tag; - op1 = sp[-1]; - tag = JS_VALUE_GET_TAG(op1); - if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { - } else { - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_neg) : { - JSValue op1; - uint32_t tag; - int val; - double d; - op1 = sp[-1]; - tag = JS_VALUE_GET_TAG(op1); - if (tag == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - /* Note: -0 cannot be expressed as integer */ - if (unlikely(val == 0)) { - d = -0.0; - goto neg_fp_res; - } - if (unlikely(val == INT32_MIN)) { - d = -(double)val; - goto neg_fp_res; - } - sp[-1] = JS_NewInt32(ctx, -val); - } else if (JS_TAG_IS_FLOAT64(tag)) { - d = -JS_VALUE_GET_FLOAT64(op1); - neg_fp_res: - sp[-1] = __JS_NewFloat64(ctx, d); - } else { - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_inc) : { - JSValue op1; - int val; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MAX)) - goto inc_slow; - sp[-1] = JS_NewInt32(ctx, val + 1); - } else { - inc_slow: - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_dec) : { - JSValue op1; - int val; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MIN)) - goto dec_slow; - sp[-1] = JS_NewInt32(ctx, val - 1); - } else { - dec_slow: - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; - } - } - BREAK; - CASE(OP_post_inc) : CASE(OP_post_dec) : if (js_post_inc_slow(ctx, sp, opcode)) goto exception; - sp++; - BREAK; - CASE(OP_inc_loc) : { - JSValue op1; - int val; - int idx; - idx = *pc; - pc += 1; - - op1 = var_buf[idx]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MAX)) - goto inc_loc_slow; - var_buf[idx] = JS_NewInt32(ctx, val + 1); - } else { - inc_loc_slow: - /* must duplicate otherwise the variable value may - be destroyed before JS code accesses it */ - op1 = JS_DupValue(ctx, op1); - if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) - goto exception; - set_value(ctx, &var_buf[idx], op1); - } - } - BREAK; - CASE(OP_dec_loc) : { - JSValue op1; - int val; - int idx; - idx = *pc; - pc += 1; - - op1 = var_buf[idx]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - val = JS_VALUE_GET_INT(op1); - if (unlikely(val == INT32_MIN)) - goto dec_loc_slow; - var_buf[idx] = JS_NewInt32(ctx, val - 1); - } else { - dec_loc_slow: - /* must duplicate otherwise the variable value may - be destroyed before JS code accesses it */ - op1 = JS_DupValue(ctx, op1); - if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) - goto exception; - set_value(ctx, &var_buf[idx], op1); - } - } - BREAK; - CASE(OP_not) : { - JSValue op1; - op1 = sp[-1]; - if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { - sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); - } else { - if (js_not_slow(ctx, sp)) - goto exception; - } - } - BREAK; - - CASE(OP_shl) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v1, v2; - v1 = JS_VALUE_GET_INT(op1); - v2 = JS_VALUE_GET_INT(op2); -#ifdef CONFIG_BIGNUM - { - int64_t r; - if (unlikely(sf->js_mode & JS_MODE_MATH)) { - if (v2 > 0x1f) - goto shl_slow; - r = (int64_t)v1 << v2; - if ((int)r != r) - goto shl_slow; - } else { - v2 &= 0x1f; - } - } -#else - v2 &= 0x1f; -#endif - sp[-2] = JS_NewInt32(ctx, v1 << v2); - sp--; - } else { -#ifdef CONFIG_BIGNUM - shl_slow: -#endif - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_shr) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v2; - v2 = JS_VALUE_GET_INT(op2); - /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */ - v2 &= 0x1f; - sp[-2] = JS_NewUint32(ctx, (uint32_t)JS_VALUE_GET_INT(op1) >> v2); - sp--; - } else { - if (js_shr_slow(ctx, sp)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_sar) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - uint32_t v2; - v2 = JS_VALUE_GET_INT(op2); -#ifdef CONFIG_BIGNUM - if (unlikely(v2 > 0x1f)) { - if (unlikely(sf->js_mode & JS_MODE_MATH)) - goto sar_slow; - else - v2 &= 0x1f; - } -#else - v2 &= 0x1f; -#endif - sp[-2] = JS_NewInt32(ctx, (int)JS_VALUE_GET_INT(op1) >> v2); - sp--; - } else { -#ifdef CONFIG_BIGNUM - sar_slow: -#endif - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_and) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1) & JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_or) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1) | JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - CASE(OP_xor) : { - JSValue op1, op2; - op1 = sp[-2]; - op2 = sp[-1]; - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { - sp[-2] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1) ^ JS_VALUE_GET_INT(op2)); - sp--; - } else { - if (js_binary_logic_slow(ctx, sp, opcode)) - goto exception; - sp--; - } - } - BREAK; - -#define OP_CMP(opcode, binary_op, slow_call) \ - CASE(opcode) : { \ - JSValue op1, op2; \ - op1 = sp[-2]; \ - op2 = sp[-1]; \ - if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ - sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ - sp--; \ - } else { \ - if (slow_call) \ - goto exception; \ - sp--; \ - } \ - } \ - BREAK - - OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); - OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); - OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); - OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); - OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); - -#ifdef CONFIG_BIGNUM - CASE(OP_mul_pow10) : if (rt->bigfloat_ops.mul_pow10(ctx, sp)) goto exception; - sp--; - BREAK; -#endif - CASE(OP_in) : if (js_operator_in(ctx, sp)) goto exception; - sp--; - BREAK; - CASE(OP_instanceof) : if (js_operator_instanceof(ctx, sp)) goto exception; - sp--; - BREAK; - CASE(OP_typeof) : { - JSValue op1; - JSAtom atom; - - op1 = sp[-1]; - atom = js_operator_typeof(ctx, op1); - JS_FreeValue(ctx, op1); - sp[-1] = JS_AtomToString(ctx, atom); - } - BREAK; - CASE(OP_delete) : if (js_operator_delete(ctx, sp)) goto exception; - sp--; - BREAK; - CASE(OP_delete_var) : { - JSAtom atom; - int ret; - - atom = get_u32(pc); - pc += 4; - - ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); - if (unlikely(ret < 0)) - goto exception; - *sp++ = JS_NewBool(ctx, ret); - } - BREAK; - - CASE(OP_to_object) : if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { - ret_val = JS_ToObject(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - } - BREAK; - - CASE(OP_to_propkey) : switch (JS_VALUE_GET_TAG(sp[-1])) { - case JS_TAG_INT: - case JS_TAG_STRING: - case JS_TAG_SYMBOL: - break; - default: - ret_val = JS_ToPropertyKey(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - break; - } - BREAK; - - CASE(OP_to_propkey2) - : /* must be tested first */ - if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { - JS_ThrowTypeError(ctx, "value has no property"); - goto exception; - } - switch (JS_VALUE_GET_TAG(sp[-1])) { - case JS_TAG_INT: - case JS_TAG_STRING: - case JS_TAG_SYMBOL: - break; - default: - ret_val = JS_ToPropertyKey(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - break; - } - BREAK; -#if 0 - CASE(OP_to_string): - if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { - ret_val = JS_ToString(ctx, sp[-1]); - if (JS_IsException(ret_val)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = ret_val; - } - BREAK; -#endif - CASE(OP_with_get_var) - : CASE(OP_with_put_var) - : CASE(OP_with_delete_var) : CASE(OP_with_make_ref) : CASE(OP_with_get_ref) : CASE(OP_with_get_ref_undef) : { - JSAtom atom; - int32_t diff; - JSValue obj, val; - int ret, is_with; - atom = get_u32(pc); - diff = get_u32(pc + 4); - is_with = pc[8]; - pc += 9; - - obj = sp[-1]; - ret = JS_HasProperty(ctx, obj, atom); - if (unlikely(ret < 0)) - goto exception; - if (ret) { - if (is_with) { - ret = js_has_unscopable(ctx, obj, atom); - if (unlikely(ret < 0)) - goto exception; - if (ret) - goto no_with; - } - switch (opcode) { - case OP_with_get_var: - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - set_value(ctx, &sp[-1], val); - break; - case OP_with_put_var: - /* XXX: check if strict mode */ - ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], JS_PROP_THROW_STRICT); - JS_FreeValue(ctx, sp[-1]); - sp -= 2; - if (unlikely(ret < 0)) - goto exception; - break; - case OP_with_delete_var: - ret = JS_DeleteProperty(ctx, obj, atom, 0); - if (unlikely(ret < 0)) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_NewBool(ctx, ret); - break; - case OP_with_make_ref: - /* produce a pair object/propname on the stack */ - *sp++ = JS_AtomToValue(ctx, atom); - break; - case OP_with_get_ref: - /* produce a pair object/method on the stack */ - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - *sp++ = val; - break; - case OP_with_get_ref_undef: - /* produce a pair undefined/function on the stack */ - val = JS_GetProperty(ctx, obj, atom); - if (unlikely(JS_IsException(val))) - goto exception; - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_UNDEFINED; - *sp++ = val; - break; - } - pc += diff - 5; - } else { - no_with: - /* if not jumping, drop the object argument */ - JS_FreeValue(ctx, sp[-1]); - sp--; - } - } - BREAK; - - CASE(OP_await) : ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT); - goto done_generator; - CASE(OP_yield) : ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); - goto done_generator; - CASE(OP_yield_star) : CASE(OP_async_yield_star) : ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); - goto done_generator; - CASE(OP_return_async) : CASE(OP_initial_yield) : ret_val = JS_UNDEFINED; - goto done_generator; - - CASE(OP_nop) : BREAK; - CASE(OP_is_undefined_or_null) - : if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { - goto set_true; - } - else { - goto free_and_set_false; - } -#if SHORT_OPCODES - CASE(OP_is_undefined) : if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { - goto set_true; - } - else { - goto free_and_set_false; - } - CASE(OP_is_null) : if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { - goto set_true; - } - else { - goto free_and_set_false; - } - /* XXX: could merge to a single opcode */ - CASE(OP_typeof_is_undefined) - : /* different from OP_is_undefined because of isHTMLDDA */ - if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { - goto free_and_set_true; - } - else { - goto free_and_set_false; - } - CASE(OP_typeof_is_function) : if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { - goto free_and_set_true; - } - else { - goto free_and_set_false; - } - free_and_set_true: - JS_FreeValue(ctx, sp[-1]); -#endif - set_true: - sp[-1] = JS_TRUE; - BREAK; - free_and_set_false: - JS_FreeValue(ctx, sp[-1]); - sp[-1] = JS_FALSE; - BREAK; - CASE(OP_invalid) - : DEFAULT - : JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", (int)(pc - b->byte_code_buf - 1), opcode); - goto exception; - } - } -exception: - if (is_backtrace_needed(ctx, rt->current_exception)) { - /* add the backtrace information now (it is not done - before if the exception happens in a bytecode - operation */ - sf->cur_pc = pc; - build_backtrace(ctx, rt->current_exception, NULL, 0, 0); - } - if (!JS_IsUncatchableError(ctx, rt->current_exception)) { - while (sp > stack_buf) { - JSValue val = *--sp; - JS_FreeValue(ctx, val); - if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { - int pos = JS_VALUE_GET_INT(val); - if (pos == 0) { - /* enumerator: close it with a throw */ - JS_FreeValue(ctx, sp[-1]); /* drop the next method */ - sp--; - JS_IteratorClose(ctx, sp[-1], TRUE); - } else { - *sp++ = rt->current_exception; - rt->current_exception = JS_NULL; - pc = b->byte_code_buf + pos; - goto restart; - } - } - } - } - ret_val = JS_EXCEPTION; - /* the local variables are freed by the caller in the generator - case. Hence the label 'done' should never be reached in a - generator function. */ - if (b->func_kind != JS_FUNC_NORMAL) { - done_generator: - sf->cur_pc = pc; - sf->cur_sp = sp; - } else { - done: - if (unlikely(!list_empty(&sf->var_ref_list))) { - /* variable references reference the stack: must close them */ - close_var_refs(rt, sf); - } - /* free the local variables and stack */ - for (pval = local_buf; pval < sp; pval++) { - JS_FreeValue(ctx, *pval); - } - } - rt->current_stack_frame = sf->prev_frame; - return ret_val; -} - -JSValue JS_Call(JSContext* ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst* argv) { - return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, argc, (JSValue*)argv, JS_CALL_FLAG_COPY_ARGV); -} - -JSValue JS_CallFree(JSContext* ctx, JSValue func_obj, JSValueConst this_obj, int argc, JSValueConst* argv) { - JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, argc, (JSValue*)argv, JS_CALL_FLAG_COPY_ARGV); - JS_FreeValue(ctx, func_obj); - return res; -} - -JSValue JS_InvokeFree(JSContext* ctx, JSValue this_val, JSAtom atom, int argc, JSValueConst* argv) { - JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); - JS_FreeValue(ctx, this_val); - return res; -} - -/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ -JSValue JS_CallConstructorInternal(JSContext* ctx, - JSValueConst func_obj, - JSValueConst new_target, - int argc, - JSValue* argv, - int flags) { - JSObject* p; - JSFunctionBytecode* b; - - if (js_poll_interrupts(ctx)) - return JS_EXCEPTION; - flags |= JS_CALL_FLAG_CONSTRUCTOR; - if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) - goto not_a_function; - p = JS_VALUE_GET_OBJ(func_obj); - if (unlikely(!p->is_constructor)) - return JS_ThrowTypeError(ctx, "not a constructor"); - if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { - JSClassCall* call_func; - call_func = ctx->rt->class_array[p->class_id].call; - if (!call_func) { - not_a_function: - return JS_ThrowTypeError(ctx, "not a function"); - } - return call_func(ctx, func_obj, new_target, argc, (JSValueConst*)argv, flags); - } - - b = p->u.func.function_bytecode; - if (b->is_derived_class_constructor) { - return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); - } else { - JSValue obj, ret; - /* legacy constructor behavior */ - obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); - if (JS_IsException(obj)) - return JS_EXCEPTION; - ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); - if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || JS_IsException(ret)) { - JS_FreeValue(ctx, obj); - return ret; - } else { - JS_FreeValue(ctx, ret); - return obj; - } - } -} - -BOOL JS_IsCFunction(JSContext* ctx, JSValueConst val, JSCFunction* func, int magic) { - JSObject* p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - if (p->class_id == JS_CLASS_C_FUNCTION) - return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); - else - return FALSE; -} - -BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val) { - JSObject* p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - return p->is_constructor; -} - -JSValue JS_CallConstructor2(JSContext* ctx, - JSValueConst func_obj, - JSValueConst new_target, - int argc, - JSValueConst* argv) { - return JS_CallConstructorInternal(ctx, func_obj, new_target, argc, (JSValue*)argv, JS_CALL_FLAG_COPY_ARGV); -} - -JSValue JS_CallConstructor(JSContext* ctx, JSValueConst func_obj, int argc, JSValueConst* argv) { - return JS_CallConstructorInternal(ctx, func_obj, func_obj, argc, (JSValue*)argv, JS_CALL_FLAG_COPY_ARGV); -} - -JSValue JS_Invoke(JSContext* ctx, JSValueConst this_val, JSAtom atom, int argc, JSValueConst* argv) { - JSValue func_obj; - func_obj = JS_GetProperty(ctx, this_val, atom); - if (JS_IsException(func_obj)) - return func_obj; - return JS_CallFree(ctx, func_obj, this_val, argc, argv); -} - -/* Note: at least 'length' arguments will be readable in 'argv' */ -JSValue JS_NewCFunction3(JSContext* ctx, - JSCFunction* func, - const char* name, - int length, - JSCFunctionEnum cproto, - int magic, - JSValueConst proto_val) { - JSValue func_obj; - JSObject* p; - JSAtom name_atom; - - func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); - if (JS_IsException(func_obj)) - return func_obj; - p = JS_VALUE_GET_OBJ(func_obj); - p->u.cfunc.realm = JS_DupContext(ctx); - p->u.cfunc.c_function.generic = func; - p->u.cfunc.length = length; - p->u.cfunc.cproto = cproto; - p->u.cfunc.magic = magic; - p->is_constructor = (cproto == JS_CFUNC_constructor || cproto == JS_CFUNC_constructor_magic || - cproto == JS_CFUNC_constructor_or_func || cproto == JS_CFUNC_constructor_or_func_magic); - if (!name) - name = ""; - name_atom = JS_NewAtom(ctx, name); - js_function_set_properties(ctx, func_obj, name_atom, length); - JS_FreeAtom(ctx, name_atom); - return func_obj; -} - -/* Note: at least 'length' arguments will be readable in 'argv' */ -JSValue JS_NewCFunction2(JSContext* ctx, - JSCFunction* func, - const char* name, - int length, - JSCFunctionEnum cproto, - int magic) { - return JS_NewCFunction3(ctx, func, name, length, cproto, magic, ctx->function_proto); -} - -/* warning: the refcount of the context is not incremented. Return - NULL in case of exception (case of revoked proxy only) */ -JSContext* JS_GetFunctionRealm(JSContext* ctx, JSValueConst func_obj) { - JSObject* p; - JSContext* realm; - - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) - return ctx; - p = JS_VALUE_GET_OBJ(func_obj); - switch (p->class_id) { - case JS_CLASS_C_FUNCTION: - realm = p->u.cfunc.realm; - break; - case JS_CLASS_BYTECODE_FUNCTION: - case JS_CLASS_GENERATOR_FUNCTION: - case JS_CLASS_ASYNC_FUNCTION: - case JS_CLASS_ASYNC_GENERATOR_FUNCTION: { - JSFunctionBytecode* b; - b = p->u.func.function_bytecode; - realm = b->realm; - } break; - case JS_CLASS_PROXY: { - JSProxyData* s = p->u.opaque; - if (!s) - return ctx; - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - return NULL; - } else { - realm = JS_GetFunctionRealm(ctx, s->target); - } - } break; - case JS_CLASS_BOUND_FUNCTION: { - JSBoundFunction* bf = p->u.bound_function; - realm = JS_GetFunctionRealm(ctx, bf->func_obj); - } break; - default: - realm = ctx; - break; - } - return realm; -} - -void js_c_function_data_finalizer(JSRuntime* rt, JSValue val) { - JSCFunctionDataRecord* s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); - int i; - - if (s) { - for (i = 0; i < s->data_len; i++) { - JS_FreeValueRT(rt, s->data[i]); - } - js_free_rt(rt, s); - } -} - -void js_c_function_data_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - JSCFunctionDataRecord* s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); - int i; - - if (s) { - for (i = 0; i < s->data_len; i++) { - JS_MarkValue(rt, s->data[i], mark_func); - } - } -} - -JSValue js_c_function_data_call(JSContext* ctx, - JSValueConst func_obj, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int flags) { - JSCFunctionDataRecord* s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); - JSValueConst* arg_buf; - int i; - - /* XXX: could add the function on the stack for debug */ - if (unlikely(argc < s->length)) { - arg_buf = alloca(sizeof(arg_buf[0]) * s->length); - for (i = 0; i < argc; i++) - arg_buf[i] = argv[i]; - for (i = argc; i < s->length; i++) - arg_buf[i] = JS_UNDEFINED; - } else { - arg_buf = argv; - } - - return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); -} - -int js_op_define_class(JSContext* ctx, - JSValue* sp, - JSAtom class_name, - int class_flags, - JSVarRef** cur_var_refs, - JSStackFrame* sf, - BOOL is_computed_name) { - JSValue bfunc, parent_class, proto = JS_UNDEFINED; - JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; - JSFunctionBytecode* b; - - parent_class = sp[-2]; - bfunc = sp[-1]; - - if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { - if (JS_IsNull(parent_class)) { - parent_proto = JS_NULL; - parent_class = JS_DupValue(ctx, ctx->function_proto); - } else { - if (!JS_IsConstructor(ctx, parent_class)) { - JS_ThrowTypeError(ctx, "parent class must be constructor"); - goto fail; - } - parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); - if (JS_IsException(parent_proto)) - goto fail; - if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { - JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); - goto fail; - } - } - } else { - /* parent_class is JS_UNDEFINED in this case */ - parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); - parent_class = JS_DupValue(ctx, ctx->function_proto); - } - proto = JS_NewObjectProto(ctx, parent_proto); - if (JS_IsException(proto)) - goto fail; - - b = JS_VALUE_GET_PTR(bfunc); - assert(b->func_kind == JS_FUNC_NORMAL); - ctor = JS_NewObjectProtoClass(ctx, parent_class, JS_CLASS_BYTECODE_FUNCTION); - if (JS_IsException(ctor)) - goto fail; - ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); - bfunc = JS_UNDEFINED; - if (JS_IsException(ctor)) - goto fail; - js_method_set_home_object(ctx, ctor, proto); - JS_SetConstructorBit(ctx, ctor, TRUE); - - JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, JS_NewInt32(ctx, b->defined_arg_count), JS_PROP_CONFIGURABLE); - - if (is_computed_name) { - if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], JS_PROP_CONFIGURABLE) < 0) - goto fail; - } else { - if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) - goto fail; - } - - /* the constructor property must be first. It can be overriden by - computed property names */ - if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, JS_DupValue(ctx, ctor), - JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_THROW) < 0) - goto fail; - /* set the prototype property */ - if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, JS_DupValue(ctx, proto), JS_PROP_THROW) < 0) - goto fail; - set_cycle_flag(ctx, ctor); - set_cycle_flag(ctx, proto); - - JS_FreeValue(ctx, parent_proto); - JS_FreeValue(ctx, parent_class); - - sp[-2] = ctor; - sp[-1] = proto; - return 0; -fail: - JS_FreeValue(ctx, parent_class); - JS_FreeValue(ctx, parent_proto); - JS_FreeValue(ctx, bfunc); - JS_FreeValue(ctx, proto); - JS_FreeValue(ctx, ctor); - sp[-2] = JS_UNDEFINED; - sp[-1] = JS_UNDEFINED; - return -1; -}; +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "function.h" +#include +#include "builtins/js-array.h" +#include "builtins/js-big-num.h" +#include "builtins/js-closures.h" +#include "builtins/js-function.h" +#include "builtins/js-object.h" +#include "builtins/js-operator.h" +#include "builtins/js-regexp.h" +#include "convertion.h" +#include "exception.h" +#include "gc.h" +#include "module.h" +#include "object.h" +#include "parser.h" +#include "runtime.h" +#include "string.h" + +JSValue js_call_c_function(JSContext* ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, + JSValueConst* argv, + int flags) { + JSRuntime* rt = ctx->rt; + JSCFunctionType func; + JSObject* p; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSValue ret_val; + JSValueConst* arg_buf; + int arg_count, i; + JSCFunctionEnum cproto; + + p = JS_VALUE_GET_OBJ(func_obj); + cproto = p->u.cfunc.cproto; + arg_count = p->u.cfunc.length; + + /* better to always check stack overflow */ + if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) + return JS_ThrowStackOverflow(ctx); + + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + ctx = p->u.cfunc.realm; /* change the current realm */ + +#ifdef CONFIG_BIGNUM + /* we only propagate the bignum mode as some runtime functions + test it */ + if (prev_sf) + sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; + else + sf->js_mode = 0; +#else + sf->js_mode = 0; +#endif + sf->cur_func = (JSValue)func_obj; + sf->arg_count = argc; + arg_buf = argv; + + if (unlikely(argc < arg_count)) { + /* ensure that at least argc_count arguments are readable */ + arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); + for (i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for (i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = arg_count; + } + sf->arg_buf = (JSValue*)arg_buf; + + func = p->u.cfunc.c_function; + switch (cproto) { + case JS_CFUNC_constructor: + case JS_CFUNC_constructor_or_func: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor) { + not_a_constructor: + ret_val = JS_ThrowTypeError(ctx, "must be called with new"); + break; + } else { + this_obj = JS_UNDEFINED; + } + } + /* here this_obj is new_target */ + /* fall thru */ + case JS_CFUNC_generic: + ret_val = func.generic(ctx, this_obj, argc, arg_buf); + break; + case JS_CFUNC_constructor_magic: + case JS_CFUNC_constructor_or_func_magic: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor_magic) { + goto not_a_constructor; + } else { + this_obj = JS_UNDEFINED; + } + } + /* fall thru */ + case JS_CFUNC_generic_magic: + ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, p->u.cfunc.magic); + break; + case JS_CFUNC_getter: + ret_val = func.getter(ctx, this_obj); + break; + case JS_CFUNC_setter: + ret_val = func.setter(ctx, this_obj, arg_buf[0]); + break; + case JS_CFUNC_getter_magic: + ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); + break; + case JS_CFUNC_setter_magic: + ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); + break; + case JS_CFUNC_f_f: { + double d1; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = JS_NewFloat64(ctx, func.f_f(d1)); + } break; + case JS_CFUNC_f_f_f: { + double d1, d2; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2)); + } break; + case JS_CFUNC_iterator_next: { + int done; + ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, &done, p->u.cfunc.magic); + if (!JS_IsException(ret_val) && done != 2) { + ret_val = js_create_iterator_result(ctx, ret_val, done); + } + } break; + default: + abort(); + } + + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +JSValue js_call_bound_function(JSContext* ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, + JSValueConst* argv, + int flags) { + JSObject* p; + JSBoundFunction* bf; + JSValueConst *arg_buf, new_target; + int arg_count, i; + + p = JS_VALUE_GET_OBJ(func_obj); + bf = p->u.bound_function; + arg_count = bf->argc + argc; + if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(sizeof(JSValue) * arg_count); + for (i = 0; i < bf->argc; i++) { + arg_buf[i] = bf->argv[i]; + } + for (i = 0; i < argc; i++) { + arg_buf[bf->argc + i] = argv[i]; + } + if (flags & JS_CALL_FLAG_CONSTRUCTOR) { + new_target = this_obj; + if (js_same_value(ctx, func_obj, new_target)) + new_target = bf->func_obj; + return JS_CallConstructor2(ctx, bf->func_obj, new_target, arg_count, arg_buf); + } else { + return JS_Call(ctx, bf->func_obj, bf->this_val, arg_count, arg_buf); + } +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +JSValue JS_CallInternal(JSContext* caller_ctx, + JSValueConst func_obj, + JSValueConst this_obj, + JSValueConst new_target, + int argc, + JSValue* argv, + int flags) { + JSRuntime* rt = caller_ctx->rt; + JSContext* ctx; + JSObject* p; + JSFunctionBytecode* b; + JSStackFrame sf_s, *sf = &sf_s; + const uint8_t* pc; + int opcode, arg_allocated_size, i; + JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; + JSVarRef** var_refs; + size_t alloca_size; + +#if !DIRECT_DISPATCH +#define SWITCH(pc) switch (opcode = *pc++) +#define CASE(op) case op +#define DEFAULT default +#define BREAK break +#else + static const void* const dispatch_table[256] = { +#define DEF(id, size, n_pop, n_push, f) &&case_OP_##id, +#if SHORT_OPCODES +#define def(id, size, n_pop, n_push, f) +#else +#define def(id, size, n_pop, n_push, f) &&case_default, +#endif +#include "quickjs/quickjs-opcode.h" + [OP_COUNT... 255] = &&case_default + }; +#define SWITCH(pc) goto* dispatch_table[opcode = *pc++]; +#define CASE(op) case_##op +#define DEFAULT case_default +#define BREAK SWITCH(pc) +#endif + + if (js_poll_interrupts(caller_ctx)) + return JS_EXCEPTION; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { + if (flags & JS_CALL_FLAG_GENERATOR) { + JSAsyncFunctionState* s = JS_VALUE_GET_PTR(func_obj); + /* func_obj get contains a pointer to JSFuncAsyncState */ + /* the stack frame is already allocated */ + sf = &s->frame; + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = p->u.func.function_bytecode; + ctx = b->realm; + var_refs = p->u.func.var_refs; + local_buf = arg_buf = sf->arg_buf; + var_buf = sf->var_buf; + stack_buf = sf->var_buf + b->var_count; + sp = sf->cur_sp; + sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ + pc = sf->cur_pc; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + if (s->throw_flag) + goto exception; + else + goto restart; + } else { + goto not_a_function; + } + } + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall* call_func; + call_func = rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(caller_ctx, "not a function"); + } + return call_func(caller_ctx, func_obj, this_obj, argc, (JSValueConst*)argv, flags); + } + b = p->u.func.function_bytecode; + + if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { + arg_allocated_size = b->arg_count; + } else { + arg_allocated_size = 0; + } + + alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + b->stack_size); + if (js_check_stack_overflow(rt, alloca_size)) + return JS_ThrowStackOverflow(caller_ctx); + + sf->js_mode = b->js_mode; + arg_buf = argv; + sf->arg_count = argc; + sf->cur_func = (JSValue)func_obj; + init_list_head(&sf->var_ref_list); + var_refs = p->u.func.var_refs; + + local_buf = alloca(alloca_size); + if (unlikely(arg_allocated_size)) { + int n = min_int(argc, b->arg_count); + arg_buf = local_buf; + for (i = 0; i < n; i++) + arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); + for (; i < b->arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = b->arg_count; + } + var_buf = local_buf + arg_allocated_size; + sf->var_buf = var_buf; + sf->arg_buf = arg_buf; + + for (i = 0; i < b->var_count; i++) + var_buf[i] = JS_UNDEFINED; + + stack_buf = var_buf + b->var_count; + sp = stack_buf; + pc = b->byte_code_buf; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ctx = b->realm; /* set the current realm */ + +restart: + for (;;) { + int call_argc; + JSValue* call_argv; + + SWITCH(pc) { + CASE(OP_push_i32) : * sp++ = JS_NewInt32(ctx, get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_push_const) : * sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]); + pc += 4; + BREAK; +#if SHORT_OPCODES + CASE(OP_push_minus1) + : CASE(OP_push_0) + : CASE(OP_push_1) + : CASE(OP_push_2) + : CASE(OP_push_3) + : CASE(OP_push_4) + : CASE(OP_push_5) : CASE(OP_push_6) : CASE(OP_push_7) : * sp++ = JS_NewInt32(ctx, opcode - OP_push_0); + BREAK; + CASE(OP_push_i8) : * sp++ = JS_NewInt32(ctx, get_i8(pc)); + pc += 1; + BREAK; + CASE(OP_push_i16) : * sp++ = JS_NewInt32(ctx, get_i16(pc)); + pc += 2; + BREAK; + CASE(OP_push_const8) : * sp++ = JS_DupValue(ctx, b->cpool[*pc++]); + BREAK; + CASE(OP_fclosure8) : * sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_push_empty_string) : * sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); + BREAK; + CASE(OP_get_length) : { + JSValue val; + + val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; +#endif + CASE(OP_push_atom_value) : * sp++ = JS_AtomToValue(ctx, get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_undefined) : * sp++ = JS_UNDEFINED; + BREAK; + CASE(OP_null) : * sp++ = JS_NULL; + BREAK; + CASE(OP_push_this) + : /* OP_push_this is only called at the start of a function */ + { + JSValue val; + if (!(b->js_mode & JS_MODE_STRICT)) { + uint32_t tag = JS_VALUE_GET_TAG(this_obj); + if (likely(tag == JS_TAG_OBJECT)) + goto normal_this; + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { + val = JS_DupValue(ctx, ctx->global_obj); + } else { + val = JS_ToObject(ctx, this_obj); + if (JS_IsException(val)) + goto exception; + } + } else { + normal_this: + val = JS_DupValue(ctx, this_obj); + } + *sp++ = val; + } + BREAK; + CASE(OP_push_false) : * sp++ = JS_FALSE; + BREAK; + CASE(OP_push_true) : * sp++ = JS_TRUE; + BREAK; + CASE(OP_object) : * sp++ = JS_NewObject(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_special_object) : { + int arg = *pc++; + switch (arg) { + case OP_SPECIAL_OBJECT_ARGUMENTS: + *sp++ = js_build_arguments(ctx, argc, (JSValueConst*)argv); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: + *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst*)argv, sf, min_int(argc, b->arg_count)); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_THIS_FUNC: + *sp++ = JS_DupValue(ctx, sf->cur_func); + break; + case OP_SPECIAL_OBJECT_NEW_TARGET: + *sp++ = JS_DupValue(ctx, new_target); + break; + case OP_SPECIAL_OBJECT_HOME_OBJECT: { + JSObject* p1; + p1 = p->u.func.home_object; + if (unlikely(!p1)) + *sp++ = JS_UNDEFINED; + else + *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + } break; + case OP_SPECIAL_OBJECT_VAR_OBJECT: + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_IMPORT_META: + *sp++ = js_import_meta(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + default: + abort(); + } + } + BREAK; + CASE(OP_rest) : { + int first = get_u16(pc); + pc += 2; + *sp++ = js_build_rest(ctx, first, argc, (JSValueConst*)argv); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + + CASE(OP_drop) : JS_FreeValue(ctx, sp[-1]); + sp--; + BREAK; + CASE(OP_nip) : JS_FreeValue(ctx, sp[-2]); + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_nip1) + : /* a b c -> b c */ + JS_FreeValue(ctx, sp[-3]); + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_dup) : sp[0] = JS_DupValue(ctx, sp[-1]); + sp++; + BREAK; + CASE(OP_dup2) + : /* a b -> a b a b */ + sp[0] = JS_DupValue(ctx, sp[-2]); + sp[1] = JS_DupValue(ctx, sp[-1]); + sp += 2; + BREAK; + CASE(OP_dup3) + : /* a b c -> a b c a b c */ + sp[0] = JS_DupValue(ctx, sp[-3]); + sp[1] = JS_DupValue(ctx, sp[-2]); + sp[2] = JS_DupValue(ctx, sp[-1]); + sp += 3; + BREAK; + CASE(OP_dup1) + : /* a b -> a a b */ + sp[0] = sp[-1]; + sp[-1] = JS_DupValue(ctx, sp[-2]); + sp++; + BREAK; + CASE(OP_insert2) + : /* obj a -> a obj a (dup_x1) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = JS_DupValue(ctx, sp[0]); + sp++; + BREAK; + CASE(OP_insert3) + : /* obj prop a -> a obj prop a (dup_x2) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = JS_DupValue(ctx, sp[0]); + sp++; + BREAK; + CASE(OP_insert4) + : /* this obj prop a -> a this obj prop a */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = JS_DupValue(ctx, sp[0]); + sp++; + BREAK; + CASE(OP_perm3) + : /* obj a b -> a obj b (213) */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_rot3l) + : /* x a b -> a b x (231) */ + { + JSValue tmp; + tmp = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot4l) + : /* x a b c -> a b c x */ + { + JSValue tmp; + tmp = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot5l) + : /* x a b c d -> a b c d x */ + { + JSValue tmp; + tmp = sp[-5]; + sp[-5] = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot3r) + : /* a b x -> x a b (312) */ + { + JSValue tmp; + tmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_perm4) + : /* obj prop a b -> a obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = tmp; + } + BREAK; + CASE(OP_perm5) + : /* this obj prop a b -> a this obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = sp[-5]; + sp[-5] = tmp; + } + BREAK; + CASE(OP_swap) + : /* a b -> b a */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_swap2) + : /* a b c d -> c d a b */ + { + JSValue tmp1, tmp2; + tmp1 = sp[-4]; + tmp2 = sp[-3]; + sp[-4] = sp[-2]; + sp[-3] = sp[-1]; + sp[-2] = tmp1; + sp[-1] = tmp2; + } + BREAK; + + CASE(OP_fclosure) : { + JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]); + pc += 4; + *sp++ = js_closure(ctx, bfunc, var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; +#if SHORT_OPCODES + CASE(OP_call0) : CASE(OP_call1) : CASE(OP_call2) : CASE(OP_call3) : call_argc = opcode - OP_call0; + goto has_call_argc; +#endif + CASE(OP_call) : CASE(OP_tail_call) : { + call_argc = get_u16(pc); + pc += 2; + goto has_call_argc; + has_call_argc: + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, JS_UNDEFINED, call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call) + goto done; + for (i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_constructor) : { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], call_argv[-1], call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + for (i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_method) : CASE(OP_tail_call_method) : { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], JS_UNDEFINED, call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call_method) + goto done; + for (i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_array_from) : { + int i, ret; + + call_argc = get_u16(pc); + pc += 2; + ret_val = JS_NewArray(ctx); + if (unlikely(JS_IsException(ret_val))) + goto exception; + call_argv = sp - call_argc; + for (i = 0; i < call_argc; i++) { + ret = + JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], JS_PROP_C_W_E | JS_PROP_THROW); + call_argv[i] = JS_UNDEFINED; + if (ret < 0) { + JS_FreeValue(ctx, ret_val); + goto exception; + } + } + sp -= call_argc; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_apply) : { + int magic; + magic = get_u16(pc); + pc += 2; + + ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst*)&sp[-2], magic); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + *sp++ = ret_val; + } + BREAK; + CASE(OP_return) : ret_val = *--sp; + goto done; + CASE(OP_return_undef) : ret_val = JS_UNDEFINED; + goto done; + + CASE(OP_check_ctor_return) + : /* return TRUE if 'this' should be returned */ + if (!JS_IsObject(sp[-1])) { + if (!JS_IsUndefined(sp[-1])) { + JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); + goto exception; + } + sp[0] = JS_TRUE; + } + else { + sp[0] = JS_FALSE; + } + sp++; + BREAK; + CASE(OP_check_ctor) : if (JS_IsUndefined(new_target)) { + JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); + goto exception; + } + BREAK; + CASE(OP_check_brand) : if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0) goto exception; + BREAK; + CASE(OP_add_brand) : if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + BREAK; + + CASE(OP_throw) : JS_Throw(ctx, *--sp); + goto exception; + + CASE(OP_throw_error) + : + { + JSAtom atom; + int type; + atom = get_u32(pc); + type = pc[4]; + pc += 5; + if (type == JS_THROW_VAR_RO) + JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); + else if (type == JS_THROW_VAR_REDECL) + JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); + else if (type == JS_THROW_VAR_UNINITIALIZED) + JS_ThrowReferenceErrorUninitialized(ctx, atom); + else if (type == JS_THROW_ERROR_DELETE_SUPER) + JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); + else if (type == JS_THROW_ERROR_ITERATOR_THROW) + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + else + JS_ThrowInternalError(ctx, "invalid throw var type %d", type); + } + goto exception; + + CASE(OP_eval) : { + JSValueConst obj; + int scope_idx; + call_argc = get_u16(pc); + scope_idx = get_u16(pc + 2) - 1; + pc += 4; + call_argv = sp - call_argc; + sf->cur_pc = pc; + if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { + if (call_argc >= 1) + obj = call_argv[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, JS_UNDEFINED, call_argc, call_argv, 0); + } + if (unlikely(JS_IsException(ret_val))) + goto exception; + for (i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + /* could merge with OP_apply */ + CASE(OP_apply_eval) : { + int scope_idx; + uint32_t len; + JSValue* tab; + JSValueConst obj; + + scope_idx = get_u16(pc) - 1; + pc += 2; + tab = build_arg_list(ctx, &len, sp[-1]); + if (!tab) + goto exception; + if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { + if (len >= 1) + obj = tab[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, (JSValueConst*)tab); + } + free_arg_list(ctx, tab, len); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_regexp) : { + sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, sp[-2], sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_super) : { + JSValue proto; + proto = JS_GetPrototype(ctx, sp[-1]); + if (JS_IsException(proto)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = proto; + } + BREAK; + + CASE(OP_import) : { + JSValue val; + val = js_dynamic_import(ctx, sp[-1]); + if (JS_IsException(val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_check_var) : { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_CheckGlobalVar(ctx, atom); + if (ret < 0) + goto exception; + *sp++ = JS_NewBool(ctx, ret); + } + BREAK; + + CASE(OP_get_var_undef) : CASE(OP_get_var) : { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_var) : CASE(OP_put_var_init) : { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_var_strict) : { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + /* sp[-2] is JS_TRUE or JS_FALSE */ + if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + goto exception; + } + ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_check_define_var) : { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_CheckDefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_var) : { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_func) : { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_loc) : { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = JS_DupValue(ctx, var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc) : { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc) : { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); + } + BREAK; + CASE(OP_get_arg) : { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = JS_DupValue(ctx, arg_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_arg) : { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_arg) : { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1])); + } + BREAK; + +#if SHORT_OPCODES + CASE(OP_get_loc8) : * sp++ = JS_DupValue(ctx, var_buf[*pc++]); + BREAK; + CASE(OP_put_loc8) : set_value(ctx, &var_buf[*pc++], *--sp); + BREAK; + CASE(OP_set_loc8) : set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); + BREAK; + + CASE(OP_get_loc0) : * sp++ = JS_DupValue(ctx, var_buf[0]); + BREAK; + CASE(OP_get_loc1) : * sp++ = JS_DupValue(ctx, var_buf[1]); + BREAK; + CASE(OP_get_loc2) : * sp++ = JS_DupValue(ctx, var_buf[2]); + BREAK; + CASE(OP_get_loc3) : * sp++ = JS_DupValue(ctx, var_buf[3]); + BREAK; + CASE(OP_put_loc0) : set_value(ctx, &var_buf[0], *--sp); + BREAK; + CASE(OP_put_loc1) : set_value(ctx, &var_buf[1], *--sp); + BREAK; + CASE(OP_put_loc2) : set_value(ctx, &var_buf[2], *--sp); + BREAK; + CASE(OP_put_loc3) : set_value(ctx, &var_buf[3], *--sp); + BREAK; + CASE(OP_set_loc0) : set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_loc1) : set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_loc2) : set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_loc3) : set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_get_arg0) : * sp++ = JS_DupValue(ctx, arg_buf[0]); + BREAK; + CASE(OP_get_arg1) : * sp++ = JS_DupValue(ctx, arg_buf[1]); + BREAK; + CASE(OP_get_arg2) : * sp++ = JS_DupValue(ctx, arg_buf[2]); + BREAK; + CASE(OP_get_arg3) : * sp++ = JS_DupValue(ctx, arg_buf[3]); + BREAK; + CASE(OP_put_arg0) : set_value(ctx, &arg_buf[0], *--sp); + BREAK; + CASE(OP_put_arg1) : set_value(ctx, &arg_buf[1], *--sp); + BREAK; + CASE(OP_put_arg2) : set_value(ctx, &arg_buf[2], *--sp); + BREAK; + CASE(OP_put_arg3) : set_value(ctx, &arg_buf[3], *--sp); + BREAK; + CASE(OP_set_arg0) : set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_arg1) : set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_arg2) : set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_arg3) : set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_get_var_ref0) : * sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); + BREAK; + CASE(OP_get_var_ref1) : * sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); + BREAK; + CASE(OP_get_var_ref2) : * sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); + BREAK; + CASE(OP_get_var_ref3) : * sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); + BREAK; + CASE(OP_put_var_ref0) : set_value(ctx, var_refs[0]->pvalue, *--sp); + BREAK; + CASE(OP_put_var_ref1) : set_value(ctx, var_refs[1]->pvalue, *--sp); + BREAK; + CASE(OP_put_var_ref2) : set_value(ctx, var_refs[2]->pvalue, *--sp); + BREAK; + CASE(OP_put_var_ref3) : set_value(ctx, var_refs[3]->pvalue, *--sp); + BREAK; + CASE(OP_set_var_ref0) : set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_var_ref1) : set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_var_ref2) : set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); + BREAK; + CASE(OP_set_var_ref3) : set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); + BREAK; +#endif + + CASE(OP_get_var_ref) : { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + sp[0] = JS_DupValue(ctx, val); + sp++; + } + BREAK; + CASE(OP_put_var_ref) : { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_var_ref) : { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1])); + } + BREAK; + CASE(OP_get_var_ref_check) : { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + sp[0] = JS_DupValue(ctx, val); + sp++; + } + BREAK; + CASE(OP_put_var_ref_check) : { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_var_ref_check_init) : { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc_uninitialized) : { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); + } + BREAK; + CASE(OP_get_loc_check) : { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); + goto exception; + } + sp[0] = JS_DupValue(ctx, var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc_check) : { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_loc_check_init) : { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceError(ctx, "'this' can be initialized only once"); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_close_loc) : { + int idx; + idx = get_u16(pc); + pc += 2; + close_lexical_var(ctx, sf, idx, FALSE); + } + BREAK; + + CASE(OP_make_loc_ref) : CASE(OP_make_arg_ref) : CASE(OP_make_var_ref_ref) : { + JSVarRef* var_ref; + JSProperty* pr; + JSAtom atom; + int idx; + atom = get_u32(pc); + idx = get_u16(pc + 4); + pc += 6; + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + if (opcode == OP_make_var_ref_ref) { + var_ref = var_refs[idx]; + var_ref->header.ref_count++; + } else { + var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); + if (!var_ref) + goto exception; + } + pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, JS_PROP_WRITABLE | JS_PROP_VARREF); + if (!pr) { + free_var_ref(rt, var_ref); + goto exception; + } + pr->u.var_ref = var_ref; + *sp++ = JS_AtomToValue(ctx, atom); + } + BREAK; + CASE(OP_make_var_ref) : { + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + if (JS_GetGlobalVarRef(ctx, atom, sp)) + goto exception; + sp += 2; + } + BREAK; + + CASE(OP_goto) : pc += (int32_t)get_u32(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; +#if SHORT_OPCODES + CASE(OP_goto16) : pc += (int16_t)get_u16(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto8) : pc += (int8_t)pc[0]; + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; +#endif + CASE(OP_if_true) : { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false) : { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; +#if SHORT_OPCODES + CASE(OP_if_true8) : { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false8) : { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; +#endif + CASE(OP_catch) : { + int32_t diff; + diff = get_u32(pc); + sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); + sp++; + pc += 4; + } + BREAK; + CASE(OP_gosub) : { + int32_t diff; + diff = get_u32(pc); + /* XXX: should have a different tag to avoid security flaw */ + sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf); + sp++; + pc += diff; + } + BREAK; + CASE(OP_ret) : { + JSValue op1; + uint32_t pos; + op1 = sp[-1]; + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) + goto ret_fail; + pos = JS_VALUE_GET_INT(op1); + if (unlikely(pos >= b->byte_code_len)) { + ret_fail: + JS_ThrowInternalError(ctx, "invalid ret value"); + goto exception; + } + sp--; + pc = b->byte_code_buf + pos; + } + BREAK; + + CASE(OP_for_in_start) : if (js_for_in_start(ctx, sp)) goto exception; + BREAK; + CASE(OP_for_in_next) : if (js_for_in_next(ctx, sp)) goto exception; + sp += 2; + BREAK; + CASE(OP_for_of_start) : if (js_for_of_start(ctx, sp, FALSE)) goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_for_of_next) : { + int offset = -3 - pc[0]; + pc += 1; + if (js_for_of_next(ctx, sp, offset)) + goto exception; + sp += 2; + } + BREAK; + CASE(OP_for_await_of_start) : if (js_for_of_start(ctx, sp, TRUE)) goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_iterator_get_value_done) : if (js_iterator_get_value_done(ctx, sp)) goto exception; + sp += 1; + BREAK; + CASE(OP_iterator_check_object) : if (unlikely(!JS_IsObject(sp[-1]))) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto exception; + } + BREAK; + + CASE(OP_iterator_close) + : /* iter_obj next catch_offset -> */ + sp--; /* drop the catch offset to avoid getting caught by exception */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + if (!JS_IsUndefined(sp[-1])) { + if (JS_IteratorClose(ctx, sp[-1], FALSE)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + } + sp--; + BREAK; + CASE(OP_iterator_close_return) : { + JSValue ret_val; + /* iter_obj next catch_offset ... ret_val -> + ret_eval iter_obj next catch_offset */ + ret_val = *--sp; + while (sp > stack_buf && JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { + JS_FreeValue(ctx, *--sp); + } + if (unlikely(sp < stack_buf + 3)) { + JS_ThrowInternalError(ctx, "iterator_close_return"); + JS_FreeValue(ctx, ret_val); + goto exception; + } + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = ret_val; + sp++; + } + BREAK; + + CASE(OP_iterator_next) + : /* stack: iter_obj next catch_offset val */ + { + JSValue ret; + ret = JS_Call(ctx, sp[-3], sp[-4], 1, (JSValueConst*)(sp - 1)); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + } + BREAK; + + CASE(OP_iterator_call) + : /* stack: iter_obj next catch_offset val */ + { + JSValue method, ret; + BOOL ret_flag; + int flags; + flags = *pc++; + method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? JS_ATOM_throw : JS_ATOM_return); + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + ret_flag = TRUE; + } else { + if (flags & 2) { + /* no argument */ + ret = JS_CallFree(ctx, method, sp[-4], 0, NULL); + } else { + ret = JS_CallFree(ctx, method, sp[-4], 1, (JSValueConst*)(sp - 1)); + } + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + ret_flag = FALSE; + } + sp[0] = JS_NewBool(ctx, ret_flag); + sp += 1; + } + BREAK; + + CASE(OP_lnot) : { + int res; + JSValue op1; + + op1 = sp[-1]; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1) != 0; + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp[-1] = JS_NewBool(ctx, !res); + } + BREAK; + + CASE(OP_get_field) : { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + val = JS_GetProperty(ctx, sp[-1], atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_get_field2) : { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + val = JS_GetProperty(ctx, sp[-1], atom); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_field) : { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_private_symbol) : { + JSAtom atom; + JSValue val; + + atom = get_u32(pc); + pc += 4; + val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(val)) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_get_private_field) : { + JSValue val; + + val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_put_private_field) : { + int ret; + ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_private_field) : { + int ret; + ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_field) : { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], JS_PROP_C_W_E | JS_PROP_THROW); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_set_name) : { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_name_computed) : { + int ret; + ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_proto) : { + JSValue proto; + proto = sp[-1]; + if (JS_IsObject(proto) || JS_IsNull(proto)) { + if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) + goto exception; + } + JS_FreeValue(ctx, proto); + sp--; + } + BREAK; + CASE(OP_set_home_object) : js_method_set_home_object(ctx, sp[-1], sp[-2]); + BREAK; + CASE(OP_define_method) : CASE(OP_define_method_computed) : { + JSValue getter, setter, value; + JSValueConst obj; + JSAtom atom; + int flags, ret, op_flags; + BOOL is_computed; + + is_computed = (opcode == OP_define_method_computed); + if (is_computed) { + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + opcode += OP_define_method - OP_define_method_computed; + } else { + atom = get_u32(pc); + pc += 4; + } + op_flags = *pc++; + + obj = sp[-2 - is_computed]; + flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; + if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) + flags |= JS_PROP_ENUMERABLE; + op_flags &= 3; + value = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (op_flags == OP_DEFINE_METHOD_METHOD) { + value = sp[-1]; + flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; + } else if (op_flags == OP_DEFINE_METHOD_GETTER) { + getter = sp[-1]; + flags |= JS_PROP_HAS_GET; + } else { + setter = sp[-1]; + flags |= JS_PROP_HAS_SET; + } + ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); + if (ret >= 0) { + ret = JS_DefineProperty(ctx, obj, atom, value, getter, setter, flags); + } + JS_FreeValue(ctx, sp[-1]); + if (is_computed) { + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-2]); + } + sp -= 1 + is_computed; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_class) : CASE(OP_define_class_computed) : { + int class_flags; + JSAtom atom; + + atom = get_u32(pc); + class_flags = pc[4]; + pc += 5; + if (js_op_define_class(ctx, sp, atom, class_flags, var_refs, sf, (opcode == OP_define_class_computed)) < 0) + goto exception; + } + BREAK; + + CASE(OP_get_array_el) : { + JSValue val; + + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_array_el2) : { + JSValue val; + + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + sp[-1] = val; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_ref_value) : { + JSValue val; + if (unlikely(JS_IsUndefined(sp[-2]))) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } + val = JS_GetPropertyValue(ctx, sp[-2], JS_DupValue(ctx, sp[-1])); + if (unlikely(JS_IsException(val))) + goto exception; + sp[0] = val; + sp++; + } + BREAK; + + CASE(OP_get_super_value) : { + JSValue val; + JSAtom atom; + atom = JS_ValueToAtom(ctx, sp[-1]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE); + JS_FreeAtom(ctx, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-3]); + sp[-3] = val; + sp -= 2; + } + BREAK; + + CASE(OP_put_array_el) : { + int ret; + + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_ref_value) : { + int ret, flags; + flags = JS_PROP_THROW_STRICT; + if (unlikely(JS_IsUndefined(sp[-3]))) { + if (is_strict_mode(ctx)) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } else { + sp[-3] = JS_DupValue(ctx, ctx->global_obj); + } + } else { + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + } + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_super_value) : { + int ret; + JSAtom atom; + if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto exception; + } + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4], JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-4]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + sp -= 4; + if (ret < 0) + goto exception; + } + BREAK; + + CASE(OP_define_array_el) : { + int ret; + ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1], JS_PROP_C_W_E | JS_PROP_THROW); + sp -= 1; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_append) + : /* array pos enumobj -- array pos */ + { + if (js_append_enumerate(ctx, sp)) + goto exception; + JS_FreeValue(ctx, *--sp); + } + BREAK; + + CASE(OP_copy_data_properties) + : /* target source excludeList */ + { + /* stack offsets (-1 based): + 2 bits for target, + 3 bits for source, + 2 bits for exclusionList */ + int mask; + + mask = *pc++; + if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], sp[-1 - ((mask >> 2) & 7)], sp[-1 - ((mask >> 5) & 7)], 0)) + goto exception; + } + BREAK; + + CASE(OP_add) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto add_slow; + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + add_slow: + if (js_add_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_add_loc) : { + JSValue* pv; + int idx; + idx = *pc; + pc += 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(*pv) + JS_VALUE_GET_INT(sp[-1]); + if (unlikely((int)r != r)) + goto add_loc_slow; + *pv = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { + JSValue op1; + op1 = sp[-1]; + sp--; + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) + goto exception; + op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1); + if (JS_IsException(op1)) + goto exception; + set_value(ctx, pv, op1); + } else { + JSValue ops[2]; + add_loc_slow: + /* In case of exception, js_add_slow frees ops[0] + and ops[1], so we must duplicate *pv */ + ops[0] = JS_DupValue(ctx, *pv); + ops[1] = sp[-1]; + sp--; + if (js_add_slow(ctx, ops + 2)) + goto exception; + set_value(ctx, pv, ops[0]); + } + } + BREAK; + CASE(OP_sub) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto binary_arith_slow; + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mul) : { + JSValue op1, op2; + double d; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int32_t v1, v2; + int64_t r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + r = (int64_t)v1 * v2; + if (unlikely((int)r != r)) { +#ifdef CONFIG_BIGNUM + if (unlikely(sf->js_mode & JS_MODE_MATH) && (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) + goto binary_arith_slow; +#endif + d = (double)r; + goto mul_fp_res; + } + /* need to test zero case for -0 result */ + if (unlikely(r == 0 && (v1 | v2) < 0)) { + d = -0.0; + goto mul_fp_res; + } + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { +#ifdef CONFIG_BIGNUM + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; +#endif + d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); + mul_fp_res: + sp[-2] = __JS_NewFloat64(ctx, d); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_div) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto binary_arith_slow; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mod) + : +#ifdef CONFIG_BIGNUM + CASE(OP_math_mod) + : +#endif + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2, r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = + -1 and the cases where the result is -0. */ + if (unlikely(v1 < 0 || v2 <= 0)) + goto binary_arith_slow; + r = v1 % v2; + sp[-2] = JS_NewInt32(ctx, r); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_pow) : binary_arith_slow : if (js_binary_arith_slow(ctx, sp, opcode)) goto exception; + sp--; + BREAK; + + CASE(OP_plus) : { + JSValue op1; + uint32_t tag; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { + } else { + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_neg) : { + JSValue op1; + uint32_t tag; + int val; + double d; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + /* Note: -0 cannot be expressed as integer */ + if (unlikely(val == 0)) { + d = -0.0; + goto neg_fp_res; + } + if (unlikely(val == INT32_MIN)) { + d = -(double)val; + goto neg_fp_res; + } + sp[-1] = JS_NewInt32(ctx, -val); + } else if (JS_TAG_IS_FLOAT64(tag)) { + d = -JS_VALUE_GET_FLOAT64(op1); + neg_fp_res: + sp[-1] = __JS_NewFloat64(ctx, d); + } else { + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_inc) : { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_slow; + sp[-1] = JS_NewInt32(ctx, val + 1); + } else { + inc_slow: + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_dec) : { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_slow; + sp[-1] = JS_NewInt32(ctx, val - 1); + } else { + dec_slow: + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_post_inc) : CASE(OP_post_dec) : if (js_post_inc_slow(ctx, sp, opcode)) goto exception; + sp++; + BREAK; + CASE(OP_inc_loc) : { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_loc_slow; + var_buf[idx] = JS_NewInt32(ctx, val + 1); + } else { + inc_loc_slow: + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = JS_DupValue(ctx, op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_dec_loc) : { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_loc_slow; + var_buf[idx] = JS_NewInt32(ctx, val - 1); + } else { + dec_loc_slow: + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = JS_DupValue(ctx, op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_not) : { + JSValue op1; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); + } else { + if (js_not_slow(ctx, sp)) + goto exception; + } + } + BREAK; + + CASE(OP_shl) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); +#ifdef CONFIG_BIGNUM + { + int64_t r; + if (unlikely(sf->js_mode & JS_MODE_MATH)) { + if (v2 > 0x1f) + goto shl_slow; + r = (int64_t)v1 << v2; + if ((int)r != r) + goto shl_slow; + } else { + v2 &= 0x1f; + } + } +#else + v2 &= 0x1f; +#endif + sp[-2] = JS_NewInt32(ctx, v1 << v2); + sp--; + } else { +#ifdef CONFIG_BIGNUM + shl_slow: +#endif + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_shr) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */ + v2 &= 0x1f; + sp[-2] = JS_NewUint32(ctx, (uint32_t)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + if (js_shr_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_sar) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); +#ifdef CONFIG_BIGNUM + if (unlikely(v2 > 0x1f)) { + if (unlikely(sf->js_mode & JS_MODE_MATH)) + goto sar_slow; + else + v2 &= 0x1f; + } +#else + v2 &= 0x1f; +#endif + sp[-2] = JS_NewInt32(ctx, (int)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { +#ifdef CONFIG_BIGNUM + sar_slow: +#endif + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_and) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1) & JS_VALUE_GET_INT(op2)); + sp--; + } else { + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_or) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1) | JS_VALUE_GET_INT(op2)); + sp--; + } else { + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_xor) : { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1) ^ JS_VALUE_GET_INT(op2)); + sp--; + } else { + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + +#define OP_CMP(opcode, binary_op, slow_call) \ + CASE(opcode) : { \ + JSValue op1, op2; \ + op1 = sp[-2]; \ + op2 = sp[-1]; \ + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ + sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ + sp--; \ + } else { \ + if (slow_call) \ + goto exception; \ + sp--; \ + } \ + } \ + BREAK + + OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); + OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); + OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); + OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); + +#ifdef CONFIG_BIGNUM + CASE(OP_mul_pow10) : if (rt->bigfloat_ops.mul_pow10(ctx, sp)) goto exception; + sp--; + BREAK; +#endif + CASE(OP_in) : if (js_operator_in(ctx, sp)) goto exception; + sp--; + BREAK; + CASE(OP_instanceof) : if (js_operator_instanceof(ctx, sp)) goto exception; + sp--; + BREAK; + CASE(OP_typeof) : { + JSValue op1; + JSAtom atom; + + op1 = sp[-1]; + atom = js_operator_typeof(ctx, op1); + JS_FreeValue(ctx, op1); + sp[-1] = JS_AtomToString(ctx, atom); + } + BREAK; + CASE(OP_delete) : if (js_operator_delete(ctx, sp)) goto exception; + sp--; + BREAK; + CASE(OP_delete_var) : { + JSAtom atom; + int ret; + + atom = get_u32(pc); + pc += 4; + + ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + *sp++ = JS_NewBool(ctx, ret); + } + BREAK; + + CASE(OP_to_object) : if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { + ret_val = JS_ToObject(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_to_propkey) : switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + + CASE(OP_to_propkey2) + : /* must be tested first */ + if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { + JS_ThrowTypeError(ctx, "value has no property"); + goto exception; + } + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; +#if 0 + CASE(OP_to_string): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { + ret_val = JS_ToString(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; +#endif + CASE(OP_with_get_var) + : CASE(OP_with_put_var) + : CASE(OP_with_delete_var) : CASE(OP_with_make_ref) : CASE(OP_with_get_ref) : CASE(OP_with_get_ref_undef) : { + JSAtom atom; + int32_t diff; + JSValue obj, val; + int ret, is_with; + atom = get_u32(pc); + diff = get_u32(pc + 4); + is_with = pc[8]; + pc += 9; + + obj = sp[-1]; + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) { + if (is_with) { + ret = js_has_unscopable(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) + goto no_with; + } + switch (opcode) { + case OP_with_get_var: + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + set_value(ctx, &sp[-1], val); + break; + case OP_with_put_var: + /* XXX: check if strict mode */ + ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + break; + case OP_with_delete_var: + ret = JS_DeleteProperty(ctx, obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_NewBool(ctx, ret); + break; + case OP_with_make_ref: + /* produce a pair object/propname on the stack */ + *sp++ = JS_AtomToValue(ctx, atom); + break; + case OP_with_get_ref: + /* produce a pair object/method on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + break; + case OP_with_get_ref_undef: + /* produce a pair undefined/function on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_UNDEFINED; + *sp++ = val; + break; + } + pc += diff - 5; + } else { + no_with: + /* if not jumping, drop the object argument */ + JS_FreeValue(ctx, sp[-1]); + sp--; + } + } + BREAK; + + CASE(OP_await) : ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT); + goto done_generator; + CASE(OP_yield) : ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); + goto done_generator; + CASE(OP_yield_star) : CASE(OP_async_yield_star) : ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); + goto done_generator; + CASE(OP_return_async) : CASE(OP_initial_yield) : ret_val = JS_UNDEFINED; + goto done_generator; + + CASE(OP_nop) : BREAK; + CASE(OP_is_undefined_or_null) + : if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } + else { + goto free_and_set_false; + } +#if SHORT_OPCODES + CASE(OP_is_undefined) : if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { + goto set_true; + } + else { + goto free_and_set_false; + } + CASE(OP_is_null) : if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } + else { + goto free_and_set_false; + } + /* XXX: could merge to a single opcode */ + CASE(OP_typeof_is_undefined) + : /* different from OP_is_undefined because of isHTMLDDA */ + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { + goto free_and_set_true; + } + else { + goto free_and_set_false; + } + CASE(OP_typeof_is_function) : if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { + goto free_and_set_true; + } + else { + goto free_and_set_false; + } + free_and_set_true: + JS_FreeValue(ctx, sp[-1]); +#endif + set_true: + sp[-1] = JS_TRUE; + BREAK; + free_and_set_false: + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_FALSE; + BREAK; + CASE(OP_invalid) + : DEFAULT + : JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", (int)(pc - b->byte_code_buf - 1), opcode); + goto exception; + } + } +exception: + if (is_backtrace_needed(ctx, rt->current_exception)) { + /* add the backtrace information now (it is not done + before if the exception happens in a bytecode + operation */ + sf->cur_pc = pc; + build_backtrace(ctx, rt->current_exception, NULL, 0, 0); + } + if (!JS_IsUncatchableError(ctx, rt->current_exception)) { + while (sp > stack_buf) { + JSValue val = *--sp; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { + int pos = JS_VALUE_GET_INT(val); + if (pos == 0) { + /* enumerator: close it with a throw */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + JS_IteratorClose(ctx, sp[-1], TRUE); + } else { + *sp++ = rt->current_exception; + rt->current_exception = JS_NULL; + pc = b->byte_code_buf + pos; + goto restart; + } + } + } + } + ret_val = JS_EXCEPTION; + /* the local variables are freed by the caller in the generator + case. Hence the label 'done' should never be reached in a + generator function. */ + if (b->func_kind != JS_FUNC_NORMAL) { + done_generator: + sf->cur_pc = pc; + sf->cur_sp = sp; + } else { + done: + if (unlikely(!list_empty(&sf->var_ref_list))) { + /* variable references reference the stack: must close them */ + close_var_refs(rt, sf); + } + /* free the local variables and stack */ + for (pval = local_buf; pval < sp; pval++) { + JS_FreeValue(ctx, *pval); + } + } + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +JSValue JS_Call(JSContext* ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst* argv) { + return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, argc, (JSValue*)argv, JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallFree(JSContext* ctx, JSValue func_obj, JSValueConst this_obj, int argc, JSValueConst* argv) { + JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, argc, (JSValue*)argv, JS_CALL_FLAG_COPY_ARGV); + JS_FreeValue(ctx, func_obj); + return res; +} + +JSValue JS_InvokeFree(JSContext* ctx, JSValue this_val, JSAtom atom, int argc, JSValueConst* argv) { + JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); + JS_FreeValue(ctx, this_val); + return res; +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +JSValue JS_CallConstructorInternal(JSContext* ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, + JSValue* argv, + int flags) { + JSObject* p; + JSFunctionBytecode* b; + + if (js_poll_interrupts(ctx)) + return JS_EXCEPTION; + flags |= JS_CALL_FLAG_CONSTRUCTOR; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) + goto not_a_function; + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(!p->is_constructor)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall* call_func; + call_func = ctx->rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(ctx, "not a function"); + } + return call_func(ctx, func_obj, new_target, argc, (JSValueConst*)argv, flags); + } + + b = p->u.func.function_bytecode; + if (b->is_derived_class_constructor) { + return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); + } else { + JSValue obj, ret; + /* legacy constructor behavior */ + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); + if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || JS_IsException(ret)) { + JS_FreeValue(ctx, obj); + return ret; + } else { + JS_FreeValue(ctx, ret); + return obj; + } + } +} + +BOOL JS_IsCFunction(JSContext* ctx, JSValueConst val, JSCFunction* func, int magic) { + JSObject* p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_C_FUNCTION) + return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); + else + return FALSE; +} + +BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val) { + JSObject* p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->is_constructor; +} + +JSValue JS_CallConstructor2(JSContext* ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, + JSValueConst* argv) { + return JS_CallConstructorInternal(ctx, func_obj, new_target, argc, (JSValue*)argv, JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallConstructor(JSContext* ctx, JSValueConst func_obj, int argc, JSValueConst* argv) { + return JS_CallConstructorInternal(ctx, func_obj, func_obj, argc, (JSValue*)argv, JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_Invoke(JSContext* ctx, JSValueConst this_val, JSAtom atom, int argc, JSValueConst* argv) { + JSValue func_obj; + func_obj = JS_GetProperty(ctx, this_val, atom); + if (JS_IsException(func_obj)) + return func_obj; + return JS_CallFree(ctx, func_obj, this_val, argc, argv); +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +JSValue JS_NewCFunction3(JSContext* ctx, + JSCFunction* func, + const char* name, + int length, + JSCFunctionEnum cproto, + int magic, + JSValueConst proto_val) { + JSValue func_obj; + JSObject* p; + JSAtom name_atom; + + func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); + if (JS_IsException(func_obj)) + return func_obj; + p = JS_VALUE_GET_OBJ(func_obj); + p->u.cfunc.realm = JS_DupContext(ctx); + p->u.cfunc.c_function.generic = func; + p->u.cfunc.length = length; + p->u.cfunc.cproto = cproto; + p->u.cfunc.magic = magic; + p->is_constructor = (cproto == JS_CFUNC_constructor || cproto == JS_CFUNC_constructor_magic || + cproto == JS_CFUNC_constructor_or_func || cproto == JS_CFUNC_constructor_or_func_magic); + if (!name) + name = ""; + name_atom = JS_NewAtom(ctx, name); + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); + return func_obj; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +JSValue JS_NewCFunction2(JSContext* ctx, + JSCFunction* func, + const char* name, + int length, + JSCFunctionEnum cproto, + int magic) { + return JS_NewCFunction3(ctx, func, name, length, cproto, magic, ctx->function_proto); +} + +/* warning: the refcount of the context is not incremented. Return + NULL in case of exception (case of revoked proxy only) */ +JSContext* JS_GetFunctionRealm(JSContext* ctx, JSValueConst func_obj) { + JSObject* p; + JSContext* realm; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return ctx; + p = JS_VALUE_GET_OBJ(func_obj); + switch (p->class_id) { + case JS_CLASS_C_FUNCTION: + realm = p->u.cfunc.realm; + break; + case JS_CLASS_BYTECODE_FUNCTION: + case JS_CLASS_GENERATOR_FUNCTION: + case JS_CLASS_ASYNC_FUNCTION: + case JS_CLASS_ASYNC_GENERATOR_FUNCTION: { + JSFunctionBytecode* b; + b = p->u.func.function_bytecode; + realm = b->realm; + } break; + case JS_CLASS_PROXY: { + JSProxyData* s = p->u.opaque; + if (!s) + return ctx; + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return NULL; + } else { + realm = JS_GetFunctionRealm(ctx, s->target); + } + } break; + case JS_CLASS_BOUND_FUNCTION: { + JSBoundFunction* bf = p->u.bound_function; + realm = JS_GetFunctionRealm(ctx, bf->func_obj); + } break; + default: + realm = ctx; + break; + } + return realm; +} + +void js_c_function_data_finalizer(JSRuntime* rt, JSValue val) { + JSCFunctionDataRecord* s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for (i = 0; i < s->data_len; i++) { + JS_FreeValueRT(rt, s->data[i]); + } + js_free_rt(rt, s); + } +} + +void js_c_function_data_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + JSCFunctionDataRecord* s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for (i = 0; i < s->data_len; i++) { + JS_MarkValue(rt, s->data[i], mark_func); + } + } +} + +JSValue js_c_function_data_call(JSContext* ctx, + JSValueConst func_obj, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int flags) { + JSCFunctionDataRecord* s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); + JSValueConst* arg_buf; + int i; + + /* XXX: could add the function on the stack for debug */ + if (unlikely(argc < s->length)) { + arg_buf = alloca(sizeof(arg_buf[0]) * s->length); + for (i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for (i = argc; i < s->length; i++) + arg_buf[i] = JS_UNDEFINED; + } else { + arg_buf = argv; + } + + return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); +} + +int js_op_define_class(JSContext* ctx, + JSValue* sp, + JSAtom class_name, + int class_flags, + JSVarRef** cur_var_refs, + JSStackFrame* sf, + BOOL is_computed_name) { + JSValue bfunc, parent_class, proto = JS_UNDEFINED; + JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; + JSFunctionBytecode* b; + + parent_class = sp[-2]; + bfunc = sp[-1]; + + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { + if (JS_IsNull(parent_class)) { + parent_proto = JS_NULL; + parent_class = JS_DupValue(ctx, ctx->function_proto); + } else { + if (!JS_IsConstructor(ctx, parent_class)) { + JS_ThrowTypeError(ctx, "parent class must be constructor"); + goto fail; + } + parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); + if (JS_IsException(parent_proto)) + goto fail; + if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { + JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); + goto fail; + } + } + } else { + /* parent_class is JS_UNDEFINED in this case */ + parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); + parent_class = JS_DupValue(ctx, ctx->function_proto); + } + proto = JS_NewObjectProto(ctx, parent_proto); + if (JS_IsException(proto)) + goto fail; + + b = JS_VALUE_GET_PTR(bfunc); + assert(b->func_kind == JS_FUNC_NORMAL); + ctor = JS_NewObjectProtoClass(ctx, parent_class, JS_CLASS_BYTECODE_FUNCTION); + if (JS_IsException(ctor)) + goto fail; + ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); + bfunc = JS_UNDEFINED; + if (JS_IsException(ctor)) + goto fail; + js_method_set_home_object(ctx, ctor, proto); + JS_SetConstructorBit(ctx, ctor, TRUE); + + JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, JS_NewInt32(ctx, b->defined_arg_count), JS_PROP_CONFIGURABLE); + + if (is_computed_name) { + if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], JS_PROP_CONFIGURABLE) < 0) + goto fail; + } else { + if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) + goto fail; + } + + /* the constructor property must be first. It can be overriden by + computed property names */ + if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, JS_DupValue(ctx, ctor), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_THROW) < 0) + goto fail; + /* set the prototype property */ + if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, JS_DupValue(ctx, proto), JS_PROP_THROW) < 0) + goto fail; + set_cycle_flag(ctx, ctor); + set_cycle_flag(ctx, proto); + + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, parent_class); + + sp[-2] = ctor; + sp[-1] = proto; + return 0; +fail: + JS_FreeValue(ctx, parent_class); + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, bfunc); + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, ctor); + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +}; diff --git a/src/core/function.h b/src/core/function.h index d188194be..a3e71c55a 100644 --- a/src/core/function.h +++ b/src/core/function.h @@ -1,153 +1,153 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_FUNCTION_H -#define QUICKJS_FUNCTION_H - -#include "quickjs/cutils.h" -#include "quickjs/quickjs.h" -#include "types.h" - -#define JS_CALL_FLAG_COPY_ARGV (1 << 1) -#define JS_CALL_FLAG_GENERATOR (1 << 2) - -#define OP_DEFINE_METHOD_METHOD 0 -#define OP_DEFINE_METHOD_GETTER 1 -#define OP_DEFINE_METHOD_SETTER 2 -#define OP_DEFINE_METHOD_ENUMERABLE 4 - -#define JS_THROW_VAR_RO 0 -#define JS_THROW_VAR_REDECL 1 -#define JS_THROW_VAR_UNINITIALIZED 2 -#define JS_THROW_ERROR_DELETE_SUPER 3 -#define JS_THROW_ERROR_ITERATOR_THROW 4 - -typedef struct JSCFunctionDataRecord { - JSCFunctionData* func; - uint8_t length; - uint8_t data_len; - uint16_t magic; - JSValue data[0]; -} JSCFunctionDataRecord; - -/* argument of OP_special_object */ -typedef enum { - OP_SPECIAL_OBJECT_ARGUMENTS, - OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, - OP_SPECIAL_OBJECT_THIS_FUNC, - OP_SPECIAL_OBJECT_NEW_TARGET, - OP_SPECIAL_OBJECT_HOME_OBJECT, - OP_SPECIAL_OBJECT_VAR_OBJECT, - OP_SPECIAL_OBJECT_IMPORT_META, -} OPSpecialObjectEnum; - -#define FUNC_RET_AWAIT 0 -#define FUNC_RET_YIELD 1 -#define FUNC_RET_YIELD_STAR 2 - -#if !defined(CONFIG_STACK_CHECK) -/* no stack limitation */ -static inline uintptr_t js_get_stack_pointer(void) { - return 0; -} - -static inline BOOL js_check_stack_overflow(JSRuntime* rt, size_t alloca_size) { - return FALSE; -} -#else -/* Note: OS and CPU dependent */ -static inline uintptr_t js_get_stack_pointer(void) { - return (uintptr_t)__builtin_frame_address(0); -} - -static inline BOOL js_check_stack_overflow(JSRuntime* rt, size_t alloca_size) { - uintptr_t sp; - sp = js_get_stack_pointer() - alloca_size; - return unlikely(sp < rt->stack_limit); -} -#endif - -JSValue js_call_c_function(JSContext* ctx, - JSValueConst func_obj, - JSValueConst this_obj, - int argc, - JSValueConst* argv, - int flags); -JSValue js_call_bound_function(JSContext* ctx, - JSValueConst func_obj, - JSValueConst this_obj, - int argc, - JSValueConst* argv, - int flags); -JSValue JS_CallInternal(JSContext* ctx, - JSValueConst func_obj, - JSValueConst this_obj, - JSValueConst new_target, - int argc, - JSValue* argv, - int flags); -JSValue JS_CallConstructorInternal(JSContext* ctx, - JSValueConst func_obj, - JSValueConst new_target, - int argc, - JSValue* argv, - int flags); -BOOL JS_IsCFunction(JSContext* ctx, JSValueConst val, JSCFunction* func, int magic); - -/* Note: at least 'length' arguments will be readable in 'argv' */ -JSValue JS_NewCFunction3(JSContext* ctx, - JSCFunction* func, - const char* name, - int length, - JSCFunctionEnum cproto, - int magic, - JSValueConst proto_val); - -/* warning: the refcount of the context is not incremented. Return - NULL in case of exception (case of revoked proxy only) */ -JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj); - -JSValue JS_CallFree(JSContext* ctx, JSValue func_obj, JSValueConst this_obj, int argc, JSValueConst* argv); -JSValue JS_InvokeFree(JSContext* ctx, JSValue this_val, JSAtom atom, int argc, JSValueConst* argv); - -void js_c_function_data_finalizer(JSRuntime* rt, JSValue val); -void js_c_function_data_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - -JSValue js_c_function_data_call(JSContext* ctx, - JSValueConst func_obj, - JSValueConst this_val, - int argc, - JSValueConst* argv, - int flags); - -int js_op_define_class(JSContext* ctx, - JSValue* sp, - JSAtom class_name, - int class_flags, - JSVarRef** cur_var_refs, - JSStackFrame* sf, - BOOL is_computed_name); - -#endif +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_FUNCTION_H +#define QUICKJS_FUNCTION_H + +#include "quickjs/cutils.h" +#include "quickjs/quickjs.h" +#include "types.h" + +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) + +#define OP_DEFINE_METHOD_METHOD 0 +#define OP_DEFINE_METHOD_GETTER 1 +#define OP_DEFINE_METHOD_SETTER 2 +#define OP_DEFINE_METHOD_ENUMERABLE 4 + +#define JS_THROW_VAR_RO 0 +#define JS_THROW_VAR_REDECL 1 +#define JS_THROW_VAR_UNINITIALIZED 2 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 + +typedef struct JSCFunctionDataRecord { + JSCFunctionData* func; + uint8_t length; + uint8_t data_len; + uint16_t magic; + JSValue data[0]; +} JSCFunctionDataRecord; + +/* argument of OP_special_object */ +typedef enum { + OP_SPECIAL_OBJECT_ARGUMENTS, + OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, + OP_SPECIAL_OBJECT_THIS_FUNC, + OP_SPECIAL_OBJECT_NEW_TARGET, + OP_SPECIAL_OBJECT_HOME_OBJECT, + OP_SPECIAL_OBJECT_VAR_OBJECT, + OP_SPECIAL_OBJECT_IMPORT_META, +} OPSpecialObjectEnum; + +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 + +#if !defined(CONFIG_STACK_CHECK) +/* no stack limitation */ +static inline uintptr_t js_get_stack_pointer(void) { + return 0; +} + +static inline BOOL js_check_stack_overflow(JSRuntime* rt, size_t alloca_size) { + return FALSE; +} +#else +/* Note: OS and CPU dependent */ +static inline uintptr_t js_get_stack_pointer(void) { + return (uintptr_t)__builtin_frame_address(0); +} + +static inline BOOL js_check_stack_overflow(JSRuntime* rt, size_t alloca_size) { + uintptr_t sp; + sp = js_get_stack_pointer() - alloca_size; + return unlikely(sp < rt->stack_limit); +} +#endif + +JSValue js_call_c_function(JSContext* ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, + JSValueConst* argv, + int flags); +JSValue js_call_bound_function(JSContext* ctx, + JSValueConst func_obj, + JSValueConst this_obj, + int argc, + JSValueConst* argv, + int flags); +JSValue JS_CallInternal(JSContext* ctx, + JSValueConst func_obj, + JSValueConst this_obj, + JSValueConst new_target, + int argc, + JSValue* argv, + int flags); +JSValue JS_CallConstructorInternal(JSContext* ctx, + JSValueConst func_obj, + JSValueConst new_target, + int argc, + JSValue* argv, + int flags); +BOOL JS_IsCFunction(JSContext* ctx, JSValueConst val, JSCFunction* func, int magic); + +/* Note: at least 'length' arguments will be readable in 'argv' */ +JSValue JS_NewCFunction3(JSContext* ctx, + JSCFunction* func, + const char* name, + int length, + JSCFunctionEnum cproto, + int magic, + JSValueConst proto_val); + +/* warning: the refcount of the context is not incremented. Return + NULL in case of exception (case of revoked proxy only) */ +JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj); + +JSValue JS_CallFree(JSContext* ctx, JSValue func_obj, JSValueConst this_obj, int argc, JSValueConst* argv); +JSValue JS_InvokeFree(JSContext* ctx, JSValue this_val, JSAtom atom, int argc, JSValueConst* argv); + +void js_c_function_data_finalizer(JSRuntime* rt, JSValue val); +void js_c_function_data_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + +JSValue js_c_function_data_call(JSContext* ctx, + JSValueConst func_obj, + JSValueConst this_val, + int argc, + JSValueConst* argv, + int flags); + +int js_op_define_class(JSContext* ctx, + JSValue* sp, + JSAtom class_name, + int class_flags, + JSVarRef** cur_var_refs, + JSStackFrame* sf, + BOOL is_computed_name); + +#endif diff --git a/src/core/gc.c b/src/core/gc.c index a260d106a..4399500eb 100644 --- a/src/core/gc.c +++ b/src/core/gc.c @@ -1,806 +1,806 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "gc.h" -#include "builtins/js-async-function.h" -#include "builtins/js-map.h" -#include "builtins/js-proxy.h" -#include "bytecode.h" -#include "malloc.h" -#include "module.h" -#include "object.h" -#include "parser.h" -#include "runtime.h" -#include "shape.h" -#include "string.h" - -__maybe_unused void JS_DumpObjectHeader(JSRuntime* rt) { - printf("%14s %4s %4s %14s %10s %s\n", "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); -} - -/* for debug only: dump an object without side effect */ -__maybe_unused void JS_DumpObject(JSRuntime* rt, JSObject* p) { - uint32_t i; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - JSShape* sh; - JSShapeProperty* prs; - JSProperty* pr; - BOOL is_first = TRUE; - - /* XXX: should encode atoms with special characters */ - sh = p->shape; /* the shape can be NULL while freeing an object */ - printf("%14p %4d ", (void*)p, p->header.ref_count); - if (sh) { - printf("%3d%c %14p ", sh->header.ref_count, " *"[sh->is_hashed], (void*)sh -> proto); - } else { - printf("%3s %14s ", "-", "-"); - } - printf("%10s ", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); - if (p->is_exotic && p->fast_array) { - printf("[ "); - for (i = 0; i < p->u.array.count; i++) { - if (i != 0) - printf(", "); - switch (p->class_id) { - case JS_CLASS_ARRAY: - case JS_CLASS_ARGUMENTS: - JS_DumpValueShort(rt, p->u.array.u.values[i]); - break; - case JS_CLASS_UINT8C_ARRAY: - case JS_CLASS_INT8_ARRAY: - case JS_CLASS_UINT8_ARRAY: - case JS_CLASS_INT16_ARRAY: - case JS_CLASS_UINT16_ARRAY: - case JS_CLASS_INT32_ARRAY: - case JS_CLASS_UINT32_ARRAY: -#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; - while (size-- > 0) - printf("%02X", *b++); - } break; - } - } - printf(" ] "); - } - - if (sh) { - printf("{ "); - for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - if (prs->atom != JS_ATOM_NULL) { - pr = &p->prop[i]; - if (!is_first) - printf(", "); - printf("%s: ", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - printf("[getset %p %p]", (void*)pr->u.getset.getter, (void*)pr->u.getset.setter); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - printf("[varref %p]", (void*)pr->u.var_ref); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - printf("[autoinit %p %d %p]", (void*)js_autoinit_get_realm(pr), js_autoinit_get_id(pr), (void*)pr->u.init.opaque); - } else { - JS_DumpValueShort(rt, pr->u.value); - } - is_first = FALSE; - } - } - printf(" }"); - } - - if (js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode* b = p->u.func.function_bytecode; - JSVarRef** var_refs; - if (b->closure_var_count) { - var_refs = p->u.func.var_refs; - printf(" Closure:"); - for (i = 0; i < b->closure_var_count; i++) { - printf(" "); - JS_DumpValueShort(rt, var_refs[i]->value); - } - if (p->u.func.home_object) { - printf(" HomeObject: "); - JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); - } - } - } - printf("\n"); -} - -__maybe_unused void JS_DumpGCObject(JSRuntime* rt, JSGCObjectHeader* p) { - if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { - JS_DumpObject(rt, (JSObject*)p); - } else { - printf("%14p %4d ", (void*)p, p->ref_count); - switch (p->gc_obj_type) { - case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: - printf("[function bytecode]"); - break; - case JS_GC_OBJ_TYPE_SHAPE: - printf("[shape]"); - break; - case JS_GC_OBJ_TYPE_VAR_REF: - printf("[var_ref]"); - break; - case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: - printf("[async_function]"); - break; - case JS_GC_OBJ_TYPE_JS_CONTEXT: - printf("[js_context]"); - break; - default: - printf("[unknown %d]", p->gc_obj_type); - break; - } - printf("\n"); - } -} - -/* return -1 if exception (proxy case) or TRUE/FALSE */ -int JS_IsArray(JSContext* ctx, JSValueConst val) { - JSObject* p; - if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(val); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_isArray(ctx, val); - else - return p->class_id == JS_CLASS_ARRAY; - } else { - return FALSE; - } -} - -__maybe_unused void JS_DumpValueShort(JSRuntime* rt, JSValueConst val) { - uint32_t tag = JS_VALUE_GET_NORM_TAG(val); - const char* str; - - switch (tag) { - case JS_TAG_INT: - printf("%d", JS_VALUE_GET_INT(val)); - break; - case JS_TAG_BOOL: - if (JS_VALUE_GET_BOOL(val)) - str = "true"; - else - str = "false"; - goto print_str; - case JS_TAG_NULL: - str = "null"; - goto print_str; - case JS_TAG_EXCEPTION: - str = "exception"; - goto print_str; - case JS_TAG_UNINITIALIZED: - str = "uninitialized"; - goto print_str; - case JS_TAG_UNDEFINED: - str = "undefined"; - print_str: - printf("%s", str); - break; - case JS_TAG_FLOAT64: - printf("%.14g", JS_VALUE_GET_FLOAT64(val)); - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - char* str; - str = bf_ftoa(NULL, &p->num, 10, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC); - printf("%sn", str); - bf_realloc(&rt->bf_ctx, str, 0); - } break; - case JS_TAG_BIG_FLOAT: { - JSBigFloat* p = JS_VALUE_GET_PTR(val); - char* str; - str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF, BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX); - printf("%sl", str); - bf_free(&rt->bf_ctx, str); - } break; - case JS_TAG_BIG_DECIMAL: { - JSBigDecimal* p = JS_VALUE_GET_PTR(val); - char* str; - str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF, BF_RNDZ | BF_FTOA_FORMAT_FREE); - printf("%sm", str); - bf_free(&rt->bf_ctx, str); - } break; -#endif - case JS_TAG_STRING: { - JSString* p; - p = JS_VALUE_GET_STRING(val); - JS_DumpString(rt, p); - } break; - case JS_TAG_FUNCTION_BYTECODE: { - JSFunctionBytecode* b = JS_VALUE_GET_PTR(val); - char buf[ATOM_GET_STR_BUF_SIZE]; - printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); - } break; - case JS_TAG_OBJECT: { - JSObject* p = JS_VALUE_GET_OBJ(val); - JSAtom atom = rt->class_array[p->class_id].class_name; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - printf("[%s %p]", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void*)p); - } break; - case JS_TAG_SYMBOL: { - JSAtomStruct* p = JS_VALUE_GET_PTR(val); - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - printf("Symbol(%s)", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); - } break; - case JS_TAG_MODULE: - printf("[module]"); - break; - default: - printf("[unknown tag %d]", tag); - break; - } -} - -__maybe_unused void JS_DumpValue(JSContext* ctx, JSValueConst val) { - JS_DumpValueShort(ctx->rt, val); -} - -__maybe_unused void JS_PrintValue(JSContext* ctx, const char* str, JSValueConst val) { - printf("%s=", str); - JS_DumpValueShort(ctx->rt, val); - printf("\n"); -} - -void js_object_list_init(JSObjectList* s) { - memset(s, 0, sizeof(*s)); -} - -uint32_t js_object_list_get_hash(JSObject* p, uint32_t hash_size) { - return ((uintptr_t)p * 3163) & (hash_size - 1); -} - -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 */ -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 */ -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; -} - -void js_object_list_end(JSContext* ctx, JSObjectList* s) { - js_free(ctx, s->object_tab); - js_free(ctx, s->hash_table); -} - -/* indicate that the object may be part of a function prototype cycle */ -void set_cycle_flag(JSContext* ctx, JSValueConst obj) {} - -void remove_gc_object(JSGCObjectHeader* h) { - list_del(&h->link); -} - -void free_var_ref(JSRuntime* rt, JSVarRef* var_ref) { - if (var_ref) { - assert(var_ref->header.ref_count > 0); - if (--var_ref->header.ref_count == 0) { - if (var_ref->is_detached) { - JS_FreeValueRT(rt, var_ref->value); - remove_gc_object(&var_ref->header); - } else { - list_del(&var_ref->header.link); /* still on the stack */ - } - js_free_rt(rt, var_ref); - } - } -} - -void free_object(JSRuntime* rt, JSObject* p) { - int i; - JSClassFinalizer* finalizer; - JSShape* sh; - JSShapeProperty* pr; - - p->free_mark = 1; /* used to tell the object is invalid when - freeing cycles */ - /* free all the fields */ - sh = p->shape; - pr = get_shape_prop(sh); - for (i = 0; i < sh->prop_count; i++) { - free_property(rt, &p->prop[i], pr->flags); - pr++; - } - js_free_rt(rt, p->prop); - /* as an optimization we destroy the shape immediately without - putting it in gc_zero_ref_count_list */ - js_free_shape(rt, sh); - - /* fail safe */ - p->shape = NULL; - p->prop = NULL; - - if (unlikely(p->first_weak_ref)) { - reset_weak_ref(rt, p); - } - - finalizer = rt->class_array[p->class_id].finalizer; - if (finalizer) - (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); - - /* fail safe */ - p->class_id = 0; - p->u.opaque = NULL; - p->u.func.var_refs = NULL; - p->u.func.home_object = NULL; - - remove_gc_object(&p->header); - if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { - list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); - } else { - js_free_rt(rt, p); - } -} - -void free_gc_object(JSRuntime* rt, JSGCObjectHeader* gp) { - switch (gp->gc_obj_type) { - case JS_GC_OBJ_TYPE_JS_OBJECT: - free_object(rt, (JSObject*)gp); - break; - case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: - free_function_bytecode(rt, (JSFunctionBytecode*)gp); - break; - default: - abort(); - } -} - -void free_zero_refcount(JSRuntime* rt) { - struct list_head* el; - JSGCObjectHeader* p; - - rt->gc_phase = JS_GC_PHASE_DECREF; - for (;;) { - el = rt->gc_zero_ref_count_list.next; - if (el == &rt->gc_zero_ref_count_list) - break; - p = list_entry(el, JSGCObjectHeader, link); - assert(p->ref_count == 0); - free_gc_object(rt, p); - } - rt->gc_phase = JS_GC_PHASE_NONE; -} - -/* called with the ref_count of 'v' reaches zero. */ -void __JS_FreeValueRT(JSRuntime* rt, JSValue v) { - uint32_t tag = JS_VALUE_GET_TAG(v); - -#ifdef DUMP_FREE - { - printf("Freeing "); - if (tag == JS_TAG_OBJECT) { - JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); - } else { - JS_DumpValueShort(rt, v); - printf("\n"); - } - } -#endif - - switch (tag) { - case JS_TAG_STRING: { - JSString* p = JS_VALUE_GET_STRING(v); - if (p->atom_type) { - JS_FreeAtomStruct(rt, p); - } else { -#ifdef DUMP_LEAKS - list_del(&p->link); -#endif - js_free_rt(rt, p); - } - } break; - case JS_TAG_OBJECT: - case JS_TAG_FUNCTION_BYTECODE: { - JSGCObjectHeader* p = JS_VALUE_GET_PTR(v); - if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { - list_del(&p->link); - list_add(&p->link, &rt->gc_zero_ref_count_list); - if (rt->gc_phase == JS_GC_PHASE_NONE) { - free_zero_refcount(rt); - } - } - } break; - case JS_TAG_MODULE: - abort(); /* never freed here */ - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: { - JSBigFloat* bf = JS_VALUE_GET_PTR(v); - bf_delete(&bf->num); - js_free_rt(rt, bf); - } break; - case JS_TAG_BIG_DECIMAL: { - JSBigDecimal* bf = JS_VALUE_GET_PTR(v); - bfdec_delete(&bf->num); - js_free_rt(rt, bf); - } break; -#endif - case JS_TAG_SYMBOL: { - JSAtomStruct* p = JS_VALUE_GET_PTR(v); - JS_FreeAtomStruct(rt, p); - } break; - default: - printf("__JS_FreeValue: unknown tag=%d\n", tag); - abort(); - } -} - -void __JS_FreeValue(JSContext* ctx, JSValue v) { - __JS_FreeValueRT(ctx->rt, v); -} - -/* garbage collection */ - -void add_gc_object(JSRuntime* rt, JSGCObjectHeader* h, JSGCObjectTypeEnum type) { - h->mark = 0; - h->gc_obj_type = type; - list_add_tail(&h->link, &rt->gc_obj_list); -} - -void JS_MarkValue(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { - if (JS_VALUE_HAS_REF_COUNT(val)) { - switch (JS_VALUE_GET_TAG(val)) { - case JS_TAG_OBJECT: - case JS_TAG_FUNCTION_BYTECODE: - mark_func(rt, JS_VALUE_GET_PTR(val)); - break; - default: - break; - } - } -} - -/* XXX: would be more efficient with separate module lists */ -void js_free_modules(JSContext* ctx, JSFreeModuleEnum flag) { - struct list_head *el, *el1; - list_for_each_safe(el, el1, &ctx->loaded_modules) { - JSModuleDef* m = list_entry(el, JSModuleDef, link); - if (flag == JS_FREE_MODULE_ALL || (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) || (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) { - js_free_module_def(ctx, m); - } - } -} - -JSContext* JS_DupContext(JSContext* ctx) { - ctx->header.ref_count++; - return ctx; -} - -/* used by the GC */ -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 mark_children(JSRuntime* rt, JSGCObjectHeader* gp, JS_MarkFunc* mark_func) { - switch (gp->gc_obj_type) { - case JS_GC_OBJ_TYPE_JS_OBJECT: { - JSObject* p = (JSObject*)gp; - JSShapeProperty* prs; - JSShape* sh; - int i; - sh = p->shape; - mark_func(rt, &sh->header); - /* mark all the fields */ - prs = get_shape_prop(sh); - for (i = 0; i < sh->prop_count; i++) { - JSProperty* pr = &p->prop[i]; - if (prs->atom != JS_ATOM_NULL) { - if (prs->flags & JS_PROP_TMASK) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - if (pr->u.getset.getter) - mark_func(rt, &pr->u.getset.getter->header); - if (pr->u.getset.setter) - mark_func(rt, &pr->u.getset.setter->header); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - if (pr->u.var_ref->is_detached) { - /* Note: the tag does not matter - provided it is a GC object */ - mark_func(rt, &pr->u.var_ref->header); - } - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - js_autoinit_mark(rt, pr, mark_func); - } - } else { - JS_MarkValue(rt, pr->u.value, mark_func); - } - } - prs++; - } - - if (p->class_id != JS_CLASS_OBJECT) { - JSClassGCMark* gc_mark; - gc_mark = rt->class_array[p->class_id].gc_mark; - if (gc_mark) - gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); - } - } break; - case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: - /* the template objects can be part of a cycle */ - { - JSFunctionBytecode* b = (JSFunctionBytecode*)gp; - int i; - for (i = 0; i < b->cpool_count; i++) { - JS_MarkValue(rt, b->cpool[i], mark_func); - } - if (b->realm) - mark_func(rt, &b->realm->header); - } - break; - case JS_GC_OBJ_TYPE_VAR_REF: { - JSVarRef* var_ref = (JSVarRef*)gp; - /* only detached variable referenced are taken into account */ - assert(var_ref->is_detached); - JS_MarkValue(rt, *var_ref->pvalue, mark_func); - } break; - case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: { - JSAsyncFunctionData* s = (JSAsyncFunctionData*)gp; - if (s->is_active) - async_func_mark(rt, &s->func_state, mark_func); - JS_MarkValue(rt, s->resolving_funcs[0], mark_func); - JS_MarkValue(rt, s->resolving_funcs[1], mark_func); - } break; - case JS_GC_OBJ_TYPE_SHAPE: { - JSShape* sh = (JSShape*)gp; - if (sh->proto != NULL) { - mark_func(rt, &sh->proto->header); - } - } break; - case JS_GC_OBJ_TYPE_JS_CONTEXT: { - JSContext* ctx = (JSContext*)gp; - JS_MarkContext(rt, ctx, mark_func); - } break; - default: - abort(); - } -} - -void gc_decref_child(JSRuntime* rt, JSGCObjectHeader* p) { - assert(p->ref_count > 0); - p->ref_count--; - if (p->ref_count == 0 && p->mark == 1) { - list_del(&p->link); - list_add_tail(&p->link, &rt->tmp_obj_list); - } -} - -void gc_decref(JSRuntime* rt) { - struct list_head *el, *el1; - JSGCObjectHeader* p; - - init_list_head(&rt->tmp_obj_list); - - /* decrement the refcount of all the children of all the GC - objects and move the GC objects with zero refcount to - tmp_obj_list */ - list_for_each_safe(el, el1, &rt->gc_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - assert(p->mark == 0); - mark_children(rt, p, gc_decref_child); - p->mark = 1; - if (p->ref_count == 0) { - list_del(&p->link); - list_add_tail(&p->link, &rt->tmp_obj_list); - } - } -} - -void gc_scan_incref_child(JSRuntime* rt, JSGCObjectHeader* p) { - p->ref_count++; - if (p->ref_count == 1) { - /* ref_count was 0: remove from tmp_obj_list and add at the - end of gc_obj_list */ - list_del(&p->link); - list_add_tail(&p->link, &rt->gc_obj_list); - p->mark = 0; /* reset the mark for the next GC call */ - } -} - -void gc_scan_incref_child2(JSRuntime* rt, JSGCObjectHeader* p) { - p->ref_count++; -} - -void gc_scan(JSRuntime* rt) { - struct list_head* el; - JSGCObjectHeader* p; - - /* keep the objects with a refcount > 0 and their children. */ - list_for_each(el, &rt->gc_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - assert(p->ref_count > 0); - p->mark = 0; /* reset the mark for the next GC call */ - mark_children(rt, p, gc_scan_incref_child); - } - - /* restore the refcount of the objects to be deleted. */ - list_for_each(el, &rt->tmp_obj_list) { - p = list_entry(el, JSGCObjectHeader, link); - mark_children(rt, p, gc_scan_incref_child2); - } -} - -void gc_free_cycles(JSRuntime* rt) { - struct list_head *el, *el1; - JSGCObjectHeader* p; -#ifdef DUMP_GC_FREE - BOOL header_done = FALSE; -#endif - - rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; - - for (;;) { - el = rt->tmp_obj_list.next; - if (el == &rt->tmp_obj_list) - break; - p = list_entry(el, JSGCObjectHeader, link); - /* Only need to free the GC object associated with JS - values. The rest will be automatically removed because they - must be referenced by them. */ - switch (p->gc_obj_type) { - case JS_GC_OBJ_TYPE_JS_OBJECT: - case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: -#ifdef DUMP_GC_FREE - if (!header_done) { - printf("Freeing cycles:\n"); - JS_DumpObjectHeader(rt); - header_done = TRUE; - } - JS_DumpGCObject(rt, p); -#endif - free_gc_object(rt, p); - break; - default: - list_del(&p->link); - list_add_tail(&p->link, &rt->gc_zero_ref_count_list); - break; - } - } - rt->gc_phase = JS_GC_PHASE_NONE; - - list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { - p = list_entry(el, JSGCObjectHeader, link); - assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); - js_free_rt(rt, p); - } - - init_list_head(&rt->gc_zero_ref_count_list); -} - -void JS_RunGC(JSRuntime* rt) { - /* decrement the reference of the children of each object. mark = - 1 after this pass. */ - gc_decref(rt); - - /* keep the GC objects with a non zero refcount and their childs */ - gc_scan(rt); - - /* free the GC objects in a cycle */ - gc_free_cycles(rt); -} - -/* Return false if not an object or if the object has already been - freed (zombie objects are visible in finalizers when freeing - cycles). */ -BOOL JS_IsLiveObject(JSRuntime* rt, JSValueConst obj) { - JSObject* p; - if (!JS_IsObject(obj)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - return !p->free_mark; +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "gc.h" +#include "builtins/js-async-function.h" +#include "builtins/js-map.h" +#include "builtins/js-proxy.h" +#include "bytecode.h" +#include "malloc.h" +#include "module.h" +#include "object.h" +#include "parser.h" +#include "runtime.h" +#include "shape.h" +#include "string.h" + +__maybe_unused void JS_DumpObjectHeader(JSRuntime* rt) { + printf("%14s %4s %4s %14s %10s %s\n", "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); +} + +/* for debug only: dump an object without side effect */ +__maybe_unused void JS_DumpObject(JSRuntime* rt, JSObject* p) { + uint32_t i; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + JSShape* sh; + JSShapeProperty* prs; + JSProperty* pr; + BOOL is_first = TRUE; + + /* XXX: should encode atoms with special characters */ + sh = p->shape; /* the shape can be NULL while freeing an object */ + printf("%14p %4d ", (void*)p, p->header.ref_count); + if (sh) { + printf("%3d%c %14p ", sh->header.ref_count, " *"[sh->is_hashed], (void*)sh -> proto); + } else { + printf("%3s %14s ", "-", "-"); + } + printf("%10s ", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); + if (p->is_exotic && p->fast_array) { + printf("[ "); + for (i = 0; i < p->u.array.count; i++) { + if (i != 0) + printf(", "); + switch (p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + JS_DumpValueShort(rt, p->u.array.u.values[i]); + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: +#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; + while (size-- > 0) + printf("%02X", *b++); + } break; + } + } + printf(" ] "); + } + + if (sh) { + printf("{ "); + for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->atom != JS_ATOM_NULL) { + pr = &p->prop[i]; + if (!is_first) + printf(", "); + printf("%s: ", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + printf("[getset %p %p]", (void*)pr->u.getset.getter, (void*)pr->u.getset.setter); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + printf("[varref %p]", (void*)pr->u.var_ref); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + printf("[autoinit %p %d %p]", (void*)js_autoinit_get_realm(pr), js_autoinit_get_id(pr), (void*)pr->u.init.opaque); + } else { + JS_DumpValueShort(rt, pr->u.value); + } + is_first = FALSE; + } + } + printf(" }"); + } + + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode* b = p->u.func.function_bytecode; + JSVarRef** var_refs; + if (b->closure_var_count) { + var_refs = p->u.func.var_refs; + printf(" Closure:"); + for (i = 0; i < b->closure_var_count; i++) { + printf(" "); + JS_DumpValueShort(rt, var_refs[i]->value); + } + if (p->u.func.home_object) { + printf(" HomeObject: "); + JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); + } + } + } + printf("\n"); +} + +__maybe_unused void JS_DumpGCObject(JSRuntime* rt, JSGCObjectHeader* p) { + if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + JS_DumpObject(rt, (JSObject*)p); + } else { + printf("%14p %4d ", (void*)p, p->ref_count); + switch (p->gc_obj_type) { + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + printf("[function bytecode]"); + break; + case JS_GC_OBJ_TYPE_SHAPE: + printf("[shape]"); + break; + case JS_GC_OBJ_TYPE_VAR_REF: + printf("[var_ref]"); + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + printf("[async_function]"); + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + printf("[js_context]"); + break; + default: + printf("[unknown %d]", p->gc_obj_type); + break; + } + printf("\n"); + } +} + +/* return -1 if exception (proxy case) or TRUE/FALSE */ +int JS_IsArray(JSContext* ctx, JSValueConst val) { + JSObject* p; + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(val); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isArray(ctx, val); + else + return p->class_id == JS_CLASS_ARRAY; + } else { + return FALSE; + } +} + +__maybe_unused void JS_DumpValueShort(JSRuntime* rt, JSValueConst val) { + uint32_t tag = JS_VALUE_GET_NORM_TAG(val); + const char* str; + + switch (tag) { + case JS_TAG_INT: + printf("%d", JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BOOL: + if (JS_VALUE_GET_BOOL(val)) + str = "true"; + else + str = "false"; + goto print_str; + case JS_TAG_NULL: + str = "null"; + goto print_str; + case JS_TAG_EXCEPTION: + str = "exception"; + goto print_str; + case JS_TAG_UNINITIALIZED: + str = "uninitialized"; + goto print_str; + case JS_TAG_UNDEFINED: + str = "undefined"; + print_str: + printf("%s", str); + break; + case JS_TAG_FLOAT64: + printf("%.14g", JS_VALUE_GET_FLOAT64(val)); + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + char* str; + str = bf_ftoa(NULL, &p->num, 10, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC); + printf("%sn", str); + bf_realloc(&rt->bf_ctx, str, 0); + } break; + case JS_TAG_BIG_FLOAT: { + JSBigFloat* p = JS_VALUE_GET_PTR(val); + char* str; + str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF, BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX); + printf("%sl", str); + bf_free(&rt->bf_ctx, str); + } break; + case JS_TAG_BIG_DECIMAL: { + JSBigDecimal* p = JS_VALUE_GET_PTR(val); + char* str; + str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF, BF_RNDZ | BF_FTOA_FORMAT_FREE); + printf("%sm", str); + bf_free(&rt->bf_ctx, str); + } break; +#endif + case JS_TAG_STRING: { + JSString* p; + p = JS_VALUE_GET_STRING(val); + JS_DumpString(rt, p); + } break; + case JS_TAG_FUNCTION_BYTECODE: { + JSFunctionBytecode* b = JS_VALUE_GET_PTR(val); + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + } break; + case JS_TAG_OBJECT: { + JSObject* p = JS_VALUE_GET_OBJ(val); + JSAtom atom = rt->class_array[p->class_id].class_name; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("[%s %p]", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void*)p); + } break; + case JS_TAG_SYMBOL: { + JSAtomStruct* p = JS_VALUE_GET_PTR(val); + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("Symbol(%s)", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); + } break; + case JS_TAG_MODULE: + printf("[module]"); + break; + default: + printf("[unknown tag %d]", tag); + break; + } +} + +__maybe_unused void JS_DumpValue(JSContext* ctx, JSValueConst val) { + JS_DumpValueShort(ctx->rt, val); +} + +__maybe_unused void JS_PrintValue(JSContext* ctx, const char* str, JSValueConst val) { + printf("%s=", str); + JS_DumpValueShort(ctx->rt, val); + printf("\n"); +} + +void js_object_list_init(JSObjectList* s) { + memset(s, 0, sizeof(*s)); +} + +uint32_t js_object_list_get_hash(JSObject* p, uint32_t hash_size) { + return ((uintptr_t)p * 3163) & (hash_size - 1); +} + +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 */ +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 */ +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; +} + +void js_object_list_end(JSContext* ctx, JSObjectList* s) { + js_free(ctx, s->object_tab); + js_free(ctx, s->hash_table); +} + +/* indicate that the object may be part of a function prototype cycle */ +void set_cycle_flag(JSContext* ctx, JSValueConst obj) {} + +void remove_gc_object(JSGCObjectHeader* h) { + list_del(&h->link); +} + +void free_var_ref(JSRuntime* rt, JSVarRef* var_ref) { + if (var_ref) { + assert(var_ref->header.ref_count > 0); + if (--var_ref->header.ref_count == 0) { + if (var_ref->is_detached) { + JS_FreeValueRT(rt, var_ref->value); + remove_gc_object(&var_ref->header); + } else { + list_del(&var_ref->header.link); /* still on the stack */ + } + js_free_rt(rt, var_ref); + } + } +} + +void free_object(JSRuntime* rt, JSObject* p) { + int i; + JSClassFinalizer* finalizer; + JSShape* sh; + JSShapeProperty* pr; + + p->free_mark = 1; /* used to tell the object is invalid when + freeing cycles */ + /* free all the fields */ + sh = p->shape; + pr = get_shape_prop(sh); + for (i = 0; i < sh->prop_count; i++) { + free_property(rt, &p->prop[i], pr->flags); + pr++; + } + js_free_rt(rt, p->prop); + /* as an optimization we destroy the shape immediately without + putting it in gc_zero_ref_count_list */ + js_free_shape(rt, sh); + + /* fail safe */ + p->shape = NULL; + p->prop = NULL; + + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, p); + } + + finalizer = rt->class_array[p->class_id].finalizer; + if (finalizer) + (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); + + /* fail safe */ + p->class_id = 0; + p->u.opaque = NULL; + p->u.func.var_refs = NULL; + p->u.func.home_object = NULL; + + remove_gc_object(&p->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { + list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, p); + } +} + +void free_gc_object(JSRuntime* rt, JSGCObjectHeader* gp) { + switch (gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + free_object(rt, (JSObject*)gp); + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + free_function_bytecode(rt, (JSFunctionBytecode*)gp); + break; + default: + abort(); + } +} + +void free_zero_refcount(JSRuntime* rt) { + struct list_head* el; + JSGCObjectHeader* p; + + rt->gc_phase = JS_GC_PHASE_DECREF; + for (;;) { + el = rt->gc_zero_ref_count_list.next; + if (el == &rt->gc_zero_ref_count_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count == 0); + free_gc_object(rt, p); + } + rt->gc_phase = JS_GC_PHASE_NONE; +} + +/* called with the ref_count of 'v' reaches zero. */ +void __JS_FreeValueRT(JSRuntime* rt, JSValue v) { + uint32_t tag = JS_VALUE_GET_TAG(v); + +#ifdef DUMP_FREE + { + printf("Freeing "); + if (tag == JS_TAG_OBJECT) { + JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); + } else { + JS_DumpValueShort(rt, v); + printf("\n"); + } + } +#endif + + switch (tag) { + case JS_TAG_STRING: { + JSString* p = JS_VALUE_GET_STRING(v); + if (p->atom_type) { + JS_FreeAtomStruct(rt, p); + } else { +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } break; + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: { + JSGCObjectHeader* p = JS_VALUE_GET_PTR(v); + if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { + list_del(&p->link); + list_add(&p->link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_NONE) { + free_zero_refcount(rt); + } + } + } break; + case JS_TAG_MODULE: + abort(); /* never freed here */ + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: { + JSBigFloat* bf = JS_VALUE_GET_PTR(v); + bf_delete(&bf->num); + js_free_rt(rt, bf); + } break; + case JS_TAG_BIG_DECIMAL: { + JSBigDecimal* bf = JS_VALUE_GET_PTR(v); + bfdec_delete(&bf->num); + js_free_rt(rt, bf); + } break; +#endif + case JS_TAG_SYMBOL: { + JSAtomStruct* p = JS_VALUE_GET_PTR(v); + JS_FreeAtomStruct(rt, p); + } break; + default: + printf("__JS_FreeValue: unknown tag=%d\n", tag); + abort(); + } +} + +void __JS_FreeValue(JSContext* ctx, JSValue v) { + __JS_FreeValueRT(ctx->rt, v); +} + +/* garbage collection */ + +void add_gc_object(JSRuntime* rt, JSGCObjectHeader* h, JSGCObjectTypeEnum type) { + h->mark = 0; + h->gc_obj_type = type; + list_add_tail(&h->link, &rt->gc_obj_list); +} + +void JS_MarkValue(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func) { + if (JS_VALUE_HAS_REF_COUNT(val)) { + switch (JS_VALUE_GET_TAG(val)) { + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + mark_func(rt, JS_VALUE_GET_PTR(val)); + break; + default: + break; + } + } +} + +/* XXX: would be more efficient with separate module lists */ +void js_free_modules(JSContext* ctx, JSFreeModuleEnum flag) { + struct list_head *el, *el1; + list_for_each_safe(el, el1, &ctx->loaded_modules) { + JSModuleDef* m = list_entry(el, JSModuleDef, link); + if (flag == JS_FREE_MODULE_ALL || (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) || (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) { + js_free_module_def(ctx, m); + } + } +} + +JSContext* JS_DupContext(JSContext* ctx) { + ctx->header.ref_count++; + return ctx; +} + +/* used by the GC */ +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 mark_children(JSRuntime* rt, JSGCObjectHeader* gp, JS_MarkFunc* mark_func) { + switch (gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: { + JSObject* p = (JSObject*)gp; + JSShapeProperty* prs; + JSShape* sh; + int i; + sh = p->shape; + mark_func(rt, &sh->header); + /* mark all the fields */ + prs = get_shape_prop(sh); + for (i = 0; i < sh->prop_count; i++) { + JSProperty* pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL) { + if (prs->flags & JS_PROP_TMASK) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + mark_func(rt, &pr->u.getset.getter->header); + if (pr->u.getset.setter) + mark_func(rt, &pr->u.getset.setter->header); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (pr->u.var_ref->is_detached) { + /* Note: the tag does not matter + provided it is a GC object */ + mark_func(rt, &pr->u.var_ref->header); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_mark(rt, pr, mark_func); + } + } else { + JS_MarkValue(rt, pr->u.value, mark_func); + } + } + prs++; + } + + if (p->class_id != JS_CLASS_OBJECT) { + JSClassGCMark* gc_mark; + gc_mark = rt->class_array[p->class_id].gc_mark; + if (gc_mark) + gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); + } + } break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + /* the template objects can be part of a cycle */ + { + JSFunctionBytecode* b = (JSFunctionBytecode*)gp; + int i; + for (i = 0; i < b->cpool_count; i++) { + JS_MarkValue(rt, b->cpool[i], mark_func); + } + if (b->realm) + mark_func(rt, &b->realm->header); + } + break; + case JS_GC_OBJ_TYPE_VAR_REF: { + JSVarRef* var_ref = (JSVarRef*)gp; + /* only detached variable referenced are taken into account */ + assert(var_ref->is_detached); + JS_MarkValue(rt, *var_ref->pvalue, mark_func); + } break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: { + JSAsyncFunctionData* s = (JSAsyncFunctionData*)gp; + if (s->is_active) + async_func_mark(rt, &s->func_state, mark_func); + JS_MarkValue(rt, s->resolving_funcs[0], mark_func); + JS_MarkValue(rt, s->resolving_funcs[1], mark_func); + } break; + case JS_GC_OBJ_TYPE_SHAPE: { + JSShape* sh = (JSShape*)gp; + if (sh->proto != NULL) { + mark_func(rt, &sh->proto->header); + } + } break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: { + JSContext* ctx = (JSContext*)gp; + JS_MarkContext(rt, ctx, mark_func); + } break; + default: + abort(); + } +} + +void gc_decref_child(JSRuntime* rt, JSGCObjectHeader* p) { + assert(p->ref_count > 0); + p->ref_count--; + if (p->ref_count == 0 && p->mark == 1) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } +} + +void gc_decref(JSRuntime* rt) { + struct list_head *el, *el1; + JSGCObjectHeader* p; + + init_list_head(&rt->tmp_obj_list); + + /* decrement the refcount of all the children of all the GC + objects and move the GC objects with zero refcount to + tmp_obj_list */ + list_for_each_safe(el, el1, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->mark == 0); + mark_children(rt, p, gc_decref_child); + p->mark = 1; + if (p->ref_count == 0) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } + } +} + +void gc_scan_incref_child(JSRuntime* rt, JSGCObjectHeader* p) { + p->ref_count++; + if (p->ref_count == 1) { + /* ref_count was 0: remove from tmp_obj_list and add at the + end of gc_obj_list */ + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_obj_list); + p->mark = 0; /* reset the mark for the next GC call */ + } +} + +void gc_scan_incref_child2(JSRuntime* rt, JSGCObjectHeader* p) { + p->ref_count++; +} + +void gc_scan(JSRuntime* rt) { + struct list_head* el; + JSGCObjectHeader* p; + + /* keep the objects with a refcount > 0 and their children. */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count > 0); + p->mark = 0; /* reset the mark for the next GC call */ + mark_children(rt, p, gc_scan_incref_child); + } + + /* restore the refcount of the objects to be deleted. */ + list_for_each(el, &rt->tmp_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + mark_children(rt, p, gc_scan_incref_child2); + } +} + +void gc_free_cycles(JSRuntime* rt) { + struct list_head *el, *el1; + JSGCObjectHeader* p; +#ifdef DUMP_GC_FREE + BOOL header_done = FALSE; +#endif + + rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; + + for (;;) { + el = rt->tmp_obj_list.next; + if (el == &rt->tmp_obj_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + /* Only need to free the GC object associated with JS + values. The rest will be automatically removed because they + must be referenced by them. */ + switch (p->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: +#ifdef DUMP_GC_FREE + if (!header_done) { + printf("Freeing cycles:\n"); + JS_DumpObjectHeader(rt); + header_done = TRUE; + } + JS_DumpGCObject(rt, p); +#endif + free_gc_object(rt, p); + break; + default: + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_zero_ref_count_list); + break; + } + } + rt->gc_phase = JS_GC_PHASE_NONE; + + list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + js_free_rt(rt, p); + } + + init_list_head(&rt->gc_zero_ref_count_list); +} + +void JS_RunGC(JSRuntime* rt) { + /* decrement the reference of the children of each object. mark = + 1 after this pass. */ + gc_decref(rt); + + /* keep the GC objects with a non zero refcount and their childs */ + gc_scan(rt); + + /* free the GC objects in a cycle */ + gc_free_cycles(rt); +} + +/* Return false if not an object or if the object has already been + freed (zombie objects are visible in finalizers when freeing + cycles). */ +BOOL JS_IsLiveObject(JSRuntime* rt, JSValueConst obj) { + JSObject* p; + if (!JS_IsObject(obj)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + return !p->free_mark; } \ No newline at end of file diff --git a/src/core/gc.h b/src/core/gc.h index 70183fdb5..eeb23febf 100644 --- a/src/core/gc.h +++ b/src/core/gc.h @@ -1,120 +1,120 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_GC_H -#define QUICKJS_GC_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "quickjs/list.h" -#include "types.h" - -/* 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; - -typedef enum JSFreeModuleEnum { - JS_FREE_MODULE_ALL, - JS_FREE_MODULE_NOT_RESOLVED, - JS_FREE_MODULE_NOT_EVALUATED, -} JSFreeModuleEnum; - -void js_object_list_init(JSObjectList* s); -uint32_t js_object_list_get_hash(JSObject* p, uint32_t hash_size); -int js_object_list_resize_hash(JSContext* ctx, JSObjectList* s, uint32_t new_hash_size); -/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if - memory error */ -int js_object_list_add(JSContext* ctx, JSObjectList* s, JSObject* obj); - -/* return -1 if not present or the object index */ -int js_object_list_find(JSContext* ctx, JSObjectList* s, JSObject* obj); - -void js_object_list_end(JSContext* ctx, JSObjectList* s); -void free_gc_object(JSRuntime* rt, JSGCObjectHeader* gp); -void free_zero_refcount(JSRuntime* rt); - -/* XXX: would be more efficient with separate module lists */ -void js_free_modules(JSContext* ctx, JSFreeModuleEnum flag); - -__maybe_unused void JS_DumpObjectHeader(JSRuntime* rt); -__maybe_unused void JS_DumpObject(JSRuntime* rt, JSObject* p); -__maybe_unused void JS_DumpGCObject(JSRuntime* rt, JSGCObjectHeader* p); -__maybe_unused void JS_DumpValueShort(JSRuntime* rt, JSValueConst val); -__maybe_unused void JS_DumpValue(JSContext* ctx, JSValueConst val); -__maybe_unused void JS_PrintValue(JSContext* ctx, const char* str, JSValueConst val); - -/* used by the GC */ -void JS_MarkContext(JSRuntime* rt, JSContext* ctx, JS_MarkFunc* mark_func); - -void mark_children(JSRuntime* rt, JSGCObjectHeader* gp, JS_MarkFunc* mark_func); -void gc_decref_child(JSRuntime* rt, JSGCObjectHeader* p); -void gc_decref(JSRuntime* rt); -void gc_scan_incref_child(JSRuntime* rt, JSGCObjectHeader* p); -void gc_scan_incref_child2(JSRuntime* rt, JSGCObjectHeader* p); -void gc_scan(JSRuntime* rt); -void gc_free_cycles(JSRuntime* rt); - - void free_var_ref(JSRuntime* rt, JSVarRef* var_ref); -void free_object(JSRuntime* rt, JSObject* p); -void add_gc_object(JSRuntime* rt, JSGCObjectHeader* h, JSGCObjectTypeEnum type); -void set_cycle_flag(JSContext* ctx, JSValueConst obj); -void remove_gc_object(JSGCObjectHeader* h); -void js_regexp_finalizer(JSRuntime* rt, JSValue val); -void js_array_buffer_finalizer(JSRuntime* rt, JSValue val); -void js_typed_array_finalizer(JSRuntime* rt, JSValue val); -void js_typed_array_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -void js_proxy_finalizer(JSRuntime* rt, JSValue val); -void js_proxy_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -void js_map_finalizer(JSRuntime* rt, JSValue val); -void js_map_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -void js_map_iterator_finalizer(JSRuntime* rt, JSValue val); -void js_map_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); - -void js_regexp_string_iterator_finalizer(JSRuntime* rt, JSValue val); -void js_regexp_string_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -void js_generator_finalizer(JSRuntime* rt, JSValue obj); -void js_generator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -void js_promise_finalizer(JSRuntime* rt, JSValue val); -void js_promise_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -void js_promise_resolve_function_finalizer(JSRuntime* rt, JSValue val); -void js_promise_resolve_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -#ifdef CONFIG_BIGNUM -void js_operator_set_finalizer(JSRuntime* rt, JSValue val); -void js_operator_set_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); -#endif - -#endif +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_GC_H +#define QUICKJS_GC_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "quickjs/list.h" +#include "types.h" + +/* 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; + +typedef enum JSFreeModuleEnum { + JS_FREE_MODULE_ALL, + JS_FREE_MODULE_NOT_RESOLVED, + JS_FREE_MODULE_NOT_EVALUATED, +} JSFreeModuleEnum; + +void js_object_list_init(JSObjectList* s); +uint32_t js_object_list_get_hash(JSObject* p, uint32_t hash_size); +int js_object_list_resize_hash(JSContext* ctx, JSObjectList* s, uint32_t new_hash_size); +/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if + memory error */ +int js_object_list_add(JSContext* ctx, JSObjectList* s, JSObject* obj); + +/* return -1 if not present or the object index */ +int js_object_list_find(JSContext* ctx, JSObjectList* s, JSObject* obj); + +void js_object_list_end(JSContext* ctx, JSObjectList* s); +void free_gc_object(JSRuntime* rt, JSGCObjectHeader* gp); +void free_zero_refcount(JSRuntime* rt); + +/* XXX: would be more efficient with separate module lists */ +void js_free_modules(JSContext* ctx, JSFreeModuleEnum flag); + +__maybe_unused void JS_DumpObjectHeader(JSRuntime* rt); +__maybe_unused void JS_DumpObject(JSRuntime* rt, JSObject* p); +__maybe_unused void JS_DumpGCObject(JSRuntime* rt, JSGCObjectHeader* p); +__maybe_unused void JS_DumpValueShort(JSRuntime* rt, JSValueConst val); +__maybe_unused void JS_DumpValue(JSContext* ctx, JSValueConst val); +__maybe_unused void JS_PrintValue(JSContext* ctx, const char* str, JSValueConst val); + +/* used by the GC */ +void JS_MarkContext(JSRuntime* rt, JSContext* ctx, JS_MarkFunc* mark_func); + +void mark_children(JSRuntime* rt, JSGCObjectHeader* gp, JS_MarkFunc* mark_func); +void gc_decref_child(JSRuntime* rt, JSGCObjectHeader* p); +void gc_decref(JSRuntime* rt); +void gc_scan_incref_child(JSRuntime* rt, JSGCObjectHeader* p); +void gc_scan_incref_child2(JSRuntime* rt, JSGCObjectHeader* p); +void gc_scan(JSRuntime* rt); +void gc_free_cycles(JSRuntime* rt); + + void free_var_ref(JSRuntime* rt, JSVarRef* var_ref); +void free_object(JSRuntime* rt, JSObject* p); +void add_gc_object(JSRuntime* rt, JSGCObjectHeader* h, JSGCObjectTypeEnum type); +void set_cycle_flag(JSContext* ctx, JSValueConst obj); +void remove_gc_object(JSGCObjectHeader* h); +void js_regexp_finalizer(JSRuntime* rt, JSValue val); +void js_array_buffer_finalizer(JSRuntime* rt, JSValue val); +void js_typed_array_finalizer(JSRuntime* rt, JSValue val); +void js_typed_array_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +void js_proxy_finalizer(JSRuntime* rt, JSValue val); +void js_proxy_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +void js_map_finalizer(JSRuntime* rt, JSValue val); +void js_map_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +void js_map_iterator_finalizer(JSRuntime* rt, JSValue val); +void js_map_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); + +void js_regexp_string_iterator_finalizer(JSRuntime* rt, JSValue val); +void js_regexp_string_iterator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +void js_generator_finalizer(JSRuntime* rt, JSValue obj); +void js_generator_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +void js_promise_finalizer(JSRuntime* rt, JSValue val); +void js_promise_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +void js_promise_resolve_function_finalizer(JSRuntime* rt, JSValue val); +void js_promise_resolve_function_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +#ifdef CONFIG_BIGNUM +void js_operator_set_finalizer(JSRuntime* rt, JSValue val); +void js_operator_set_mark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* mark_func); +#endif + +#endif diff --git a/src/core/malloc.c b/src/core/malloc.c index abe08160d..8b27eb95b 100644 --- a/src/core/malloc.c +++ b/src/core/malloc.c @@ -1,242 +1,242 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "quickjs/cutils.h" -#include "malloc.h" -#include "exception.h" - -void js_trigger_gc(JSRuntime* rt, size_t size) { - BOOL force_gc; -#ifdef FORCE_GC_AT_MALLOC - force_gc = TRUE; -#else - force_gc = ((rt->malloc_state.malloc_size + size) > rt->malloc_gc_threshold); -#endif - if (force_gc) { -#ifdef DUMP_GC - printf("GC: size=%" PRIu64 "\n", (uint64_t)rt->malloc_state.malloc_size); -#endif - JS_RunGC(rt); - rt->malloc_gc_threshold = rt->malloc_state.malloc_size + (rt->malloc_state.malloc_size >> 1); - } -} - - -/* default memory allocation functions with memory limitation */ -static inline size_t js_def_malloc_usable_size(void* ptr) { -#if defined(__APPLE__) - return malloc_size(ptr); -#elif defined(_WIN32) - return _msize(ptr); -#elif defined(EMSCRIPTEN) - return 0; -#elif defined(__linux__) - return malloc_usable_size(ptr); -#else - /* change this to `return 0;` if compilation fails */ - return malloc_usable_size(ptr); -#endif -} - -size_t js_malloc_usable_size_unknown(const void* ptr) { - return 0; -} - -void* js_malloc_rt(JSRuntime* rt, size_t size) { - return rt->mf.js_malloc(&rt->malloc_state, size); -} - -void js_free_rt(JSRuntime* rt, void* ptr) { - rt->mf.js_free(&rt->malloc_state, ptr); -} - -void* js_realloc_rt(JSRuntime* rt, void* ptr, size_t size) { - return rt->mf.js_realloc(&rt->malloc_state, ptr, size); -} - -size_t js_malloc_usable_size_rt(JSRuntime* rt, const void* ptr) { - return rt->mf.js_malloc_usable_size(ptr); -} - -void* js_mallocz_rt(JSRuntime* rt, size_t size) { - void* ptr; - ptr = js_malloc_rt(rt, size); - if (!ptr) - return NULL; - return memset(ptr, 0, size); -} - -#ifdef CONFIG_BIGNUM -/* called by libbf */ -void* js_bf_realloc(void* opaque, void* ptr, size_t size) { - JSRuntime* rt = opaque; - return js_realloc_rt(rt, ptr, size); -} -#endif /* CONFIG_BIGNUM */ - -/* Throw out of memory in case of error */ -void* js_malloc(JSContext* ctx, size_t size) { - void* ptr; - ptr = js_malloc_rt(ctx->rt, size); - if (unlikely(!ptr)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - return ptr; -} - -/* Throw out of memory in case of error */ -void* js_mallocz(JSContext* ctx, size_t size) { - void* ptr; - ptr = js_mallocz_rt(ctx->rt, size); - if (unlikely(!ptr)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - return ptr; -} - -void js_free(JSContext* ctx, void* ptr) { - js_free_rt(ctx->rt, ptr); -} - -/* Throw out of memory in case of error */ -void* js_realloc(JSContext* ctx, void* ptr, size_t size) { - void* ret; - ret = js_realloc_rt(ctx->rt, ptr, size); - if (unlikely(!ret && size != 0)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - return ret; -} - -/* store extra allocated size in *pslack if successful */ -void* js_realloc2(JSContext* ctx, void* ptr, size_t size, size_t* pslack) { - void* ret; - ret = js_realloc_rt(ctx->rt, ptr, size); - if (unlikely(!ret && size != 0)) { - JS_ThrowOutOfMemory(ctx); - return NULL; - } - if (pslack) { - size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); - *pslack = (new_size > size) ? new_size - size : 0; - } - return ret; -} - -size_t js_malloc_usable_size(JSContext* ctx, const void* ptr) { - return js_malloc_usable_size_rt(ctx->rt, ptr); -} - -/* Throw out of memory exception in case of error */ -char* js_strndup(JSContext* ctx, const char* s, size_t n) { - char* ptr; - ptr = js_malloc(ctx, n + 1); - if (ptr) { - memcpy(ptr, s, n); - ptr[n] = '\0'; - } - return ptr; -} - -char* js_strdup(JSContext* ctx, const char* str) { - return js_strndup(ctx, str, strlen(str)); -} - -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; -} - -void* js_def_malloc(JSMallocState* s, size_t size) { - void* ptr; - - /* Do not allocate zero bytes: behavior is platform dependent */ - assert(size != 0); - - if (unlikely(s->malloc_size + size > s->malloc_limit)) - return NULL; - - ptr = malloc(size); - if (!ptr) - return NULL; - - s->malloc_count++; - s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - return ptr; -} - -void js_def_free(JSMallocState* s, void* ptr) { - if (!ptr) - return; - - s->malloc_count--; - s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; - free(ptr); -} - -void* js_def_realloc(JSMallocState* s, void* ptr, size_t size) { - size_t old_size; - - if (!ptr) { - if (size == 0) - return NULL; - return js_def_malloc(s, size); - } - old_size = js_def_malloc_usable_size(ptr); - if (size == 0) { - s->malloc_count--; - s->malloc_size -= old_size + MALLOC_OVERHEAD; - free(ptr); - return NULL; - } - if (s->malloc_size + size - old_size > s->malloc_limit) - return NULL; - - ptr = realloc(ptr, size); - if (!ptr) - return NULL; - - s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; - return ptr; -} - -/* use -1 to disable automatic GC */ -void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) -{ - rt->malloc_gc_threshold = gc_threshold; +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "quickjs/cutils.h" +#include "malloc.h" +#include "exception.h" + +void js_trigger_gc(JSRuntime* rt, size_t size) { + BOOL force_gc; +#ifdef FORCE_GC_AT_MALLOC + force_gc = TRUE; +#else + force_gc = ((rt->malloc_state.malloc_size + size) > rt->malloc_gc_threshold); +#endif + if (force_gc) { +#ifdef DUMP_GC + printf("GC: size=%" PRIu64 "\n", (uint64_t)rt->malloc_state.malloc_size); +#endif + JS_RunGC(rt); + rt->malloc_gc_threshold = rt->malloc_state.malloc_size + (rt->malloc_state.malloc_size >> 1); + } +} + + +/* default memory allocation functions with memory limitation */ +static inline size_t js_def_malloc_usable_size(void* ptr) { +#if defined(__APPLE__) + return malloc_size(ptr); +#elif defined(_WIN32) + return _msize(ptr); +#elif defined(EMSCRIPTEN) + return 0; +#elif defined(__linux__) + return malloc_usable_size(ptr); +#else + /* change this to `return 0;` if compilation fails */ + return malloc_usable_size(ptr); +#endif +} + +size_t js_malloc_usable_size_unknown(const void* ptr) { + return 0; +} + +void* js_malloc_rt(JSRuntime* rt, size_t size) { + return rt->mf.js_malloc(&rt->malloc_state, size); +} + +void js_free_rt(JSRuntime* rt, void* ptr) { + rt->mf.js_free(&rt->malloc_state, ptr); +} + +void* js_realloc_rt(JSRuntime* rt, void* ptr, size_t size) { + return rt->mf.js_realloc(&rt->malloc_state, ptr, size); +} + +size_t js_malloc_usable_size_rt(JSRuntime* rt, const void* ptr) { + return rt->mf.js_malloc_usable_size(ptr); +} + +void* js_mallocz_rt(JSRuntime* rt, size_t size) { + void* ptr; + ptr = js_malloc_rt(rt, size); + if (!ptr) + return NULL; + return memset(ptr, 0, size); +} + +#ifdef CONFIG_BIGNUM +/* called by libbf */ +void* js_bf_realloc(void* opaque, void* ptr, size_t size) { + JSRuntime* rt = opaque; + return js_realloc_rt(rt, ptr, size); +} +#endif /* CONFIG_BIGNUM */ + +/* Throw out of memory in case of error */ +void* js_malloc(JSContext* ctx, size_t size) { + void* ptr; + ptr = js_malloc_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void* js_mallocz(JSContext* ctx, size_t size) { + void* ptr; + ptr = js_mallocz_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +void js_free(JSContext* ctx, void* ptr) { + js_free_rt(ctx->rt, ptr); +} + +/* Throw out of memory in case of error */ +void* js_realloc(JSContext* ctx, void* ptr, size_t size) { + void* ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ret; +} + +/* store extra allocated size in *pslack if successful */ +void* js_realloc2(JSContext* ctx, void* ptr, size_t size, size_t* pslack) { + void* ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + if (pslack) { + size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); + *pslack = (new_size > size) ? new_size - size : 0; + } + return ret; +} + +size_t js_malloc_usable_size(JSContext* ctx, const void* ptr) { + return js_malloc_usable_size_rt(ctx->rt, ptr); +} + +/* Throw out of memory exception in case of error */ +char* js_strndup(JSContext* ctx, const char* s, size_t n) { + char* ptr; + ptr = js_malloc(ctx, n + 1); + if (ptr) { + memcpy(ptr, s, n); + ptr[n] = '\0'; + } + return ptr; +} + +char* js_strdup(JSContext* ctx, const char* str) { + return js_strndup(ctx, str, strlen(str)); +} + +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; +} + +void* js_def_malloc(JSMallocState* s, size_t size) { + void* ptr; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(size != 0); + + if (unlikely(s->malloc_size + size > s->malloc_limit)) + return NULL; + + ptr = malloc(size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +void js_def_free(JSMallocState* s, void* ptr) { + if (!ptr) + return; + + s->malloc_count--; + s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; + free(ptr); +} + +void* js_def_realloc(JSMallocState* s, void* ptr, size_t size) { + size_t old_size; + + if (!ptr) { + if (size == 0) + return NULL; + return js_def_malloc(s, size); + } + old_size = js_def_malloc_usable_size(ptr); + if (size == 0) { + s->malloc_count--; + s->malloc_size -= old_size + MALLOC_OVERHEAD; + free(ptr); + return NULL; + } + if (s->malloc_size + size - old_size > s->malloc_limit) + return NULL; + + ptr = realloc(ptr, size); + if (!ptr) + return NULL; + + s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; + return ptr; +} + +/* use -1 to disable automatic GC */ +void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) +{ + rt->malloc_gc_threshold = gc_threshold; } \ No newline at end of file diff --git a/src/core/malloc.h b/src/core/malloc.h index e9c4b39a8..0cd202f85 100644 --- a/src/core/malloc.h +++ b/src/core/malloc.h @@ -1,59 +1,59 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_MALLOC_H -#define QUICKJS_MALLOC_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" -#include "types.h" - -void js_trigger_gc(JSRuntime* rt, size_t size); -no_inline int js_realloc_array(JSContext* ctx, void** parray, int elem_size, int* psize, int req_size); - -/* 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); -} - - -void* js_def_malloc(JSMallocState* s, size_t size); -void js_def_free(JSMallocState* s, void* ptr); -void* js_def_realloc(JSMallocState* s, void* ptr, size_t size); -size_t js_malloc_usable_size_unknown(const void* ptr); - - -#if CONFIG_BIGNUM -void* js_bf_realloc(void* opaque, void* ptr, size_t size); -#endif - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_MALLOC_H +#define QUICKJS_MALLOC_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" +#include "types.h" + +void js_trigger_gc(JSRuntime* rt, size_t size); +no_inline int js_realloc_array(JSContext* ctx, void** parray, int elem_size, int* psize, int req_size); + +/* 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); +} + + +void* js_def_malloc(JSMallocState* s, size_t size); +void js_def_free(JSMallocState* s, void* ptr); +void* js_def_realloc(JSMallocState* s, void* ptr, size_t size); +size_t js_malloc_usable_size_unknown(const void* ptr); + + +#if CONFIG_BIGNUM +void* js_bf_realloc(void* opaque, void* ptr, size_t size); +#endif + #endif \ No newline at end of file diff --git a/src/core/memory.c b/src/core/memory.c index 674358566..af01f9ec3 100644 --- a/src/core/memory.c +++ b/src/core/memory.c @@ -1,529 +1,529 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "memory.h" -#include "function.h" -#include "runtime.h" -#include "shape.h" -#include "string.h" - -/* Compute memory used by various object types */ -/* XXX: poor man's approach to handling multiply referenced objects */ -typedef struct JSMemoryUsage_helper { - double memory_used_count; - double str_count; - double str_size; - int64_t js_func_count; - double js_func_size; - int64_t js_func_code_size; - int64_t js_func_pc2line_count; - int64_t js_func_pc2line_size; -} JSMemoryUsage_helper; - -static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp); - -static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) -{ - if (!str->atom_type) { /* atoms are handled separately */ - double s_ref_count = str->header.ref_count; - hp->str_count += 1 / s_ref_count; - hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + - 1 - str->is_wide_char) / s_ref_count); - } -} - -static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) -{ - int memory_used_count, js_func_size, i; - - memory_used_count = 0; - js_func_size = offsetof(JSFunctionBytecode, debug); - if (b->vardefs) { - js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); - } - if (b->cpool) { - js_func_size += b->cpool_count * sizeof(*b->cpool); - for (i = 0; i < b->cpool_count; i++) { - JSValueConst val = b->cpool[i]; - compute_value_size(val, hp); - } - } - if (b->closure_var) { - js_func_size += b->closure_var_count * sizeof(*b->closure_var); - } - if (!b->read_only_bytecode && b->byte_code_buf) { - hp->js_func_code_size += b->byte_code_len; - } - if (b->has_debug) { - js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug); - if (b->debug.source) { - memory_used_count++; - js_func_size += b->debug.source_len + 1; - } - if (b->debug.pc2line_len) { - memory_used_count++; - hp->js_func_pc2line_count += 1; - hp->js_func_pc2line_size += b->debug.pc2line_len; - } - } - hp->js_func_size += js_func_size; - hp->js_func_count += 1; - hp->memory_used_count += memory_used_count; -} - -static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) -{ - switch(JS_VALUE_GET_TAG(val)) { - case JS_TAG_STRING: - compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); - break; -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_INT: - case JS_TAG_BIG_FLOAT: - case JS_TAG_BIG_DECIMAL: - /* should track JSBigFloat usage */ - break; -#endif - } -} - -void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) -{ - struct list_head *el, *el1; - int i; - JSMemoryUsage_helper mem = { 0 }, *hp = &mem; - - memset(s, 0, sizeof(*s)); - s->malloc_count = rt->malloc_state.malloc_count; - s->malloc_size = rt->malloc_state.malloc_size; - s->malloc_limit = rt->malloc_state.malloc_limit; - - s->memory_used_count = 2; /* rt + rt->class_array */ - s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count; - - list_for_each(el, &rt->context_list) { - JSContext *ctx = list_entry(el, JSContext, link); - JSShape *sh = ctx->array_shape; - s->memory_used_count += 2; /* ctx + ctx->class_proto */ - s->memory_used_size += sizeof(JSContext) + - sizeof(JSValue) * rt->class_count; - s->binary_object_count += ctx->binary_object_count; - s->binary_object_size += ctx->binary_object_size; - - /* the hashed shapes are counted separately */ - if (sh && !sh->is_hashed) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size(hash_size, sh->prop_size); - } - list_for_each(el1, &ctx->loaded_modules) { - JSModuleDef *m = list_entry(el1, JSModuleDef, link); - s->memory_used_count += 1; - s->memory_used_size += sizeof(*m); - if (m->req_module_entries) { - s->memory_used_count += 1; - s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); - } - if (m->export_entries) { - s->memory_used_count += 1; - s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); - for (i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { - /* potential multiple count */ - s->memory_used_count += 1; - compute_value_size(me->u.local.var_ref->value, hp); - } - } - } - if (m->star_export_entries) { - s->memory_used_count += 1; - s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); - } - if (m->import_entries) { - s->memory_used_count += 1; - s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); - } - compute_value_size(m->module_ns, hp); - compute_value_size(m->func_obj, hp); - } - } - - list_for_each(el, &rt->gc_obj_list) { - JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); - JSObject *p; - JSShape *sh; - JSShapeProperty *prs; - - /* XXX: could count the other GC object types too */ - if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { - compute_bytecode_size((JSFunctionBytecode *)gp, hp); - continue; - } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { - continue; - } - p = (JSObject *)gp; - sh = p->shape; - s->obj_count++; - if (p->prop) { - s->memory_used_count++; - s->prop_size += sh->prop_size * sizeof(*p->prop); - s->prop_count += sh->prop_count; - prs = get_shape_prop(sh); - for(i = 0; i < sh->prop_count; i++) { - JSProperty *pr = &p->prop[i]; - if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { - compute_value_size(pr->u.value, hp); - } - prs++; - } - } - /* the hashed shapes are counted separately */ - if (!sh->is_hashed) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size(hash_size, sh->prop_size); - } - - switch(p->class_id) { - case JS_CLASS_ARRAY: /* u.array | length */ - case JS_CLASS_ARGUMENTS: /* u.array | length */ - s->array_count++; - if (p->fast_array) { - s->fast_array_count++; - if (p->u.array.u.values) { - s->memory_used_count++; - s->memory_used_size += p->u.array.count * - sizeof(*p->u.array.u.values); - s->fast_array_elements += p->u.array.count; - for (i = 0; i < p->u.array.count; i++) { - compute_value_size(p->u.array.u.values[i], hp); - } - } - } - break; - case JS_CLASS_NUMBER: /* u.object_data */ - case JS_CLASS_STRING: /* u.object_data */ - case JS_CLASS_BOOLEAN: /* u.object_data */ - case JS_CLASS_SYMBOL: /* u.object_data */ - case JS_CLASS_DATE: /* u.object_data */ -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT: /* u.object_data */ - case JS_CLASS_BIG_FLOAT: /* u.object_data */ - case JS_CLASS_BIG_DECIMAL: /* u.object_data */ -#endif - compute_value_size(p->u.object_data, hp); - break; - case JS_CLASS_C_FUNCTION: /* u.cfunc */ - s->c_func_count++; - break; - case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ - { - JSFunctionBytecode *b = p->u.func.function_bytecode; - JSVarRef **var_refs = p->u.func.var_refs; - /* home_object: object will be accounted for in list scan */ - if (var_refs) { - s->memory_used_count++; - s->js_func_size += b->closure_var_count * sizeof(*var_refs); - for (i = 0; i < b->closure_var_count; i++) { - if (var_refs[i]) { - double ref_count = var_refs[i]->header.ref_count; - s->memory_used_count += 1 / ref_count; - s->js_func_size += sizeof(*var_refs[i]) / ref_count; - /* handle non object closed values */ - if (var_refs[i]->pvalue == &var_refs[i]->value) { - /* potential multiple count */ - compute_value_size(var_refs[i]->value, hp); - } - } - } - } - } - break; - case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ - { - JSBoundFunction *bf = p->u.bound_function; - /* func_obj and this_val are objects */ - for (i = 0; i < bf->argc; i++) { - compute_value_size(bf->argv[i], hp); - } - s->memory_used_count += 1; - s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); - } - break; - case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ - { - JSCFunctionDataRecord *fd = p->u.c_function_data_record; - if (fd) { - for (i = 0; i < fd->data_len; i++) { - compute_value_size(fd->data[i], hp); - } - s->memory_used_count += 1; - s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); - } - } - break; - case JS_CLASS_REGEXP: /* u.regexp */ - compute_jsstring_size(p->u.regexp.pattern, hp); - compute_jsstring_size(p->u.regexp.bytecode, hp); - break; - - case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ - { - JSForInIterator *it = p->u.for_in_iterator; - if (it) { - compute_value_size(it->obj, hp); - s->memory_used_count += 1; - s->memory_used_size += sizeof(*it); - } - } - break; - case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ - case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ - { - JSArrayBuffer *abuf = p->u.array_buffer; - if (abuf) { - s->memory_used_count += 1; - s->memory_used_size += sizeof(*abuf); - if (abuf->data) { - s->memory_used_count += 1; - s->memory_used_size += abuf->byte_length; - } - } - } - break; - case JS_CLASS_GENERATOR: /* u.generator_data */ - case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ -#endif - case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ - case JS_CLASS_DATAVIEW: /* u.typed_array */ -#ifdef CONFIG_BIGNUM - case JS_CLASS_FLOAT_ENV: /* u.float_env */ -#endif - case JS_CLASS_MAP: /* u.map_state */ - case JS_CLASS_SET: /* u.map_state */ - case JS_CLASS_WEAKMAP: /* u.map_state */ - case JS_CLASS_WEAKSET: /* u.map_state */ - case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ - case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ - case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ - case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ - case JS_CLASS_PROXY: /* u.proxy_data */ - case JS_CLASS_PROMISE: /* u.promise_data */ - case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ - case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ - case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ - case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ - case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ - case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ - /* TODO */ - default: - /* XXX: class definition should have an opaque block size */ - if (p->u.opaque) { - s->memory_used_count += 1; - } - break; - } - } - s->obj_size += s->obj_count * sizeof(JSObject); - - /* hashed shapes */ - s->memory_used_count++; /* rt->shape_hash */ - s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; - for(i = 0; i < rt->shape_hash_size; i++) { - JSShape *sh; - for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { - int hash_size = sh->prop_hash_mask + 1; - s->shape_count++; - s->shape_size += get_shape_size(hash_size, sh->prop_size); - } - } - - /* atoms */ - s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ - s->atom_count = rt->atom_count; - s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + - sizeof(rt->atom_hash[0]) * rt->atom_hash_size; - for(i = 0; i < rt->atom_size; i++) { - JSAtomStruct *p = rt->atom_array[i]; - if (!atom_is_free(p)) { - s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + - 1 - p->is_wide_char); - } - } - s->str_count = round(mem.str_count); - s->str_size = round(mem.str_size); - s->js_func_count = mem.js_func_count; - s->js_func_size = round(mem.js_func_size); - s->js_func_code_size = mem.js_func_code_size; - s->js_func_pc2line_count = mem.js_func_pc2line_count; - s->js_func_pc2line_size = mem.js_func_pc2line_size; - s->memory_used_count += round(mem.memory_used_count) + - s->atom_count + s->str_count + - s->obj_count + s->shape_count + - s->js_func_count + s->js_func_pc2line_count; - s->memory_used_size += s->atom_size + s->str_size + - s->obj_size + s->prop_size + s->shape_size + - s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; -} - -void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) -{ - fprintf(fp, "QuickJS memory usage -- " -#ifdef CONFIG_BIGNUM - "BigNum " -#endif - CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", - (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit); -#if 1 - if (rt) { - static const struct { - const char *name; - size_t size; - } object_types[] = { - { "JSRuntime", sizeof(JSRuntime) }, - { "JSContext", sizeof(JSContext) }, - { "JSObject", sizeof(JSObject) }, - { "JSString", sizeof(JSString) }, - { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, - }; - int i, usage_size_ok = 0; - for(i = 0; i < countof(object_types); i++) { - unsigned int size = object_types[i].size; - void *p = js_malloc_rt(rt, size); - if (p) { - unsigned int size1 = js_malloc_usable_size_rt(rt, p); - if (size1 >= size) { - usage_size_ok = 1; - fprintf(fp, " %3u + %-2u %s\n", - size, size1 - size, object_types[i].name); - } - js_free_rt(rt, p); - } - } - if (!usage_size_ok) { - fprintf(fp, " malloc_usable_size unavailable\n"); - } - { - int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; - int class_id; - struct list_head *el; - list_for_each(el, &rt->gc_obj_list) { - JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); - JSObject *p; - if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { - p = (JSObject *)gp; - obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; - } - } - fprintf(fp, "\n" "JSObject classes\n"); - if (obj_classes[0]) - fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); - for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { - if (obj_classes[class_id]) { - char buf[ATOM_GET_STR_BUF_SIZE]; - fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, - JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name)); - } - } - if (obj_classes[JS_CLASS_INIT_COUNT]) - fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); - } - fprintf(fp, "\n"); - } -#endif - fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); - - if (s->malloc_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", - "memory allocated", s->malloc_count, s->malloc_size, - (double)s->malloc_size / s->malloc_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", - "memory used", s->memory_used_count, s->memory_used_size, - MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / - s->memory_used_count)); - } - if (s->atom_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", - "atoms", s->atom_count, s->atom_size, - (double)s->atom_size / s->atom_count); - } - if (s->str_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", - "strings", s->str_count, s->str_size, - (double)s->str_size / s->str_count); - } - if (s->obj_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", - "objects", s->obj_count, s->obj_size, - (double)s->obj_size / s->obj_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", - " properties", s->prop_count, s->prop_size, - (double)s->prop_count / s->obj_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", - " shapes", s->shape_count, s->shape_size, - (double)s->shape_size / s->shape_count); - } - if (s->js_func_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", - "bytecode functions", s->js_func_count, s->js_func_size); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", - " bytecode", s->js_func_count, s->js_func_code_size, - (double)s->js_func_code_size / s->js_func_count); - if (s->js_func_pc2line_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", - " pc2line", s->js_func_pc2line_count, - s->js_func_pc2line_size, - (double)s->js_func_pc2line_size / s->js_func_pc2line_count); - } - } - if (s->c_func_count) { - fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); - } - if (s->array_count) { - fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); - if (s->fast_array_count) { - fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); - fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", - " elements", s->fast_array_elements, - s->fast_array_elements * (int)sizeof(JSValue), - (double)s->fast_array_elements / s->fast_array_count); - } - } - if (s->binary_object_count) { - fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", - "binary objects", s->binary_object_count, s->binary_object_size); - } -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "memory.h" +#include "function.h" +#include "runtime.h" +#include "shape.h" +#include "string.h" + +/* Compute memory used by various object types */ +/* XXX: poor man's approach to handling multiply referenced objects */ +typedef struct JSMemoryUsage_helper { + double memory_used_count; + double str_count; + double str_size; + int64_t js_func_count; + double js_func_size; + int64_t js_func_code_size; + int64_t js_func_pc2line_count; + int64_t js_func_pc2line_size; +} JSMemoryUsage_helper; + +static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp); + +static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) +{ + if (!str->atom_type) { /* atoms are handled separately */ + double s_ref_count = str->header.ref_count; + hp->str_count += 1 / s_ref_count; + hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + + 1 - str->is_wide_char) / s_ref_count); + } +} + +static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) +{ + int memory_used_count, js_func_size, i; + + memory_used_count = 0; + js_func_size = offsetof(JSFunctionBytecode, debug); + if (b->vardefs) { + js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); + } + if (b->cpool) { + js_func_size += b->cpool_count * sizeof(*b->cpool); + for (i = 0; i < b->cpool_count; i++) { + JSValueConst val = b->cpool[i]; + compute_value_size(val, hp); + } + } + if (b->closure_var) { + js_func_size += b->closure_var_count * sizeof(*b->closure_var); + } + if (!b->read_only_bytecode && b->byte_code_buf) { + hp->js_func_code_size += b->byte_code_len; + } + if (b->has_debug) { + js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug); + if (b->debug.source) { + memory_used_count++; + js_func_size += b->debug.source_len + 1; + } + if (b->debug.pc2line_len) { + memory_used_count++; + hp->js_func_pc2line_count += 1; + hp->js_func_pc2line_size += b->debug.pc2line_len; + } + } + hp->js_func_size += js_func_size; + hp->js_func_count += 1; + hp->memory_used_count += memory_used_count; +} + +static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) +{ + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_STRING: + compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); + break; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_INT: + case JS_TAG_BIG_FLOAT: + case JS_TAG_BIG_DECIMAL: + /* should track JSBigFloat usage */ + break; +#endif + } +} + +void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) +{ + struct list_head *el, *el1; + int i; + JSMemoryUsage_helper mem = { 0 }, *hp = &mem; + + memset(s, 0, sizeof(*s)); + s->malloc_count = rt->malloc_state.malloc_count; + s->malloc_size = rt->malloc_state.malloc_size; + s->malloc_limit = rt->malloc_state.malloc_limit; + + s->memory_used_count = 2; /* rt + rt->class_array */ + s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count; + + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSShape *sh = ctx->array_shape; + s->memory_used_count += 2; /* ctx + ctx->class_proto */ + s->memory_used_size += sizeof(JSContext) + + sizeof(JSValue) * rt->class_count; + s->binary_object_count += ctx->binary_object_count; + s->binary_object_size += ctx->binary_object_size; + + /* the hashed shapes are counted separately */ + if (sh && !sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + list_for_each(el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el1, JSModuleDef, link); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*m); + if (m->req_module_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); + } + if (m->export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); + for (i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { + /* potential multiple count */ + s->memory_used_count += 1; + compute_value_size(me->u.local.var_ref->value, hp); + } + } + } + if (m->star_export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); + } + if (m->import_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); + } + compute_value_size(m->module_ns, hp); + compute_value_size(m->func_obj, hp); + } + } + + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + JSShape *sh; + JSShapeProperty *prs; + + /* XXX: could count the other GC object types too */ + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { + compute_bytecode_size((JSFunctionBytecode *)gp, hp); + continue; + } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { + continue; + } + p = (JSObject *)gp; + sh = p->shape; + s->obj_count++; + if (p->prop) { + s->memory_used_count++; + s->prop_size += sh->prop_size * sizeof(*p->prop); + s->prop_count += sh->prop_count; + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { + compute_value_size(pr->u.value, hp); + } + prs++; + } + } + /* the hashed shapes are counted separately */ + if (!sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + + switch(p->class_id) { + case JS_CLASS_ARRAY: /* u.array | length */ + case JS_CLASS_ARGUMENTS: /* u.array | length */ + s->array_count++; + if (p->fast_array) { + s->fast_array_count++; + if (p->u.array.u.values) { + s->memory_used_count++; + s->memory_used_size += p->u.array.count * + sizeof(*p->u.array.u.values); + s->fast_array_elements += p->u.array.count; + for (i = 0; i < p->u.array.count; i++) { + compute_value_size(p->u.array.u.values[i], hp); + } + } + } + break; + case JS_CLASS_NUMBER: /* u.object_data */ + case JS_CLASS_STRING: /* u.object_data */ + case JS_CLASS_BOOLEAN: /* u.object_data */ + case JS_CLASS_SYMBOL: /* u.object_data */ + case JS_CLASS_DATE: /* u.object_data */ +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: /* u.object_data */ + case JS_CLASS_BIG_FLOAT: /* u.object_data */ + case JS_CLASS_BIG_DECIMAL: /* u.object_data */ +#endif + compute_value_size(p->u.object_data, hp); + break; + case JS_CLASS_C_FUNCTION: /* u.cfunc */ + s->c_func_count++; + break; + case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ + { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs = p->u.func.var_refs; + /* home_object: object will be accounted for in list scan */ + if (var_refs) { + s->memory_used_count++; + s->js_func_size += b->closure_var_count * sizeof(*var_refs); + for (i = 0; i < b->closure_var_count; i++) { + if (var_refs[i]) { + double ref_count = var_refs[i]->header.ref_count; + s->memory_used_count += 1 / ref_count; + s->js_func_size += sizeof(*var_refs[i]) / ref_count; + /* handle non object closed values */ + if (var_refs[i]->pvalue == &var_refs[i]->value) { + /* potential multiple count */ + compute_value_size(var_refs[i]->value, hp); + } + } + } + } + } + break; + case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ + { + JSBoundFunction *bf = p->u.bound_function; + /* func_obj and this_val are objects */ + for (i = 0; i < bf->argc; i++) { + compute_value_size(bf->argv[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); + } + break; + case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ + { + JSCFunctionDataRecord *fd = p->u.c_function_data_record; + if (fd) { + for (i = 0; i < fd->data_len; i++) { + compute_value_size(fd->data[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); + } + } + break; + case JS_CLASS_REGEXP: /* u.regexp */ + compute_jsstring_size(p->u.regexp.pattern, hp); + compute_jsstring_size(p->u.regexp.bytecode, hp); + break; + + case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ + { + JSForInIterator *it = p->u.for_in_iterator; + if (it) { + compute_value_size(it->obj, hp); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*it); + } + } + break; + case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ + case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ + { + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf) { + s->memory_used_count += 1; + s->memory_used_size += sizeof(*abuf); + if (abuf->data) { + s->memory_used_count += 1; + s->memory_used_size += abuf->byte_length; + } + } + } + break; + case JS_CLASS_GENERATOR: /* u.generator_data */ + case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ +#endif + case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_DATAVIEW: /* u.typed_array */ +#ifdef CONFIG_BIGNUM + case JS_CLASS_FLOAT_ENV: /* u.float_env */ +#endif + case JS_CLASS_MAP: /* u.map_state */ + case JS_CLASS_SET: /* u.map_state */ + case JS_CLASS_WEAKMAP: /* u.map_state */ + case JS_CLASS_WEAKSET: /* u.map_state */ + case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_PROXY: /* u.proxy_data */ + case JS_CLASS_PROMISE: /* u.promise_data */ + case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ + case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ + case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ + case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ + /* TODO */ + default: + /* XXX: class definition should have an opaque block size */ + if (p->u.opaque) { + s->memory_used_count += 1; + } + break; + } + } + s->obj_size += s->obj_count * sizeof(JSObject); + + /* hashed shapes */ + s->memory_used_count++; /* rt->shape_hash */ + s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; + for(i = 0; i < rt->shape_hash_size; i++) { + JSShape *sh; + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + } + + /* atoms */ + s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ + s->atom_count = rt->atom_count; + s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + + sizeof(rt->atom_hash[0]) * rt->atom_hash_size; + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { + s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + + 1 - p->is_wide_char); + } + } + s->str_count = round(mem.str_count); + s->str_size = round(mem.str_size); + s->js_func_count = mem.js_func_count; + s->js_func_size = round(mem.js_func_size); + s->js_func_code_size = mem.js_func_code_size; + s->js_func_pc2line_count = mem.js_func_pc2line_count; + s->js_func_pc2line_size = mem.js_func_pc2line_size; + s->memory_used_count += round(mem.memory_used_count) + + s->atom_count + s->str_count + + s->obj_count + s->shape_count + + s->js_func_count + s->js_func_pc2line_count; + s->memory_used_size += s->atom_size + s->str_size + + s->obj_size + s->prop_size + s->shape_size + + s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; +} + +void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) +{ + fprintf(fp, "QuickJS memory usage -- " +#ifdef CONFIG_BIGNUM + "BigNum " +#endif + CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", + (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit); +#if 1 + if (rt) { + static const struct { + const char *name; + size_t size; + } object_types[] = { + { "JSRuntime", sizeof(JSRuntime) }, + { "JSContext", sizeof(JSContext) }, + { "JSObject", sizeof(JSObject) }, + { "JSString", sizeof(JSString) }, + { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, + }; + int i, usage_size_ok = 0; + for(i = 0; i < countof(object_types); i++) { + unsigned int size = object_types[i].size; + void *p = js_malloc_rt(rt, size); + if (p) { + unsigned int size1 = js_malloc_usable_size_rt(rt, p); + if (size1 >= size) { + usage_size_ok = 1; + fprintf(fp, " %3u + %-2u %s\n", + size, size1 - size, object_types[i].name); + } + js_free_rt(rt, p); + } + } + if (!usage_size_ok) { + fprintf(fp, " malloc_usable_size unavailable\n"); + } + { + int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; + int class_id; + struct list_head *el; + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; + } + } + fprintf(fp, "\n" "JSObject classes\n"); + if (obj_classes[0]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); + for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { + if (obj_classes[class_id]) { + char buf[ATOM_GET_STR_BUF_SIZE]; + fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, + JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name)); + } + } + if (obj_classes[JS_CLASS_INIT_COUNT]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); + } + fprintf(fp, "\n"); + } +#endif + fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); + + if (s->malloc_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", + "memory allocated", s->malloc_count, s->malloc_size, + (double)s->malloc_size / s->malloc_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", + "memory used", s->memory_used_count, s->memory_used_size, + MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / + s->memory_used_count)); + } + if (s->atom_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", + "atoms", s->atom_count, s->atom_size, + (double)s->atom_size / s->atom_count); + } + if (s->str_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", + "strings", s->str_count, s->str_size, + (double)s->str_size / s->str_count); + } + if (s->obj_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + "objects", s->obj_count, s->obj_size, + (double)s->obj_size / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + " properties", s->prop_count, s->prop_size, + (double)s->prop_count / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", + " shapes", s->shape_count, s->shape_size, + (double)s->shape_size / s->shape_count); + } + if (s->js_func_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "bytecode functions", s->js_func_count, s->js_func_size); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " bytecode", s->js_func_count, s->js_func_code_size, + (double)s->js_func_code_size / s->js_func_count); + if (s->js_func_pc2line_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " pc2line", s->js_func_pc2line_count, + s->js_func_pc2line_size, + (double)s->js_func_pc2line_size / s->js_func_pc2line_count); + } + } + if (s->c_func_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); + } + if (s->array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); + if (s->fast_array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", + " elements", s->fast_array_elements, + s->fast_array_elements * (int)sizeof(JSValue), + (double)s->fast_array_elements / s->fast_array_count); + } + } + if (s->binary_object_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "binary objects", s->binary_object_count, s->binary_object_size); + } +} diff --git a/src/core/memory.h b/src/core/memory.h index 1154ab566..4bc3f3653 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -1,32 +1,32 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_MEMORY_H -#define QUICKJS_MEMORY_H - -#include "quickjs/quickjs.h" -#include "quickjs/cutils.h" - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_MEMORY_H +#define QUICKJS_MEMORY_H + +#include "quickjs/quickjs.h" +#include "quickjs/cutils.h" + #endif \ No newline at end of file diff --git a/src/core/module.c b/src/core/module.c index 5edb48921..191653aa0 100644 --- a/src/core/module.c +++ b/src/core/module.c @@ -1,1298 +1,1298 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "module.h" -#include "exception.h" -#include "function.h" -#include "gc.h" -#include "malloc.h" -#include "object.h" -#include "parser.h" -#include "runtime.h" -#include "string.h" - -/* 'name' is freed */ -JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) -{ - JSModuleDef *m; - m = js_mallocz(ctx, sizeof(*m)); - if (!m) { - JS_FreeAtom(ctx, name); - return NULL; - } - m->header.ref_count = 1; - m->module_name = name; - m->module_ns = JS_UNDEFINED; - m->func_obj = JS_UNDEFINED; - m->eval_exception = JS_UNDEFINED; - m->meta_obj = JS_UNDEFINED; - list_add_tail(&m->link, &ctx->loaded_modules); - return m; -} - -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); -} - -void js_free_module_def(JSContext *ctx, JSModuleDef *m) -{ - int i; - - JS_FreeAtom(ctx, m->module_name); - - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - JS_FreeAtom(ctx, rme->module_name); - } - js_free(ctx, m->req_module_entries); - - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) - free_var_ref(ctx->rt, me->u.local.var_ref); - JS_FreeAtom(ctx, me->export_name); - JS_FreeAtom(ctx, me->local_name); - } - js_free(ctx, m->export_entries); - - js_free(ctx, m->star_export_entries); - - for(i = 0; i < m->import_entries_count; i++) { - JSImportEntry *mi = &m->import_entries[i]; - JS_FreeAtom(ctx, mi->import_name); - } - js_free(ctx, m->import_entries); - - JS_FreeValue(ctx, m->module_ns); - JS_FreeValue(ctx, m->func_obj); - JS_FreeValue(ctx, m->eval_exception); - JS_FreeValue(ctx, m->meta_obj); - list_del(&m->link); - js_free(ctx, m); -} - -int add_req_module_entry(JSContext *ctx, JSModuleDef *m, - JSAtom module_name) -{ - JSReqModuleEntry *rme; - int i; - - /* no need to add the module request if it is already present */ - for(i = 0; i < m->req_module_entries_count; i++) { - rme = &m->req_module_entries[i]; - if (rme->module_name == module_name) - return i; - } - - if (js_resize_array(ctx, (void **)&m->req_module_entries, - sizeof(JSReqModuleEntry), - &m->req_module_entries_size, - m->req_module_entries_count + 1)) - return -1; - rme = &m->req_module_entries[m->req_module_entries_count++]; - rme->module_name = JS_DupAtom(ctx, module_name); - rme->module = NULL; - return i; -} - -JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, - JSAtom export_name) -{ - JSExportEntry *me; - int i; - for(i = 0; i < m->export_entries_count; i++) { - me = &m->export_entries[i]; - if (me->export_name == export_name) - return me; - } - return NULL; -} - -/* create a C module */ -JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, - JSModuleInitFunc *func) -{ - JSModuleDef *m; - JSAtom name; - name = JS_NewAtom(ctx, name_str); - if (name == JS_ATOM_NULL) - return NULL; - m = js_new_module_def(ctx, name); - m->init_func = func; - return m; -} - -int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name) -{ - JSExportEntry *me; - JSAtom name; - name = JS_NewAtom(ctx, export_name); - if (name == JS_ATOM_NULL) - return -1; - me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name, - JS_EXPORT_TYPE_LOCAL); - JS_FreeAtom(ctx, name); - if (!me) - return -1; - else - return 0; -} - -int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, - JSValue val) -{ - JSExportEntry *me; - JSAtom name; - name = JS_NewAtom(ctx, export_name); - if (name == JS_ATOM_NULL) - goto fail; - me = find_export_entry(ctx, m, name); - JS_FreeAtom(ctx, name); - if (!me) - goto fail; - set_value(ctx, me->u.local.var_ref->pvalue, val); - return 0; -fail: - JS_FreeValue(ctx, val); - return -1; -} - -void JS_SetModuleLoaderFunc(JSRuntime *rt, - JSModuleNormalizeFunc *module_normalize, - JSModuleLoaderFunc *module_loader, void *opaque) -{ - rt->module_normalize_func = module_normalize; - rt->module_loader_func = module_loader; - rt->module_loader_opaque = opaque; -} - -/* default module filename normalizer */ -char *js_default_module_normalize_name(JSContext *ctx, - const char *base_name, - const char *name) -{ - char *filename, *p; - const char *r; - int len; - - if (name[0] != '.') { - /* if no initial dot, the module name is not modified */ - return js_strdup(ctx, name); - } - - p = strrchr(base_name, '/'); - if (p) - len = p - base_name; - else - len = 0; - - filename = js_malloc(ctx, len + strlen(name) + 1 + 1); - if (!filename) - return NULL; - memcpy(filename, base_name, len); - filename[len] = '\0'; - - /* we only normalize the leading '..' or '.' */ - r = name; - for(;;) { - if (r[0] == '.' && r[1] == '/') { - r += 2; - } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') { - /* remove the last path element of filename, except if "." - or ".." */ - if (filename[0] == '\0') - break; - p = strrchr(filename, '/'); - if (!p) - p = filename; - else - p++; - if (!strcmp(p, ".") || !strcmp(p, "..")) - break; - if (p > filename) - p--; - *p = '\0'; - r += 3; - } else { - break; - } - } - if (filename[0] != '\0') - strcat(filename, "/"); - strcat(filename, r); - // printf("normalize: %s %s -> %s\n", base_name, name, filename); - return filename; -} - -JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) -{ - struct list_head *el; - JSModuleDef *m; - - /* first look at the loaded modules */ - list_for_each(el, &ctx->loaded_modules) { - m = list_entry(el, JSModuleDef, link); - if (m->module_name == name) - return m; - } - return NULL; -} - -/* return NULL in case of exception (e.g. module could not be loaded) */ -JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, - const char *base_cname, - const char *cname1) -{ - JSRuntime *rt = ctx->rt; - JSModuleDef *m; - char *cname; - JSAtom module_name; - - if (!rt->module_normalize_func) { - cname = js_default_module_normalize_name(ctx, base_cname, cname1); - } else { - cname = rt->module_normalize_func(ctx, base_cname, cname1, - rt->module_loader_opaque); - } - if (!cname) - return NULL; - - module_name = JS_NewAtom(ctx, cname); - if (module_name == JS_ATOM_NULL) { - js_free(ctx, cname); - return NULL; - } - - /* first look at the loaded modules */ - m = js_find_loaded_module(ctx, module_name); - if (m) { - js_free(ctx, cname); - JS_FreeAtom(ctx, module_name); - return m; - } - - JS_FreeAtom(ctx, module_name); - - /* load the module */ - if (!rt->module_loader_func) { - /* XXX: use a syntax error ? */ - JS_ThrowReferenceError(ctx, "could not load module '%s'", - cname); - js_free(ctx, cname); - return NULL; - } - - m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque); - js_free(ctx, cname); - return m; -} - -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; -} - -static int find_resolve_entry(JSResolveState *s, - JSModuleDef *m, JSAtom name) -{ - int i; - for(i = 0; i < s->count; i++) { - JSResolveEntry *re = &s->array[i]; - if (re->module == m && re->name == name) - return i; - } - return -1; -} - -static int add_resolve_entry(JSContext *ctx, JSResolveState *s, - JSModuleDef *m, JSAtom name) -{ - JSResolveEntry *re; - - if (js_resize_array(ctx, (void **)&s->array, - sizeof(JSResolveEntry), - &s->size, s->count + 1)) - return -1; - re = &s->array[s->count++]; - re->module = m; - re->name = JS_DupAtom(ctx, name); - return 0; -} - -typedef enum JSResolveResultEnum { - JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */ - JS_RESOLVE_RES_FOUND = 0, - JS_RESOLVE_RES_NOT_FOUND, - JS_RESOLVE_RES_CIRCULAR, - JS_RESOLVE_RES_AMBIGUOUS, -} JSResolveResultEnum; - -static JSResolveResultEnum js_resolve_export1(JSContext *ctx, - JSModuleDef **pmodule, - JSExportEntry **pme, - JSModuleDef *m, - JSAtom export_name, - JSResolveState *s) -{ - JSExportEntry *me; - - *pmodule = NULL; - *pme = NULL; - if (find_resolve_entry(s, m, export_name) >= 0) - return JS_RESOLVE_RES_CIRCULAR; - if (add_resolve_entry(ctx, s, m, export_name) < 0) - return JS_RESOLVE_RES_EXCEPTION; - me = find_export_entry(ctx, m, export_name); - if (me) { - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - /* local export */ - *pmodule = m; - *pme = me; - return JS_RESOLVE_RES_FOUND; - } else { - /* indirect export */ - JSModuleDef *m1; - m1 = m->req_module_entries[me->u.req_module_idx].module; - if (me->local_name == JS_ATOM__star_) { - /* export ns from */ - *pmodule = m; - *pme = me; - return JS_RESOLVE_RES_FOUND; - } else { - return js_resolve_export1(ctx, pmodule, pme, m1, - me->local_name, s); - } - } - } else { - if (export_name != JS_ATOM_default) { - /* not found in direct or indirect exports: try star exports */ - int i; - - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - JSModuleDef *m1, *res_m; - JSExportEntry *res_me; - JSResolveResultEnum ret; - - m1 = m->req_module_entries[se->req_module_idx].module; - ret = js_resolve_export1(ctx, &res_m, &res_me, m1, - export_name, s); - if (ret == JS_RESOLVE_RES_AMBIGUOUS || - ret == JS_RESOLVE_RES_EXCEPTION) { - return ret; - } else if (ret == JS_RESOLVE_RES_FOUND) { - if (*pme != NULL) { - if (*pmodule != res_m || - res_me->local_name != (*pme)->local_name) { - *pmodule = NULL; - *pme = NULL; - return JS_RESOLVE_RES_AMBIGUOUS; - } - } else { - *pmodule = res_m; - *pme = res_me; - } - } - } - if (*pme != NULL) - return JS_RESOLVE_RES_FOUND; - } - return JS_RESOLVE_RES_NOT_FOUND; - } -} - -/* If the return value is JS_RESOLVE_RES_FOUND, return the module - (*pmodule) and the corresponding local export entry - (*pme). Otherwise return (NULL, NULL) */ -static JSResolveResultEnum js_resolve_export(JSContext *ctx, - JSModuleDef **pmodule, - JSExportEntry **pme, - JSModuleDef *m, - JSAtom export_name) -{ - JSResolveState ss, *s = &ss; - int i; - JSResolveResultEnum ret; - - s->array = NULL; - s->size = 0; - s->count = 0; - - ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s); - - for(i = 0; i < s->count; i++) - JS_FreeAtom(ctx, s->array[i].name); - js_free(ctx, s->array); - - return ret; -} - -static void js_resolve_export_throw_error(JSContext *ctx, - JSResolveResultEnum res, - JSModuleDef *m, JSAtom export_name) -{ - char buf1[ATOM_GET_STR_BUF_SIZE]; - char buf2[ATOM_GET_STR_BUF_SIZE]; - switch(res) { - case JS_RESOLVE_RES_EXCEPTION: - break; - default: - case JS_RESOLVE_RES_NOT_FOUND: - JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), - JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); - break; - case JS_RESOLVE_RES_CIRCULAR: - JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), - JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); - break; - case JS_RESOLVE_RES_AMBIGUOUS: - JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", - JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), - JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); - break; - } -} - - -typedef enum { - EXPORTED_NAME_AMBIGUOUS, - EXPORTED_NAME_NORMAL, - EXPORTED_NAME_NS, -} ExportedNameEntryEnum; - -typedef struct ExportedNameEntry { - JSAtom export_name; - ExportedNameEntryEnum export_type; - union { - JSExportEntry *me; /* using when the list is built */ - JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */ - JSModuleDef *module; /* for EXPORTED_NAME_NS */ - } u; -} ExportedNameEntry; - -typedef struct GetExportNamesState { - JSModuleDef **modules; - int modules_size; - int modules_count; - - ExportedNameEntry *exported_names; - int exported_names_size; - int exported_names_count; -} GetExportNamesState; - -static int find_exported_name(GetExportNamesState *s, JSAtom name) -{ - int i; - for(i = 0; i < s->exported_names_count; i++) { - if (s->exported_names[i].export_name == name) - return i; - } - return -1; -} - -static __exception int get_exported_names(JSContext *ctx, - GetExportNamesState *s, - JSModuleDef *m, BOOL from_star) -{ - ExportedNameEntry *en; - int i, j; - - /* check circular reference */ - for(i = 0; i < s->modules_count; i++) { - if (s->modules[i] == m) - return 0; - } - if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]), - &s->modules_size, s->modules_count + 1)) - return -1; - s->modules[s->modules_count++] = m; - - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (from_star && me->export_name == JS_ATOM_default) - continue; - j = find_exported_name(s, me->export_name); - if (j < 0) { - if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]), - &s->exported_names_size, - s->exported_names_count + 1)) - return -1; - en = &s->exported_names[s->exported_names_count++]; - en->export_name = me->export_name; - /* avoid a second lookup for simple module exports */ - if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL) - en->u.me = NULL; - else - en->u.me = me; - } else { - en = &s->exported_names[j]; - en->u.me = NULL; - } - } - for(i = 0; i < m->star_export_entries_count; i++) { - JSStarExportEntry *se = &m->star_export_entries[i]; - JSModuleDef *m1; - m1 = m->req_module_entries[se->req_module_idx].module; - if (get_exported_names(ctx, s, m1, TRUE)) - return -1; - } - return 0; -} - -/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */ -static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom) -{ - return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL); -} - -static const JSClassExoticMethods js_module_ns_exotic_methods = { - .has_property = js_module_ns_has, -}; - -static int exported_names_cmp(const void *p1, const void *p2, void *opaque) -{ - JSContext *ctx = opaque; - const ExportedNameEntry *me1 = p1; - const ExportedNameEntry *me2 = p2; - JSValue str1, str2; - int ret; - - /* XXX: should avoid allocation memory in atom comparison */ - str1 = JS_AtomToString(ctx, me1->export_name); - str2 = JS_AtomToString(ctx, me2->export_name); - if (JS_IsException(str1) || JS_IsException(str2)) { - /* XXX: raise an error ? */ - ret = 0; - } else { - ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1), - JS_VALUE_GET_STRING(str2)); - } - JS_FreeValue(ctx, str1); - JS_FreeValue(ctx, str2); - return ret; -} - -static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m); - -JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, - void *opaque) -{ - JSModuleDef *m = opaque; - return js_get_module_ns(ctx, m); -} - -static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) -{ - JSValue obj; - JSObject *p; - GetExportNamesState s_s, *s = &s_s; - int i, ret; - JSProperty *pr; - - obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS); - if (JS_IsException(obj)) - return obj; - p = JS_VALUE_GET_OBJ(obj); - - memset(s, 0, sizeof(*s)); - ret = get_exported_names(ctx, s, m, FALSE); - js_free(ctx, s->modules); - if (ret) - goto fail; - - /* Resolve the exported names. The ambiguous exports are removed */ - for(i = 0; i < s->exported_names_count; i++) { - ExportedNameEntry *en = &s->exported_names[i]; - JSResolveResultEnum res; - JSExportEntry *res_me; - JSModuleDef *res_m; - - if (en->u.me) { - res_me = en->u.me; /* fast case: no resolution needed */ - res_m = m; - res = JS_RESOLVE_RES_FOUND; - } else { - res = js_resolve_export(ctx, &res_m, &res_me, m, - en->export_name); - } - if (res != JS_RESOLVE_RES_FOUND) { - if (res != JS_RESOLVE_RES_AMBIGUOUS) { - js_resolve_export_throw_error(ctx, res, m, en->export_name); - goto fail; - } - en->export_type = EXPORTED_NAME_AMBIGUOUS; - } else { - if (res_me->local_name == JS_ATOM__star_) { - en->export_type = EXPORTED_NAME_NS; - en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module; - } else { - en->export_type = EXPORTED_NAME_NORMAL; - if (res_me->u.local.var_ref) { - en->u.var_ref = res_me->u.local.var_ref; - } else { - JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); - p1 = JS_VALUE_GET_OBJ(res_m->func_obj); - en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; - } - } - } - } - - /* sort the exported names */ - rqsort(s->exported_names, s->exported_names_count, - sizeof(s->exported_names[0]), exported_names_cmp, ctx); - - for(i = 0; i < s->exported_names_count; i++) { - ExportedNameEntry *en = &s->exported_names[i]; - switch(en->export_type) { - case EXPORTED_NAME_NORMAL: - { - JSVarRef *var_ref = en->u.var_ref; - pr = add_property(ctx, p, en->export_name, - JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | - JS_PROP_VARREF); - if (!pr) - goto fail; - var_ref->header.ref_count++; - pr->u.var_ref = var_ref; - } - break; - case EXPORTED_NAME_NS: - /* the exported namespace must be created on demand */ - if (JS_DefineAutoInitProperty(ctx, obj, - en->export_name, - JS_AUTOINIT_ID_MODULE_NS, - en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) - goto fail; - break; - default: - break; - } - } - - js_free(ctx, s->exported_names); - - JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag, - JS_AtomToString(ctx, JS_ATOM_Module), - 0); - - p->extensible = FALSE; - return obj; -fail: - js_free(ctx, s->exported_names); - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; -} - -static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m) -{ - if (JS_IsUndefined(m->module_ns)) { - JSValue val; - val = js_build_module_ns(ctx, m); - if (JS_IsException(val)) - return JS_EXCEPTION; - m->module_ns = val; - } - return JS_DupValue(ctx, m->module_ns); -} - -/* Load all the required modules for module 'm' */ -int js_resolve_module(JSContext *ctx, JSModuleDef *m) -{ - int i; - JSModuleDef *m1; - - if (m->resolved) - return 0; -#ifdef DUMP_MODULE_RESOLVE - { - char buf1[ATOM_GET_STR_BUF_SIZE]; - printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); - } -#endif - m->resolved = TRUE; - /* resolve each requested module */ - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, - rme->module_name); - if (!m1) - return -1; - rme->module = m1; - /* already done in js_host_resolve_imported_module() except if - the module was loaded with JS_EvalBinary() */ - if (js_resolve_module(ctx, m1) < 0) - return -1; - } - return 0; -} - -static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical) -{ - JSVarRef *var_ref; - var_ref = js_malloc(ctx, sizeof(JSVarRef)); - if (!var_ref) - return NULL; - var_ref->header.ref_count = 1; - if (is_lexical) - var_ref->value = JS_UNINITIALIZED; - else - var_ref->value = JS_UNDEFINED; - var_ref->pvalue = &var_ref->value; - var_ref->is_detached = TRUE; - add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); - return var_ref; -} - -/* Create the function associated with the module */ -static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) -{ - JSFunctionBytecode *b; - int i; - JSVarRef **var_refs; - JSValue func_obj, bfunc; - JSObject *p; - - bfunc = m->func_obj; - func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_BYTECODE_FUNCTION); - - if (JS_IsException(func_obj)) - return -1; - b = JS_VALUE_GET_PTR(bfunc); - - p = JS_VALUE_GET_OBJ(func_obj); - p->u.func.function_bytecode = b; - b->header.ref_count++; - p->u.func.home_object = NULL; - p->u.func.var_refs = NULL; - if (b->closure_var_count) { - var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); - if (!var_refs) - goto fail; - p->u.func.var_refs = var_refs; - - /* create the global variables. The other variables are - imported from other modules */ - for(i = 0; i < b->closure_var_count; i++) { - JSClosureVar *cv = &b->closure_var[i]; - JSVarRef *var_ref; - if (cv->is_local) { - var_ref = js_create_module_var(ctx, cv->is_lexical); - if (!var_ref) - goto fail; -#ifdef DUMP_MODULE_RESOLVE - printf("local %d: %p\n", i, var_ref); -#endif - var_refs[i] = var_ref; - } - } - } - m->func_obj = func_obj; - JS_FreeValue(ctx, bfunc); - return 0; -fail: - JS_FreeValue(ctx, func_obj); - return -1; -} - -/* must be done before js_link_module() because of cyclic references */ -int js_create_module_function(JSContext *ctx, JSModuleDef *m) -{ - BOOL is_c_module; - int i; - JSVarRef *var_ref; - - if (m->func_created) - return 0; - - is_c_module = (m->init_func != NULL); - - if (is_c_module) { - /* initialize the exported variables */ - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - var_ref = js_create_module_var(ctx, FALSE); - if (!var_ref) - return -1; - me->u.local.var_ref = var_ref; - } - } - } else { - if (js_create_module_bytecode_function(ctx, m)) - return -1; - } - m->func_created = TRUE; - - /* do it on the dependencies */ - - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (js_create_module_function(ctx, rme->module) < 0) - return -1; - } - - return 0; -} - - -/* Prepare a module to be executed by resolving all the imported - variables. */ -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_link_module(ctx, rme->module) < 0) - goto fail; - } - -#ifdef DUMP_MODULE_RESOLVE - { - char buf1[ATOM_GET_STR_BUF_SIZE]; - printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); - } -#endif - /* check the indirect exports */ - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_INDIRECT && - me->local_name != JS_ATOM__star_) { - JSResolveResultEnum ret; - JSExportEntry *res_me; - JSModuleDef *res_m, *m1; - m1 = m->req_module_entries[me->u.req_module_idx].module; - ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name); - if (ret != JS_RESOLVE_RES_FOUND) { - js_resolve_export_throw_error(ctx, ret, m, me->export_name); - goto fail; - } - } - } - -#ifdef DUMP_MODULE_RESOLVE - { - printf("exported bindings:\n"); - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - printf(" name="); print_atom(ctx, me->export_name); - printf(" local="); print_atom(ctx, me->local_name); - printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx); - } - } -#endif - - is_c_module = (m->init_func != NULL); - - if (!is_c_module) { - p = JS_VALUE_GET_OBJ(m->func_obj); - var_refs = p->u.func.var_refs; - - for(i = 0; i < m->import_entries_count; i++) { - mi = &m->import_entries[i]; -#ifdef DUMP_MODULE_RESOLVE - printf("import var_idx=%d name=", mi->var_idx); - print_atom(ctx, mi->import_name); - printf(": "); -#endif - m1 = m->req_module_entries[mi->req_module_idx].module; - if (mi->import_name == JS_ATOM__star_) { - JSValue val; - /* name space import */ - val = js_get_module_ns(ctx, m1); - if (JS_IsException(val)) - goto fail; - set_value(ctx, &var_refs[mi->var_idx]->value, val); -#ifdef DUMP_MODULE_RESOLVE - printf("namespace\n"); -#endif - } else { - JSResolveResultEnum ret; - JSExportEntry *res_me; - JSModuleDef *res_m; - JSObject *p1; - - ret = js_resolve_export(ctx, &res_m, - &res_me, m1, mi->import_name); - if (ret != JS_RESOLVE_RES_FOUND) { - js_resolve_export_throw_error(ctx, ret, m1, mi->import_name); - goto fail; - } - if (res_me->local_name == JS_ATOM__star_) { - JSValue val; - JSModuleDef *m2; - /* name space import from */ - m2 = res_m->req_module_entries[res_me->u.req_module_idx].module; - val = js_get_module_ns(ctx, m2); - if (JS_IsException(val)) - goto fail; - var_ref = js_create_module_var(ctx, TRUE); - if (!var_ref) { - JS_FreeValue(ctx, val); - goto fail; - } - set_value(ctx, &var_ref->value, val); - var_refs[mi->var_idx] = var_ref; -#ifdef DUMP_MODULE_RESOLVE - printf("namespace from\n"); -#endif - } else { - var_ref = res_me->u.local.var_ref; - if (!var_ref) { - p1 = JS_VALUE_GET_OBJ(res_m->func_obj); - var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; - } - var_ref->header.ref_count++; - var_refs[mi->var_idx] = var_ref; -#ifdef DUMP_MODULE_RESOLVE - printf("local export (var_ref=%p)\n", var_ref); -#endif - } - } - } - - /* keep the exported variables in the module export entries (they - are used when the eval function is deleted and cannot be - initialized before in case imports are exported) */ - for(i = 0; i < m->export_entries_count; i++) { - JSExportEntry *me = &m->export_entries[i]; - if (me->export_type == JS_EXPORT_TYPE_LOCAL) { - var_ref = var_refs[me->u.local.var_idx]; - var_ref->header.ref_count++; - me->u.local.var_ref = var_ref; - } - } - - /* initialize the global variables */ - ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL); - if (JS_IsException(ret_val)) - goto fail; - JS_FreeValue(ctx, ret_val); - } - -#ifdef DUMP_MODULE_RESOLVE - printf("done instantiate\n"); -#endif - return 0; -fail: - return -1; -} - -/* return JS_ATOM_NULL if the name cannot be found. Only works with - not striped bytecode functions. */ -JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) -{ - JSStackFrame *sf; - JSFunctionBytecode *b; - JSObject *p; - /* XXX: currently we just use the filename of the englobing - function. It does not work for eval(). Need to add a - ScriptOrModule info in JSFunctionBytecode */ - sf = ctx->rt->current_stack_frame; - if (!sf) - return JS_ATOM_NULL; - while (n_stack_levels-- > 0) { - sf = sf->prev_frame; - if (!sf) - return JS_ATOM_NULL; - } - if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) - return JS_ATOM_NULL; - p = JS_VALUE_GET_OBJ(sf->cur_func); - if (!js_class_has_bytecode(p->class_id)) - return JS_ATOM_NULL; - b = p->u.func.function_bytecode; - if (!b->has_debug) - return JS_ATOM_NULL; - return JS_DupAtom(ctx, b->debug.filename); -} - -JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) -{ - return JS_DupAtom(ctx, m->module_name); -} - -JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m) -{ - JSValue obj; - /* allocate meta_obj only if requested to save memory */ - obj = m->meta_obj; - if (JS_IsUndefined(obj)) { - obj = JS_NewObjectProto(ctx, JS_NULL); - if (JS_IsException(obj)) - return JS_EXCEPTION; - m->meta_obj = obj; - } - return JS_DupValue(ctx, obj); -} - -JSValue js_import_meta(JSContext *ctx) -{ - JSAtom filename; - JSModuleDef *m; - - filename = JS_GetScriptOrModuleName(ctx, 0); - if (filename == JS_ATOM_NULL) - goto fail; - - /* XXX: inefficient, need to add a module or script pointer in - JSFunctionBytecode */ - m = js_find_loaded_module(ctx, filename); - JS_FreeAtom(ctx, filename); - if (!m) { - fail: - JS_ThrowTypeError(ctx, "import.meta not supported in this context"); - return JS_EXCEPTION; - } - return JS_GetImportMeta(ctx, m); -} - -/* used by os.Worker() and import() */ -JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, - const char *filename) -{ - JSModuleDef *m; - JSValue ret, func_obj; - - m = js_host_resolve_imported_module(ctx, basename, filename); - if (!m) - return NULL; - - if (js_resolve_module(ctx, m) < 0) { - js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); - 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)) - 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(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_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; -} - -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; -} - -/* Run the function of the module and of all its requested - modules. */ -JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) -{ - JSModuleDef *m1; - int i; - JSValue ret_val; - - if (m->eval_mark) - return JS_UNDEFINED; /* avoid cycles */ - - if (m->evaluated) { - /* if the module was already evaluated, rethrow the exception - it raised */ - if (m->eval_has_exception) { - return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception)); - } else { - return JS_UNDEFINED; - } - } - - m->eval_mark = TRUE; - - for(i = 0; i < m->req_module_entries_count; i++) { - JSReqModuleEntry *rme = &m->req_module_entries[i]; - m1 = rme->module; - if (!m1->eval_mark) { - ret_val = js_evaluate_module(ctx, m1); - if (JS_IsException(ret_val)) { - m->eval_mark = FALSE; - return ret_val; - } - JS_FreeValue(ctx, ret_val); - } - } - - if (m->init_func) { - /* C module init */ - if (m->init_func(ctx, m) < 0) - ret_val = JS_EXCEPTION; - else - ret_val = JS_UNDEFINED; - } else { - ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL); - m->func_obj = JS_UNDEFINED; - } - if (JS_IsException(ret_val)) { - /* save the thrown exception value */ - m->eval_has_exception = TRUE; - m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception); - } - m->eval_mark = FALSE; - m->evaluated = TRUE; - return ret_val; -} - -int JS_ResolveModule(JSContext *ctx, JSValueConst obj) -{ - if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { - JSModuleDef *m = JS_VALUE_GET_PTR(obj); - if (js_resolve_module(ctx, m) < 0) { - js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); - return -1; - } - } - return 0; -} +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "module.h" +#include "exception.h" +#include "function.h" +#include "gc.h" +#include "malloc.h" +#include "object.h" +#include "parser.h" +#include "runtime.h" +#include "string.h" + +/* 'name' is freed */ +JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) +{ + JSModuleDef *m; + m = js_mallocz(ctx, sizeof(*m)); + if (!m) { + JS_FreeAtom(ctx, name); + return NULL; + } + m->header.ref_count = 1; + m->module_name = name; + m->module_ns = JS_UNDEFINED; + m->func_obj = JS_UNDEFINED; + m->eval_exception = JS_UNDEFINED; + m->meta_obj = JS_UNDEFINED; + list_add_tail(&m->link, &ctx->loaded_modules); + return m; +} + +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); +} + +void js_free_module_def(JSContext *ctx, JSModuleDef *m) +{ + int i; + + JS_FreeAtom(ctx, m->module_name); + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + JS_FreeAtom(ctx, rme->module_name); + } + js_free(ctx, m->req_module_entries); + + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) + free_var_ref(ctx->rt, me->u.local.var_ref); + JS_FreeAtom(ctx, me->export_name); + JS_FreeAtom(ctx, me->local_name); + } + js_free(ctx, m->export_entries); + + js_free(ctx, m->star_export_entries); + + for(i = 0; i < m->import_entries_count; i++) { + JSImportEntry *mi = &m->import_entries[i]; + JS_FreeAtom(ctx, mi->import_name); + } + js_free(ctx, m->import_entries); + + JS_FreeValue(ctx, m->module_ns); + JS_FreeValue(ctx, m->func_obj); + JS_FreeValue(ctx, m->eval_exception); + JS_FreeValue(ctx, m->meta_obj); + list_del(&m->link); + js_free(ctx, m); +} + +int add_req_module_entry(JSContext *ctx, JSModuleDef *m, + JSAtom module_name) +{ + JSReqModuleEntry *rme; + int i; + + /* no need to add the module request if it is already present */ + for(i = 0; i < m->req_module_entries_count; i++) { + rme = &m->req_module_entries[i]; + if (rme->module_name == module_name) + return i; + } + + if (js_resize_array(ctx, (void **)&m->req_module_entries, + sizeof(JSReqModuleEntry), + &m->req_module_entries_size, + m->req_module_entries_count + 1)) + return -1; + rme = &m->req_module_entries[m->req_module_entries_count++]; + rme->module_name = JS_DupAtom(ctx, module_name); + rme->module = NULL; + return i; +} + +JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, + JSAtom export_name) +{ + JSExportEntry *me; + int i; + for(i = 0; i < m->export_entries_count; i++) { + me = &m->export_entries[i]; + if (me->export_name == export_name) + return me; + } + return NULL; +} + +/* create a C module */ +JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, + JSModuleInitFunc *func) +{ + JSModuleDef *m; + JSAtom name; + name = JS_NewAtom(ctx, name_str); + if (name == JS_ATOM_NULL) + return NULL; + m = js_new_module_def(ctx, name); + m->init_func = func; + return m; +} + +int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name) +{ + JSExportEntry *me; + JSAtom name; + name = JS_NewAtom(ctx, export_name); + if (name == JS_ATOM_NULL) + return -1; + me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name, + JS_EXPORT_TYPE_LOCAL); + JS_FreeAtom(ctx, name); + if (!me) + return -1; + else + return 0; +} + +int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, + JSValue val) +{ + JSExportEntry *me; + JSAtom name; + name = JS_NewAtom(ctx, export_name); + if (name == JS_ATOM_NULL) + goto fail; + me = find_export_entry(ctx, m, name); + JS_FreeAtom(ctx, name); + if (!me) + goto fail; + set_value(ctx, me->u.local.var_ref->pvalue, val); + return 0; +fail: + JS_FreeValue(ctx, val); + return -1; +} + +void JS_SetModuleLoaderFunc(JSRuntime *rt, + JSModuleNormalizeFunc *module_normalize, + JSModuleLoaderFunc *module_loader, void *opaque) +{ + rt->module_normalize_func = module_normalize; + rt->module_loader_func = module_loader; + rt->module_loader_opaque = opaque; +} + +/* default module filename normalizer */ +char *js_default_module_normalize_name(JSContext *ctx, + const char *base_name, + const char *name) +{ + char *filename, *p; + const char *r; + int len; + + if (name[0] != '.') { + /* if no initial dot, the module name is not modified */ + return js_strdup(ctx, name); + } + + p = strrchr(base_name, '/'); + if (p) + len = p - base_name; + else + len = 0; + + filename = js_malloc(ctx, len + strlen(name) + 1 + 1); + if (!filename) + return NULL; + memcpy(filename, base_name, len); + filename[len] = '\0'; + + /* we only normalize the leading '..' or '.' */ + r = name; + for(;;) { + if (r[0] == '.' && r[1] == '/') { + r += 2; + } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') { + /* remove the last path element of filename, except if "." + or ".." */ + if (filename[0] == '\0') + break; + p = strrchr(filename, '/'); + if (!p) + p = filename; + else + p++; + if (!strcmp(p, ".") || !strcmp(p, "..")) + break; + if (p > filename) + p--; + *p = '\0'; + r += 3; + } else { + break; + } + } + if (filename[0] != '\0') + strcat(filename, "/"); + strcat(filename, r); + // printf("normalize: %s %s -> %s\n", base_name, name, filename); + return filename; +} + +JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) +{ + struct list_head *el; + JSModuleDef *m; + + /* first look at the loaded modules */ + list_for_each(el, &ctx->loaded_modules) { + m = list_entry(el, JSModuleDef, link); + if (m->module_name == name) + return m; + } + return NULL; +} + +/* return NULL in case of exception (e.g. module could not be loaded) */ +JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, + const char *base_cname, + const char *cname1) +{ + JSRuntime *rt = ctx->rt; + JSModuleDef *m; + char *cname; + JSAtom module_name; + + if (!rt->module_normalize_func) { + cname = js_default_module_normalize_name(ctx, base_cname, cname1); + } else { + cname = rt->module_normalize_func(ctx, base_cname, cname1, + rt->module_loader_opaque); + } + if (!cname) + return NULL; + + module_name = JS_NewAtom(ctx, cname); + if (module_name == JS_ATOM_NULL) { + js_free(ctx, cname); + return NULL; + } + + /* first look at the loaded modules */ + m = js_find_loaded_module(ctx, module_name); + if (m) { + js_free(ctx, cname); + JS_FreeAtom(ctx, module_name); + return m; + } + + JS_FreeAtom(ctx, module_name); + + /* load the module */ + if (!rt->module_loader_func) { + /* XXX: use a syntax error ? */ + JS_ThrowReferenceError(ctx, "could not load module '%s'", + cname); + js_free(ctx, cname); + return NULL; + } + + m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque); + js_free(ctx, cname); + return m; +} + +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; +} + +static int find_resolve_entry(JSResolveState *s, + JSModuleDef *m, JSAtom name) +{ + int i; + for(i = 0; i < s->count; i++) { + JSResolveEntry *re = &s->array[i]; + if (re->module == m && re->name == name) + return i; + } + return -1; +} + +static int add_resolve_entry(JSContext *ctx, JSResolveState *s, + JSModuleDef *m, JSAtom name) +{ + JSResolveEntry *re; + + if (js_resize_array(ctx, (void **)&s->array, + sizeof(JSResolveEntry), + &s->size, s->count + 1)) + return -1; + re = &s->array[s->count++]; + re->module = m; + re->name = JS_DupAtom(ctx, name); + return 0; +} + +typedef enum JSResolveResultEnum { + JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */ + JS_RESOLVE_RES_FOUND = 0, + JS_RESOLVE_RES_NOT_FOUND, + JS_RESOLVE_RES_CIRCULAR, + JS_RESOLVE_RES_AMBIGUOUS, +} JSResolveResultEnum; + +static JSResolveResultEnum js_resolve_export1(JSContext *ctx, + JSModuleDef **pmodule, + JSExportEntry **pme, + JSModuleDef *m, + JSAtom export_name, + JSResolveState *s) +{ + JSExportEntry *me; + + *pmodule = NULL; + *pme = NULL; + if (find_resolve_entry(s, m, export_name) >= 0) + return JS_RESOLVE_RES_CIRCULAR; + if (add_resolve_entry(ctx, s, m, export_name) < 0) + return JS_RESOLVE_RES_EXCEPTION; + me = find_export_entry(ctx, m, export_name); + if (me) { + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + /* local export */ + *pmodule = m; + *pme = me; + return JS_RESOLVE_RES_FOUND; + } else { + /* indirect export */ + JSModuleDef *m1; + m1 = m->req_module_entries[me->u.req_module_idx].module; + if (me->local_name == JS_ATOM__star_) { + /* export ns from */ + *pmodule = m; + *pme = me; + return JS_RESOLVE_RES_FOUND; + } else { + return js_resolve_export1(ctx, pmodule, pme, m1, + me->local_name, s); + } + } + } else { + if (export_name != JS_ATOM_default) { + /* not found in direct or indirect exports: try star exports */ + int i; + + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + JSModuleDef *m1, *res_m; + JSExportEntry *res_me; + JSResolveResultEnum ret; + + m1 = m->req_module_entries[se->req_module_idx].module; + ret = js_resolve_export1(ctx, &res_m, &res_me, m1, + export_name, s); + if (ret == JS_RESOLVE_RES_AMBIGUOUS || + ret == JS_RESOLVE_RES_EXCEPTION) { + return ret; + } else if (ret == JS_RESOLVE_RES_FOUND) { + if (*pme != NULL) { + if (*pmodule != res_m || + res_me->local_name != (*pme)->local_name) { + *pmodule = NULL; + *pme = NULL; + return JS_RESOLVE_RES_AMBIGUOUS; + } + } else { + *pmodule = res_m; + *pme = res_me; + } + } + } + if (*pme != NULL) + return JS_RESOLVE_RES_FOUND; + } + return JS_RESOLVE_RES_NOT_FOUND; + } +} + +/* If the return value is JS_RESOLVE_RES_FOUND, return the module + (*pmodule) and the corresponding local export entry + (*pme). Otherwise return (NULL, NULL) */ +static JSResolveResultEnum js_resolve_export(JSContext *ctx, + JSModuleDef **pmodule, + JSExportEntry **pme, + JSModuleDef *m, + JSAtom export_name) +{ + JSResolveState ss, *s = &ss; + int i; + JSResolveResultEnum ret; + + s->array = NULL; + s->size = 0; + s->count = 0; + + ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s); + + for(i = 0; i < s->count; i++) + JS_FreeAtom(ctx, s->array[i].name); + js_free(ctx, s->array); + + return ret; +} + +static void js_resolve_export_throw_error(JSContext *ctx, + JSResolveResultEnum res, + JSModuleDef *m, JSAtom export_name) +{ + char buf1[ATOM_GET_STR_BUF_SIZE]; + char buf2[ATOM_GET_STR_BUF_SIZE]; + switch(res) { + case JS_RESOLVE_RES_EXCEPTION: + break; + default: + case JS_RESOLVE_RES_NOT_FOUND: + JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", + JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), + JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); + break; + case JS_RESOLVE_RES_CIRCULAR: + JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", + JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), + JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); + break; + case JS_RESOLVE_RES_AMBIGUOUS: + JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", + JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), + JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); + break; + } +} + + +typedef enum { + EXPORTED_NAME_AMBIGUOUS, + EXPORTED_NAME_NORMAL, + EXPORTED_NAME_NS, +} ExportedNameEntryEnum; + +typedef struct ExportedNameEntry { + JSAtom export_name; + ExportedNameEntryEnum export_type; + union { + JSExportEntry *me; /* using when the list is built */ + JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */ + JSModuleDef *module; /* for EXPORTED_NAME_NS */ + } u; +} ExportedNameEntry; + +typedef struct GetExportNamesState { + JSModuleDef **modules; + int modules_size; + int modules_count; + + ExportedNameEntry *exported_names; + int exported_names_size; + int exported_names_count; +} GetExportNamesState; + +static int find_exported_name(GetExportNamesState *s, JSAtom name) +{ + int i; + for(i = 0; i < s->exported_names_count; i++) { + if (s->exported_names[i].export_name == name) + return i; + } + return -1; +} + +static __exception int get_exported_names(JSContext *ctx, + GetExportNamesState *s, + JSModuleDef *m, BOOL from_star) +{ + ExportedNameEntry *en; + int i, j; + + /* check circular reference */ + for(i = 0; i < s->modules_count; i++) { + if (s->modules[i] == m) + return 0; + } + if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]), + &s->modules_size, s->modules_count + 1)) + return -1; + s->modules[s->modules_count++] = m; + + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (from_star && me->export_name == JS_ATOM_default) + continue; + j = find_exported_name(s, me->export_name); + if (j < 0) { + if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]), + &s->exported_names_size, + s->exported_names_count + 1)) + return -1; + en = &s->exported_names[s->exported_names_count++]; + en->export_name = me->export_name; + /* avoid a second lookup for simple module exports */ + if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL) + en->u.me = NULL; + else + en->u.me = me; + } else { + en = &s->exported_names[j]; + en->u.me = NULL; + } + } + for(i = 0; i < m->star_export_entries_count; i++) { + JSStarExportEntry *se = &m->star_export_entries[i]; + JSModuleDef *m1; + m1 = m->req_module_entries[se->req_module_idx].module; + if (get_exported_names(ctx, s, m1, TRUE)) + return -1; + } + return 0; +} + +/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */ +static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom) +{ + return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL); +} + +static const JSClassExoticMethods js_module_ns_exotic_methods = { + .has_property = js_module_ns_has, +}; + +static int exported_names_cmp(const void *p1, const void *p2, void *opaque) +{ + JSContext *ctx = opaque; + const ExportedNameEntry *me1 = p1; + const ExportedNameEntry *me2 = p2; + JSValue str1, str2; + int ret; + + /* XXX: should avoid allocation memory in atom comparison */ + str1 = JS_AtomToString(ctx, me1->export_name); + str2 = JS_AtomToString(ctx, me2->export_name); + if (JS_IsException(str1) || JS_IsException(str2)) { + /* XXX: raise an error ? */ + ret = 0; + } else { + ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1), + JS_VALUE_GET_STRING(str2)); + } + JS_FreeValue(ctx, str1); + JS_FreeValue(ctx, str2); + return ret; +} + +static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m); + +JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque) +{ + JSModuleDef *m = opaque; + return js_get_module_ns(ctx, m); +} + +static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) +{ + JSValue obj; + JSObject *p; + GetExportNamesState s_s, *s = &s_s; + int i, ret; + JSProperty *pr; + + obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_GET_OBJ(obj); + + memset(s, 0, sizeof(*s)); + ret = get_exported_names(ctx, s, m, FALSE); + js_free(ctx, s->modules); + if (ret) + goto fail; + + /* Resolve the exported names. The ambiguous exports are removed */ + for(i = 0; i < s->exported_names_count; i++) { + ExportedNameEntry *en = &s->exported_names[i]; + JSResolveResultEnum res; + JSExportEntry *res_me; + JSModuleDef *res_m; + + if (en->u.me) { + res_me = en->u.me; /* fast case: no resolution needed */ + res_m = m; + res = JS_RESOLVE_RES_FOUND; + } else { + res = js_resolve_export(ctx, &res_m, &res_me, m, + en->export_name); + } + if (res != JS_RESOLVE_RES_FOUND) { + if (res != JS_RESOLVE_RES_AMBIGUOUS) { + js_resolve_export_throw_error(ctx, res, m, en->export_name); + goto fail; + } + en->export_type = EXPORTED_NAME_AMBIGUOUS; + } else { + if (res_me->local_name == JS_ATOM__star_) { + en->export_type = EXPORTED_NAME_NS; + en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module; + } else { + en->export_type = EXPORTED_NAME_NORMAL; + if (res_me->u.local.var_ref) { + en->u.var_ref = res_me->u.local.var_ref; + } else { + JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); + p1 = JS_VALUE_GET_OBJ(res_m->func_obj); + en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; + } + } + } + } + + /* sort the exported names */ + rqsort(s->exported_names, s->exported_names_count, + sizeof(s->exported_names[0]), exported_names_cmp, ctx); + + for(i = 0; i < s->exported_names_count; i++) { + ExportedNameEntry *en = &s->exported_names[i]; + switch(en->export_type) { + case EXPORTED_NAME_NORMAL: + { + JSVarRef *var_ref = en->u.var_ref; + pr = add_property(ctx, p, en->export_name, + JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | + JS_PROP_VARREF); + if (!pr) + goto fail; + var_ref->header.ref_count++; + pr->u.var_ref = var_ref; + } + break; + case EXPORTED_NAME_NS: + /* the exported namespace must be created on demand */ + if (JS_DefineAutoInitProperty(ctx, obj, + en->export_name, + JS_AUTOINIT_ID_MODULE_NS, + en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) + goto fail; + break; + default: + break; + } + } + + js_free(ctx, s->exported_names); + + JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag, + JS_AtomToString(ctx, JS_ATOM_Module), + 0); + + p->extensible = FALSE; + return obj; +fail: + js_free(ctx, s->exported_names); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m) +{ + if (JS_IsUndefined(m->module_ns)) { + JSValue val; + val = js_build_module_ns(ctx, m); + if (JS_IsException(val)) + return JS_EXCEPTION; + m->module_ns = val; + } + return JS_DupValue(ctx, m->module_ns); +} + +/* Load all the required modules for module 'm' */ +int js_resolve_module(JSContext *ctx, JSModuleDef *m) +{ + int i; + JSModuleDef *m1; + + if (m->resolved) + return 0; +#ifdef DUMP_MODULE_RESOLVE + { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + m->resolved = TRUE; + /* resolve each requested module */ + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, + rme->module_name); + if (!m1) + return -1; + rme->module = m1; + /* already done in js_host_resolve_imported_module() except if + the module was loaded with JS_EvalBinary() */ + if (js_resolve_module(ctx, m1) < 0) + return -1; + } + return 0; +} + +static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical) +{ + JSVarRef *var_ref; + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + if (is_lexical) + var_ref->value = JS_UNINITIALIZED; + else + var_ref->value = JS_UNDEFINED; + var_ref->pvalue = &var_ref->value; + var_ref->is_detached = TRUE; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + return var_ref; +} + +/* Create the function associated with the module */ +static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) +{ + JSFunctionBytecode *b; + int i; + JSVarRef **var_refs; + JSValue func_obj, bfunc; + JSObject *p; + + bfunc = m->func_obj; + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_BYTECODE_FUNCTION); + + if (JS_IsException(func_obj)) + return -1; + b = JS_VALUE_GET_PTR(bfunc); + + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + b->header.ref_count++; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + + /* create the global variables. The other variables are + imported from other modules */ + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + if (cv->is_local) { + var_ref = js_create_module_var(ctx, cv->is_lexical); + if (!var_ref) + goto fail; +#ifdef DUMP_MODULE_RESOLVE + printf("local %d: %p\n", i, var_ref); +#endif + var_refs[i] = var_ref; + } + } + } + m->func_obj = func_obj; + JS_FreeValue(ctx, bfunc); + return 0; +fail: + JS_FreeValue(ctx, func_obj); + return -1; +} + +/* must be done before js_link_module() because of cyclic references */ +int js_create_module_function(JSContext *ctx, JSModuleDef *m) +{ + BOOL is_c_module; + int i; + JSVarRef *var_ref; + + if (m->func_created) + return 0; + + is_c_module = (m->init_func != NULL); + + if (is_c_module) { + /* initialize the exported variables */ + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + var_ref = js_create_module_var(ctx, FALSE); + if (!var_ref) + return -1; + me->u.local.var_ref = var_ref; + } + } + } else { + if (js_create_module_bytecode_function(ctx, m)) + return -1; + } + m->func_created = TRUE; + + /* do it on the dependencies */ + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + if (js_create_module_function(ctx, rme->module) < 0) + return -1; + } + + return 0; +} + + +/* Prepare a module to be executed by resolving all the imported + variables. */ +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_link_module(ctx, rme->module) < 0) + goto fail; + } + +#ifdef DUMP_MODULE_RESOLVE + { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + /* check the indirect exports */ + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_INDIRECT && + me->local_name != JS_ATOM__star_) { + JSResolveResultEnum ret; + JSExportEntry *res_me; + JSModuleDef *res_m, *m1; + m1 = m->req_module_entries[me->u.req_module_idx].module; + ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name); + if (ret != JS_RESOLVE_RES_FOUND) { + js_resolve_export_throw_error(ctx, ret, m, me->export_name); + goto fail; + } + } + } + +#ifdef DUMP_MODULE_RESOLVE + { + printf("exported bindings:\n"); + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + printf(" name="); print_atom(ctx, me->export_name); + printf(" local="); print_atom(ctx, me->local_name); + printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx); + } + } +#endif + + is_c_module = (m->init_func != NULL); + + if (!is_c_module) { + p = JS_VALUE_GET_OBJ(m->func_obj); + var_refs = p->u.func.var_refs; + + for(i = 0; i < m->import_entries_count; i++) { + mi = &m->import_entries[i]; +#ifdef DUMP_MODULE_RESOLVE + printf("import var_idx=%d name=", mi->var_idx); + print_atom(ctx, mi->import_name); + printf(": "); +#endif + m1 = m->req_module_entries[mi->req_module_idx].module; + if (mi->import_name == JS_ATOM__star_) { + JSValue val; + /* name space import */ + val = js_get_module_ns(ctx, m1); + if (JS_IsException(val)) + goto fail; + set_value(ctx, &var_refs[mi->var_idx]->value, val); +#ifdef DUMP_MODULE_RESOLVE + printf("namespace\n"); +#endif + } else { + JSResolveResultEnum ret; + JSExportEntry *res_me; + JSModuleDef *res_m; + JSObject *p1; + + ret = js_resolve_export(ctx, &res_m, + &res_me, m1, mi->import_name); + if (ret != JS_RESOLVE_RES_FOUND) { + js_resolve_export_throw_error(ctx, ret, m1, mi->import_name); + goto fail; + } + if (res_me->local_name == JS_ATOM__star_) { + JSValue val; + JSModuleDef *m2; + /* name space import from */ + m2 = res_m->req_module_entries[res_me->u.req_module_idx].module; + val = js_get_module_ns(ctx, m2); + if (JS_IsException(val)) + goto fail; + var_ref = js_create_module_var(ctx, TRUE); + if (!var_ref) { + JS_FreeValue(ctx, val); + goto fail; + } + set_value(ctx, &var_ref->value, val); + var_refs[mi->var_idx] = var_ref; +#ifdef DUMP_MODULE_RESOLVE + printf("namespace from\n"); +#endif + } else { + var_ref = res_me->u.local.var_ref; + if (!var_ref) { + p1 = JS_VALUE_GET_OBJ(res_m->func_obj); + var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; + } + var_ref->header.ref_count++; + var_refs[mi->var_idx] = var_ref; +#ifdef DUMP_MODULE_RESOLVE + printf("local export (var_ref=%p)\n", var_ref); +#endif + } + } + } + + /* keep the exported variables in the module export entries (they + are used when the eval function is deleted and cannot be + initialized before in case imports are exported) */ + for(i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL) { + var_ref = var_refs[me->u.local.var_idx]; + var_ref->header.ref_count++; + me->u.local.var_ref = var_ref; + } + } + + /* initialize the global variables */ + ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL); + if (JS_IsException(ret_val)) + goto fail; + JS_FreeValue(ctx, ret_val); + } + +#ifdef DUMP_MODULE_RESOLVE + printf("done instantiate\n"); +#endif + return 0; +fail: + return -1; +} + +/* return JS_ATOM_NULL if the name cannot be found. Only works with + not striped bytecode functions. */ +JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) +{ + JSStackFrame *sf; + JSFunctionBytecode *b; + JSObject *p; + /* XXX: currently we just use the filename of the englobing + function. It does not work for eval(). Need to add a + ScriptOrModule info in JSFunctionBytecode */ + sf = ctx->rt->current_stack_frame; + if (!sf) + return JS_ATOM_NULL; + while (n_stack_levels-- > 0) { + sf = sf->prev_frame; + if (!sf) + return JS_ATOM_NULL; + } + if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) + return JS_ATOM_NULL; + p = JS_VALUE_GET_OBJ(sf->cur_func); + if (!js_class_has_bytecode(p->class_id)) + return JS_ATOM_NULL; + b = p->u.func.function_bytecode; + if (!b->has_debug) + return JS_ATOM_NULL; + return JS_DupAtom(ctx, b->debug.filename); +} + +JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) +{ + return JS_DupAtom(ctx, m->module_name); +} + +JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m) +{ + JSValue obj; + /* allocate meta_obj only if requested to save memory */ + obj = m->meta_obj; + if (JS_IsUndefined(obj)) { + obj = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(obj)) + return JS_EXCEPTION; + m->meta_obj = obj; + } + return JS_DupValue(ctx, obj); +} + +JSValue js_import_meta(JSContext *ctx) +{ + JSAtom filename; + JSModuleDef *m; + + filename = JS_GetScriptOrModuleName(ctx, 0); + if (filename == JS_ATOM_NULL) + goto fail; + + /* XXX: inefficient, need to add a module or script pointer in + JSFunctionBytecode */ + m = js_find_loaded_module(ctx, filename); + JS_FreeAtom(ctx, filename); + if (!m) { + fail: + JS_ThrowTypeError(ctx, "import.meta not supported in this context"); + return JS_EXCEPTION; + } + return JS_GetImportMeta(ctx, m); +} + +/* used by os.Worker() and import() */ +JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, + const char *filename) +{ + JSModuleDef *m; + JSValue ret, func_obj; + + m = js_host_resolve_imported_module(ctx, basename, filename); + if (!m) + return NULL; + + if (js_resolve_module(ctx, m) < 0) { + js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); + 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)) + 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(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_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; +} + +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; +} + +/* Run the function of the module and of all its requested + modules. */ +JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) +{ + JSModuleDef *m1; + int i; + JSValue ret_val; + + if (m->eval_mark) + return JS_UNDEFINED; /* avoid cycles */ + + if (m->evaluated) { + /* if the module was already evaluated, rethrow the exception + it raised */ + if (m->eval_has_exception) { + return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception)); + } else { + return JS_UNDEFINED; + } + } + + m->eval_mark = TRUE; + + for(i = 0; i < m->req_module_entries_count; i++) { + JSReqModuleEntry *rme = &m->req_module_entries[i]; + m1 = rme->module; + if (!m1->eval_mark) { + ret_val = js_evaluate_module(ctx, m1); + if (JS_IsException(ret_val)) { + m->eval_mark = FALSE; + return ret_val; + } + JS_FreeValue(ctx, ret_val); + } + } + + if (m->init_func) { + /* C module init */ + if (m->init_func(ctx, m) < 0) + ret_val = JS_EXCEPTION; + else + ret_val = JS_UNDEFINED; + } else { + ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL); + m->func_obj = JS_UNDEFINED; + } + if (JS_IsException(ret_val)) { + /* save the thrown exception value */ + m->eval_has_exception = TRUE; + m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception); + } + m->eval_mark = FALSE; + m->evaluated = TRUE; + return ret_val; +} + +int JS_ResolveModule(JSContext *ctx, JSValueConst obj) +{ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + JSModuleDef *m = JS_VALUE_GET_PTR(obj); + if (js_resolve_module(ctx, m) < 0) { + js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); + return -1; + } + } + return 0; +} diff --git a/src/core/module.h b/src/core/module.h index d5061359b..acebd0211 100644 --- a/src/core/module.h +++ b/src/core/module.h @@ -1,85 +1,85 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#ifndef QUICKJS_MODULE_H -#define QUICKJS_MODULE_H - -#include "quickjs/quickjs.h" -#include "types.h" - -typedef struct JSResolveEntry { - JSModuleDef *module; - JSAtom name; -} JSResolveEntry; - -typedef struct JSResolveState { - JSResolveEntry *array; - int size; - int count; -} JSResolveState; - -/* 'name' is freed */ -JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name); - -void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, - JS_MarkFunc *mark_func); - -int add_req_module_entry(JSContext *ctx, JSModuleDef *m, - JSAtom module_name); - -JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, - JSAtom export_name); - -char *js_default_module_normalize_name(JSContext *ctx, - const char *base_name, - const char *name); - -JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name); - -/* return NULL in case of exception (e.g. module could not be loaded) */ -JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, - const char *base_cname, - const char *cname1); - -JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, - JSAtom base_module_name, - JSAtom module_name1); - -int js_create_module_function(JSContext *ctx, JSModuleDef *m); - -/* Load all the required modules for module 'm' */ -int js_resolve_module(JSContext *ctx, JSModuleDef *m); - -/* Prepare a module to be executed by resolving all the imported - variables. */ -int js_link_module(JSContext *ctx, JSModuleDef *m); - -/* Run the function of the module and of all its requested - modules. */ -JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m); - -JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); - +/* +* QuickJS Javascript Engine +* +* Copyright (c) 2017-2021 Fabrice Bellard +* Copyright (c) 2017-2021 Charlie Gordon +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. + */ + +#ifndef QUICKJS_MODULE_H +#define QUICKJS_MODULE_H + +#include "quickjs/quickjs.h" +#include "types.h" + +typedef struct JSResolveEntry { + JSModuleDef *module; + JSAtom name; +} JSResolveEntry; + +typedef struct JSResolveState { + JSResolveEntry *array; + int size; + int count; +} JSResolveState; + +/* 'name' is freed */ +JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name); + +void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, + JS_MarkFunc *mark_func); + +int add_req_module_entry(JSContext *ctx, JSModuleDef *m, + JSAtom module_name); + +JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, + JSAtom export_name); + +char *js_default_module_normalize_name(JSContext *ctx, + const char *base_name, + const char *name); + +JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name); + +/* return NULL in case of exception (e.g. module could not be loaded) */ +JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, + const char *base_cname, + const char *cname1); + +JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, + JSAtom base_module_name, + JSAtom module_name1); + +int js_create_module_function(JSContext *ctx, JSModuleDef *m); + +/* Load all the required modules for module 'm' */ +int js_resolve_module(JSContext *ctx, JSModuleDef *m); + +/* Prepare a module to be executed by resolving all the imported + variables. */ +int js_link_module(JSContext *ctx, JSModuleDef *m); + +/* Run the function of the module and of all its requested + modules. */ +JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m); + +JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); + #endif \ No newline at end of file diff --git a/src/core/object.c b/src/core/object.c index 5eb81b692..7c8676ea1 100644 --- a/src/core/object.c +++ b/src/core/object.c @@ -1,2217 +1,2217 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "object.h" -#include "builtins/js-function.h" -#include "builtins/js-object.h" -#include "builtins/js-operator.h" -#include "builtins/js-proxy.h" -#include "convertion.h" -#include "exception.h" -#include "function.h" -#include "gc.h" -#include "parser.h" -#include "runtime.h" -#include "shape.h" -#include "string.h" -#include "types.h" - -JSValue JS_GetPropertyValue(JSContext* ctx, JSValueConst this_obj, JSValue prop) { - JSAtom atom; - JSValue ret; - - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { - JSObject* p; - uint32_t idx, len; - /* fast path for array access */ - p = JS_VALUE_GET_OBJ(this_obj); - idx = JS_VALUE_GET_INT(prop); - len = (uint32_t)p->u.array.count; - if (unlikely(idx >= len)) - goto slow_path; - switch (p->class_id) { - case JS_CLASS_ARRAY: - case JS_CLASS_ARGUMENTS: - return JS_DupValue(ctx, p->u.array.u.values[idx]); - case JS_CLASS_INT8_ARRAY: - return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]); - case JS_CLASS_UINT8C_ARRAY: - case JS_CLASS_UINT8_ARRAY: - return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]); - case JS_CLASS_INT16_ARRAY: - return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]); - case JS_CLASS_UINT16_ARRAY: - return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]); - case JS_CLASS_INT32_ARRAY: - return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]); - case JS_CLASS_UINT32_ARRAY: - return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]); -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT64_ARRAY: - return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); - case JS_CLASS_BIG_UINT64_ARRAY: - return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); -#endif - case JS_CLASS_FLOAT32_ARRAY: - return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); - case JS_CLASS_FLOAT64_ARRAY: - return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); - default: - goto slow_path; - } - } else { - slow_path: - atom = JS_ValueToAtom(ctx, prop); - JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) - return JS_EXCEPTION; - ret = JS_GetProperty(ctx, this_obj, atom); - JS_FreeAtom(ctx, atom); - return ret; - } -} - -JSValue JS_GetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx) { - return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx)); -} - -/* Check if an object has a generalized numeric property. Return value: - -1 for exception, - TRUE if property exists, stored into *pval, - FALSE if proprty does not exist. - */ -int JS_TryGetPropertyInt64(JSContext* ctx, JSValueConst obj, int64_t idx, JSValue* pval) { - JSValue val = JS_UNDEFINED; - JSAtom prop; - int present; - - if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { - /* fast path */ - present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); - if (present > 0) { - val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); - if (unlikely(JS_IsException(val))) - present = -1; - } - } else { - prop = JS_NewAtomInt64(ctx, idx); - present = -1; - if (likely(prop != JS_ATOM_NULL)) { - present = JS_HasProperty(ctx, obj, prop); - if (present > 0) { - val = JS_GetProperty(ctx, obj, prop); - if (unlikely(JS_IsException(val))) - present = -1; - } - JS_FreeAtom(ctx, prop); - } - } - *pval = val; - return present; -} - -JSValue JS_GetPropertyInt64(JSContext* ctx, JSValueConst obj, int64_t idx) { - JSAtom prop; - JSValue val; - - if ((uint64_t)idx <= INT32_MAX) { - /* fast path for fast arrays */ - return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); - } - prop = JS_NewAtomInt64(ctx, idx); - if (prop == JS_ATOM_NULL) - return JS_EXCEPTION; - - val = JS_GetProperty(ctx, obj, prop); - JS_FreeAtom(ctx, prop); - return val; -} - -JSValue JS_GetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop) { - JSAtom atom; - JSValue ret; - atom = JS_NewAtom(ctx, prop); - ret = JS_GetProperty(ctx, this_obj, atom); - JS_FreeAtom(ctx, atom); - return ret; -} - -/* Note: the property value is not initialized. Return NULL if memory - error. */ -JSProperty* add_property(JSContext* ctx, JSObject* p, JSAtom prop, int prop_flags) { - JSShape *sh, *new_sh; - - sh = p->shape; - if (sh->is_hashed) { - /* try to find an existing shape */ - new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); - if (new_sh) { - /* matching shape found: use it */ - /* the property array may need to be resized */ - if (new_sh->prop_size != sh->prop_size) { - JSProperty* new_prop; - new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * new_sh->prop_size); - if (!new_prop) - return NULL; - p->prop = new_prop; - } - p->shape = js_dup_shape(new_sh); - js_free_shape(ctx->rt, sh); - return &p->prop[new_sh->prop_count - 1]; - } else if (sh->header.ref_count != 1) { - /* if the shape is shared, clone it */ - new_sh = js_clone_shape(ctx, sh); - if (!new_sh) - return NULL; - /* hash the cloned shape */ - new_sh->is_hashed = TRUE; - js_shape_hash_link(ctx->rt, new_sh); - js_free_shape(ctx->rt, p->shape); - p->shape = new_sh; - } - } - assert(p->shape->header.ref_count == 1); - if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) - return NULL; - return &p->prop[p->shape->prop_count - 1]; -} - -/* can be called on Array or Arguments objects. return < 0 if - memory alloc error. */ -no_inline __exception int convert_fast_array_to_array(JSContext* ctx, JSObject* p) { - JSProperty* pr; - JSShape* sh; - JSValue* tab; - uint32_t i, len, new_count; - - if (js_shape_prepare_update(ctx, p, NULL)) - return -1; - len = p->u.array.count; - /* resize the properties once to simplify the error handling */ - sh = p->shape; - new_count = sh->prop_count + len; - if (new_count > sh->prop_size) { - if (resize_properties(ctx, &p->shape, p, new_count)) - return -1; - } - - tab = p->u.array.u.values; - for (i = 0; i < len; i++) { - /* add_property cannot fail here but - __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ - pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); - pr->u.value = *tab++; - } - js_free(ctx, p->u.array.u.values); - p->u.array.count = 0; - p->u.array.u.values = NULL; /* fail safe */ - p->u.array.u1.size = 0; - p->fast_array = 0; - return 0; -} - -int delete_property(JSContext* ctx, JSObject* p, JSAtom atom) { - JSShape* sh; - JSShapeProperty *pr, *lpr, *prop; - JSProperty* pr1; - uint32_t lpr_idx; - intptr_t h, h1; - -redo: - sh = p->shape; - h1 = atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h1 - 1]; - prop = get_shape_prop(sh); - lpr = NULL; - lpr_idx = 0; /* prevent warning */ - while (h != 0) { - pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { - /* found ! */ - if (!(pr->flags & JS_PROP_CONFIGURABLE)) - return FALSE; - /* realloc the shape if needed */ - if (lpr) - lpr_idx = lpr - get_shape_prop(sh); - if (js_shape_prepare_update(ctx, p, &pr)) - return -1; - sh = p->shape; - /* remove property */ - if (lpr) { - lpr = get_shape_prop(sh) + lpr_idx; - lpr->hash_next = pr->hash_next; - } else { - prop_hash_end(sh)[-h1 - 1] = pr->hash_next; - } - sh->deleted_prop_count++; - /* free the entry */ - pr1 = &p->prop[h - 1]; - free_property(ctx->rt, pr1, pr->flags); - JS_FreeAtom(ctx, pr->atom); - /* put default values */ - pr->flags = 0; - pr->atom = JS_ATOM_NULL; - pr1->u.value = JS_UNDEFINED; - - /* compact the properties if too many deleted properties */ - if (sh->deleted_prop_count >= 8 && sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { - compact_properties(ctx, p); - } - return TRUE; - } - lpr = pr; - h = pr->hash_next; - } - - if (p->is_exotic) { - if (p->fast_array) { - uint32_t idx; - if (JS_AtomIsArrayIndex(ctx, &idx, atom) && idx < p->u.array.count) { - if (p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) { - /* Special case deleting the last element of a fast Array */ - if (idx == p->u.array.count - 1) { - JS_FreeValue(ctx, p->u.array.u.values[idx]); - p->u.array.count = idx; - return TRUE; - } - if (convert_fast_array_to_array(ctx, p)) - return -1; - goto redo; - } else { - return FALSE; - } - } - } else { - const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->delete_property) { - return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); - } - } - } - /* not found */ - return TRUE; -} - -int call_setter(JSContext* ctx, JSObject* setter, JSValueConst this_obj, JSValue val, int flags) { - JSValue ret, func; - if (likely(setter)) { - func = JS_MKPTR(JS_TAG_OBJECT, setter); - /* Note: the field could be removed in the setter */ - func = JS_DupValue(ctx, func); - ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst*)&val); - JS_FreeValue(ctx, val); - if (JS_IsException(ret)) - return -1; - JS_FreeValue(ctx, ret); - return TRUE; - } else { - JS_FreeValue(ctx, val); - if ((flags & JS_PROP_THROW) || ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { - JS_ThrowTypeError(ctx, "no setter for property"); - return -1; - } - return FALSE; - } -} - -void free_property(JSRuntime* rt, JSProperty* pr, int prop_flags) { - if (unlikely(prop_flags & JS_PROP_TMASK)) { - if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - if (pr->u.getset.getter) - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (pr->u.getset.setter) - JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); - } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - free_var_ref(rt, pr->u.var_ref); - } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - js_autoinit_free(rt, pr); - } - } else { - JS_FreeValueRT(rt, pr->u.value); - } -} - -/* return the value associated to the autoinit property or an exception */ -typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); - -static JSAutoInitFunc *js_autoinit_func_table[] = { - js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ - js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ - JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ -}; - -/* warning: 'prs' is reallocated after it */ -static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, - JSProperty *pr, JSShapeProperty *prs) -{ - JSValue val; - JSContext *realm; - JSAutoInitFunc *func; - - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - - realm = js_autoinit_get_realm(pr); - func = js_autoinit_func_table[js_autoinit_get_id(pr)]; - /* 'func' shall not modify the object properties 'pr' */ - val = func(realm, p, prop, pr->u.init.opaque); - js_autoinit_free(ctx->rt, pr); - prs->flags &= ~JS_PROP_TMASK; - pr->u.value = JS_UNDEFINED; - if (JS_IsException(val)) - return -1; - pr->u.value = val; - return 0; -} - -JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst this_obj, - BOOL throw_ref_error) -{ - JSObject *p; - JSProperty *pr; - JSShapeProperty *prs; - uint32_t tag; - - tag = JS_VALUE_GET_TAG(obj); - if (unlikely(tag != JS_TAG_OBJECT)) { - switch(tag) { - case JS_TAG_NULL: - return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); - case JS_TAG_UNDEFINED: - return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); - case JS_TAG_EXCEPTION: - return JS_EXCEPTION; - case JS_TAG_STRING: - { - JSString *p1 = JS_VALUE_GET_STRING(obj); - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx, ch; - idx = __JS_AtomToUInt32(prop); - if (idx < p1->len) { - if (p1->is_wide_char) - ch = p1->u.str16[idx]; - else - ch = p1->u.str8[idx]; - return js_new_string_char(ctx, ch); - } - } else if (prop == JS_ATOM_length) { - return JS_NewInt32(ctx, p1->len); - } - } - break; - default: - break; - } - /* cannot raise an exception */ - p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); - if (!p) - return JS_UNDEFINED; - } else { - p = JS_VALUE_GET_OBJ(obj); - } - - for(;;) { - prs = find_own_property(&pr, p, prop); - if (prs) { - /* found */ - if (unlikely(prs->flags & JS_PROP_TMASK)) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - if (unlikely(!pr->u.getset.getter)) { - return JS_UNDEFINED; - } else { - JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); - /* Note: the field could be removed in the getter */ - func = JS_DupValue(ctx, func); - return JS_CallFree(ctx, func, this_obj, 0, NULL); - } - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - JSValue val = *pr->u.var_ref->pvalue; - if (unlikely(JS_IsUninitialized(val))) - return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return JS_DupValue(ctx, val); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) - return JS_EXCEPTION; - continue; - } - } else { - return JS_DupValue(ctx, pr->u.value); - } - } - if (unlikely(p->is_exotic)) { - /* exotic behaviors */ - if (p->fast_array) { - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - /* we avoid duplicating the code */ - return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - return JS_UNDEFINED; - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && - p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - int ret; - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) - return JS_EXCEPTION; - return JS_UNDEFINED; - } - } - } else { - const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; - if (em) { - if (em->get_property) { - JSValue obj1, retval; - /* XXX: should pass throw_ref_error */ - /* Note: if 'p' is a prototype, it can be - freed in the called function */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - retval = em->get_property(ctx, obj1, prop, this_obj); - JS_FreeValue(ctx, obj1); - return retval; - } - if (em->get_own_property) { - JSPropertyDescriptor desc; - int ret; - JSValue obj1; - - /* Note: if 'p' is a prototype, it can be - freed in the called function */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - ret = em->get_own_property(ctx, &desc, obj1, prop); - JS_FreeValue(ctx, obj1); - if (ret < 0) - return JS_EXCEPTION; - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JS_FreeValue(ctx, desc.setter); - return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); - } else { - return desc.value; - } - } - } - } - } - } - p = p->shape->proto; - if (!p) - break; - } - if (unlikely(throw_ref_error)) { - return JS_ThrowReferenceErrorNotDefined(ctx, prop); - } else { - return JS_UNDEFINED; - } -} - -JSValue JS_GetOwnPropertyNames2(JSContext* ctx, JSValueConst obj1, int flags, int kind) { - JSValue obj, r, val, key, value; - JSObject* p; - JSPropertyEnum* atoms; - uint32_t len, i, j; - - r = JS_UNDEFINED; - val = JS_UNDEFINED; - obj = JS_ToObject(ctx, obj1); - if (JS_IsException(obj)) - return JS_EXCEPTION; - p = JS_VALUE_GET_OBJ(obj); - if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY)) - goto exception; - r = JS_NewArray(ctx); - if (JS_IsException(r)) - goto exception; - for (j = i = 0; i < len; i++) { - JSAtom atom = atoms[i].atom; - if (flags & JS_GPN_ENUM_ONLY) { - JSPropertyDescriptor desc; - int res; - - /* Check if property is still enumerable */ - res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); - if (res < 0) - goto exception; - if (!res) - continue; - js_free_desc(ctx, &desc); - if (!(desc.flags & JS_PROP_ENUMERABLE)) - continue; - } - switch (kind) { - default: - case JS_ITERATOR_KIND_KEY: - val = JS_AtomToValue(ctx, atom); - if (JS_IsException(val)) - goto exception; - break; - case JS_ITERATOR_KIND_VALUE: - val = JS_GetProperty(ctx, obj, atom); - if (JS_IsException(val)) - goto exception; - break; - case JS_ITERATOR_KIND_KEY_AND_VALUE: - val = JS_NewArray(ctx); - if (JS_IsException(val)) - goto exception; - key = JS_AtomToValue(ctx, atom); - if (JS_IsException(key)) - goto exception1; - if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0) - goto exception1; - value = JS_GetProperty(ctx, obj, atom); - if (JS_IsException(value)) - goto exception1; - if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0) - goto exception1; - break; - } - if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0) - goto exception; - } - goto done; - -exception1: - JS_FreeValue(ctx, val); -exception: - JS_FreeValue(ctx, r); - r = JS_EXCEPTION; -done: - js_free_prop_enum(ctx, atoms, len); - JS_FreeValue(ctx, obj); - return r; -} - - -/* return FALSE if not OK */ -BOOL check_define_prop_flags(int prop_flags, int flags) -{ - BOOL has_accessor, is_getset; - - if (!(prop_flags & JS_PROP_CONFIGURABLE)) { - if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == - (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { - return FALSE; - } - if ((flags & JS_PROP_HAS_ENUMERABLE) && - (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) - return FALSE; - } - if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | - JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - if (!(prop_flags & JS_PROP_CONFIGURABLE)) { - has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); - is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); - if (has_accessor != is_getset) - return FALSE; - if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { - /* not writable: cannot set the writable bit */ - if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == - (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) - return FALSE; - } - } - } - return TRUE; -} - -void js_free_prop_enum(JSContext* ctx, JSPropertyEnum* tab, uint32_t len) { - uint32_t i; - if (tab) { - for (i = 0; i < len; i++) - JS_FreeAtom(ctx, tab[i].atom); - js_free(ctx, tab); - } -} - -void js_free_desc(JSContext* ctx, JSPropertyDescriptor* desc) { - JS_FreeValue(ctx, desc->getter); - JS_FreeValue(ctx, desc->setter); - JS_FreeValue(ctx, desc->value); -} - -__exception int JS_CopyDataProperties(JSContext* ctx, JSValueConst target, JSValueConst source, JSValueConst excluded, BOOL setprop) { - JSPropertyEnum* tab_atom; - JSValue val; - uint32_t i, tab_atom_count; - JSObject* p; - JSObject* pexcl = NULL; - int ret, gpn_flags; - JSPropertyDescriptor desc; - BOOL is_enumerable; - - if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) - return 0; - - if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) - pexcl = JS_VALUE_GET_OBJ(excluded); - - p = JS_VALUE_GET_OBJ(source); - - gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; - if (p->is_exotic) { - const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; - /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it - introduces a visible change */ - if (em && em->get_own_property_names) { - gpn_flags &= ~JS_GPN_ENUM_ONLY; - } - } - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, gpn_flags)) - return -1; - - for (i = 0; i < tab_atom_count; i++) { - if (pexcl) { - ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); - if (ret) { - if (ret < 0) - goto exception; - continue; - } - } - if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { - /* test if the property is enumerable */ - ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); - if (ret < 0) - goto exception; - if (!ret) - continue; - is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; - js_free_desc(ctx, &desc); - if (!is_enumerable) - continue; - } - val = JS_GetProperty(ctx, source, tab_atom[i].atom); - if (JS_IsException(val)) - goto exception; - if (setprop) - ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); - else - ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, JS_PROP_C_W_E); - if (ret < 0) - goto exception; - } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - return 0; -exception: - js_free_prop_enum(ctx, tab_atom, tab_atom_count); - return -1; -} - -JSValue js_instantiate_prototype(JSContext* ctx, JSObject* p, JSAtom atom, void* opaque) { - JSValue obj, this_val; - int ret; - - this_val = JS_MKPTR(JS_TAG_OBJECT, p); - obj = JS_NewObject(ctx); - if (JS_IsException(obj)) - return JS_EXCEPTION; - set_cycle_flag(ctx, obj); - set_cycle_flag(ctx, this_val); - ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, JS_DupValue(ctx, this_val), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (ret < 0) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - return obj; -} - -JSValue js_create_from_ctor(JSContext* ctx, JSValueConst ctor, int class_id) { - JSValue proto, obj; - JSContext* realm; - - if (JS_IsUndefined(ctor)) { - proto = JS_DupValue(ctx, ctx->class_proto[class_id]); - } else { - proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); - if (JS_IsException(proto)) - return proto; - if (!JS_IsObject(proto)) { - JS_FreeValue(ctx, proto); - realm = JS_GetFunctionRealm(ctx, ctor); - if (!realm) - return JS_EXCEPTION; - proto = JS_DupValue(ctx, realm->class_proto[class_id]); - } - } - obj = JS_NewObjectProtoClass(ctx, proto, class_id); - JS_FreeValue(ctx, proto); - return obj; -} - -/* return -1 if exception (Proxy object only) or TRUE/FALSE */ -int JS_IsExtensible(JSContext* ctx, JSValueConst obj) { - JSObject* p; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_isExtensible(ctx, obj); - else - return p->extensible; -} - -/* return -1 if exception (Proxy object only) or TRUE/FALSE */ -int JS_PreventExtensions(JSContext* ctx, JSValueConst obj) { - JSObject* p; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_preventExtensions(ctx, obj); - p->extensible = FALSE; - return TRUE; -} - -/* return -1 if exception otherwise TRUE or FALSE */ -int JS_HasProperty(JSContext* ctx, JSValueConst obj, JSAtom prop) { - JSObject* p; - int ret; - JSValue obj1; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return FALSE; - p = JS_VALUE_GET_OBJ(obj); - for (;;) { - if (p->is_exotic) { - const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->has_property) { - /* has_property can free the prototype */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - ret = em->has_property(ctx, obj1, prop); - JS_FreeValue(ctx, obj1); - return ret; - } - } - /* JS_GetOwnPropertyInternal can free the prototype */ - JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - if (ret != 0) - return ret; - if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) - return -1; - return FALSE; - } - } - p = p->shape->proto; - if (!p) - break; - } - return FALSE; -} - -/* Private fields can be added even on non extensible objects or - Proxies */ -int JS_DefinePrivateField(JSContext* ctx, JSValueConst obj, JSValueConst name, JSValue val) { - JSObject* p; - JSShapeProperty* prs; - JSProperty* pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail; - } - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { - JS_ThrowTypeErrorNotASymbol(ctx); - goto fail; - } - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (prs) { - JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", prop); - goto fail; - } - pr = add_property(ctx, p, prop, JS_PROP_C_W_E); - if (unlikely(!pr)) { - fail: - JS_FreeValue(ctx, val); - return -1; - } - pr->u.value = val; - return 0; -} - -JSValue JS_GetPrivateField(JSContext* ctx, JSValueConst obj, JSValueConst name) { - JSObject* p; - JSShapeProperty* prs; - JSProperty* pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - return JS_ThrowTypeErrorNotAnObject(ctx); - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) - return JS_ThrowTypeErrorNotASymbol(ctx); - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { - JS_ThrowTypeErrorPrivateNotFound(ctx, prop); - return JS_EXCEPTION; - } - return JS_DupValue(ctx, pr->u.value); -} - -int JS_SetPrivateField(JSContext* ctx, JSValueConst obj, JSValueConst name, JSValue val) { - JSObject* p; - JSShapeProperty* prs; - JSProperty* pr; - JSAtom prop; - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - goto fail; - } - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { - JS_ThrowTypeErrorNotASymbol(ctx); - goto fail; - } - prop = js_symbol_to_atom(ctx, (JSValue)name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { - JS_ThrowTypeErrorPrivateNotFound(ctx, prop); - fail: - JS_FreeValue(ctx, val); - return -1; - } - set_value(ctx, &pr->u.value, val); - return 0; -} - -int JS_AddBrand(JSContext* ctx, JSValueConst obj, JSValueConst home_obj) { - JSObject *p, *p1; - JSShapeProperty* prs; - JSProperty* pr; - JSValue brand; - JSAtom brand_atom; - - if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - p = JS_VALUE_GET_OBJ(home_obj); - prs = find_own_property(&pr, p, JS_ATOM_Private_brand); - if (!prs) { - brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); - if (JS_IsException(brand)) - return -1; - /* if the brand is not present, add it */ - pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); - if (!pr) { - JS_FreeValue(ctx, brand); - return -1; - } - pr->u.value = JS_DupValue(ctx, brand); - } else { - brand = JS_DupValue(ctx, pr->u.value); - } - brand_atom = js_symbol_to_atom(ctx, brand); - - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); - JS_FreeAtom(ctx, brand_atom); - return -1; - } - p1 = JS_VALUE_GET_OBJ(obj); - pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); - JS_FreeAtom(ctx, brand_atom); - if (!pr) - return -1; - pr->u.value = JS_UNDEFINED; - return 0; -} - -int JS_CheckBrand(JSContext* ctx, JSValueConst obj, JSValueConst func) { - JSObject *p, *p1, *home_obj; - JSShapeProperty* prs; - JSProperty* pr; - JSValueConst brand; - - /* get the home object of 'func' */ - if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) { - not_obj: - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - p1 = JS_VALUE_GET_OBJ(func); - if (!js_class_has_bytecode(p1->class_id)) - goto not_obj; - home_obj = p1->u.func.home_object; - if (!home_obj) - goto not_obj; - prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); - if (!prs) { - JS_ThrowTypeError(ctx, "expecting private field"); - return -1; - } - brand = pr->u.value; - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) - goto not_obj; - - /* get the brand array of 'obj' */ - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - goto not_obj; - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); - if (!prs) { - JS_ThrowTypeError(ctx, "invalid brand on object"); - return -1; - } - return 0; -} - -uint32_t js_string_obj_get_length(JSContext* ctx, JSValueConst obj) { - JSObject* p; - JSString* p1; - uint32_t len = 0; - - /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ - p = JS_VALUE_GET_OBJ(obj); - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { - p1 = JS_VALUE_GET_STRING(p->u.object_data); - len = p1->len; - } - return len; -} - - -static int num_keys_cmp(const void *p1, const void *p2, void *opaque) -{ - JSContext *ctx = opaque; - JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; - JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; - uint32_t v1, v2; - BOOL atom1_is_integer, atom2_is_integer; - - atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); - atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); - assert(atom1_is_integer && atom2_is_integer); - if (v1 < v2) - return -1; - else if (v1 == v2) - return 0; - else - return 1; -} - - -/* return < 0 in case if exception, 0 if OK. ptab and its atoms must - be freed by the user. */ -int __exception JS_GetOwnPropertyNamesInternal(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSObject* p, int flags) { - int i, j; - JSShape* sh; - JSShapeProperty* prs; - JSPropertyEnum *tab_atom, *tab_exotic; - JSAtom atom; - uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; - uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; - BOOL is_enumerable, num_sorted; - uint32_t num_key; - JSAtomKindEnum kind; - - /* clear pointer for consistency in case of failure */ - *ptab = NULL; - *plen = 0; - - /* compute the number of returned properties */ - num_keys_count = 0; - str_keys_count = 0; - sym_keys_count = 0; - exotic_keys_count = 0; - exotic_count = 0; - tab_exotic = NULL; - sh = p->shape; - for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - atom = prs->atom; - if (atom != JS_ATOM_NULL) { - is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); - kind = JS_AtomGetKind(ctx, atom); - if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && ((flags >> kind) & 1) != 0) { - /* need to raise an exception in case of the module - name space (implicit GetOwnProperty) */ - if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { - JSVarRef* var_ref = p->prop[i].u.var_ref; - if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - } - if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { - num_keys_count++; - } else if (kind == JS_ATOM_KIND_STRING) { - str_keys_count++; - } else { - sym_keys_count++; - } - } - } - } - - if (p->is_exotic) { - if (p->fast_array) { - if (flags & JS_GPN_STRING_MASK) { - num_keys_count += p->u.array.count; - } - } else if (p->class_id == JS_CLASS_STRING) { - if (flags & JS_GPN_STRING_MASK) { - num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - } - } else { - const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->get_own_property_names) { - if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, JS_MKPTR(JS_TAG_OBJECT, p))) - return -1; - for (i = 0; i < exotic_count; i++) { - atom = tab_exotic[i].atom; - kind = JS_AtomGetKind(ctx, atom); - if (((flags >> kind) & 1) != 0) { - is_enumerable = FALSE; - if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { - JSPropertyDescriptor desc; - int res; - /* set the "is_enumerable" field if necessary */ - res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); - if (res < 0) { - js_free_prop_enum(ctx, tab_exotic, exotic_count); - return -1; - } - if (res) { - is_enumerable = ((desc.flags & JS_PROP_ENUMERABLE) != 0); - js_free_desc(ctx, &desc); - } - tab_exotic[i].is_enumerable = is_enumerable; - } - if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { - exotic_keys_count++; - } - } - } - } - } - } - - /* fill them */ - - atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; - /* avoid allocating 0 bytes */ - tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); - if (!tab_atom) { - js_free_prop_enum(ctx, tab_exotic, exotic_count); - return -1; - } - - num_index = 0; - str_index = num_keys_count; - sym_index = str_index + str_keys_count; - - num_sorted = TRUE; - sh = p->shape; - for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { - atom = prs->atom; - if (atom != JS_ATOM_NULL) { - is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); - kind = JS_AtomGetKind(ctx, atom); - if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && ((flags >> kind) & 1) != 0) { - if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { - j = num_index++; - num_sorted = FALSE; - } else if (kind == JS_ATOM_KIND_STRING) { - j = str_index++; - } else { - j = sym_index++; - } - tab_atom[j].atom = JS_DupAtom(ctx, atom); - tab_atom[j].is_enumerable = is_enumerable; - } - } - } - - if (p->is_exotic) { - int len; - if (p->fast_array) { - if (flags & JS_GPN_STRING_MASK) { - len = p->u.array.count; - goto add_array_keys; - } - } else if (p->class_id == JS_CLASS_STRING) { - if (flags & JS_GPN_STRING_MASK) { - len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - add_array_keys: - for (i = 0; i < len; i++) { - tab_atom[num_index].atom = __JS_AtomFromUInt32(i); - if (tab_atom[num_index].atom == JS_ATOM_NULL) { - js_free_prop_enum(ctx, tab_atom, num_index); - return -1; - } - tab_atom[num_index].is_enumerable = TRUE; - num_index++; - } - } - } else { - /* Note: exotic keys are not reordered and comes after the object own properties. */ - for (i = 0; i < exotic_count; i++) { - atom = tab_exotic[i].atom; - is_enumerable = tab_exotic[i].is_enumerable; - kind = JS_AtomGetKind(ctx, atom); - if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && ((flags >> kind) & 1) != 0) { - tab_atom[sym_index].atom = atom; - tab_atom[sym_index].is_enumerable = is_enumerable; - sym_index++; - } else { - JS_FreeAtom(ctx, atom); - } - } - js_free(ctx, tab_exotic); - } - } - - assert(num_index == num_keys_count); - assert(str_index == num_keys_count + str_keys_count); - assert(sym_index == atom_count); - - if (num_keys_count != 0 && !num_sorted) { - rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, ctx); - } - *ptab = tab_atom; - *plen = atom_count; - return 0; -} - -int JS_GetOwnPropertyNames(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSValueConst obj, int flags) { - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, JS_VALUE_GET_OBJ(obj), flags); -} - -/* Return -1 if exception, - FALSE if the property does not exist, TRUE if it exists. If TRUE is - returned, the property descriptor 'desc' is filled present. */ -int JS_GetOwnPropertyInternal(JSContext* ctx, JSPropertyDescriptor* desc, JSObject* p, JSAtom prop) { - JSShapeProperty* prs; - JSProperty* pr; - -retry: - prs = find_own_property(&pr, p, prop); - if (prs) { - if (desc) { - desc->flags = prs->flags & JS_PROP_C_W_E; - desc->getter = JS_UNDEFINED; - desc->setter = JS_UNDEFINED; - desc->value = JS_UNDEFINED; - if (unlikely(prs->flags & JS_PROP_TMASK)) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - desc->flags |= JS_PROP_GETSET; - if (pr->u.getset.getter) - desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (pr->u.getset.setter) - desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - JSValue val = *pr->u.var_ref->pvalue; - if (unlikely(JS_IsUninitialized(val))) { - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - desc->value = JS_DupValue(ctx, val); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) - return -1; - goto retry; - } - } else { - desc->value = JS_DupValue(ctx, pr->u.value); - } - } else { - /* for consistency, send the exception even if desc is NULL */ - if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { - if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { - JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return -1; - } - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* nothing to do: delay instantiation until actual value and/or attributes are read */ - } - } - return TRUE; - } - if (p->is_exotic) { - if (p->fast_array) { - /* specific case for fast arrays */ - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx; - idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - if (desc) { - desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE; - desc->getter = JS_UNDEFINED; - desc->setter = JS_UNDEFINED; - desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); - } - return TRUE; - } - } - } else { - const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; - if (em && em->get_own_property) { - return em->get_own_property(ctx, desc, JS_MKPTR(JS_TAG_OBJECT, p), prop); - } - } - } - return FALSE; -} - -int JS_GetOwnProperty(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop) { - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); -} - -/* allowed flags: - JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE - JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, - JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, - JS_PROP_THROW, JS_PROP_NO_EXOTIC. - If JS_PROP_THROW is set, return an exception instead of FALSE. - if JS_PROP_NO_EXOTIC is set, do not call the exotic - define_own_property callback. - return -1 (exception), FALSE or TRUE. -*/ -int JS_DefineProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags) { - JSObject* p; - JSShapeProperty* prs; - JSProperty* pr; - int mask, res; - - if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - p = JS_VALUE_GET_OBJ(this_obj); - -redo_prop_update: - prs = find_own_property(&pr, p, prop); - if (prs) { - /* the range of the Array length property is always tested before */ - if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { - uint32_t array_length; - if (JS_ToArrayLengthFree(ctx, &array_length, JS_DupValue(ctx, val), FALSE)) { - return -1; - } - /* this code relies on the fact that Uint32 are never allocated */ - val = (JSValueConst)JS_NewUint32(ctx, array_length); - /* prs may have been modified */ - prs = find_own_property(&pr, p, prop); - assert(prs != NULL); - } - /* property already exists */ - if (!check_define_prop_flags(prs->flags, flags)) { - not_configurable: - return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); - } - - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry */ - if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) - return -1; - goto redo_prop_update; - } - - if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - JSObject *new_getter, *new_setter; - - if (JS_IsFunction(ctx, getter)) { - new_getter = JS_VALUE_GET_OBJ(getter); - } else { - new_getter = NULL; - } - if (JS_IsFunction(ctx, setter)) { - new_setter = JS_VALUE_GET_OBJ(setter); - } else { - new_setter = NULL; - } - - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - /* convert to getset */ - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - free_var_ref(ctx->rt, pr->u.var_ref); - } else { - JS_FreeValue(ctx, pr->u.value); - } - prs->flags = (prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | JS_PROP_GETSET; - pr->u.getset.getter = NULL; - pr->u.getset.setter = NULL; - } else { - if (!(prs->flags & JS_PROP_CONFIGURABLE)) { - if ((flags & JS_PROP_HAS_GET) && new_getter != pr->u.getset.getter) { - goto not_configurable; - } - if ((flags & JS_PROP_HAS_SET) && new_setter != pr->u.getset.setter) { - goto not_configurable; - } - } - } - if (flags & JS_PROP_HAS_GET) { - if (pr->u.getset.getter) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (new_getter) - JS_DupValue(ctx, getter); - pr->u.getset.getter = new_getter; - } - if (flags & JS_PROP_HAS_SET) { - if (pr->u.getset.setter) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); - if (new_setter) - JS_DupValue(ctx, setter); - pr->u.getset.setter = new_setter; - } - } else { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - /* convert to data descriptor */ - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - if (pr->u.getset.getter) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); - if (pr->u.getset.setter) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); - prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); - pr->u.value = JS_UNDEFINED; - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - /* Note: JS_PROP_VARREF is always writable */ - } else { - if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && (flags & JS_PROP_HAS_VALUE)) { - if (!js_same_value(ctx, val, pr->u.value)) { - goto not_configurable; - } else { - return TRUE; - } - } - } - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - if (flags & JS_PROP_HAS_VALUE) { - if (p->class_id == JS_CLASS_MODULE_NS) { - /* JS_PROP_WRITABLE is always true for variable - references, but they are write protected in module name - spaces. */ - if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) - goto not_configurable; - } - /* update the reference */ - set_value(ctx, pr->u.var_ref->pvalue, JS_DupValue(ctx, val)); - } - /* if writable is set to false, no longer a - reference (for mapped arguments) */ - if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { - JSValue val1; - if (js_shape_prepare_update(ctx, p, &prs)) - return -1; - val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue); - free_var_ref(ctx->rt, pr->u.var_ref); - pr->u.value = val1; - prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); - } - } else if (prs->flags & JS_PROP_LENGTH) { - if (flags & JS_PROP_HAS_VALUE) { - /* Note: no JS code is executable because - 'val' is guaranted to be a Uint32 */ - res = set_array_length(ctx, p, JS_DupValue(ctx, val), flags); - } else { - res = TRUE; - } - /* still need to reset the writable flag if - needed. The JS_PROP_LENGTH is kept because the - Uint32 test is still done if the length - property is read-only. */ - if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { - prs = get_shape_prop(p->shape); - if (js_update_property_flags(ctx, p, &prs, prs->flags & ~JS_PROP_WRITABLE)) - return -1; - } - return res; - } else { - if (flags & JS_PROP_HAS_VALUE) { - JS_FreeValue(ctx, pr->u.value); - pr->u.value = JS_DupValue(ctx, val); - } - if (flags & JS_PROP_HAS_WRITABLE) { - if (js_update_property_flags(ctx, p, &prs, (prs->flags & ~JS_PROP_WRITABLE) | (flags & JS_PROP_WRITABLE))) - return -1; - } - } - } - } - mask = 0; - if (flags & JS_PROP_HAS_CONFIGURABLE) - mask |= JS_PROP_CONFIGURABLE; - if (flags & JS_PROP_HAS_ENUMERABLE) - mask |= JS_PROP_ENUMERABLE; - if (js_update_property_flags(ctx, p, &prs, (prs->flags & ~mask) | (flags & mask))) - return -1; - return TRUE; - } - - /* handle modification of fast array elements */ - if (p->fast_array) { - uint32_t idx; - uint32_t prop_flags; - if (p->class_id == JS_CLASS_ARRAY) { - if (__JS_AtomIsTaggedInt(prop)) { - idx = __JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); - if (prop_flags != JS_PROP_C_W_E) - goto convert_to_slow_array; - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { - convert_to_slow_array: - if (convert_fast_array_to_array(ctx, p)) - return -1; - else - goto redo_prop_update; - } - if (flags & JS_PROP_HAS_VALUE) { - set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val)); - } - return TRUE; - } - } - } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { - JSValue num; - int ret; - - if (!__JS_AtomIsTaggedInt(prop)) { - /* slow path with to handle all numeric indexes */ - num = JS_AtomIsNumericIndex1(ctx, prop); - if (JS_IsUndefined(num)) - goto typed_array_done; - if (JS_IsException(num)) - return -1; - ret = JS_NumberIsInteger(ctx, num); - if (ret < 0) { - JS_FreeValue(ctx, num); - return -1; - } - if (!ret) { - JS_FreeValue(ctx, num); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); - } - ret = JS_NumberIsNegativeOrMinusZero(ctx, num); - JS_FreeValue(ctx, num); - if (ret) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); - } - if (!__JS_AtomIsTaggedInt(prop)) - goto typed_array_oob; - } - idx = __JS_AtomToUInt32(prop); - /* if the typed array is detached, p->u.array.count = 0 */ - if (idx >= typed_array_get_length(ctx, p)) { - typed_array_oob: - return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); - } - prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); - if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); - } - if (flags & JS_PROP_HAS_VALUE) { - return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags); - } - return TRUE; - typed_array_done:; - } - } - - return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); -} - -int JS_DefineAutoInitProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSAutoInitIDEnum id, void* opaque, int flags) { - JSObject* p; - JSProperty* pr; - - if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) - return FALSE; - - p = JS_VALUE_GET_OBJ(this_obj); - - if (find_own_property(&pr, p, prop)) { - /* property already exists */ - abort(); - return FALSE; - } - - /* Specialized CreateProperty */ - pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); - if (unlikely(!pr)) - return -1; - pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); - assert((pr->u.init.realm_and_id & 3) == 0); - assert(id <= 3); - pr->u.init.realm_and_id |= id; - pr->u.init.opaque = opaque; - return TRUE; -} - -/* shortcut to add or redefine a new property value */ -int JS_DefinePropertyValue(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags) { - int ret; - ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); - JS_FreeValue(ctx, val); - return ret; -} - -int JS_DefinePropertyValueValue(JSContext* ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { - JSAtom atom; - int ret; - atom = JS_ValueToAtom(ctx, prop); - JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) { - JS_FreeValue(ctx, val); - return -1; - } - ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); - JS_FreeAtom(ctx, atom); - return ret; -} - -int JS_DefinePropertyValueUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx, JSValue val, int flags) { - return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx), val, flags); -} - -int JS_DefinePropertyValueInt64(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags) { - return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), val, flags); -} - -int JS_DefinePropertyValueStr(JSContext* ctx, JSValueConst this_obj, const char* prop, JSValue val, int flags) { - JSAtom atom; - int ret; - atom = JS_NewAtom(ctx, prop); - ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); - JS_FreeAtom(ctx, atom); - return ret; -} - -/* shortcut to add getter & setter */ -int JS_DefinePropertyGetSet(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue getter, JSValue setter, int flags) { - int ret; - ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); - JS_FreeValue(ctx, getter); - JS_FreeValue(ctx, setter); - return ret; -} - -/* generic (and slower) version of JS_SetProperty() for - * Reflect.set(). 'obj' must be an object. */ -int JS_SetPropertyGeneric(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValue val, JSValueConst this_obj, int flags) { - int ret; - JSPropertyDescriptor desc; - JSValue obj1; - JSObject* p; - - obj1 = JS_DupValue(ctx, obj); - for (;;) { - p = JS_VALUE_GET_OBJ(obj1); - 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, obj1, prop, val, this_obj, flags); - JS_FreeValue(ctx, obj1); - JS_FreeValue(ctx, val); - return ret; - } - } - - ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (ret < 0) { - JS_FreeValue(ctx, obj1); - JS_FreeValue(ctx, val); - return ret; - } - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JSObject* setter; - if (JS_IsUndefined(desc.setter)) - setter = NULL; - else - setter = JS_VALUE_GET_OBJ(desc.setter); - ret = call_setter(ctx, setter, this_obj, val, flags); - JS_FreeValue(ctx, desc.getter); - JS_FreeValue(ctx, desc.setter); - JS_FreeValue(ctx, obj1); - return ret; - } else { - JS_FreeValue(ctx, desc.value); - if (!(desc.flags & JS_PROP_WRITABLE)) { - JS_FreeValue(ctx, obj1); - goto read_only_error; - } - } - break; - } - /* Note: at this point 'obj1' cannot be a proxy. XXX: may have - to check recursion */ - obj1 = JS_GetPrototypeFree(ctx, obj1); - if (JS_IsNull(obj1)) - break; - } - JS_FreeValue(ctx, obj1); - - if (!JS_IsObject(this_obj)) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object"); - } - - p = JS_VALUE_GET_OBJ(this_obj); - - /* modify the property in this_obj if it already exists */ - ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (ret < 0) { - JS_FreeValue(ctx, val); - return ret; - } - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JS_FreeValue(ctx, desc.getter); - JS_FreeValue(ctx, desc.setter); - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); - } else { - JS_FreeValue(ctx, desc.value); - if (!(desc.flags & JS_PROP_WRITABLE) || p->class_id == JS_CLASS_MODULE_NS) { - read_only_error: - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); - } - } - ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, JS_PROP_HAS_VALUE); - JS_FreeValue(ctx, val); - return ret; - } - - ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_ENUMERABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_C_W_E); - JS_FreeValue(ctx, val); - return ret; -} - -/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is - freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, - JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, - the new property is not added and an error is raised. */ -int JS_SetPropertyInternal(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags) { - JSObject *p, *p1; - JSShapeProperty* prs; - JSProperty* pr; - uint32_t tag; - JSPropertyDescriptor desc; - int ret; -#if 0 - printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n"); -#endif - tag = JS_VALUE_GET_TAG(this_obj); - if (unlikely(tag != JS_TAG_OBJECT)) { - switch (tag) { - case JS_TAG_NULL: - JS_FreeValue(ctx, val); - JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); - return -1; - case JS_TAG_UNDEFINED: - JS_FreeValue(ctx, val); - JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); - return -1; - default: - /* even on a primitive type we can have setters on the prototype */ - p = NULL; - p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj)); - goto prototype_lookup; - } - } - p = JS_VALUE_GET_OBJ(this_obj); -retry: - prs = find_own_property(&pr, p, prop); - if (prs) { - if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { - /* fast case */ - set_value(ctx, &pr->u.value, val); - return TRUE; - } else if (prs->flags & JS_PROP_LENGTH) { - assert(p->class_id == JS_CLASS_ARRAY); - assert(prop == JS_ATOM_length); - return set_array_length(ctx, p, val, flags); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { - /* JS_PROP_WRITABLE is always true for variable - references, but they are write protected in module name - spaces. */ - if (p->class_id == JS_CLASS_MODULE_NS) - goto read_only_prop; - set_value(ctx, pr->u.var_ref->pvalue, val); - return TRUE; - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry (potentially useless) */ - if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) { - JS_FreeValue(ctx, val); - return -1; - } - goto retry; - } else { - goto read_only_prop; - } - } - - p1 = p; - for (;;) { - if (p1->is_exotic) { - if (p1->fast_array) { - if (__JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx < p1->u.array.count) { - if (unlikely(p == p1)) - return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); - else - break; - } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { - goto typed_array_oob; - } - } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { - ret = JS_AtomIsNumericIndex(ctx, prop); - if (ret != 0) { - if (ret < 0) { - JS_FreeValue(ctx, val); - return -1; - } - typed_array_oob: - val = JS_ToNumberFree(ctx, val); - JS_FreeValue(ctx, val); - if (JS_IsException(val)) - return -1; - 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) { - /* set_property can free the prototype */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - ret = em->set_property(ctx, obj1, prop, val, this_obj, flags); - JS_FreeValue(ctx, obj1); - JS_FreeValue(ctx, val); - return ret; - } - if (em->get_own_property) { - /* get_own_property can free the prototype */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); - ret = em->get_own_property(ctx, &desc, obj1, prop); - JS_FreeValue(ctx, obj1); - if (ret < 0) { - JS_FreeValue(ctx, val); - return ret; - } - if (ret) { - if (desc.flags & JS_PROP_GETSET) { - JSObject* setter; - if (JS_IsUndefined(desc.setter)) - setter = NULL; - else - setter = JS_VALUE_GET_OBJ(desc.setter); - ret = call_setter(ctx, setter, this_obj, val, flags); - JS_FreeValue(ctx, desc.getter); - JS_FreeValue(ctx, desc.setter); - return ret; - } else { - JS_FreeValue(ctx, desc.value); - if (!(desc.flags & JS_PROP_WRITABLE)) - goto read_only_prop; - if (likely(p == p1)) { - ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, JS_PROP_HAS_VALUE); - JS_FreeValue(ctx, val); - return ret; - } else { - break; - } - } - } - } - } - } - } - p1 = p1->shape->proto; - prototype_lookup: - if (!p1) - break; - - retry2: - prs = find_own_property(&pr, p1, prop); - if (prs) { - if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { - return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); - } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { - /* Instantiate property and retry (potentially useless) */ - if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) - return -1; - goto retry2; - } else if (!(prs->flags & JS_PROP_WRITABLE)) { - read_only_prop: - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); - } - } - } - - if (unlikely(flags & JS_PROP_NO_ADD)) { - JS_FreeValue(ctx, val); - JS_ThrowReferenceErrorNotDefined(ctx, prop); - return -1; - } - - if (unlikely(!p)) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); - } - - if (unlikely(!p->extensible)) { - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); - } - - if (p->is_exotic) { - if (p->class_id == JS_CLASS_ARRAY && p->fast_array && __JS_AtomIsTaggedInt(prop)) { - uint32_t idx = __JS_AtomToUInt32(prop); - if (idx == p->u.array.count) { - /* fast case */ - return add_fast_array_element(ctx, p, val, flags); - } else { - goto generic_create_prop; - } - } else { - generic_create_prop: - ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_ENUMERABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_C_W_E); - JS_FreeValue(ctx, val); - return ret; - } - } - - pr = add_property(ctx, p, prop, JS_PROP_C_W_E); - if (unlikely(!pr)) { - JS_FreeValue(ctx, val); - return -1; - } - pr->u.value = val; - return TRUE; -} - -/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ -int JS_SetPropertyValue(JSContext* ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { - if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { - JSObject* p; - uint32_t idx; - double d; - int32_t v; - - /* fast path for array access */ - p = JS_VALUE_GET_OBJ(this_obj); - idx = JS_VALUE_GET_INT(prop); - switch (p->class_id) { - case JS_CLASS_ARRAY: - if (unlikely(idx >= (uint32_t)p->u.array.count)) { - JSObject* p1; - JSShape* sh1; - - /* fast path to add an element to the array */ - if (idx != (uint32_t)p->u.array.count || !p->fast_array || !p->extensible) - goto slow_path; - /* check if prototype chain has a numeric property */ - p1 = p->shape->proto; - while (p1 != NULL) { - sh1 = p1->shape; - if (p1->class_id == JS_CLASS_ARRAY) { - if (unlikely(!p1->fast_array)) - goto slow_path; - } else if (p1->class_id == JS_CLASS_OBJECT) { - if (unlikely(sh1->has_small_array_index)) - goto slow_path; - } else { - goto slow_path; - } - p1 = sh1->proto; - } - /* add element */ - return add_fast_array_element(ctx, p, val, flags); - } - set_value(ctx, &p->u.array.u.values[idx], val); - break; - case JS_CLASS_ARGUMENTS: - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto slow_path; - set_value(ctx, &p->u.array.u.values[idx], val); - break; - case JS_CLASS_UINT8C_ARRAY: - if (JS_ToUint8ClampFree(ctx, &v, val)) - return -1; - /* Note: the conversion can detach the typed array, so the - array bound check must be done after */ - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint8_ptr[idx] = v; - break; - case JS_CLASS_INT8_ARRAY: - case JS_CLASS_UINT8_ARRAY: - if (JS_ToInt32Free(ctx, &v, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint8_ptr[idx] = v; - break; - case JS_CLASS_INT16_ARRAY: - case JS_CLASS_UINT16_ARRAY: - if (JS_ToInt32Free(ctx, &v, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint16_ptr[idx] = v; - break; - case JS_CLASS_INT32_ARRAY: - case JS_CLASS_UINT32_ARRAY: - if (JS_ToInt32Free(ctx, &v, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint32_ptr[idx] = v; - break; -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT64_ARRAY: - case JS_CLASS_BIG_UINT64_ARRAY: - /* XXX: need specific conversion function */ - { - int64_t v; - if (JS_ToBigInt64Free(ctx, &v, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.uint64_ptr[idx] = v; - } - break; -#endif - case JS_CLASS_FLOAT32_ARRAY: - if (JS_ToFloat64Free(ctx, &d, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) - goto ta_out_of_bound; - p->u.array.u.float_ptr[idx] = d; - break; - case JS_CLASS_FLOAT64_ARRAY: - if (JS_ToFloat64Free(ctx, &d, val)) - return -1; - if (unlikely(idx >= (uint32_t)p->u.array.count)) { - ta_out_of_bound: - return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); - } - p->u.array.u.double_ptr[idx] = d; - break; - default: - goto slow_path; - } - return TRUE; - } else { - JSAtom atom; - int ret; - slow_path: - atom = JS_ValueToAtom(ctx, prop); - JS_FreeValue(ctx, prop); - if (unlikely(atom == JS_ATOM_NULL)) { - JS_FreeValue(ctx, val); - return -1; - } - ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags); - JS_FreeAtom(ctx, atom); - return ret; - } -} - -int JS_SetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx, JSValue val) { - return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val, JS_PROP_THROW); -} - -int JS_SetPropertyInt64(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val) { - JSAtom prop; - int res; - - if ((uint64_t)idx <= INT32_MAX) { - /* fast path for fast arrays */ - return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, JS_PROP_THROW); - } - prop = JS_NewAtomInt64(ctx, idx); - if (prop == JS_ATOM_NULL) { - JS_FreeValue(ctx, val); - return -1; - } - res = JS_SetProperty(ctx, this_obj, prop, val); - JS_FreeAtom(ctx, prop); - return res; -} - -int JS_SetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop, JSValue val) { - JSAtom atom; - int ret; - atom = JS_NewAtom(ctx, prop); - ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); - JS_FreeAtom(ctx, atom); - return ret; -} - -int JS_CreateDataPropertyUint32(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags) { - return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), val, flags | JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); -} - -/* return TRUE if 'obj' has a non empty 'name' string */ -BOOL js_object_has_name(JSContext* ctx, JSValueConst obj) { - JSProperty* pr; - JSShapeProperty* prs; - JSValueConst val; - JSString* p; - - prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); - if (!prs) - return FALSE; - if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) - return TRUE; - val = pr->u.value; - if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) - return TRUE; - p = JS_VALUE_GET_STRING(val); - return (p->len != 0); -} - -int JS_DefineObjectName(JSContext* ctx, JSValueConst obj, JSAtom name, int flags) { - if (name != JS_ATOM_NULL && JS_IsObject(obj) && !js_object_has_name(ctx, obj) && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { - return -1; - } - return 0; -} - -int JS_DefineObjectNameComputed(JSContext* ctx, JSValueConst obj, JSValueConst str, int flags) { - if (JS_IsObject(obj) && !js_object_has_name(ctx, obj)) { - JSAtom prop; - JSValue name_str; - prop = JS_ValueToAtom(ctx, str); - if (prop == JS_ATOM_NULL) - return -1; - name_str = js_get_function_name(ctx, prop); - JS_FreeAtom(ctx, prop); - if (JS_IsException(name_str)) - return -1; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) - return -1; - } - return 0; -} - -#if 0 -static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(obj); - switch(p->class_id) { - case JS_CLASS_NUMBER: - case JS_CLASS_STRING: - case JS_CLASS_BOOLEAN: - case JS_CLASS_SYMBOL: - case JS_CLASS_DATE: -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT: - case JS_CLASS_BIG_FLOAT: - case JS_CLASS_BIG_DECIMAL: -#endif - return JS_DupValue(ctx, p->u.object_data); - } - } - return JS_UNDEFINED; -} -#endif - -int JS_SetObjectData(JSContext* ctx, JSValueConst obj, JSValue val) { - JSObject* p; - - if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(obj); - switch (p->class_id) { - case JS_CLASS_NUMBER: - case JS_CLASS_STRING: - case JS_CLASS_BOOLEAN: - case JS_CLASS_SYMBOL: - case JS_CLASS_DATE: -#ifdef CONFIG_BIGNUM - case JS_CLASS_BIG_INT: - case JS_CLASS_BIG_FLOAT: - case JS_CLASS_BIG_DECIMAL: -#endif - JS_FreeValue(ctx, p->u.object_data); - p->u.object_data = val; - return 0; - } - } - JS_FreeValue(ctx, val); - if (!JS_IsException(obj)) - JS_ThrowTypeError(ctx, "invalid object type"); - return -1; -} - -JSValue JS_NewObjectClass(JSContext* ctx, int class_id) { - return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); -} - -JSValue JS_NewObjectProto(JSContext* ctx, JSValueConst proto) { - return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); -} - -JSValue JS_NewObject(JSContext* ctx) { - /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ - return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "object.h" +#include "builtins/js-function.h" +#include "builtins/js-object.h" +#include "builtins/js-operator.h" +#include "builtins/js-proxy.h" +#include "convertion.h" +#include "exception.h" +#include "function.h" +#include "gc.h" +#include "parser.h" +#include "runtime.h" +#include "shape.h" +#include "string.h" +#include "types.h" + +JSValue JS_GetPropertyValue(JSContext* ctx, JSValueConst this_obj, JSValue prop) { + JSAtom atom; + JSValue ret; + + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject* p; + uint32_t idx, len; + /* fast path for array access */ + p = JS_VALUE_GET_OBJ(this_obj); + idx = JS_VALUE_GET_INT(prop); + len = (uint32_t)p->u.array.count; + if (unlikely(idx >= len)) + goto slow_path; + switch (p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + return JS_DupValue(ctx, p->u.array.u.values[idx]); + case JS_CLASS_INT8_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]); + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]); + case JS_CLASS_INT16_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]); + case JS_CLASS_UINT16_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]); + case JS_CLASS_INT32_ARRAY: + return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]); + case JS_CLASS_UINT32_ARRAY: + return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]); +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); + case JS_CLASS_BIG_UINT64_ARRAY: + return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); +#endif + case JS_CLASS_FLOAT32_ARRAY: + return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); + case JS_CLASS_FLOAT64_ARRAY: + return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); + default: + goto slow_path; + } + } else { + slow_path: + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; + } +} + +JSValue JS_GetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx) { + return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx)); +} + +/* Check if an object has a generalized numeric property. Return value: + -1 for exception, + TRUE if property exists, stored into *pval, + FALSE if proprty does not exist. + */ +int JS_TryGetPropertyInt64(JSContext* ctx, JSValueConst obj, int64_t idx, JSValue* pval) { + JSValue val = JS_UNDEFINED; + JSAtom prop; + int present; + + if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { + /* fast path */ + present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); + if (present > 0) { + val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); + if (unlikely(JS_IsException(val))) + present = -1; + } + } else { + prop = JS_NewAtomInt64(ctx, idx); + present = -1; + if (likely(prop != JS_ATOM_NULL)) { + present = JS_HasProperty(ctx, obj, prop); + if (present > 0) { + val = JS_GetProperty(ctx, obj, prop); + if (unlikely(JS_IsException(val))) + present = -1; + } + JS_FreeAtom(ctx, prop); + } + } + *pval = val; + return present; +} + +JSValue JS_GetPropertyInt64(JSContext* ctx, JSValueConst obj, int64_t idx) { + JSAtom prop; + JSValue val; + + if ((uint64_t)idx <= INT32_MAX) { + /* fast path for fast arrays */ + return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return JS_EXCEPTION; + + val = JS_GetProperty(ctx, obj, prop); + JS_FreeAtom(ctx, prop); + return val; +} + +JSValue JS_GetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop) { + JSAtom atom; + JSValue ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* Note: the property value is not initialized. Return NULL if memory + error. */ +JSProperty* add_property(JSContext* ctx, JSObject* p, JSAtom prop, int prop_flags) { + JSShape *sh, *new_sh; + + sh = p->shape; + if (sh->is_hashed) { + /* try to find an existing shape */ + new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); + if (new_sh) { + /* matching shape found: use it */ + /* the property array may need to be resized */ + if (new_sh->prop_size != sh->prop_size) { + JSProperty* new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * new_sh->prop_size); + if (!new_prop) + return NULL; + p->prop = new_prop; + } + p->shape = js_dup_shape(new_sh); + js_free_shape(ctx->rt, sh); + return &p->prop[new_sh->prop_count - 1]; + } else if (sh->header.ref_count != 1) { + /* if the shape is shared, clone it */ + new_sh = js_clone_shape(ctx, sh); + if (!new_sh) + return NULL; + /* hash the cloned shape */ + new_sh->is_hashed = TRUE; + js_shape_hash_link(ctx->rt, new_sh); + js_free_shape(ctx->rt, p->shape); + p->shape = new_sh; + } + } + assert(p->shape->header.ref_count == 1); + if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) + return NULL; + return &p->prop[p->shape->prop_count - 1]; +} + +/* can be called on Array or Arguments objects. return < 0 if + memory alloc error. */ +no_inline __exception int convert_fast_array_to_array(JSContext* ctx, JSObject* p) { + JSProperty* pr; + JSShape* sh; + JSValue* tab; + uint32_t i, len, new_count; + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + len = p->u.array.count; + /* resize the properties once to simplify the error handling */ + sh = p->shape; + new_count = sh->prop_count + len; + if (new_count > sh->prop_size) { + if (resize_properties(ctx, &p->shape, p, new_count)) + return -1; + } + + tab = p->u.array.u.values; + for (i = 0; i < len; i++) { + /* add_property cannot fail here but + __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); + pr->u.value = *tab++; + } + js_free(ctx, p->u.array.u.values); + p->u.array.count = 0; + p->u.array.u.values = NULL; /* fail safe */ + p->u.array.u1.size = 0; + p->fast_array = 0; + return 0; +} + +int delete_property(JSContext* ctx, JSObject* p, JSAtom atom) { + JSShape* sh; + JSShapeProperty *pr, *lpr, *prop; + JSProperty* pr1; + uint32_t lpr_idx; + intptr_t h, h1; + +redo: + sh = p->shape; + h1 = atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h1 - 1]; + prop = get_shape_prop(sh); + lpr = NULL; + lpr_idx = 0; /* prevent warning */ + while (h != 0) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + /* found ! */ + if (!(pr->flags & JS_PROP_CONFIGURABLE)) + return FALSE; + /* realloc the shape if needed */ + if (lpr) + lpr_idx = lpr - get_shape_prop(sh); + if (js_shape_prepare_update(ctx, p, &pr)) + return -1; + sh = p->shape; + /* remove property */ + if (lpr) { + lpr = get_shape_prop(sh) + lpr_idx; + lpr->hash_next = pr->hash_next; + } else { + prop_hash_end(sh)[-h1 - 1] = pr->hash_next; + } + sh->deleted_prop_count++; + /* free the entry */ + pr1 = &p->prop[h - 1]; + free_property(ctx->rt, pr1, pr->flags); + JS_FreeAtom(ctx, pr->atom); + /* put default values */ + pr->flags = 0; + pr->atom = JS_ATOM_NULL; + pr1->u.value = JS_UNDEFINED; + + /* compact the properties if too many deleted properties */ + if (sh->deleted_prop_count >= 8 && sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { + compact_properties(ctx, p); + } + return TRUE; + } + lpr = pr; + h = pr->hash_next; + } + + if (p->is_exotic) { + if (p->fast_array) { + uint32_t idx; + if (JS_AtomIsArrayIndex(ctx, &idx, atom) && idx < p->u.array.count) { + if (p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) { + /* Special case deleting the last element of a fast Array */ + if (idx == p->u.array.count - 1) { + JS_FreeValue(ctx, p->u.array.u.values[idx]); + p->u.array.count = idx; + return TRUE; + } + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto redo; + } else { + return FALSE; + } + } + } else { + const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->delete_property) { + return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); + } + } + } + /* not found */ + return TRUE; +} + +int call_setter(JSContext* ctx, JSObject* setter, JSValueConst this_obj, JSValue val, int flags) { + JSValue ret, func; + if (likely(setter)) { + func = JS_MKPTR(JS_TAG_OBJECT, setter); + /* Note: the field could be removed in the setter */ + func = JS_DupValue(ctx, func); + ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst*)&val); + JS_FreeValue(ctx, val); + if (JS_IsException(ret)) + return -1; + JS_FreeValue(ctx, ret); + return TRUE; + } else { + JS_FreeValue(ctx, val); + if ((flags & JS_PROP_THROW) || ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "no setter for property"); + return -1; + } + return FALSE; + } +} + +void free_property(JSRuntime* rt, JSProperty* pr, int prop_flags) { + if (unlikely(prop_flags & JS_PROP_TMASK)) { + if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(rt, pr->u.var_ref); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_free(rt, pr); + } + } else { + JS_FreeValueRT(rt, pr->u.value); + } +} + +/* return the value associated to the autoinit property or an exception */ +typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); + +static JSAutoInitFunc *js_autoinit_func_table[] = { + js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ + js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ + JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ +}; + +/* warning: 'prs' is reallocated after it */ +static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + JSProperty *pr, JSShapeProperty *prs) +{ + JSValue val; + JSContext *realm; + JSAutoInitFunc *func; + + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + + realm = js_autoinit_get_realm(pr); + func = js_autoinit_func_table[js_autoinit_get_id(pr)]; + /* 'func' shall not modify the object properties 'pr' */ + val = func(realm, p, prop, pr->u.init.opaque); + js_autoinit_free(ctx->rt, pr); + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + if (JS_IsException(val)) + return -1; + pr->u.value = val; + return 0; +} + +JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValueConst this_obj, + BOOL throw_ref_error) +{ + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + uint32_t tag; + + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) { + switch(tag) { + case JS_TAG_NULL: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); + case JS_TAG_UNDEFINED: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_STRING: + { + JSString *p1 = JS_VALUE_GET_STRING(obj); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx, ch; + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + if (p1->is_wide_char) + ch = p1->u.str16[idx]; + else + ch = p1->u.str8[idx]; + return js_new_string_char(ctx, ch); + } + } else if (prop == JS_ATOM_length) { + return JS_NewInt32(ctx, p1->len); + } + } + break; + default: + break; + } + /* cannot raise an exception */ + p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); + if (!p) + return JS_UNDEFINED; + } else { + p = JS_VALUE_GET_OBJ(obj); + } + + for(;;) { + prs = find_own_property(&pr, p, prop); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (unlikely(!pr->u.getset.getter)) { + return JS_UNDEFINED; + } else { + JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); + /* Note: the field could be removed in the getter */ + func = JS_DupValue(ctx, func); + return JS_CallFree(ctx, func, this_obj, 0, NULL); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return JS_DupValue(ctx, val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return JS_EXCEPTION; + continue; + } + } else { + return JS_DupValue(ctx, pr->u.value); + } + } + if (unlikely(p->is_exotic)) { + /* exotic behaviors */ + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + /* we avoid duplicating the code */ + return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + return JS_UNDEFINED; + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + int ret; + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return JS_EXCEPTION; + return JS_UNDEFINED; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->get_property) { + JSValue obj1, retval; + /* XXX: should pass throw_ref_error */ + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + retval = em->get_property(ctx, obj1, prop, this_obj); + JS_FreeValue(ctx, obj1); + return retval; + } + if (em->get_own_property) { + JSPropertyDescriptor desc; + int ret; + JSValue obj1; + + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->get_own_property(ctx, &desc, obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + return JS_EXCEPTION; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.setter); + return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); + } else { + return desc.value; + } + } + } + } + } + } + p = p->shape->proto; + if (!p) + break; + } + if (unlikely(throw_ref_error)) { + return JS_ThrowReferenceErrorNotDefined(ctx, prop); + } else { + return JS_UNDEFINED; + } +} + +JSValue JS_GetOwnPropertyNames2(JSContext* ctx, JSValueConst obj1, int flags, int kind) { + JSValue obj, r, val, key, value; + JSObject* p; + JSPropertyEnum* atoms; + uint32_t len, i, j; + + r = JS_UNDEFINED; + val = JS_UNDEFINED; + obj = JS_ToObject(ctx, obj1); + if (JS_IsException(obj)) + return JS_EXCEPTION; + p = JS_VALUE_GET_OBJ(obj); + if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY)) + goto exception; + r = JS_NewArray(ctx); + if (JS_IsException(r)) + goto exception; + for (j = i = 0; i < len; i++) { + JSAtom atom = atoms[i].atom; + if (flags & JS_GPN_ENUM_ONLY) { + JSPropertyDescriptor desc; + int res; + + /* Check if property is still enumerable */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) + goto exception; + if (!res) + continue; + js_free_desc(ctx, &desc); + if (!(desc.flags & JS_PROP_ENUMERABLE)) + continue; + } + switch (kind) { + default: + case JS_ITERATOR_KIND_KEY: + val = JS_AtomToValue(ctx, atom); + if (JS_IsException(val)) + goto exception; + break; + case JS_ITERATOR_KIND_VALUE: + val = JS_GetProperty(ctx, obj, atom); + if (JS_IsException(val)) + goto exception; + break; + case JS_ITERATOR_KIND_KEY_AND_VALUE: + val = JS_NewArray(ctx); + if (JS_IsException(val)) + goto exception; + key = JS_AtomToValue(ctx, atom); + if (JS_IsException(key)) + goto exception1; + if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0) + goto exception1; + value = JS_GetProperty(ctx, obj, atom); + if (JS_IsException(value)) + goto exception1; + if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0) + goto exception1; + break; + } + if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0) + goto exception; + } + goto done; + +exception1: + JS_FreeValue(ctx, val); +exception: + JS_FreeValue(ctx, r); + r = JS_EXCEPTION; +done: + js_free_prop_enum(ctx, atoms, len); + JS_FreeValue(ctx, obj); + return r; +} + + +/* return FALSE if not OK */ +BOOL check_define_prop_flags(int prop_flags, int flags) +{ + BOOL has_accessor, is_getset; + + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == + (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { + return FALSE; + } + if ((flags & JS_PROP_HAS_ENUMERABLE) && + (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) + return FALSE; + } + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); + is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); + if (has_accessor != is_getset) + return FALSE; + if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { + /* not writable: cannot set the writable bit */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) + return FALSE; + } + } + } + return TRUE; +} + +void js_free_prop_enum(JSContext* ctx, JSPropertyEnum* tab, uint32_t len) { + uint32_t i; + if (tab) { + for (i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + } +} + +void js_free_desc(JSContext* ctx, JSPropertyDescriptor* desc) { + JS_FreeValue(ctx, desc->getter); + JS_FreeValue(ctx, desc->setter); + JS_FreeValue(ctx, desc->value); +} + +__exception int JS_CopyDataProperties(JSContext* ctx, JSValueConst target, JSValueConst source, JSValueConst excluded, BOOL setprop) { + JSPropertyEnum* tab_atom; + JSValue val; + uint32_t i, tab_atom_count; + JSObject* p; + JSObject* pexcl = NULL; + int ret, gpn_flags; + JSPropertyDescriptor desc; + BOOL is_enumerable; + + if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) + return 0; + + if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) + pexcl = JS_VALUE_GET_OBJ(excluded); + + p = JS_VALUE_GET_OBJ(source); + + gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; + if (p->is_exotic) { + const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; + /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it + introduces a visible change */ + if (em && em->get_own_property_names) { + gpn_flags &= ~JS_GPN_ENUM_ONLY; + } + } + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, gpn_flags)) + return -1; + + for (i = 0; i < tab_atom_count; i++) { + if (pexcl) { + ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); + if (ret) { + if (ret < 0) + goto exception; + continue; + } + } + if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { + /* test if the property is enumerable */ + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); + if (ret < 0) + goto exception; + if (!ret) + continue; + is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; + js_free_desc(ctx, &desc); + if (!is_enumerable) + continue; + } + val = JS_GetProperty(ctx, source, tab_atom[i].atom); + if (JS_IsException(val)) + goto exception; + if (setprop) + ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); + else + ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, JS_PROP_C_W_E); + if (ret < 0) + goto exception; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return 0; +exception: + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return -1; +} + +JSValue js_instantiate_prototype(JSContext* ctx, JSObject* p, JSAtom atom, void* opaque) { + JSValue obj, this_val; + int ret; + + this_val = JS_MKPTR(JS_TAG_OBJECT, p); + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + set_cycle_flag(ctx, obj); + set_cycle_flag(ctx, this_val); + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, JS_DupValue(ctx, this_val), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (ret < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +JSValue js_create_from_ctor(JSContext* ctx, JSValueConst ctor, int class_id) { + JSValue proto, obj; + JSContext* realm; + + if (JS_IsUndefined(ctor)) { + proto = JS_DupValue(ctx, ctx->class_proto[class_id]); + } else { + proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(proto)) { + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) + return JS_EXCEPTION; + proto = JS_DupValue(ctx, realm->class_proto[class_id]); + } + } + obj = JS_NewObjectProtoClass(ctx, proto, class_id); + JS_FreeValue(ctx, proto); + return obj; +} + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_IsExtensible(JSContext* ctx, JSValueConst obj) { + JSObject* p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isExtensible(ctx, obj); + else + return p->extensible; +} + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_PreventExtensions(JSContext* ctx, JSValueConst obj) { + JSObject* p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_preventExtensions(ctx, obj); + p->extensible = FALSE; + return TRUE; +} + +/* return -1 if exception otherwise TRUE or FALSE */ +int JS_HasProperty(JSContext* ctx, JSValueConst obj, JSAtom prop) { + JSObject* p; + int ret; + JSValue obj1; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + for (;;) { + if (p->is_exotic) { + const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->has_property) { + /* has_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->has_property(ctx, obj1, prop); + JS_FreeValue(ctx, obj1); + return ret; + } + } + /* JS_GetOwnPropertyInternal can free the prototype */ + JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret != 0) + return ret; + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return FALSE; + } + } + p = p->shape->proto; + if (!p) + break; + } + return FALSE; +} + +/* Private fields can be added even on non extensible objects or + Proxies */ +int JS_DefinePrivateField(JSContext* ctx, JSValueConst obj, JSValueConst name, JSValue val) { + JSObject* p; + JSShapeProperty* prs; + JSProperty* pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, (JSValue)name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", prop); + goto fail; + } + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (unlikely(!pr)) { + fail: + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return 0; +} + +JSValue JS_GetPrivateField(JSContext* ctx, JSValueConst obj, JSValueConst name) { + JSObject* p; + JSShapeProperty* prs; + JSProperty* pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return JS_ThrowTypeErrorNotAnObject(ctx); + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) + return JS_ThrowTypeErrorNotASymbol(ctx); + prop = js_symbol_to_atom(ctx, (JSValue)name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + return JS_EXCEPTION; + } + return JS_DupValue(ctx, pr->u.value); +} + +int JS_SetPrivateField(JSContext* ctx, JSValueConst obj, JSValueConst name, JSValue val) { + JSObject* p; + JSShapeProperty* prs; + JSProperty* pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, (JSValue)name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + fail: + JS_FreeValue(ctx, val); + return -1; + } + set_value(ctx, &pr->u.value, val); + return 0; +} + +int JS_AddBrand(JSContext* ctx, JSValueConst obj, JSValueConst home_obj) { + JSObject *p, *p1; + JSShapeProperty* prs; + JSProperty* pr; + JSValue brand; + JSAtom brand_atom; + + if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(home_obj); + prs = find_own_property(&pr, p, JS_ATOM_Private_brand); + if (!prs) { + brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(brand)) + return -1; + /* if the brand is not present, add it */ + pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); + if (!pr) { + JS_FreeValue(ctx, brand); + return -1; + } + pr->u.value = JS_DupValue(ctx, brand); + } else { + brand = JS_DupValue(ctx, pr->u.value); + } + brand_atom = js_symbol_to_atom(ctx, brand); + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + JS_FreeAtom(ctx, brand_atom); + return -1; + } + p1 = JS_VALUE_GET_OBJ(obj); + pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); + JS_FreeAtom(ctx, brand_atom); + if (!pr) + return -1; + pr->u.value = JS_UNDEFINED; + return 0; +} + +int JS_CheckBrand(JSContext* ctx, JSValueConst obj, JSValueConst func) { + JSObject *p, *p1, *home_obj; + JSShapeProperty* prs; + JSProperty* pr; + JSValueConst brand; + + /* get the home object of 'func' */ + if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p1 = JS_VALUE_GET_OBJ(func); + if (!js_class_has_bytecode(p1->class_id)) + goto not_obj; + home_obj = p1->u.func.home_object; + if (!home_obj) + goto not_obj; + prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); + if (!prs) { + JS_ThrowTypeError(ctx, "expecting private field"); + return -1; + } + brand = pr->u.value; + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) + goto not_obj; + + /* get the brand array of 'obj' */ + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + goto not_obj; + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); + if (!prs) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + return -1; + } + return 0; +} + +uint32_t js_string_obj_get_length(JSContext* ctx, JSValueConst obj) { + JSObject* p; + JSString* p1; + uint32_t len = 0; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + len = p1->len; + } + return len; +} + + +static int num_keys_cmp(const void *p1, const void *p2, void *opaque) +{ + JSContext *ctx = opaque; + JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; + JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; + uint32_t v1, v2; + BOOL atom1_is_integer, atom2_is_integer; + + atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); + atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); + assert(atom1_is_integer && atom2_is_integer); + if (v1 < v2) + return -1; + else if (v1 == v2) + return 0; + else + return 1; +} + + +/* return < 0 in case if exception, 0 if OK. ptab and its atoms must + be freed by the user. */ +int __exception JS_GetOwnPropertyNamesInternal(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSObject* p, int flags) { + int i, j; + JSShape* sh; + JSShapeProperty* prs; + JSPropertyEnum *tab_atom, *tab_exotic; + JSAtom atom; + uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; + uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; + BOOL is_enumerable, num_sorted; + uint32_t num_key; + JSAtomKindEnum kind; + + /* clear pointer for consistency in case of failure */ + *ptab = NULL; + *plen = 0; + + /* compute the number of returned properties */ + num_keys_count = 0; + str_keys_count = 0; + sym_keys_count = 0; + exotic_keys_count = 0; + exotic_count = 0; + tab_exotic = NULL; + sh = p->shape; + for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && ((flags >> kind) & 1) != 0) { + /* need to raise an exception in case of the module + name space (implicit GetOwnProperty) */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { + JSVarRef* var_ref = p->prop[i].u.var_ref; + if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + num_keys_count++; + } else if (kind == JS_ATOM_KIND_STRING) { + str_keys_count++; + } else { + sym_keys_count++; + } + } + } + } + + if (p->is_exotic) { + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += p->u.array.count; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property_names) { + if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, JS_MKPTR(JS_TAG_OBJECT, p))) + return -1; + for (i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + kind = JS_AtomGetKind(ctx, atom); + if (((flags >> kind) & 1) != 0) { + is_enumerable = FALSE; + if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { + JSPropertyDescriptor desc; + int res; + /* set the "is_enumerable" field if necessary */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + if (res) { + is_enumerable = ((desc.flags & JS_PROP_ENUMERABLE) != 0); + js_free_desc(ctx, &desc); + } + tab_exotic[i].is_enumerable = is_enumerable; + } + if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { + exotic_keys_count++; + } + } + } + } + } + } + + /* fill them */ + + atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; + /* avoid allocating 0 bytes */ + tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); + if (!tab_atom) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + + num_index = 0; + str_index = num_keys_count; + sym_index = str_index + str_keys_count; + + num_sorted = TRUE; + sh = p->shape; + for (i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && ((flags >> kind) & 1) != 0) { + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + j = num_index++; + num_sorted = FALSE; + } else if (kind == JS_ATOM_KIND_STRING) { + j = str_index++; + } else { + j = sym_index++; + } + tab_atom[j].atom = JS_DupAtom(ctx, atom); + tab_atom[j].is_enumerable = is_enumerable; + } + } + } + + if (p->is_exotic) { + int len; + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + len = p->u.array.count; + goto add_array_keys; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + add_array_keys: + for (i = 0; i < len; i++) { + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); + if (tab_atom[num_index].atom == JS_ATOM_NULL) { + js_free_prop_enum(ctx, tab_atom, num_index); + return -1; + } + tab_atom[num_index].is_enumerable = TRUE; + num_index++; + } + } + } else { + /* Note: exotic keys are not reordered and comes after the object own properties. */ + for (i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + is_enumerable = tab_exotic[i].is_enumerable; + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && ((flags >> kind) & 1) != 0) { + tab_atom[sym_index].atom = atom; + tab_atom[sym_index].is_enumerable = is_enumerable; + sym_index++; + } else { + JS_FreeAtom(ctx, atom); + } + } + js_free(ctx, tab_exotic); + } + } + + assert(num_index == num_keys_count); + assert(str_index == num_keys_count + str_keys_count); + assert(sym_index == atom_count); + + if (num_keys_count != 0 && !num_sorted) { + rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, ctx); + } + *ptab = tab_atom; + *plen = atom_count; + return 0; +} + +int JS_GetOwnPropertyNames(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSValueConst obj, int flags) { + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, JS_VALUE_GET_OBJ(obj), flags); +} + +/* Return -1 if exception, + FALSE if the property does not exist, TRUE if it exists. If TRUE is + returned, the property descriptor 'desc' is filled present. */ +int JS_GetOwnPropertyInternal(JSContext* ctx, JSPropertyDescriptor* desc, JSObject* p, JSAtom prop) { + JSShapeProperty* prs; + JSProperty* pr; + +retry: + prs = find_own_property(&pr, p, prop); + if (prs) { + if (desc) { + desc->flags = prs->flags & JS_PROP_C_W_E; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_UNDEFINED; + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + desc->flags |= JS_PROP_GETSET; + if (pr->u.getset.getter) + desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + desc->value = JS_DupValue(ctx, val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto retry; + } + } else { + desc->value = JS_DupValue(ctx, pr->u.value); + } + } else { + /* for consistency, send the exception even if desc is NULL */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { + if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* nothing to do: delay instantiation until actual value and/or attributes are read */ + } + } + return TRUE; + } + if (p->is_exotic) { + if (p->fast_array) { + /* specific case for fast arrays */ + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx; + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + if (desc) { + desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } + return TRUE; + } + } + } else { + const JSClassExoticMethods* em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property) { + return em->get_own_property(ctx, desc, JS_MKPTR(JS_TAG_OBJECT, p), prop); + } + } + } + return FALSE; +} + +int JS_GetOwnProperty(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom prop) { + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); +} + +/* allowed flags: + JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE + JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, + JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, + JS_PROP_THROW, JS_PROP_NO_EXOTIC. + If JS_PROP_THROW is set, return an exception instead of FALSE. + if JS_PROP_NO_EXOTIC is set, do not call the exotic + define_own_property callback. + return -1 (exception), FALSE or TRUE. +*/ +int JS_DefineProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags) { + JSObject* p; + JSShapeProperty* prs; + JSProperty* pr; + int mask, res; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(this_obj); + +redo_prop_update: + prs = find_own_property(&pr, p, prop); + if (prs) { + /* the range of the Array length property is always tested before */ + if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { + uint32_t array_length; + if (JS_ToArrayLengthFree(ctx, &array_length, JS_DupValue(ctx, val), FALSE)) { + return -1; + } + /* this code relies on the fact that Uint32 are never allocated */ + val = (JSValueConst)JS_NewUint32(ctx, array_length); + /* prs may have been modified */ + prs = find_own_property(&pr, p, prop); + assert(prs != NULL); + } + /* property already exists */ + if (!check_define_prop_flags(prs->flags, flags)) { + not_configurable: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto redo_prop_update; + } + + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + JSObject *new_getter, *new_setter; + + if (JS_IsFunction(ctx, getter)) { + new_getter = JS_VALUE_GET_OBJ(getter); + } else { + new_getter = NULL; + } + if (JS_IsFunction(ctx, setter)) { + new_setter = JS_VALUE_GET_OBJ(setter); + } else { + new_setter = NULL; + } + + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + /* convert to getset */ + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(ctx->rt, pr->u.var_ref); + } else { + JS_FreeValue(ctx, pr->u.value); + } + prs->flags = (prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | JS_PROP_GETSET; + pr->u.getset.getter = NULL; + pr->u.getset.setter = NULL; + } else { + if (!(prs->flags & JS_PROP_CONFIGURABLE)) { + if ((flags & JS_PROP_HAS_GET) && new_getter != pr->u.getset.getter) { + goto not_configurable; + } + if ((flags & JS_PROP_HAS_SET) && new_setter != pr->u.getset.setter) { + goto not_configurable; + } + } + } + if (flags & JS_PROP_HAS_GET) { + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (new_getter) + JS_DupValue(ctx, getter); + pr->u.getset.getter = new_getter; + } + if (flags & JS_PROP_HAS_SET) { + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + if (new_setter) + JS_DupValue(ctx, setter); + pr->u.getset.setter = new_setter; + } + } else { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + /* convert to data descriptor */ + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + pr->u.value = JS_UNDEFINED; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* Note: JS_PROP_VARREF is always writable */ + } else { + if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && (flags & JS_PROP_HAS_VALUE)) { + if (!js_same_value(ctx, val, pr->u.value)) { + goto not_configurable; + } else { + return TRUE; + } + } + } + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (flags & JS_PROP_HAS_VALUE) { + if (p->class_id == JS_CLASS_MODULE_NS) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) + goto not_configurable; + } + /* update the reference */ + set_value(ctx, pr->u.var_ref->pvalue, JS_DupValue(ctx, val)); + } + /* if writable is set to false, no longer a + reference (for mapped arguments) */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { + JSValue val1; + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue); + free_var_ref(ctx->rt, pr->u.var_ref); + pr->u.value = val1; + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + } + } else if (prs->flags & JS_PROP_LENGTH) { + if (flags & JS_PROP_HAS_VALUE) { + /* Note: no JS code is executable because + 'val' is guaranted to be a Uint32 */ + res = set_array_length(ctx, p, JS_DupValue(ctx, val), flags); + } else { + res = TRUE; + } + /* still need to reset the writable flag if + needed. The JS_PROP_LENGTH is kept because the + Uint32 test is still done if the length + property is read-only. */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { + prs = get_shape_prop(p->shape); + if (js_update_property_flags(ctx, p, &prs, prs->flags & ~JS_PROP_WRITABLE)) + return -1; + } + return res; + } else { + if (flags & JS_PROP_HAS_VALUE) { + JS_FreeValue(ctx, pr->u.value); + pr->u.value = JS_DupValue(ctx, val); + } + if (flags & JS_PROP_HAS_WRITABLE) { + if (js_update_property_flags(ctx, p, &prs, (prs->flags & ~JS_PROP_WRITABLE) | (flags & JS_PROP_WRITABLE))) + return -1; + } + } + } + } + mask = 0; + if (flags & JS_PROP_HAS_CONFIGURABLE) + mask |= JS_PROP_CONFIGURABLE; + if (flags & JS_PROP_HAS_ENUMERABLE) + mask |= JS_PROP_ENUMERABLE; + if (js_update_property_flags(ctx, p, &prs, (prs->flags & ~mask) | (flags & mask))) + return -1; + return TRUE; + } + + /* handle modification of fast array elements */ + if (p->fast_array) { + uint32_t idx; + uint32_t prop_flags; + if (p->class_id == JS_CLASS_ARRAY) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_slow_array; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + convert_to_slow_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + else + goto redo_prop_update; + } + if (flags & JS_PROP_HAS_VALUE) { + set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val)); + } + return TRUE; + } + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + JSValue num; + int ret; + + if (!__JS_AtomIsTaggedInt(prop)) { + /* slow path with to handle all numeric indexes */ + num = JS_AtomIsNumericIndex1(ctx, prop); + if (JS_IsUndefined(num)) + goto typed_array_done; + if (JS_IsException(num)) + return -1; + ret = JS_NumberIsInteger(ctx, num); + if (ret < 0) { + JS_FreeValue(ctx, num); + return -1; + } + if (!ret) { + JS_FreeValue(ctx, num); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); + } + ret = JS_NumberIsNegativeOrMinusZero(ctx, num); + JS_FreeValue(ctx, num); + if (ret) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); + } + if (!__JS_AtomIsTaggedInt(prop)) + goto typed_array_oob; + } + idx = __JS_AtomToUInt32(prop); + /* if the typed array is detached, p->u.array.count = 0 */ + if (idx >= typed_array_get_length(ctx, p)) { + typed_array_oob: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); + } + prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); + } + if (flags & JS_PROP_HAS_VALUE) { + return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags); + } + return TRUE; + typed_array_done:; + } + } + + return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); +} + +int JS_DefineAutoInitProperty(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSAutoInitIDEnum id, void* opaque, int flags) { + JSObject* p; + JSProperty* pr; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) + return FALSE; + + p = JS_VALUE_GET_OBJ(this_obj); + + if (find_own_property(&pr, p, prop)) { + /* property already exists */ + abort(); + return FALSE; + } + + /* Specialized CreateProperty */ + pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); + if (unlikely(!pr)) + return -1; + pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); + assert((pr->u.init.realm_and_id & 3) == 0); + assert(id <= 3); + pr->u.init.realm_and_id |= id; + pr->u.init.opaque = opaque; + return TRUE; +} + +/* shortcut to add or redefine a new property value */ +int JS_DefinePropertyValue(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags) { + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, val); + return ret; +} + +int JS_DefinePropertyValueValue(JSContext* ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { + JSAtom atom; + int ret; + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +int JS_DefinePropertyValueUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx, JSValue val, int flags) { + return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx), val, flags); +} + +int JS_DefinePropertyValueInt64(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags) { + return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), val, flags); +} + +int JS_DefinePropertyValueStr(JSContext* ctx, JSValueConst this_obj, const char* prop, JSValue val, int flags) { + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* shortcut to add getter & setter */ +int JS_DefinePropertyGetSet(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue getter, JSValue setter, int flags) { + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, getter); + JS_FreeValue(ctx, setter); + return ret; +} + +/* generic (and slower) version of JS_SetProperty() for + * Reflect.set(). 'obj' must be an object. */ +int JS_SetPropertyGeneric(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValue val, JSValueConst this_obj, int flags) { + int ret; + JSPropertyDescriptor desc; + JSValue obj1; + JSObject* p; + + obj1 = JS_DupValue(ctx, obj); + for (;;) { + p = JS_VALUE_GET_OBJ(obj1); + 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, obj1, prop, val, this_obj, flags); + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, val); + return ret; + } + } + + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (ret < 0) { + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, val); + return ret; + } + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JSObject* setter; + if (JS_IsUndefined(desc.setter)) + setter = NULL; + else + setter = JS_VALUE_GET_OBJ(desc.setter); + ret = call_setter(ctx, setter, this_obj, val, flags); + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + JS_FreeValue(ctx, obj1); + return ret; + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE)) { + JS_FreeValue(ctx, obj1); + goto read_only_error; + } + } + break; + } + /* Note: at this point 'obj1' cannot be a proxy. XXX: may have + to check recursion */ + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + } + JS_FreeValue(ctx, obj1); + + if (!JS_IsObject(this_obj)) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object"); + } + + p = JS_VALUE_GET_OBJ(this_obj); + + /* modify the property in this_obj if it already exists */ + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (ret < 0) { + JS_FreeValue(ctx, val); + return ret; + } + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE) || p->class_id == JS_CLASS_MODULE_NS) { + read_only_error: + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + } + } + ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, JS_PROP_HAS_VALUE); + JS_FreeValue(ctx, val); + return ret; + } + + ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_ENUMERABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_C_W_E); + JS_FreeValue(ctx, val); + return ret; +} + +/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is + freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, + JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, + the new property is not added and an error is raised. */ +int JS_SetPropertyInternal(JSContext* ctx, JSValueConst this_obj, JSAtom prop, JSValue val, int flags) { + JSObject *p, *p1; + JSShapeProperty* prs; + JSProperty* pr; + uint32_t tag; + JSPropertyDescriptor desc; + int ret; +#if 0 + printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n"); +#endif + tag = JS_VALUE_GET_TAG(this_obj); + if (unlikely(tag != JS_TAG_OBJECT)) { + switch (tag) { + case JS_TAG_NULL: + JS_FreeValue(ctx, val); + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); + return -1; + case JS_TAG_UNDEFINED: + JS_FreeValue(ctx, val); + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); + return -1; + default: + /* even on a primitive type we can have setters on the prototype */ + p = NULL; + p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj)); + goto prototype_lookup; + } + } + p = JS_VALUE_GET_OBJ(this_obj); +retry: + prs = find_own_property(&pr, p, prop); + if (prs) { + if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { + /* fast case */ + set_value(ctx, &pr->u.value, val); + return TRUE; + } else if (prs->flags & JS_PROP_LENGTH) { + assert(p->class_id == JS_CLASS_ARRAY); + assert(prop == JS_ATOM_length); + return set_array_length(ctx, p, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (p->class_id == JS_CLASS_MODULE_NS) + goto read_only_prop; + set_value(ctx, pr->u.var_ref->pvalue, val); + return TRUE; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) { + JS_FreeValue(ctx, val); + return -1; + } + goto retry; + } else { + goto read_only_prop; + } + } + + p1 = p; + for (;;) { + if (p1->is_exotic) { + if (p1->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p1->u.array.count) { + if (unlikely(p == p1)) + return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); + else + break; + } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + goto typed_array_oob; + } + } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) { + JS_FreeValue(ctx, val); + return -1; + } + typed_array_oob: + val = JS_ToNumberFree(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(val)) + return -1; + 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) { + /* set_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->set_property(ctx, obj1, prop, val, this_obj, flags); + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, val); + return ret; + } + if (em->get_own_property) { + /* get_own_property can free the prototype */ + obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->get_own_property(ctx, &desc, obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) { + JS_FreeValue(ctx, val); + return ret; + } + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JSObject* setter; + if (JS_IsUndefined(desc.setter)) + setter = NULL; + else + setter = JS_VALUE_GET_OBJ(desc.setter); + ret = call_setter(ctx, setter, this_obj, val, flags); + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + return ret; + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE)) + goto read_only_prop; + if (likely(p == p1)) { + ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, JS_PROP_HAS_VALUE); + JS_FreeValue(ctx, val); + return ret; + } else { + break; + } + } + } + } + } + } + } + p1 = p1->shape->proto; + prototype_lookup: + if (!p1) + break; + + retry2: + prs = find_own_property(&pr, p1, prop); + if (prs) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) + return -1; + goto retry2; + } else if (!(prs->flags & JS_PROP_WRITABLE)) { + read_only_prop: + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + } + } + } + + if (unlikely(flags & JS_PROP_NO_ADD)) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorNotDefined(ctx, prop); + return -1; + } + + if (unlikely(!p)) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); + } + + if (unlikely(!p->extensible)) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + } + + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY && p->fast_array && __JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + /* fast case */ + return add_fast_array_element(ctx, p, val, flags); + } else { + goto generic_create_prop; + } + } else { + generic_create_prop: + ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_ENUMERABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_C_W_E); + JS_FreeValue(ctx, val); + return ret; + } + } + + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (unlikely(!pr)) { + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return TRUE; +} + +/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ +int JS_SetPropertyValue(JSContext* ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags) { + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject* p; + uint32_t idx; + double d; + int32_t v; + + /* fast path for array access */ + p = JS_VALUE_GET_OBJ(this_obj); + idx = JS_VALUE_GET_INT(prop); + switch (p->class_id) { + case JS_CLASS_ARRAY: + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + JSObject* p1; + JSShape* sh1; + + /* fast path to add an element to the array */ + if (idx != (uint32_t)p->u.array.count || !p->fast_array || !p->extensible) + goto slow_path; + /* check if prototype chain has a numeric property */ + p1 = p->shape->proto; + while (p1 != NULL) { + sh1 = p1->shape; + if (p1->class_id == JS_CLASS_ARRAY) { + if (unlikely(!p1->fast_array)) + goto slow_path; + } else if (p1->class_id == JS_CLASS_OBJECT) { + if (unlikely(sh1->has_small_array_index)) + goto slow_path; + } else { + goto slow_path; + } + p1 = sh1->proto; + } + /* add element */ + return add_fast_array_element(ctx, p, val, flags); + } + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto slow_path; + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_UINT8C_ARRAY: + if (JS_ToUint8ClampFree(ctx, &v, val)) + return -1; + /* Note: the conversion can detach the typed array, so the + array bound check must be done after */ + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint16_ptr[idx] = v; + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint32_ptr[idx] = v; + break; +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + /* XXX: need specific conversion function */ + { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint64_ptr[idx] = v; + } + break; +#endif + case JS_CLASS_FLOAT32_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.float_ptr[idx] = d; + break; + case JS_CLASS_FLOAT64_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + return -1; + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + ta_out_of_bound: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); + } + p->u.array.u.double_ptr[idx] = d; + break; + default: + goto slow_path; + } + return TRUE; + } else { + JSAtom atom; + int ret; + slow_path: + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; + } +} + +int JS_SetPropertyUint32(JSContext* ctx, JSValueConst this_obj, uint32_t idx, JSValue val) { + return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val, JS_PROP_THROW); +} + +int JS_SetPropertyInt64(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val) { + JSAtom prop; + int res; + + if ((uint64_t)idx <= INT32_MAX) { + /* fast path for fast arrays */ + return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, JS_PROP_THROW); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + res = JS_SetProperty(ctx, this_obj, prop, val); + JS_FreeAtom(ctx, prop); + return res; +} + +int JS_SetPropertyStr(JSContext* ctx, JSValueConst this_obj, const char* prop, JSValue val) { + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); + JS_FreeAtom(ctx, atom); + return ret; +} + +int JS_CreateDataPropertyUint32(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags) { + return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), val, flags | JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); +} + +/* return TRUE if 'obj' has a non empty 'name' string */ +BOOL js_object_has_name(JSContext* ctx, JSValueConst obj) { + JSProperty* pr; + JSShapeProperty* prs; + JSValueConst val; + JSString* p; + + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); + if (!prs) + return FALSE; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return TRUE; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return TRUE; + p = JS_VALUE_GET_STRING(val); + return (p->len != 0); +} + +int JS_DefineObjectName(JSContext* ctx, JSValueConst obj, JSAtom name, int flags) { + if (name != JS_ATOM_NULL && JS_IsObject(obj) && !js_object_has_name(ctx, obj) && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { + return -1; + } + return 0; +} + +int JS_DefineObjectNameComputed(JSContext* ctx, JSValueConst obj, JSValueConst str, int flags) { + if (JS_IsObject(obj) && !js_object_has_name(ctx, obj)) { + JSAtom prop; + JSValue name_str; + prop = JS_ValueToAtom(ctx, str); + if (prop == JS_ATOM_NULL) + return -1; + name_str = js_get_function_name(ctx, prop); + JS_FreeAtom(ctx, prop); + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) + return -1; + } + return 0; +} + +#if 0 +static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: + case JS_CLASS_BIG_FLOAT: + case JS_CLASS_BIG_DECIMAL: +#endif + return JS_DupValue(ctx, p->u.object_data); + } + } + return JS_UNDEFINED; +} +#endif + +int JS_SetObjectData(JSContext* ctx, JSValueConst obj, JSValue val) { + JSObject* p; + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + switch (p->class_id) { + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: +#ifdef CONFIG_BIGNUM + case JS_CLASS_BIG_INT: + case JS_CLASS_BIG_FLOAT: + case JS_CLASS_BIG_DECIMAL: +#endif + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = val; + return 0; + } + } + JS_FreeValue(ctx, val); + if (!JS_IsException(obj)) + JS_ThrowTypeError(ctx, "invalid object type"); + return -1; +} + +JSValue JS_NewObjectClass(JSContext* ctx, int class_id) { + return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); +} + +JSValue JS_NewObjectProto(JSContext* ctx, JSValueConst proto) { + return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); +} + +JSValue JS_NewObject(JSContext* ctx) { + /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); } \ No newline at end of file diff --git a/src/core/object.h b/src/core/object.h index 45ce183bb..85db283d2 100644 --- a/src/core/object.h +++ b/src/core/object.h @@ -1,170 +1,170 @@ -/* - * QuickJS Javascript Engine - * - * Copyright (c) 2017-2021 Fabrice Bellard - * Copyright (c) 2017-2021 Charlie Gordon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QUICKJS_OBJECT_H -#define QUICKJS_OBJECT_H - -#include "quickjs/cutils.h" -#include "quickjs/quickjs.h" -#include "shape.h" -#include "types.h" - -JSValue JS_GetPropertyValue(JSContext* ctx, JSValueConst this_obj, JSValue prop); - -/* Check if an object has a generalized numeric property. Return value: - -1 for exception, - TRUE if property exists, stored into *pval, - FALSE if proprty does not exist. - */ -int JS_TryGetPropertyInt64(JSContext* ctx, JSValueConst obj, int64_t idx, JSValue* pval); -JSValue JS_GetPropertyInt64(JSContext* ctx, JSValueConst obj, int64_t idx); - -/* can be called on Array or Arguments objects. return < 0 if - memory alloc error. */ -no_inline __exception int convert_fast_array_to_array(JSContext* ctx, JSObject* p); - -int delete_property(JSContext* ctx, JSObject* p, JSAtom atom); -int call_setter(JSContext* ctx, JSObject* setter, JSValueConst this_obj, JSValue val, int flags); -void free_property(JSRuntime* rt, JSProperty* pr, int prop_flags); -JSProperty* add_property(JSContext* ctx, JSObject* p, JSAtom prop, int prop_flags); - -static force_inline JSShapeProperty* find_own_property1(JSObject* p, JSAtom atom) { - JSShape* sh; - JSShapeProperty *pr, *prop; - intptr_t h; - sh = p->shape; - h = (uintptr_t)atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h - 1]; - prop = get_shape_prop(sh); - while (h) { - pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { - return pr; - } - h = pr->hash_next; - } - return NULL; -}; -static force_inline JSShapeProperty* find_own_property(JSProperty** ppr, JSObject* p, JSAtom atom) { - JSShape* sh; - JSShapeProperty *pr, *prop; - intptr_t h; - sh = p->shape; - h = (uintptr_t)atom & sh->prop_hash_mask; - h = prop_hash_end(sh)[-h - 1]; - prop = get_shape_prop(sh); - while (h) { - pr = &prop[h - 1]; - if (likely(pr->atom == atom)) { - *ppr = &p->prop[h - 1]; - /* the compiler should be able to assume that pr != NULL here */ - return pr; - } - h = pr->hash_next; - } - *ppr = NULL; - return NULL; -}; - -/* return FALSE if not OK */ -BOOL check_define_prop_flags(int prop_flags, int flags);; -void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len); -void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);; - -JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); -JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, - int class_id); - -__exception int JS_CopyDataProperties(JSContext *ctx, - JSValueConst target, - JSValueConst source, - JSValueConst excluded, - BOOL setprop); - -int JS_DefinePropertyValueValue(JSContext* ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags); -int JS_DefinePropertyValueInt64(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags); -int JS_SetPropertyGeneric(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValue val, JSValueConst this_obj, int flags); -/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ -int JS_SetPropertyValue(JSContext* ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags); - -/* return -1 if exception (Proxy object only) or TRUE/FALSE */ -int JS_IsExtensible(JSContext *ctx, JSValueConst obj); - -/* return -1 if exception (Proxy object only) or TRUE/FALSE */ -int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); - -JSValue JS_GetOwnPropertyNames2(JSContext* ctx, JSValueConst obj1, int flags, int kind); -/* return -1 if exception otherwise TRUE or FALSE */ -int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop); - -/* Private fields can be added even on non extensible objects or - Proxies */ -int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name, JSValue val); - -JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name); - -int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, - JSValueConst name, JSValue val); - -int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj); -int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func); - -uint32_t js_string_obj_get_length(JSContext *ctx, - JSValueConst obj); - -/* return < 0 in case if exception, 0 if OK. ptab and its atoms must - be freed by the user. */ -int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, - JSPropertyEnum **ptab, - uint32_t *plen, - JSObject *p, int flags); -/* Return -1 if exception, - FALSE if the property does not exist, TRUE if it exists. If TRUE is - returned, the property descriptor 'desc' is filled present. */ -int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, - JSObject *p, JSAtom prop); - -int JS_CreateDataPropertyUint32(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags); - -int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, - JSValueConst obj, JSAtom prop); - -int JS_DefineAutoInitProperty(JSContext* ctx, - JSValueConst this_obj, - JSAtom prop, - JSAutoInitIDEnum id, - void* opaque, - int flags); - -/* return TRUE if 'obj' has a non empty 'name' string */ -BOOL js_object_has_name(JSContext* ctx, JSValueConst obj); -int JS_DefineObjectName(JSContext* ctx, JSValueConst obj, JSAtom name, int flags); -int JS_DefineObjectNameComputed(JSContext* ctx, JSValueConst obj, JSValueConst str, int flags); - -int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val); - +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QUICKJS_OBJECT_H +#define QUICKJS_OBJECT_H + +#include "quickjs/cutils.h" +#include "quickjs/quickjs.h" +#include "shape.h" +#include "types.h" + +JSValue JS_GetPropertyValue(JSContext* ctx, JSValueConst this_obj, JSValue prop); + +/* Check if an object has a generalized numeric property. Return value: + -1 for exception, + TRUE if property exists, stored into *pval, + FALSE if proprty does not exist. + */ +int JS_TryGetPropertyInt64(JSContext* ctx, JSValueConst obj, int64_t idx, JSValue* pval); +JSValue JS_GetPropertyInt64(JSContext* ctx, JSValueConst obj, int64_t idx); + +/* can be called on Array or Arguments objects. return < 0 if + memory alloc error. */ +no_inline __exception int convert_fast_array_to_array(JSContext* ctx, JSObject* p); + +int delete_property(JSContext* ctx, JSObject* p, JSAtom atom); +int call_setter(JSContext* ctx, JSObject* setter, JSValueConst this_obj, JSValue val, int flags); +void free_property(JSRuntime* rt, JSProperty* pr, int prop_flags); +JSProperty* add_property(JSContext* ctx, JSObject* p, JSAtom prop, int prop_flags); + +static force_inline JSShapeProperty* find_own_property1(JSObject* p, JSAtom atom) { + JSShape* sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + return pr; + } + h = pr->hash_next; + } + return NULL; +}; +static force_inline JSShapeProperty* find_own_property(JSProperty** ppr, JSObject* p, JSAtom atom) { + JSShape* sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + *ppr = &p->prop[h - 1]; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +}; + +/* return FALSE if not OK */ +BOOL check_define_prop_flags(int prop_flags, int flags);; +void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len); +void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);; + +JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, + int class_id); + +__exception int JS_CopyDataProperties(JSContext *ctx, + JSValueConst target, + JSValueConst source, + JSValueConst excluded, + BOOL setprop); + +int JS_DefinePropertyValueValue(JSContext* ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags); +int JS_DefinePropertyValueInt64(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags); +int JS_SetPropertyGeneric(JSContext* ctx, JSValueConst obj, JSAtom prop, JSValue val, JSValueConst this_obj, int flags); +/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ +int JS_SetPropertyValue(JSContext* ctx, JSValueConst this_obj, JSValue prop, JSValue val, int flags); + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_IsExtensible(JSContext *ctx, JSValueConst obj); + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_PreventExtensions(JSContext *ctx, JSValueConst obj); + +JSValue JS_GetOwnPropertyNames2(JSContext* ctx, JSValueConst obj1, int flags, int kind); +/* return -1 if exception otherwise TRUE or FALSE */ +int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop); + +/* Private fields can be added even on non extensible objects or + Proxies */ +int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name, JSValue val); + +JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name); + +int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, + JSValueConst name, JSValue val); + +int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj); +int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func); + +uint32_t js_string_obj_get_length(JSContext *ctx, + JSValueConst obj); + +/* return < 0 in case if exception, 0 if OK. ptab and its atoms must + be freed by the user. */ +int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSObject *p, int flags); +/* Return -1 if exception, + FALSE if the property does not exist, TRUE if it exists. If TRUE is + returned, the property descriptor 'desc' is filled present. */ +int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop); + +int JS_CreateDataPropertyUint32(JSContext* ctx, JSValueConst this_obj, int64_t idx, JSValue val, int flags); + +int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValueConst obj, JSAtom prop); + +int JS_DefineAutoInitProperty(JSContext* ctx, + JSValueConst this_obj, + JSAtom prop, + JSAutoInitIDEnum id, + void* opaque, + int flags); + +/* return TRUE if 'obj' has a non empty 'name' string */ +BOOL js_object_has_name(JSContext* ctx, JSValueConst obj); +int JS_DefineObjectName(JSContext* ctx, JSValueConst obj, JSAtom name, int flags); +int JS_DefineObjectNameComputed(JSContext* ctx, JSValueConst obj, JSValueConst str, int flags); + +int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val); + #endif \ No newline at end of file diff --git a/src/core/parser.c b/src/core/parser.c index dbed59004..9a9f55c4e 100644 --- a/src/core/parser.c +++ b/src/core/parser.c @@ -1,12208 +1,12208 @@ -/* -* QuickJS Javascript Engine -* -* Copyright (c) 2017-2021 Fabrice Bellard -* Copyright (c) 2017-2021 Charlie Gordon -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. - */ - -#include "parser.h" -#include "builtins/js-function.h" -#include "convertion.h" -#include "exception.h" -#include "function.h" -#include "bytecode.h" -#include "gc.h" -#include "malloc.h" -#include "module.h" -#include "object.h" -#include "quickjs/libregexp.h" -#include "runtime.h" -#include "string.h" - -static __exception int next_token(JSParseState *s); - -void free_token(JSParseState *s, JSToken *token) -{ - switch(token->val) { -#ifdef CONFIG_BIGNUM - case TOK_NUMBER: - JS_FreeValue(s->ctx, token->u.num.val); - break; -#endif - case TOK_STRING: - case TOK_TEMPLATE: - JS_FreeValue(s->ctx, token->u.str.str); - break; - case TOK_REGEXP: - JS_FreeValue(s->ctx, token->u.regexp.body); - JS_FreeValue(s->ctx, token->u.regexp.flags); - break; - case TOK_IDENT: - case TOK_PRIVATE_NAME: - JS_FreeAtom(s->ctx, token->u.ident.atom); - break; - default: - if (token->val >= TOK_FIRST_KEYWORD && - token->val <= TOK_LAST_KEYWORD) { - JS_FreeAtom(s->ctx, token->u.ident.atom); - } - break; - } -} - -static void __attribute((unused)) dump_token(JSParseState *s, - const JSToken *token) -{ - switch(token->val) { - case TOK_NUMBER: - { - double d; - JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ - printf("number: %.14g\n", d); - } - break; - case TOK_IDENT: - dump_atom: - { - char buf[ATOM_GET_STR_BUF_SIZE]; - printf("ident: '%s'\n", - JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); - } - break; - case TOK_STRING: - { - const char *str; - /* XXX: quote the string */ - str = JS_ToCString(s->ctx, token->u.str.str); - printf("string: '%s'\n", str); - JS_FreeCString(s->ctx, str); - } - break; - case TOK_TEMPLATE: - { - const char *str; - str = JS_ToCString(s->ctx, token->u.str.str); - printf("template: `%s`\n", str); - JS_FreeCString(s->ctx, str); - } - break; - case TOK_REGEXP: - { - const char *str, *str2; - str = JS_ToCString(s->ctx, token->u.regexp.body); - str2 = JS_ToCString(s->ctx, token->u.regexp.flags); - printf("regexp: '%s' '%s'\n", str, str2); - JS_FreeCString(s->ctx, str); - JS_FreeCString(s->ctx, str2); - } - break; - case TOK_EOF: - printf("eof\n"); - break; - default: - if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { - goto dump_atom; - } else if (s->token.val >= 256) { - printf("token: %d\n", token->val); - } else { - printf("token: '%c'\n", token->val); - } - break; - } -} - -int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...) -{ - JSContext *ctx = s->ctx; - va_list ap; - int backtrace_flags; - - va_start(ap, fmt); - JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); - va_end(ap); - backtrace_flags = 0; - if (s->cur_func && s->cur_func->backtrace_barrier) - backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; - build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num, - backtrace_flags); - return -1; -} - -static int js_parse_expect(JSParseState *s, int tok) -{ - if (s->token.val != tok) { - /* XXX: dump token correctly in all cases */ - return js_parse_error(s, "expecting '%c'", tok); - } - return next_token(s); -} - -static int js_parse_expect_semi(JSParseState *s) -{ - if (s->token.val != ';') { - /* automatic insertion of ';' */ - if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { - return 0; - } - return js_parse_error(s, "expecting '%c'", ';'); - } - return next_token(s); -} - -static int js_parse_error_reserved_identifier(JSParseState *s) -{ - char buf1[ATOM_GET_STR_BUF_SIZE]; - return js_parse_error(s, "'%s' is a reserved identifier", - JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), - s->token.u.ident.atom)); -} - -static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) -{ - uint32_t c; - StringBuffer b_s, *b = &b_s; - - /* p points to the first byte of the template part */ - if (string_buffer_init(s->ctx, b, 32)) - goto fail; - for(;;) { - if (p >= s->buf_end) - goto unexpected_eof; - c = *p++; - if (c == '`') { - /* template end part */ - break; - } - if (c == '$' && *p == '{') { - /* template start or middle part */ - p++; - break; - } - if (c == '\\') { - if (string_buffer_putc8(b, c)) - goto fail; - if (p >= s->buf_end) - goto unexpected_eof; - c = *p++; - } - /* newline sequences are normalized as single '\n' bytes */ - if (c == '\r') { - if (*p == '\n') - p++; - c = '\n'; - } - if (c == '\n') { - s->line_num++; - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; - } - p = p_next; - } - if (string_buffer_putc(b, c)) - goto fail; - } - s->token.val = TOK_TEMPLATE; - s->token.u.str.sep = c; - s->token.u.str.str = string_buffer_end(b); - s->buf_ptr = p; - return 0; - -unexpected_eof: - js_parse_error(s, "unexpected end of string"); -fail: - string_buffer_free(b); - return -1; -} - -static __exception int js_parse_string(JSParseState *s, int sep, - BOOL do_throw, const uint8_t *p, - JSToken *token, const uint8_t **pp) -{ - int ret; - uint32_t c; - StringBuffer b_s, *b = &b_s; - - /* string */ - if (string_buffer_init(s->ctx, b, 32)) - goto fail; - for(;;) { - if (p >= s->buf_end) - goto invalid_char; - c = *p; - if (c < 0x20) { - if (!s->cur_func) { - if (do_throw) - js_parse_error(s, "invalid character in a JSON string"); - goto fail; - } - if (sep == '`') { - if (c == '\r') { - if (p[1] == '\n') - p++; - c = '\n'; - } - /* do not update s->line_num */ - } else if (c == '\n' || c == '\r') - goto invalid_char; - } - p++; - if (c == sep) - break; - if (c == '$' && *p == '{' && sep == '`') { - /* template start or middle part */ - p++; - break; - } - if (c == '\\') { - c = *p; - /* XXX: need a specific JSON case to avoid - accepting invalid escapes */ - switch(c) { - case '\0': - if (p >= s->buf_end) - goto invalid_char; - p++; - break; - case '\'': - case '\"': - case '\\': - p++; - break; - case '\r': /* accept DOS and MAC newline sequences */ - if (p[1] == '\n') { - p++; - } - /* fall thru */ - case '\n': - /* ignore escaped newline sequence */ - p++; - if (sep != '`') - s->line_num++; - continue; - default: - if (c >= '0' && c <= '9') { - if (!s->cur_func) - goto invalid_escape; /* JSON case */ - if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') - goto parse_escape; - if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { - p++; - c = '\0'; - } else { - if (c >= '8' || sep == '`') { - /* Note: according to ES2021, \8 and \9 are not - accepted in strict mode or in templates. */ - goto invalid_escape; - } else { - if (do_throw) - js_parse_error(s, "octal escape sequences are not allowed in strict mode"); - } - goto fail; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - p = p_next; - /* LS or PS are skipped */ - if (c == CP_LS || c == CP_PS) - continue; - } else { - parse_escape: - ret = lre_parse_escape(&p, TRUE); - if (ret == -1) { - invalid_escape: - if (do_throw) - js_parse_error(s, "malformed escape sequence in string literal"); - goto fail; - } else if (ret < 0) { - /* ignore the '\' (could output a warning) */ - p++; - } else { - c = ret; - } - } - break; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) - goto invalid_utf8; - p = p_next; - } - if (string_buffer_putc(b, c)) - goto fail; - } - token->val = TOK_STRING; - token->u.str.sep = c; - token->u.str.str = string_buffer_end(b); - *pp = p; - return 0; - -invalid_utf8: - if (do_throw) - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; -invalid_char: - if (do_throw) - js_parse_error(s, "unexpected end of string"); -fail: - string_buffer_free(b); - return -1; -} - -static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { - return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && - !s->token.u.ident.has_escape; -} - -static __exception int js_parse_regexp(JSParseState *s) -{ - const uint8_t *p; - BOOL in_class; - StringBuffer b_s, *b = &b_s; - StringBuffer b2_s, *b2 = &b2_s; - uint32_t c; - - p = s->buf_ptr; - p++; - in_class = FALSE; - if (string_buffer_init(s->ctx, b, 32)) - return -1; - if (string_buffer_init(s->ctx, b2, 1)) - goto fail; - for(;;) { - if (p >= s->buf_end) { - eof_error: - js_parse_error(s, "unexpected end of regexp"); - goto fail; - } - c = *p++; - if (c == '\n' || c == '\r') { - goto eol_error; - } else if (c == '/') { - if (!in_class) - break; - } else if (c == '[') { - in_class = TRUE; - } else if (c == ']') { - /* XXX: incorrect as the first character in a class */ - in_class = FALSE; - } else if (c == '\\') { - if (string_buffer_putc8(b, c)) - goto fail; - c = *p++; - if (c == '\n' || c == '\r') - goto eol_error; - else if (c == '\0' && p >= s->buf_end) - goto eof_error; - else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - p = p_next; - if (c == CP_LS || c == CP_PS) - goto eol_error; - } - } else if (c >= 0x80) { - const uint8_t *p_next; - c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - invalid_utf8: - js_parse_error(s, "invalid UTF-8 sequence"); - goto fail; - } - p = p_next; - /* LS or PS are considered as line terminator */ - if (c == CP_LS || c == CP_PS) { - eol_error: - js_parse_error(s, "unexpected line terminator in regexp"); - goto fail; - } - } - if (string_buffer_putc(b, c)) - goto fail; - } - - /* flags */ - for(;;) { - const uint8_t *p_next = p; - c = *p_next++; - if (c >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); - if (c > 0x10FFFF) { - goto invalid_utf8; - } - } - if (!lre_js_is_ident_next(c)) - break; - if (string_buffer_putc(b2, c)) - goto fail; - p = p_next; - } - - s->token.val = TOK_REGEXP; - s->token.u.regexp.body = string_buffer_end(b); - s->token.u.regexp.flags = string_buffer_end(b2); - s->buf_ptr = p; - return 0; -fail: - string_buffer_free(b); - string_buffer_free(b2); - return -1; -} - -static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, - char *static_buf) -{ - char *buf, *new_buf; - size_t size, new_size; - - buf = *pbuf; - size = *psize; - if (size >= (SIZE_MAX / 3) * 2) - new_size = SIZE_MAX; - else - new_size = size + (size >> 1); - if (buf == static_buf) { - new_buf = js_malloc(ctx, new_size); - if (!new_buf) - return -1; - memcpy(new_buf, buf, size); - } else { - new_buf = js_realloc(ctx, buf, new_size); - if (!new_buf) - return -1; - } - *pbuf = new_buf; - *psize = new_size; - return 0; -} - -/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ -static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, - BOOL *pident_has_escape, int c, BOOL is_private) -{ - const uint8_t *p, *p1; - char ident_buf[128], *buf; - size_t ident_size, ident_pos; - JSAtom atom; - - p = *pp; - buf = ident_buf; - ident_size = sizeof(ident_buf); - ident_pos = 0; - if (is_private) - buf[ident_pos++] = '#'; - for(;;) { - p1 = p; - - if (c < 128) { - buf[ident_pos++] = c; - } else { - ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c); - } - c = *p1++; - if (c == '\\' && *p1 == 'u') { - c = lre_parse_escape(&p1, TRUE); - *pident_has_escape = TRUE; - } else if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - } - if (!lre_js_is_ident_next(c)) - break; - p = p1; - if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { - if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { - atom = JS_ATOM_NULL; - goto done; - } - } - } - atom = JS_NewAtomLen(s->ctx, buf, ident_pos); -done: - if (unlikely(buf != ident_buf)) - js_free(s->ctx, buf); - *pp = p; - return atom; -} - - -static __exception int next_token(JSParseState *s) -{ - const uint8_t *p; - int c; - BOOL ident_has_escape; - JSAtom atom; - - if (js_check_stack_overflow(s->ctx->rt, 0)) { - return js_parse_error(s, "stack overflow"); - } - - free_token(s, &s->token); - - p = s->last_ptr = s->buf_ptr; - s->got_lf = FALSE; - s->last_line_num = s->token.line_num; -redo: - s->token.line_num = s->line_num; - s->token.ptr = p; - c = *p; - switch(c) { - case 0: - if (p >= s->buf_end) { - s->token.val = TOK_EOF; - } else { - goto def_token; - } - break; - case '`': - if (js_parse_template_part(s, p + 1)) - goto fail; - p = s->buf_ptr; - break; - case '\'': - case '\"': - if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) - goto fail; - break; - case '\r': /* accept DOS and MAC newline sequences */ - if (p[1] == '\n') { - p++; - } - /* fall thru */ - case '\n': - p++; - line_terminator: - s->got_lf = TRUE; - s->line_num++; - goto redo; - case '\f': - case '\v': - case ' ': - case '\t': - p++; - goto redo; - case '/': - if (p[1] == '*') { - /* comment */ - p += 2; - for(;;) { - if (*p == '\0' && p >= s->buf_end) { - js_parse_error(s, "unexpected end of comment"); - goto fail; - } - if (p[0] == '*' && p[1] == '/') { - p += 2; - break; - } - if (*p == '\n') { - s->line_num++; - s->got_lf = TRUE; /* considered as LF for ASI */ - p++; - } else if (*p == '\r') { - s->got_lf = TRUE; /* considered as LF for ASI */ - p++; - } else if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (c == CP_LS || c == CP_PS) { - s->got_lf = TRUE; /* considered as LF for ASI */ - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - p++; - } - } - goto redo; - } else if (p[1] == '/') { - /* line comment */ - p += 2; - skip_line_comment: - for(;;) { - if (*p == '\0' && p >= s->buf_end) - break; - if (*p == '\r' || *p == '\n') - break; - if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - /* LS or PS are considered as line terminator */ - if (c == CP_LS || c == CP_PS) { - break; - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - p++; - } - } - goto redo; - } else if (p[1] == '=') { - p += 2; - s->token.val = TOK_DIV_ASSIGN; - } else { - p++; - s->token.val = c; - } - break; - case '\\': - if (p[1] == 'u') { - const uint8_t *p1 = p + 1; - int c1 = lre_parse_escape(&p1, TRUE); - if (c1 >= 0 && lre_js_is_ident_first(c1)) { - c = c1; - p = p1; - ident_has_escape = TRUE; - goto has_ident; - } else { - /* XXX: syntax error? */ - } - } - goto def_token; - case 'a': case 'b': case 'c': case 'd': - case 'e': case 'f': case 'g': case 'h': - case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': - case 'q': case 'r': case 's': case 't': - case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': - case 'E': case 'F': case 'G': case 'H': - case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': - case 'Q': case 'R': case 'S': case 'T': - case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': - case '_': - case '$': - /* identifier */ - p++; - ident_has_escape = FALSE; - has_ident: - atom = parse_ident(s, &p, &ident_has_escape, c, FALSE); - if (atom == JS_ATOM_NULL) - goto fail; - s->token.u.ident.atom = atom; - s->token.u.ident.has_escape = ident_has_escape; - s->token.u.ident.is_reserved = FALSE; - if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || - (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && - (s->cur_func->js_mode & JS_MODE_STRICT)) || - (s->token.u.ident.atom == JS_ATOM_yield && - ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || - (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && - !s->cur_func->in_function_body && s->cur_func->parent && - (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || - (s->token.u.ident.atom == JS_ATOM_await && - (s->is_module || - (((s->cur_func->func_kind & JS_FUNC_ASYNC) || - (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && - !s->cur_func->in_function_body && s->cur_func->parent && - (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) { - if (ident_has_escape) { - s->token.u.ident.is_reserved = TRUE; - s->token.val = TOK_IDENT; - } else { - /* The keywords atoms are pre allocated */ - s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; - } - } else { - s->token.val = TOK_IDENT; - } - break; - case '#': - /* private name */ - { - const uint8_t *p1; - p++; - p1 = p; - c = *p1++; - if (c == '\\' && *p1 == 'u') { - c = lre_parse_escape(&p1, TRUE); - } else if (c >= 128) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); - } - if (!lre_js_is_ident_first(c)) { - js_parse_error(s, "invalid first character of private name"); - goto fail; - } - p = p1; - ident_has_escape = FALSE; /* not used */ - atom = parse_ident(s, &p, &ident_has_escape, c, TRUE); - if (atom == JS_ATOM_NULL) - goto fail; - s->token.u.ident.atom = atom; - s->token.val = TOK_PRIVATE_NAME; - } - break; - case '.': - if (p[1] == '.' && p[2] == '.') { - p += 3; - s->token.val = TOK_ELLIPSIS; - break; - } - if (p[1] >= '0' && p[1] <= '9') { - goto parse_number; - } else { - goto def_token; - } - break; - case '0': - /* in strict mode, octal literals are not accepted */ - if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) { - js_parse_error(s, "octal literals are deprecated in strict mode"); - goto fail; - } - goto parse_number; - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': - case '9': - /* number */ - parse_number: - { - JSValue ret; - const uint8_t *p1; - int flags, radix; - flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | - ATOD_ACCEPT_UNDERSCORES; -#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; - } -#endif - radix = 0; -#ifdef CONFIG_BIGNUM - s->token.u.num.exponent = 0; - ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix, - flags, &s->token.u.num.exponent); -#else - ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix, - flags); -#endif - if (JS_IsException(ret)) - goto fail; - /* reject `10instanceof Number` */ - if (JS_VALUE_IS_NAN(ret) || - lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) { - JS_FreeValue(s->ctx, ret); - js_parse_error(s, "invalid number literal"); - goto fail; - } - s->token.val = TOK_NUMBER; - s->token.u.num.val = ret; - } - break; - case '*': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_MUL_ASSIGN; - } else if (p[1] == '*') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_POW_ASSIGN; - } else { - p += 2; - s->token.val = TOK_POW; - } - } else { - goto def_token; - } - break; - case '%': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_MOD_ASSIGN; - } else { - goto def_token; - } - break; - case '+': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_PLUS_ASSIGN; - } else if (p[1] == '+') { - p += 2; - s->token.val = TOK_INC; - } else { - goto def_token; - } - break; - case '-': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_MINUS_ASSIGN; - } else if (p[1] == '-') { - if (s->allow_html_comments && - p[2] == '>' && s->last_line_num != s->line_num) { - /* Annex B: `-->` at beginning of line is an html comment end. - It extends to the end of the line. - */ - goto skip_line_comment; - } - p += 2; - s->token.val = TOK_DEC; - } else { - goto def_token; - } - break; - case '<': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_LTE; - } else if (p[1] == '<') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_SHL_ASSIGN; - } else { - p += 2; - s->token.val = TOK_SHL; - } - } else if (s->allow_html_comments && - p[1] == '!' && p[2] == '-' && p[3] == '-') { - /* Annex B: handle `` at beginning of line is an html comment end. + It extends to the end of the line. + */ + goto skip_line_comment; + } + p += 2; + s->token.val = TOK_DEC; + } else { + goto def_token; + } + break; + case '<': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_LTE; + } else if (p[1] == '<') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_SHL_ASSIGN; + } else { + p += 2; + s->token.val = TOK_SHL; + } + } else if (s->allow_html_comments && + p[1] == '!' && p[2] == '-' && p[3] == '-') { + /* Annex B: handle `` at beginning of line is an html comment end. - It extends to the end of the line. - */ - goto skip_line_comment; - } - p += 2; - s->token.val = TOK_DEC; - } else { - goto def_token; - } - break; - case '<': - if (p[1] == '=') { - p += 2; - s->token.val = TOK_LTE; - } else if (p[1] == '<') { - if (p[2] == '=') { - p += 3; - s->token.val = TOK_SHL_ASSIGN; - } else { - p += 2; - s->token.val = TOK_SHL; - } - } else if (s->allow_html_comments && - p[1] == '!' && p[2] == '-' && p[3] == '-') { - /* Annex B: handle `` at beginning of line is an html comment end. + It extends to the end of the line. + */ + goto skip_line_comment; + } + p += 2; + s->token.val = TOK_DEC; + } else { + goto def_token; + } + break; + case '<': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_LTE; + } else if (p[1] == '<') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_SHL_ASSIGN; + } else { + p += 2; + s->token.val = TOK_SHL; + } + } else if (s->allow_html_comments && + p[1] == '!' && p[2] == '-' && p[3] == '-') { + /* Annex B: handle `