diff --git a/lib/mquickjs/LICENSE b/lib/mquickjs/LICENSE new file mode 100644 index 00000000..a08db69d --- /dev/null +++ b/lib/mquickjs/LICENSE @@ -0,0 +1,22 @@ +Micro QuickJS Javascript Engine + +Copyright (c) 2017-2025 Fabrice Bellard +Copyright (c) 2017-2025 Charlie Gordon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +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/lib/mquickjs/Makefile b/lib/mquickjs/Makefile new file mode 100644 index 00000000..0736a9bb --- /dev/null +++ b/lib/mquickjs/Makefile @@ -0,0 +1,158 @@ +#CONFIG_PROFILE=y +#CONFIG_X86_32=y +#CONFIG_ARM32=y +#CONFIG_WIN32=y +#CONFIG_SOFTFLOAT=y +#CONFIG_ASAN=y +#CONFIG_GPROF=y +CONFIG_SMALL=y +# consider warnings as errors (for development) +#CONFIG_WERROR=y + +ifdef CONFIG_ARM32 +CROSS_PREFIX=arm-linux-gnu- +endif + +ifdef CONFIG_WIN32 + ifdef CONFIG_X86_32 + CROSS_PREFIX?=i686-w64-mingw32- + else + CROSS_PREFIX?=x86_64-w64-mingw32- + endif + EXE=.exe +else + CROSS_PREFIX?= + EXE= +endif + +HOST_CC=gcc +CC=$(CROSS_PREFIX)gcc +CFLAGS=-Wall -g -MMD -D_GNU_SOURCE -fno-math-errno -fno-trapping-math +HOST_CFLAGS=-Wall -g -MMD -D_GNU_SOURCE -fno-math-errno -fno-trapping-math +ifdef CONFIG_WERROR +CFLAGS+=-Werror +HOST_CFLAGS+=-Werror +endif +ifdef CONFIG_ARM32 +CFLAGS+=-mthumb +endif +ifdef CONFIG_SMALL +CFLAGS+=-Os +else +CFLAGS+=-O2 +endif +#CFLAGS+=-fstack-usage +ifdef CONFIG_SOFTFLOAT +CFLAGS+=-msoft-float +CFLAGS+=-DUSE_SOFTFLOAT +endif # CONFIG_SOFTFLOAT +HOST_CFLAGS+=-O2 +LDFLAGS=-g +HOST_LDFLAGS=-g +ifdef CONFIG_GPROF +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_X86_32 +CFLAGS+=-m32 +LDFLAGS+=-m32 +endif +ifdef CONFIG_PROFILE +CFLAGS+=-p +LDFLAGS+=-p +endif + +# when cross compiling from a 64 bit system to a 32 bit system, force +# a 32 bit output +ifdef CONFIG_X86_32 +MQJS_BUILD_FLAGS=-m32 +endif +ifdef CONFIG_ARM32 +MQJS_BUILD_FLAGS=-m32 +endif + +PROGS=mqjs$(EXE) example$(EXE) +TEST_PROGS=dtoa_test libm_test + +all: $(PROGS) + +MQJS_OBJS=mqjs.o readline_tty.o readline.o mquickjs.o dtoa.o libm.o cutils.o +LIBS=-lm + +mqjs$(EXE): $(MQJS_OBJS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +mquickjs.o: mquickjs_atom.h + +mqjs_stdlib: mqjs_stdlib.host.o mquickjs_build.host.o + $(HOST_CC) $(HOST_LDFLAGS) -o $@ $^ + +mquickjs_atom.h: mqjs_stdlib + ./mqjs_stdlib -a $(MQJS_BUILD_FLAGS) > $@ + +mqjs_stdlib.h: mqjs_stdlib + ./mqjs_stdlib $(MQJS_BUILD_FLAGS) > $@ + +mqjs.o: mqjs_stdlib.h + +# C API example +example.o: example_stdlib.h + +example$(EXE): example.o mquickjs.o dtoa.o libm.o cutils.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +example_stdlib: example_stdlib.host.o mquickjs_build.host.o + $(HOST_CC) $(HOST_LDFLAGS) -o $@ $^ + +example_stdlib.h: example_stdlib + ./example_stdlib $(MQJS_BUILD_FLAGS) > $@ + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.host.o: %.c + $(HOST_CC) $(HOST_CFLAGS) -c -o $@ $< + +test: mqjs example + ./mqjs tests/test_closure.js + ./mqjs tests/test_language.js + ./mqjs tests/test_loop.js + ./mqjs tests/test_builtin.js +# test bytecode generation and loading + ./mqjs -o test_builtin.bin tests/test_builtin.js +# @sha256sum -c test_builtin.sha256 + ./mqjs -b test_builtin.bin + ./example tests/test_rect.js + +microbench: mqjs + ./mqjs tests/microbench.js + +octane: mqjs + ./mqjs --memory-limit 256M tests/octane/run.js + +size: mqjs + size mqjs mqjs.o readline.o cutils.o dtoa.o libm.o mquickjs.o + +dtoa_test: tests/dtoa_test.o dtoa.o cutils.o tests/gay-fixed.o tests/gay-precision.o tests/gay-shortest.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +libm_test: tests/libm_test.o libm.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +rempio2_test: tests/rempio2_test.o libm.o + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + rm -f *.o *.d *~ tests/*.o tests/*.d tests/*~ test_builtin.bin mqjs_stdlib mqjs_stdlib.h mquickjs_build_atoms mquickjs_atom.h mqjs_example example_stdlib example_stdlib.h $(PROGS) $(TEST_PROGS) + +-include $(wildcard *.d) + + +# ADDED FOR CROSSPOINT +mqjs_stdlib.host.o: scripts/crosspoint_stdlib.c + $(HOST_CC) $(HOST_CFLAGS) -c -o $@ $< +# END OF ADDED PART diff --git a/lib/mquickjs/crosspoint_stdlib.h b/lib/mquickjs/crosspoint_stdlib.h new file mode 100644 index 00000000..7532e81e --- /dev/null +++ b/lib/mquickjs/crosspoint_stdlib.h @@ -0,0 +1,3344 @@ +/* this file is automatically generated - do not edit */ + +#include "mquickjs_priv.h" + +static const uint32_t __attribute((aligned(64))) js_stdlib_table[] = { + /* atom_table */ + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "null" (offset=0) */ + 0x6c6c756e, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "false" (offset=3) */ + 0x736c6166, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "true" (offset=6) */ + 0x65757274, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "if" (offset=9) */ + 0x00006669, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "else" (offset=11) */ + 0x65736c65, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "return" (offset=14) */ + 0x75746572, + 0x00006e72, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "var" (offset=17) */ + 0x00726176, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "this" (offset=19) */ + 0x73696874, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "delete" (offset=22) */ + 0x656c6564, + 0x00006574, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "void" (offset=25) */ + 0x64696f76, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "typeof" (offset=28) */ + 0x65707974, + 0x0000666f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "new" (offset=31) */ + 0x0077656e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "in" (offset=33) */ + 0x00006e69, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "instanceof" (offset=35) */ + 0x74736e69, + 0x65636e61, + 0x0000666f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "do" (offset=39) */ + 0x00006f64, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "while" (offset=41) */ + 0x6c696877, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "for" (offset=44) */ + 0x00726f66, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "break" (offset=46) */ + 0x61657262, + 0x0000006b, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "continue" (offset=49) */ + 0x746e6f63, + 0x65756e69, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "switch" (offset=53) */ + 0x74697773, + 0x00006863, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "case" (offset=56) */ + 0x65736163, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "default" (offset=59) */ + 0x61666564, + 0x00746c75, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "throw" (offset=62) */ + 0x6f726874, + 0x00000077, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "try" (offset=65) */ + 0x00797274, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "catch" (offset=67) */ + 0x63746163, + 0x00000068, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "finally" (offset=70) */ + 0x616e6966, + 0x00796c6c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "function" (offset=73) */ + 0x636e7566, + 0x6e6f6974, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "debugger" (offset=77) */ + 0x75626564, + 0x72656767, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "with" (offset=81) */ + 0x68746977, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "class" (offset=84) */ + 0x73616c63, + 0x00000073, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "const" (offset=87) */ + 0x736e6f63, + 0x00000074, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "enum" (offset=90) */ + 0x6d756e65, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "export" (offset=93) */ + 0x6f707865, + 0x00007472, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "extends" (offset=96) */ + 0x65747865, + 0x0073646e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "import" (offset=99) */ + 0x6f706d69, + 0x00007472, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "super" (offset=102) */ + 0x65707573, + 0x00000072, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "implements" (offset=105) */ + 0x6c706d69, + 0x6e656d65, + 0x00007374, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "interface" (offset=109) */ + 0x65746e69, + 0x63616672, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "let" (offset=113) */ + 0x0074656c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "package" (offset=115) */ + 0x6b636170, + 0x00656761, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "private" (offset=118) */ + 0x76697270, + 0x00657461, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "protected" (offset=121) */ + 0x746f7270, + 0x65746365, + 0x00000064, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "public" (offset=125) */ + 0x6c627570, + 0x00006369, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "static" (offset=128) */ + 0x74617473, + 0x00006369, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "yield" (offset=131) */ + 0x6c656979, + 0x00000064, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (0 << (JS_MTAG_BITS + 3)), /* "" (offset=134) */ + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "toString" (offset=136) */ + 0x74536f74, + 0x676e6972, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "valueOf" (offset=140) */ + 0x756c6176, + 0x00664f65, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "number" (offset=143) */ + 0x626d756e, + 0x00007265, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "object" (offset=146) */ + 0x656a626f, + 0x00007463, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "undefined" (offset=149) */ + 0x65646e75, + 0x656e6966, + 0x00000064, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "string" (offset=153) */ + 0x69727473, + 0x0000676e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "boolean" (offset=156) */ + 0x6c6f6f62, + 0x006e6165, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "" (offset=159) */ + 0x7465723c, + 0x0000003e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "" (offset=162) */ + 0x6176653c, + 0x00003e6c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "eval" (offset=165) */ + 0x6c617665, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "arguments" (offset=168) */ + 0x75677261, + 0x746e656d, + 0x00000073, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "value" (offset=172) */ + 0x756c6176, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "get" (offset=175) */ + 0x00746567, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "set" (offset=177) */ + 0x00746573, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "prototype" (offset=179) */ + 0x746f7270, + 0x7079746f, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "constructor" (offset=183) */ + 0x736e6f63, + 0x63757274, + 0x00726f74, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "length" (offset=187) */ + 0x676e656c, + 0x00006874, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "target" (offset=190) */ + 0x67726174, + 0x00007465, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "of" (offset=193) */ + 0x0000666f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (1 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "NaN" (offset=195) */ + 0x004e614e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (1 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "Infinity" (offset=197) */ + 0x69666e49, + 0x7974696e, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (1 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "-Infinity" (offset=201) */ + 0x666e492d, + 0x74696e69, + 0x00000079, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "name" (offset=205) */ + 0x656d616e, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "Error" (offset=208) */ + 0x6f727245, + 0x00000072, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "__proto__" (offset=211) */ + 0x72705f5f, + 0x5f6f746f, + 0x0000005f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "index" (offset=215) */ + 0x65646e69, + 0x00000078, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "input" (offset=218) */ + 0x75706e69, + 0x00000074, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "bound" (offset=221) */ + 0x6e756f62, + 0x00000064, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "Object" (offset=224) */ + 0x656a624f, + 0x00007463, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "defineProperty" (offset=227) */ + 0x69666564, + 0x7250656e, + 0x7265706f, + 0x00007974, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "getPrototypeOf" (offset=232) */ + 0x50746567, + 0x6f746f72, + 0x65707974, + 0x0000664f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "setPrototypeOf" (offset=237) */ + 0x50746573, + 0x6f746f72, + 0x65707974, + 0x0000664f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "create" (offset=242) */ + 0x61657263, + 0x00006574, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "keys" (offset=245) */ + 0x7379656b, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "hasOwnProperty" (offset=248) */ + 0x4f736168, + 0x72506e77, + 0x7265706f, + 0x00007974, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "Function" (offset=253) */ + 0x636e7546, + 0x6e6f6974, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "get prototype" (offset=257) */ + 0x20746567, + 0x746f7270, + 0x7079746f, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "set prototype" (offset=262) */ + 0x20746573, + 0x746f7270, + 0x7079746f, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "call" (offset=267) */ + 0x6c6c6163, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "apply" (offset=270) */ + 0x6c707061, + 0x00000079, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "bind" (offset=273) */ + 0x646e6962, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "get length" (offset=276) */ + 0x20746567, + 0x676e656c, + 0x00006874, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "get name" (offset=280) */ + 0x20746567, + 0x656d616e, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "Number" (offset=284) */ + 0x626d754e, + 0x00007265, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "parseInt" (offset=287) */ + 0x73726170, + 0x746e4965, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "parseFloat" (offset=291) */ + 0x73726170, + 0x6f6c4665, + 0x00007461, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "MAX_VALUE" (offset=295) */ + 0x5f58414d, + 0x554c4156, + 0x00000045, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "MIN_VALUE" (offset=299) */ + 0x5f4e494d, + 0x554c4156, + 0x00000045, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (17 << (JS_MTAG_BITS + 3)), /* "NEGATIVE_INFINITY" (offset=303) */ + 0x4147454e, + 0x45564954, + 0x464e495f, + 0x54494e49, + 0x00000059, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (17 << (JS_MTAG_BITS + 3)), /* "POSITIVE_INFINITY" (offset=309) */ + 0x49534f50, + 0x45564954, + 0x464e495f, + 0x54494e49, + 0x00000059, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "EPSILON" (offset=315) */ + 0x49535045, + 0x004e4f4c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (16 << (JS_MTAG_BITS + 3)), /* "MAX_SAFE_INTEGER" (offset=318) */ + 0x5f58414d, + 0x45464153, + 0x544e495f, + 0x52454745, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (16 << (JS_MTAG_BITS + 3)), /* "MIN_SAFE_INTEGER" (offset=324) */ + 0x5f4e494d, + 0x45464153, + 0x544e495f, + 0x52454745, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "toExponential" (offset=330) */ + 0x78456f74, + 0x656e6f70, + 0x6169746e, + 0x0000006c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "toFixed" (offset=335) */ + 0x69466f74, + 0x00646578, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "toPrecision" (offset=338) */ + 0x72506f74, + 0x73696365, + 0x006e6f69, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "Boolean" (offset=342) */ + 0x6c6f6f42, + 0x006e6165, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "String" (offset=345) */ + 0x69727453, + 0x0000676e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "fromCharCode" (offset=348) */ + 0x6d6f7266, + 0x72616843, + 0x65646f43, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "fromCodePoint" (offset=353) */ + 0x6d6f7266, + 0x65646f43, + 0x6e696f50, + 0x00000074, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "set length" (offset=358) */ + 0x20746573, + 0x676e656c, + 0x00006874, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "charAt" (offset=362) */ + 0x72616863, + 0x00007441, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "charCodeAt" (offset=365) */ + 0x72616863, + 0x65646f43, + 0x00007441, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "codePointAt" (offset=369) */ + 0x65646f63, + 0x6e696f50, + 0x00744174, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "slice" (offset=373) */ + 0x63696c73, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "substring" (offset=376) */ + 0x73627573, + 0x6e697274, + 0x00000067, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "concat" (offset=380) */ + 0x636e6f63, + 0x00007461, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "indexOf" (offset=383) */ + 0x65646e69, + 0x00664f78, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "lastIndexOf" (offset=386) */ + 0x7473616c, + 0x65646e49, + 0x00664f78, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "match" (offset=390) */ + 0x6374616d, + 0x00000068, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "replace" (offset=393) */ + 0x6c706572, + 0x00656361, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "replaceAll" (offset=396) */ + 0x6c706572, + 0x41656361, + 0x00006c6c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "search" (offset=400) */ + 0x72616573, + 0x00006863, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "split" (offset=403) */ + 0x696c7073, + 0x00000074, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "toLowerCase" (offset=406) */ + 0x6f4c6f74, + 0x43726577, + 0x00657361, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "toUpperCase" (offset=410) */ + 0x70556f74, + 0x43726570, + 0x00657361, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "trim" (offset=414) */ + 0x6d697274, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "trimEnd" (offset=417) */ + 0x6d697274, + 0x00646e45, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "trimStart" (offset=420) */ + 0x6d697274, + 0x72617453, + 0x00000074, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "repeat" (offset=424) */ + 0x65706572, + 0x00007461, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "Array" (offset=427) */ + 0x61727241, + 0x00000079, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "isArray" (offset=430) */ + 0x72417369, + 0x00796172, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "push" (offset=433) */ + 0x68737570, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "pop" (offset=436) */ + 0x00706f70, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "join" (offset=438) */ + 0x6e696f6a, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "reverse" (offset=441) */ + 0x65766572, + 0x00657372, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "shift" (offset=444) */ + 0x66696873, + 0x00000074, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "splice" (offset=447) */ + 0x696c7073, + 0x00006563, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "unshift" (offset=450) */ + 0x68736e75, + 0x00746669, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "every" (offset=453) */ + 0x72657665, + 0x00000079, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "some" (offset=456) */ + 0x656d6f73, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "forEach" (offset=459) */ + 0x45726f66, + 0x00686361, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "map" (offset=462) */ + 0x0070616d, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "filter" (offset=464) */ + 0x746c6966, + 0x00007265, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "reduce" (offset=467) */ + 0x75646572, + 0x00006563, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "reduceRight" (offset=470) */ + 0x75646572, + 0x69526563, + 0x00746867, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "sort" (offset=474) */ + 0x74726f73, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "Math" (offset=477) */ + 0x6874614d, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "min" (offset=480) */ + 0x006e696d, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "max" (offset=482) */ + 0x0078616d, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "sign" (offset=484) */ + 0x6e676973, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "abs" (offset=487) */ + 0x00736261, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "floor" (offset=489) */ + 0x6f6f6c66, + 0x00000072, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "ceil" (offset=492) */ + 0x6c696563, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "round" (offset=495) */ + 0x6e756f72, + 0x00000064, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "sqrt" (offset=498) */ + 0x74727173, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "E" (offset=501) */ + 0x00000045, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "LN10" (offset=503) */ + 0x30314e4c, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "LN2" (offset=506) */ + 0x00324e4c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "LOG2E" (offset=508) */ + 0x32474f4c, + 0x00000045, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "LOG10E" (offset=511) */ + 0x31474f4c, + 0x00004530, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "PI" (offset=514) */ + 0x00004950, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "SQRT1_2" (offset=516) */ + 0x54525153, + 0x00325f31, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "SQRT2" (offset=519) */ + 0x54525153, + 0x00000032, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "sin" (offset=522) */ + 0x006e6973, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "cos" (offset=524) */ + 0x00736f63, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "tan" (offset=526) */ + 0x006e6174, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "asin" (offset=528) */ + 0x6e697361, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "acos" (offset=531) */ + 0x736f6361, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "atan" (offset=534) */ + 0x6e617461, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "atan2" (offset=537) */ + 0x6e617461, + 0x00000032, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "exp" (offset=540) */ + 0x00707865, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "log" (offset=542) */ + 0x00676f6c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "pow" (offset=544) */ + 0x00776f70, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "random" (offset=546) */ + 0x646e6172, + 0x00006d6f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "imul" (offset=549) */ + 0x6c756d69, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "clz32" (offset=552) */ + 0x337a6c63, + 0x00000032, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "fround" (offset=555) */ + 0x756f7266, + 0x0000646e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "trunc" (offset=558) */ + 0x6e757274, + 0x00000063, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "log2" (offset=561) */ + 0x32676f6c, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "log10" (offset=564) */ + 0x31676f6c, + 0x00000030, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "Date" (offset=567) */ + 0x65746144, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (3 << (JS_MTAG_BITS + 3)), /* "now" (offset=570) */ + 0x00776f6e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "JSON" (offset=572) */ + 0x4e4f534a, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "parse" (offset=575) */ + 0x73726170, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "stringify" (offset=578) */ + 0x69727473, + 0x6669676e, + 0x00000079, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "RegExp" (offset=582) */ + 0x45676552, + 0x00007078, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "lastIndex" (offset=585) */ + 0x7473616c, + 0x65646e49, + 0x00000078, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "get lastIndex" (offset=589) */ + 0x20746567, + 0x7473616c, + 0x65646e49, + 0x00000078, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "set lastIndex" (offset=594) */ + 0x20746573, + 0x7473616c, + 0x65646e49, + 0x00000078, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "source" (offset=599) */ + 0x72756f73, + 0x00006563, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "get source" (offset=602) */ + 0x20746567, + 0x72756f73, + 0x00006563, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "flags" (offset=606) */ + 0x67616c66, + 0x00000073, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "get flags" (offset=609) */ + 0x20746567, + 0x67616c66, + 0x00000073, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "exec" (offset=613) */ + 0x63657865, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "test" (offset=616) */ + 0x74736574, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "message" (offset=619) */ + 0x7373656d, + 0x00656761, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "get message" (offset=622) */ + 0x20746567, + 0x7373656d, + 0x00656761, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "stack" (offset=626) */ + 0x63617473, + 0x0000006b, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "get stack" (offset=629) */ + 0x20746567, + 0x63617473, + 0x0000006b, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "EvalError" (offset=633) */ + 0x6c617645, + 0x6f727245, + 0x00000072, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "RangeError" (offset=637) */ + 0x676e6152, + 0x72724565, + 0x0000726f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "ReferenceError" (offset=641) */ + 0x65666552, + 0x636e6572, + 0x72724565, + 0x0000726f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "SyntaxError" (offset=646) */ + 0x746e7953, + 0x72457861, + 0x00726f72, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "TypeError" (offset=650) */ + 0x65707954, + 0x6f727245, + 0x00000072, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "URIError" (offset=654) */ + 0x45495255, + 0x726f7272, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "InternalError" (offset=658) */ + 0x65746e49, + 0x6c616e72, + 0x6f727245, + 0x00000072, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "ArrayBuffer" (offset=663) */ + 0x61727241, + 0x66754279, + 0x00726566, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "byteLength" (offset=667) */ + 0x65747962, + 0x676e654c, + 0x00006874, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "get byteLength" (offset=671) */ + 0x20746567, + 0x65747962, + 0x676e654c, + 0x00006874, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (17 << (JS_MTAG_BITS + 3)), /* "Uint8ClampedArray" (offset=676) */ + 0x746e6955, + 0x616c4338, + 0x6465706d, + 0x61727241, + 0x00000079, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "TypedArray" (offset=682) */ + 0x65707954, + 0x72724164, + 0x00007961, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "byteOffset" (offset=686) */ + 0x65747962, + 0x7366664f, + 0x00007465, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "get byteOffset" (offset=690) */ + 0x20746567, + 0x65747962, + 0x7366664f, + 0x00007465, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "buffer" (offset=695) */ + 0x66667562, + 0x00007265, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "get buffer" (offset=698) */ + 0x20746567, + 0x66667562, + 0x00007265, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "subarray" (offset=702) */ + 0x61627573, + 0x79617272, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (17 << (JS_MTAG_BITS + 3)), /* "BYTES_PER_ELEMENT" (offset=706) */ + 0x45545942, + 0x45505f53, + 0x4c455f52, + 0x4e454d45, + 0x00000054, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "Int8Array" (offset=712) */ + 0x38746e49, + 0x61727241, + 0x00000079, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "Uint8Array" (offset=716) */ + 0x746e6955, + 0x72724138, + 0x00007961, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "Int16Array" (offset=720) */ + 0x31746e49, + 0x72724136, + 0x00007961, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "Uint16Array" (offset=724) */ + 0x746e6955, + 0x72413631, + 0x00796172, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "Int32Array" (offset=728) */ + 0x33746e49, + 0x72724132, + 0x00007961, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "Uint32Array" (offset=732) */ + 0x746e6955, + 0x72413233, + 0x00796172, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "Float32Array" (offset=736) */ + 0x616f6c46, + 0x41323374, + 0x79617272, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "Float64Array" (offset=741) */ + 0x616f6c46, + 0x41343674, + 0x79617272, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "isNaN" (offset=746) */ + 0x614e7369, + 0x0000004e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "isFinite" (offset=749) */ + 0x69467369, + 0x6574696e, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "globalThis" (offset=753) */ + 0x626f6c67, + 0x68546c61, + 0x00007369, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (7 << (JS_MTAG_BITS + 3)), /* "console" (offset=757) */ + 0x736e6f63, + 0x00656c6f, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "performance" (offset=760) */ + 0x66726570, + 0x616d726f, + 0x0065636e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (5 << (JS_MTAG_BITS + 3)), /* "print" (offset=764) */ + 0x6e697270, + 0x00000074, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "gc" (offset=767) */ + 0x00006367, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "load" (offset=769) */ + 0x64616f6c, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "setTimeout" (offset=772) */ + 0x54746573, + 0x6f656d69, + 0x00007475, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "clearTimeout" (offset=776) */ + 0x61656c63, + 0x6d695472, + 0x74756f65, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "CP" (offset=781) */ + 0x00005043, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "FAST_REFRESH" (offset=783) */ + 0x54534146, + 0x4645525f, + 0x48534552, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "F" (offset=788) */ + 0x00000046, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "HALF_REFRESH" (offset=790) */ + 0x464c4148, + 0x4645525f, + 0x48534552, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "H" (offset=795) */ + 0x00000048, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "FULL_REFRESH" (offset=797) */ + 0x4c4c5546, + 0x4645525f, + 0x48534552, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "A" (offset=802) */ + 0x00000041, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "FONT_UI_10" (offset=804) */ + 0x544e4f46, + 0x5f49555f, + 0x00003031, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "UI10" (offset=808) */ + 0x30314955, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "FONT_UI_12" (offset=811) */ + 0x544e4f46, + 0x5f49555f, + 0x00003231, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (4 << (JS_MTAG_BITS + 3)), /* "UI12" (offset=815) */ + 0x32314955, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (10 << (JS_MTAG_BITS + 3)), /* "FONT_SMALL" (offset=818) */ + 0x544e4f46, + 0x414d535f, + 0x00004c4c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (2 << (JS_MTAG_BITS + 3)), /* "SM" (offset=822) */ + 0x00004d53, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "TEXT_REGULAR" (offset=824) */ + 0x54584554, + 0x4745525f, + 0x52414c55, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "R" (offset=829) */ + 0x00000052, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "TEXT_BOLD" (offset=831) */ + 0x54584554, + 0x4c4f425f, + 0x00000044, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "B" (offset=835) */ + 0x00000042, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "TEXT_ITALIC" (offset=837) */ + 0x54584554, + 0x4154495f, + 0x0043494c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "I" (offset=841) */ + 0x00000049, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (16 << (JS_MTAG_BITS + 3)), /* "TEXT_BOLD_ITALIC" (offset=843) */ + 0x54584554, + 0x4c4f425f, + 0x54495f44, + 0x43494c41, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "J" (offset=849) */ + 0x0000004a, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "BTN_BACK" (offset=851) */ + 0x5f4e5442, + 0x4b434142, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "BTN_CONFIRM" (offset=855) */ + 0x5f4e5442, + 0x464e4f43, + 0x004d5249, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "C" (offset=859) */ + 0x00000043, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "BTN_LEFT" (offset=861) */ + 0x5f4e5442, + 0x5446454c, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "L" (offset=865) */ + 0x0000004c, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "BTN_RIGHT" (offset=867) */ + 0x5f4e5442, + 0x48474952, + 0x00000054, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "BTN_UP" (offset=871) */ + 0x5f4e5442, + 0x00005055, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "U" (offset=874) */ + 0x00000055, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "BTN_DOWN" (offset=876) */ + 0x5f4e5442, + 0x4e574f44, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (1 << (JS_MTAG_BITS + 3)), /* "D" (offset=880) */ + 0x00000044, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (6 << (JS_MTAG_BITS + 3)), /* "millis" (offset=882) */ + 0x6c6c696d, + 0x00007369, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "btnIsPressed" (offset=885) */ + 0x496e7462, + 0x65725073, + 0x64657373, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (14 << (JS_MTAG_BITS + 3)), /* "getScreenWidth" (offset=890) */ + 0x53746567, + 0x65657263, + 0x6469576e, + 0x00006874, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (15 << (JS_MTAG_BITS + 3)), /* "getScreenHeight" (offset=895) */ + 0x53746567, + 0x65657263, + 0x6965486e, + 0x00746867, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (11 << (JS_MTAG_BITS + 3)), /* "clearScreen" (offset=900) */ + 0x61656c63, + 0x72635372, + 0x006e6565, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (13 << (JS_MTAG_BITS + 3)), /* "displayBuffer" (offset=904) */ + 0x70736964, + 0x4279616c, + 0x65666675, + 0x00000072, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "drawLine" (offset=909) */ + 0x77617264, + 0x656e694c, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "drawRect" (offset=913) */ + 0x77617264, + 0x74636552, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "fillRect" (offset=917) */ + 0x6c6c6966, + 0x74636552, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (9 << (JS_MTAG_BITS + 3)), /* "drawImage" (offset=921) */ + 0x77617264, + 0x67616d49, + 0x00000065, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (12 << (JS_MTAG_BITS + 3)), /* "getTextWidth" (offset=925) */ + 0x54746567, + 0x57747865, + 0x68746469, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (16 << (JS_MTAG_BITS + 3)), /* "drawCenteredText" (offset=930) */ + 0x77617264, + 0x746e6543, + 0x64657265, + 0x74786554, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (8 << (JS_MTAG_BITS + 3)), /* "drawText" (offset=936) */ + 0x77617264, + 0x74786554, + 0x00000000, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (15 << (JS_MTAG_BITS + 3)), /* "drawButtonHints" (offset=940) */ + 0x77617264, + 0x74747542, + 0x69486e6f, + 0x0073746e, + (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (1 << (JS_MTAG_BITS + 1)) | (0 << (JS_MTAG_BITS + 2)) | (19 << (JS_MTAG_BITS + 3)), /* "drawSideButtonHints" (offset=945) */ + 0x77617264, + 0x65646953, + 0x74747542, + 0x69486e6f, + 0x0073746e, + + /* sorted atom table (offset=951) */ + JS_VALUE_ARRAY_HEADER(278), + JS_ROM_VALUE(134), /* empty */ + JS_ROM_VALUE(201), /* _Infinity */ + JS_ROM_VALUE(162), /* _eval_ */ + JS_ROM_VALUE(159), /* _ret_ */ + JS_ROM_VALUE(802), /* A */ + JS_ROM_VALUE(427), /* Array */ + JS_ROM_VALUE(663), /* ArrayBuffer */ + JS_ROM_VALUE(835), /* B */ + JS_ROM_VALUE(851), /* BTN_BACK */ + JS_ROM_VALUE(855), /* BTN_CONFIRM */ + JS_ROM_VALUE(876), /* BTN_DOWN */ + JS_ROM_VALUE(861), /* BTN_LEFT */ + JS_ROM_VALUE(867), /* BTN_RIGHT */ + JS_ROM_VALUE(871), /* BTN_UP */ + JS_ROM_VALUE(706), /* BYTES_PER_ELEMENT */ + JS_ROM_VALUE(342), /* Boolean */ + JS_ROM_VALUE(859), /* C */ + JS_ROM_VALUE(781), /* CP */ + JS_ROM_VALUE(880), /* D */ + JS_ROM_VALUE(567), /* Date */ + JS_ROM_VALUE(501), /* E */ + JS_ROM_VALUE(315), /* EPSILON */ + JS_ROM_VALUE(208), /* Error */ + JS_ROM_VALUE(633), /* EvalError */ + JS_ROM_VALUE(788), /* F */ + JS_ROM_VALUE(783), /* FAST_REFRESH */ + JS_ROM_VALUE(818), /* FONT_SMALL */ + JS_ROM_VALUE(804), /* FONT_UI_10 */ + JS_ROM_VALUE(811), /* FONT_UI_12 */ + JS_ROM_VALUE(797), /* FULL_REFRESH */ + JS_ROM_VALUE(736), /* Float32Array */ + JS_ROM_VALUE(741), /* Float64Array */ + JS_ROM_VALUE(253), /* Function */ + JS_ROM_VALUE(795), /* H */ + JS_ROM_VALUE(790), /* HALF_REFRESH */ + JS_ROM_VALUE(841), /* I */ + JS_ROM_VALUE(197), /* Infinity */ + JS_ROM_VALUE(720), /* Int16Array */ + JS_ROM_VALUE(728), /* Int32Array */ + JS_ROM_VALUE(712), /* Int8Array */ + JS_ROM_VALUE(658), /* InternalError */ + JS_ROM_VALUE(849), /* J */ + JS_ROM_VALUE(572), /* JSON */ + JS_ROM_VALUE(865), /* L */ + JS_ROM_VALUE(503), /* LN10 */ + JS_ROM_VALUE(506), /* LN2 */ + JS_ROM_VALUE(511), /* LOG10E */ + JS_ROM_VALUE(508), /* LOG2E */ + JS_ROM_VALUE(318), /* MAX_SAFE_INTEGER */ + JS_ROM_VALUE(295), /* MAX_VALUE */ + JS_ROM_VALUE(324), /* MIN_SAFE_INTEGER */ + JS_ROM_VALUE(299), /* MIN_VALUE */ + JS_ROM_VALUE(477), /* Math */ + JS_ROM_VALUE(303), /* NEGATIVE_INFINITY */ + JS_ROM_VALUE(195), /* NaN */ + JS_ROM_VALUE(284), /* Number */ + JS_ROM_VALUE(224), /* Object */ + JS_ROM_VALUE(514), /* PI */ + JS_ROM_VALUE(309), /* POSITIVE_INFINITY */ + JS_ROM_VALUE(829), /* R */ + JS_ROM_VALUE(637), /* RangeError */ + JS_ROM_VALUE(641), /* ReferenceError */ + JS_ROM_VALUE(582), /* RegExp */ + JS_ROM_VALUE(822), /* SM */ + JS_ROM_VALUE(516), /* SQRT1_2 */ + JS_ROM_VALUE(519), /* SQRT2 */ + JS_ROM_VALUE(345), /* String */ + JS_ROM_VALUE(646), /* SyntaxError */ + JS_ROM_VALUE(831), /* TEXT_BOLD */ + JS_ROM_VALUE(843), /* TEXT_BOLD_ITALIC */ + JS_ROM_VALUE(837), /* TEXT_ITALIC */ + JS_ROM_VALUE(824), /* TEXT_REGULAR */ + JS_ROM_VALUE(650), /* TypeError */ + JS_ROM_VALUE(682), /* TypedArray */ + JS_ROM_VALUE(874), /* U */ + JS_ROM_VALUE(808), /* UI10 */ + JS_ROM_VALUE(815), /* UI12 */ + JS_ROM_VALUE(654), /* URIError */ + JS_ROM_VALUE(724), /* Uint16Array */ + JS_ROM_VALUE(732), /* Uint32Array */ + JS_ROM_VALUE(716), /* Uint8Array */ + JS_ROM_VALUE(676), /* Uint8ClampedArray */ + JS_ROM_VALUE(211), /* __proto__ */ + JS_ROM_VALUE(487), /* abs */ + JS_ROM_VALUE(531), /* acos */ + JS_ROM_VALUE(270), /* apply */ + JS_ROM_VALUE(168), /* arguments */ + JS_ROM_VALUE(528), /* asin */ + JS_ROM_VALUE(534), /* atan */ + JS_ROM_VALUE(537), /* atan2 */ + JS_ROM_VALUE(273), /* bind */ + JS_ROM_VALUE(156), /* boolean */ + JS_ROM_VALUE(221), /* bound */ + JS_ROM_VALUE(46), /* break */ + JS_ROM_VALUE(885), /* btnIsPressed */ + JS_ROM_VALUE(695), /* buffer */ + JS_ROM_VALUE(667), /* byteLength */ + JS_ROM_VALUE(686), /* byteOffset */ + JS_ROM_VALUE(267), /* call */ + JS_ROM_VALUE(56), /* case */ + JS_ROM_VALUE(67), /* catch */ + JS_ROM_VALUE(492), /* ceil */ + JS_ROM_VALUE(362), /* charAt */ + JS_ROM_VALUE(365), /* charCodeAt */ + JS_ROM_VALUE(84), /* class */ + JS_ROM_VALUE(900), /* clearScreen */ + JS_ROM_VALUE(776), /* clearTimeout */ + JS_ROM_VALUE(552), /* clz32 */ + JS_ROM_VALUE(369), /* codePointAt */ + JS_ROM_VALUE(380), /* concat */ + JS_ROM_VALUE(757), /* console */ + JS_ROM_VALUE(87), /* const */ + JS_ROM_VALUE(183), /* constructor */ + JS_ROM_VALUE(49), /* continue */ + JS_ROM_VALUE(524), /* cos */ + JS_ROM_VALUE(242), /* create */ + JS_ROM_VALUE(77), /* debugger */ + JS_ROM_VALUE(59), /* default */ + JS_ROM_VALUE(227), /* defineProperty */ + JS_ROM_VALUE(22), /* delete */ + JS_ROM_VALUE(904), /* displayBuffer */ + JS_ROM_VALUE(39), /* do */ + JS_ROM_VALUE(940), /* drawButtonHints */ + JS_ROM_VALUE(930), /* drawCenteredText */ + JS_ROM_VALUE(921), /* drawImage */ + JS_ROM_VALUE(909), /* drawLine */ + JS_ROM_VALUE(913), /* drawRect */ + JS_ROM_VALUE(945), /* drawSideButtonHints */ + JS_ROM_VALUE(936), /* drawText */ + JS_ROM_VALUE(11), /* else */ + JS_ROM_VALUE(90), /* enum */ + JS_ROM_VALUE(165), /* eval */ + JS_ROM_VALUE(453), /* every */ + JS_ROM_VALUE(613), /* exec */ + JS_ROM_VALUE(540), /* exp */ + JS_ROM_VALUE(93), /* export */ + JS_ROM_VALUE(96), /* extends */ + JS_ROM_VALUE(3), /* false */ + JS_ROM_VALUE(917), /* fillRect */ + JS_ROM_VALUE(464), /* filter */ + JS_ROM_VALUE(70), /* finally */ + JS_ROM_VALUE(606), /* flags */ + JS_ROM_VALUE(489), /* floor */ + JS_ROM_VALUE(44), /* for */ + JS_ROM_VALUE(459), /* forEach */ + JS_ROM_VALUE(348), /* fromCharCode */ + JS_ROM_VALUE(353), /* fromCodePoint */ + JS_ROM_VALUE(555), /* fround */ + JS_ROM_VALUE(73), /* function */ + JS_ROM_VALUE(767), /* gc */ + JS_ROM_VALUE(175), /* get */ + JS_ROM_VALUE(698), /* get buffer */ + JS_ROM_VALUE(671), /* get byteLength */ + JS_ROM_VALUE(690), /* get byteOffset */ + JS_ROM_VALUE(609), /* get flags */ + JS_ROM_VALUE(589), /* get lastIndex */ + JS_ROM_VALUE(276), /* get length */ + JS_ROM_VALUE(622), /* get message */ + JS_ROM_VALUE(280), /* get name */ + JS_ROM_VALUE(257), /* get prototype */ + JS_ROM_VALUE(602), /* get source */ + JS_ROM_VALUE(629), /* get stack */ + JS_ROM_VALUE(232), /* getPrototypeOf */ + JS_ROM_VALUE(895), /* getScreenHeight */ + JS_ROM_VALUE(890), /* getScreenWidth */ + JS_ROM_VALUE(925), /* getTextWidth */ + JS_ROM_VALUE(753), /* globalThis */ + JS_ROM_VALUE(248), /* hasOwnProperty */ + JS_ROM_VALUE(9), /* if */ + JS_ROM_VALUE(105), /* implements */ + JS_ROM_VALUE(99), /* import */ + JS_ROM_VALUE(549), /* imul */ + JS_ROM_VALUE(33), /* in */ + JS_ROM_VALUE(215), /* index */ + JS_ROM_VALUE(383), /* indexOf */ + JS_ROM_VALUE(218), /* input */ + JS_ROM_VALUE(35), /* instanceof */ + JS_ROM_VALUE(109), /* interface */ + JS_ROM_VALUE(430), /* isArray */ + JS_ROM_VALUE(749), /* isFinite */ + JS_ROM_VALUE(746), /* isNaN */ + JS_ROM_VALUE(438), /* join */ + JS_ROM_VALUE(245), /* keys */ + JS_ROM_VALUE(585), /* lastIndex */ + JS_ROM_VALUE(386), /* lastIndexOf */ + JS_ROM_VALUE(187), /* length */ + JS_ROM_VALUE(113), /* let */ + JS_ROM_VALUE(769), /* load */ + JS_ROM_VALUE(542), /* log */ + JS_ROM_VALUE(564), /* log10 */ + JS_ROM_VALUE(561), /* log2 */ + JS_ROM_VALUE(462), /* map */ + JS_ROM_VALUE(390), /* match */ + JS_ROM_VALUE(482), /* max */ + JS_ROM_VALUE(619), /* message */ + JS_ROM_VALUE(882), /* millis */ + JS_ROM_VALUE(480), /* min */ + JS_ROM_VALUE(205), /* name */ + JS_ROM_VALUE(31), /* new */ + JS_ROM_VALUE(570), /* now */ + JS_ROM_VALUE(0), /* null */ + JS_ROM_VALUE(143), /* number */ + JS_ROM_VALUE(146), /* object */ + JS_ROM_VALUE(193), /* of */ + JS_ROM_VALUE(115), /* package */ + JS_ROM_VALUE(575), /* parse */ + JS_ROM_VALUE(291), /* parseFloat */ + JS_ROM_VALUE(287), /* parseInt */ + JS_ROM_VALUE(760), /* performance */ + JS_ROM_VALUE(436), /* pop */ + JS_ROM_VALUE(544), /* pow */ + JS_ROM_VALUE(764), /* print */ + JS_ROM_VALUE(118), /* private */ + JS_ROM_VALUE(121), /* protected */ + JS_ROM_VALUE(179), /* prototype */ + JS_ROM_VALUE(125), /* public */ + JS_ROM_VALUE(433), /* push */ + JS_ROM_VALUE(546), /* random */ + JS_ROM_VALUE(467), /* reduce */ + JS_ROM_VALUE(470), /* reduceRight */ + JS_ROM_VALUE(424), /* repeat */ + JS_ROM_VALUE(393), /* replace */ + JS_ROM_VALUE(396), /* replaceAll */ + JS_ROM_VALUE(14), /* return */ + JS_ROM_VALUE(441), /* reverse */ + JS_ROM_VALUE(495), /* round */ + JS_ROM_VALUE(400), /* search */ + JS_ROM_VALUE(177), /* set */ + JS_ROM_VALUE(594), /* set lastIndex */ + JS_ROM_VALUE(358), /* set length */ + JS_ROM_VALUE(262), /* set prototype */ + JS_ROM_VALUE(237), /* setPrototypeOf */ + JS_ROM_VALUE(772), /* setTimeout */ + JS_ROM_VALUE(444), /* shift */ + JS_ROM_VALUE(484), /* sign */ + JS_ROM_VALUE(522), /* sin */ + JS_ROM_VALUE(373), /* slice */ + JS_ROM_VALUE(456), /* some */ + JS_ROM_VALUE(474), /* sort */ + JS_ROM_VALUE(599), /* source */ + JS_ROM_VALUE(447), /* splice */ + JS_ROM_VALUE(403), /* split */ + JS_ROM_VALUE(498), /* sqrt */ + JS_ROM_VALUE(626), /* stack */ + JS_ROM_VALUE(128), /* static */ + JS_ROM_VALUE(153), /* string */ + JS_ROM_VALUE(578), /* stringify */ + JS_ROM_VALUE(702), /* subarray */ + JS_ROM_VALUE(376), /* substring */ + JS_ROM_VALUE(102), /* super */ + JS_ROM_VALUE(53), /* switch */ + JS_ROM_VALUE(526), /* tan */ + JS_ROM_VALUE(190), /* target */ + JS_ROM_VALUE(616), /* test */ + JS_ROM_VALUE(19), /* this */ + JS_ROM_VALUE(62), /* throw */ + JS_ROM_VALUE(330), /* toExponential */ + JS_ROM_VALUE(335), /* toFixed */ + JS_ROM_VALUE(406), /* toLowerCase */ + JS_ROM_VALUE(338), /* toPrecision */ + JS_ROM_VALUE(136), /* toString */ + JS_ROM_VALUE(410), /* toUpperCase */ + JS_ROM_VALUE(414), /* trim */ + JS_ROM_VALUE(417), /* trimEnd */ + JS_ROM_VALUE(420), /* trimStart */ + JS_ROM_VALUE(6), /* true */ + JS_ROM_VALUE(558), /* trunc */ + JS_ROM_VALUE(65), /* try */ + JS_ROM_VALUE(28), /* typeof */ + JS_ROM_VALUE(149), /* undefined */ + JS_ROM_VALUE(450), /* unshift */ + JS_ROM_VALUE(172), /* value */ + JS_ROM_VALUE(140), /* valueOf */ + JS_ROM_VALUE(17), /* var */ + JS_ROM_VALUE(25), /* void */ + JS_ROM_VALUE(41), /* while */ + JS_ROM_VALUE(81), /* with */ + JS_ROM_VALUE(131), /* yield */ + + /* properties (offset=1230) */ + JS_VALUE_ARRAY_HEADER(24), + 6 << 1, /* n_props */ + 3 << 1, /* hash_mask */ + 18 << 1, + 9 << 1, + 21 << 1, + 15 << 1, + JS_ROM_VALUE(227) /* defineProperty */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 2), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(232) /* getPrototypeOf */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 3), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(237) /* setPrototypeOf */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 4), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(242) /* create */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 5), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(245) /* keys */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 6), + (12 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_OBJECT << 1, + (6 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=1255) */ + JS_VALUE_ARRAY_HEADER(13), + 3 << 1, /* n_props */ + 1 << 1, /* hash_mask */ + 10 << 1, + 7 << 1, + JS_ROM_VALUE(248) /* hasOwnProperty */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 7), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(136) /* toString */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 8), + (4 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_OBJECT - 1) << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1269) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1230), + 1, + JS_ROM_VALUE(1255), + JS_NULL, + + /* properties (offset=1274) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_CLOSURE << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* getset (offset=1281) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 10), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 11), + + /* getset (offset=1284) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 12), + JS_UNDEFINED, + + /* getset (offset=1287) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 13), + JS_UNDEFINED, + + /* properties (offset=1290) */ + JS_VALUE_ARRAY_HEADER(30), + 8 << 1, /* n_props */ + 3 << 1, /* hash_mask */ + 24 << 1, + 18 << 1, + 27 << 1, + 12 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_ROM_VALUE(1281), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(267) /* call */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 14), + (6 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(270) /* apply */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 15), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(273) /* bind */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 16), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(136) /* toString */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 17), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(187) /* length */, + JS_ROM_VALUE(1284), + (9 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(1287), + (15 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_CLOSURE - 1) << 1, + (21 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1321) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1274), + 9, + JS_ROM_VALUE(1290), + JS_NULL, + + /* float64 (offset=1326) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0xffffffff, + 0x7fefffff, + + /* float64 (offset=1329) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x00000001, + 0x00000000, + + /* float64 (offset=1332) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x00000000, + 0x7ff80000, + + /* float64 (offset=1335) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x00000000, + 0xfff00000, + + /* float64 (offset=1338) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x00000000, + 0x7ff00000, + + /* float64 (offset=1341) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x00000000, + 0x3cb00000, + + /* float64 (offset=1344) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0xffffffff, + 0x433fffff, + + /* float64 (offset=1347) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0xffffffff, + 0xc33fffff, + + /* properties (offset=1350) */ + JS_VALUE_ARRAY_HEADER(43), + 11 << 1, /* n_props */ + 7 << 1, /* hash_mask */ + 0 << 1, + 0 << 1, + 40 << 1, + 0 << 1, + 28 << 1, + 37 << 1, + 25 << 1, + 34 << 1, + JS_ROM_VALUE(287) /* parseInt */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 19), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(291) /* parseFloat */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 20), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(295) /* MAX_VALUE */, + JS_ROM_VALUE(1326), + (10 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(299) /* MIN_VALUE */, + JS_ROM_VALUE(1329), + (13 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(195) /* NaN */, + JS_ROM_VALUE(1332), + (19 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(303) /* NEGATIVE_INFINITY */, + JS_ROM_VALUE(1335), + (16 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(309) /* POSITIVE_INFINITY */, + JS_ROM_VALUE(1338), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(315) /* EPSILON */, + JS_ROM_VALUE(1341), + (22 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(318) /* MAX_SAFE_INTEGER */, + JS_ROM_VALUE(1344), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(324) /* MIN_SAFE_INTEGER */, + JS_ROM_VALUE(1347), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_NUMBER << 1, + (31 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=1394) */ + JS_VALUE_ARRAY_HEADER(21), + 5 << 1, /* n_props */ + 3 << 1, /* hash_mask */ + 0 << 1, + 15 << 1, + 18 << 1, + 12 << 1, + JS_ROM_VALUE(330) /* toExponential */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 21), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(335) /* toFixed */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 22), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(338) /* toPrecision */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 23), + (6 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(136) /* toString */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 24), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_NUMBER - 1) << 1, + (9 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1416) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1350), + 18, + JS_ROM_VALUE(1394), + JS_NULL, + + /* properties (offset=1421) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_BOOLEAN << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=1428) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_BOOLEAN - 1) << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1435) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1421), + 25, + JS_ROM_VALUE(1428), + JS_NULL, + + /* properties (offset=1440) */ + JS_VALUE_ARRAY_HEADER(13), + 3 << 1, /* n_props */ + 1 << 1, /* hash_mask */ + 10 << 1, + 4 << 1, + JS_ROM_VALUE(348) /* fromCharCode */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 27), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(353) /* fromCodePoint */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 28), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_STRING << 1, + (7 << 1) | (JS_PROP_SPECIAL << 30), + /* getset (offset=1454) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 29), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 30), + + /* properties (offset=1457) */ + JS_VALUE_ARRAY_HEADER(84), + 22 << 1, /* n_props */ + 15 << 1, /* hash_mask */ + 69 << 1, + 54 << 1, + 57 << 1, + 42 << 1, + 30 << 1, + 72 << 1, + 81 << 1, + 60 << 1, + 48 << 1, + 78 << 1, + 18 << 1, + 63 << 1, + 24 << 1, + 51 << 1, + 39 << 1, + 66 << 1, + JS_ROM_VALUE(187) /* length */, + JS_ROM_VALUE(1454), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(362) /* charAt */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 31), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(365) /* charCodeAt */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 32), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(369) /* codePointAt */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 33), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(373) /* slice */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 34), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(376) /* substring */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 35), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(380) /* concat */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 36), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(383) /* indexOf */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 37), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(386) /* lastIndexOf */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 38), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(390) /* match */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 39), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(393) /* replace */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 40), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(396) /* replaceAll */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 41), + (36 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(400) /* search */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 42), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(403) /* split */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 43), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(406) /* toLowerCase */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 44), + (45 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(410) /* toUpperCase */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 45), + (21 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(414) /* trim */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 46), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(417) /* trimEnd */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 47), + (27 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(420) /* trimStart */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 48), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(136) /* toString */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 49), + (33 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(424) /* repeat */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 50), + (75 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_STRING - 1) << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1542) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1440), + 26, + JS_ROM_VALUE(1457), + JS_NULL, + + /* properties (offset=1547) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(430) /* isArray */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 52), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* getset (offset=1557) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 53), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 54), + + /* properties (offset=1560) */ + JS_VALUE_ARRAY_HEADER(87), + 23 << 1, /* n_props */ + 15 << 1, /* hash_mask */ + 24 << 1, + 69 << 1, + 78 << 1, + 54 << 1, + 57 << 1, + 27 << 1, + 84 << 1, + 75 << 1, + 36 << 1, + 60 << 1, + 63 << 1, + 81 << 1, + 0 << 1, + 39 << 1, + 51 << 1, + 66 << 1, + JS_ROM_VALUE(380) /* concat */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 55), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(187) /* length */, + JS_ROM_VALUE(1557), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(433) /* push */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 56), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(436) /* pop */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 57), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(438) /* join */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 58), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(136) /* toString */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 59), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(441) /* reverse */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 60), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(444) /* shift */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 61), + (18 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(373) /* slice */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 62), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(447) /* splice */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 63), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(450) /* unshift */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 64), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(383) /* indexOf */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 65), + (45 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(386) /* lastIndexOf */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 66), + (48 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(453) /* every */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 67), + (42 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(456) /* some */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 68), + (33 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(459) /* forEach */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 69), + (21 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(462) /* map */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 70), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(464) /* filter */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 71), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(467) /* reduce */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 72), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(470) /* reduceRight */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 73), + (30 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(467) /* reduce */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 72), + (72 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(474) /* sort */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 74), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_ARRAY - 1) << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1648) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1547), + 51, + JS_ROM_VALUE(1560), + JS_NULL, + + /* float64 (offset=1653) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x8b145769, + 0x4005bf0a, + + /* float64 (offset=1656) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0xbbb55516, + 0x40026bb1, + + /* float64 (offset=1659) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0xfefa39ef, + 0x3fe62e42, + + /* float64 (offset=1662) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x652b82fe, + 0x3ff71547, + + /* float64 (offset=1665) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x1526e50e, + 0x3fdbcb7b, + + /* float64 (offset=1668) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x54442d18, + 0x400921fb, + + /* float64 (offset=1671) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x667f3bcd, + 0x3fe6a09e, + + /* float64 (offset=1674) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x667f3bcd, + 0x3ff6a09e, + + /* properties (offset=1677) */ + JS_VALUE_ARRAY_HEADER(117), + 33 << 1, /* n_props */ + 15 << 1, /* hash_mask */ + 111 << 1, + 93 << 1, + 78 << 1, + 96 << 1, + 99 << 1, + 114 << 1, + 63 << 1, + 81 << 1, + 84 << 1, + 102 << 1, + 105 << 1, + 66 << 1, + 0 << 1, + 87 << 1, + 54 << 1, + 108 << 1, + JS_ROM_VALUE(480) /* min */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 75), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(482) /* max */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 76), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(484) /* sign */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 77), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(487) /* abs */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 78), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(489) /* floor */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 79), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(492) /* ceil */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 80), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(495) /* round */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 81), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(498) /* sqrt */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 82), + (21 << 1) | (JS_PROP_NORMAL << 30), + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 69) /* E */, + JS_ROM_VALUE(1653), + (33 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(503) /* LN10 */, + JS_ROM_VALUE(1656), + (27 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(506) /* LN2 */, + JS_ROM_VALUE(1659), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(508) /* LOG2E */, + JS_ROM_VALUE(1662), + (42 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(511) /* LOG10E */, + JS_ROM_VALUE(1665), + (36 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(514) /* PI */, + JS_ROM_VALUE(1668), + (39 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(516) /* SQRT1_2 */, + JS_ROM_VALUE(1671), + (24 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(519) /* SQRT2 */, + JS_ROM_VALUE(1674), + (45 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(522) /* sin */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 83), + (48 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(524) /* cos */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 84), + (51 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(526) /* tan */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 85), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(528) /* asin */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 86), + (18 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(531) /* acos */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 87), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(534) /* atan */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 88), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(537) /* atan2 */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 89), + (30 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(540) /* exp */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 90), + (69 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(542) /* log */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 91), + (72 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(544) /* pow */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 92), + (75 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(546) /* random */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 93), + (57 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(549) /* imul */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 94), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(552) /* clz32 */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 95), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(555) /* fround */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 96), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(558) /* trunc */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 97), + (90 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(561) /* log2 */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 98), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(564) /* log10 */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 99), + (60 << 1) | (JS_PROP_NORMAL << 30), + /* class (offset=1795) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1677), + -1, + JS_NULL, + JS_NULL, + + /* properties (offset=1800) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(570) /* now */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 101), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_DATE << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=1810) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_DATE - 1) << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1817) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1800), + 100, + JS_ROM_VALUE(1810), + JS_NULL, + + /* properties (offset=1822) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(575) /* parse */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 102), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(578) /* stringify */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 103), + (3 << 1) | (JS_PROP_NORMAL << 30), + /* class (offset=1832) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1822), + -1, + JS_NULL, + JS_NULL, + + /* properties (offset=1837) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_REGEXP << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* getset (offset=1844) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 105), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 106), + + /* getset (offset=1847) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 107), + JS_UNDEFINED, + + /* getset (offset=1850) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 108), + JS_UNDEFINED, + + /* properties (offset=1853) */ + JS_VALUE_ARRAY_HEADER(24), + 6 << 1, /* n_props */ + 3 << 1, /* hash_mask */ + 15 << 1, + 18 << 1, + 21 << 1, + 12 << 1, + JS_ROM_VALUE(585) /* lastIndex */, + JS_ROM_VALUE(1844), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(599) /* source */, + JS_ROM_VALUE(1847), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(606) /* flags */, + JS_ROM_VALUE(1850), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(613) /* exec */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 109), + (6 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(616) /* test */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 110), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_REGEXP - 1) << 1, + (9 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1878) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1837), + 104, + JS_ROM_VALUE(1853), + JS_NULL, + + /* properties (offset=1883) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_ERROR << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* getset (offset=1890) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 112), + JS_UNDEFINED, + + /* getset (offset=1893) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 113), + JS_UNDEFINED, + + /* properties (offset=1896) */ + JS_VALUE_ARRAY_HEADER(21), + 5 << 1, /* n_props */ + 3 << 1, /* hash_mask */ + 9 << 1, + 6 << 1, + 18 << 1, + 15 << 1, + JS_ROM_VALUE(136) /* toString */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 114), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(208) /* Error */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(619) /* message */, + JS_ROM_VALUE(1890), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(626) /* stack */, + JS_ROM_VALUE(1893), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_ERROR - 1) << 1, + (12 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1918) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1883), + 111, + JS_ROM_VALUE(1896), + JS_NULL, + + /* properties (offset=1923) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_EVAL_ERROR << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=1930) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(633) /* EvalError */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_EVAL_ERROR - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1940) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1923), + 115, + JS_ROM_VALUE(1930), + JS_ROM_VALUE(1918), + + /* properties (offset=1945) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_RANGE_ERROR << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=1952) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(637) /* RangeError */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_RANGE_ERROR - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1962) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1945), + 116, + JS_ROM_VALUE(1952), + JS_ROM_VALUE(1918), + + /* properties (offset=1967) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_REFERENCE_ERROR << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=1974) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(641) /* ReferenceError */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_REFERENCE_ERROR - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=1984) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1967), + 117, + JS_ROM_VALUE(1974), + JS_ROM_VALUE(1918), + + /* properties (offset=1989) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_SYNTAX_ERROR << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=1996) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(646) /* SyntaxError */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_SYNTAX_ERROR - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2006) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(1989), + 118, + JS_ROM_VALUE(1996), + JS_ROM_VALUE(1918), + + /* properties (offset=2011) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_TYPE_ERROR << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2018) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(650) /* TypeError */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_TYPE_ERROR - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2028) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2011), + 119, + JS_ROM_VALUE(2018), + JS_ROM_VALUE(1918), + + /* properties (offset=2033) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_URI_ERROR << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2040) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(654) /* URIError */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_URI_ERROR - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2050) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2033), + 120, + JS_ROM_VALUE(2040), + JS_ROM_VALUE(1918), + + /* properties (offset=2055) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_INTERNAL_ERROR << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2062) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(205) /* name */, + JS_ROM_VALUE(658) /* InternalError */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_INTERNAL_ERROR - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2072) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2055), + 121, + JS_ROM_VALUE(2062), + JS_ROM_VALUE(1918), + + /* properties (offset=2077) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_ARRAY_BUFFER << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* getset (offset=2084) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 123), + JS_UNDEFINED, + + /* properties (offset=2087) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(667) /* byteLength */, + JS_ROM_VALUE(2084), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_ARRAY_BUFFER - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2097) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2077), + 122, + JS_ROM_VALUE(2087), + JS_NULL, + + /* properties (offset=2102) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_TYPED_ARRAY << 1, + (0 << 1) | (JS_PROP_SPECIAL << 30), + /* getset (offset=2109) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 125), + JS_UNDEFINED, + + /* getset (offset=2112) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 126), + JS_UNDEFINED, + + /* getset (offset=2115) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 127), + JS_UNDEFINED, + + /* getset (offset=2118) */ + JS_VALUE_ARRAY_HEADER(2), + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 128), + JS_UNDEFINED, + + /* properties (offset=2121) */ + JS_VALUE_ARRAY_HEADER(37), + 9 << 1, /* n_props */ + 7 << 1, /* hash_mask */ + 31 << 1, + 25 << 1, + 13 << 1, + 0 << 1, + 0 << 1, + 0 << 1, + 34 << 1, + 28 << 1, + JS_ROM_VALUE(187) /* length */, + JS_ROM_VALUE(2109), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(667) /* byteLength */, + JS_ROM_VALUE(2112), + (10 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(686) /* byteOffset */, + JS_ROM_VALUE(2115), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(695) /* buffer */, + JS_ROM_VALUE(2118), + (0 << 1) | (JS_PROP_GETSET << 30), + JS_ROM_VALUE(438) /* join */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 58), + (16 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(136) /* toString */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 59), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(702) /* subarray */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 129), + (22 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(177) /* set */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 130), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_TYPED_ARRAY - 1) << 1, + (19 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2159) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2102), + 124, + JS_ROM_VALUE(2121), + JS_NULL, + + /* properties (offset=2164) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 1 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_UINT8C_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2174) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 1 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_UINT8C_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2184) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2164), + 131, + JS_ROM_VALUE(2174), + JS_ROM_VALUE(2159), + + /* properties (offset=2189) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 1 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_INT8_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2199) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 1 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_INT8_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2209) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2189), + 132, + JS_ROM_VALUE(2199), + JS_ROM_VALUE(2159), + + /* properties (offset=2214) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 1 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_UINT8_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2224) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 1 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_UINT8_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2234) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2214), + 133, + JS_ROM_VALUE(2224), + JS_ROM_VALUE(2159), + + /* properties (offset=2239) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 2 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_INT16_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2249) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 2 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_INT16_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2259) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2239), + 134, + JS_ROM_VALUE(2249), + JS_ROM_VALUE(2159), + + /* properties (offset=2264) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 2 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_UINT16_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2274) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 2 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_UINT16_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2284) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2264), + 135, + JS_ROM_VALUE(2274), + JS_ROM_VALUE(2159), + + /* properties (offset=2289) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 4 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_INT32_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2299) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 4 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_INT32_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2309) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2289), + 136, + JS_ROM_VALUE(2299), + JS_ROM_VALUE(2159), + + /* properties (offset=2314) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 4 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_UINT32_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2324) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 4 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_UINT32_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2334) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2314), + 137, + JS_ROM_VALUE(2324), + JS_ROM_VALUE(2159), + + /* properties (offset=2339) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 4 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_FLOAT32_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2349) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 4 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_FLOAT32_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2359) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2339), + 138, + JS_ROM_VALUE(2349), + JS_ROM_VALUE(2159), + + /* properties (offset=2364) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 8 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(179) /* prototype */, + JS_CLASS_FLOAT64_ARRAY << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* properties (offset=2374) */ + JS_VALUE_ARRAY_HEADER(9), + 2 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 6 << 1, + JS_ROM_VALUE(706) /* BYTES_PER_ELEMENT */, + 8 << 1, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(183) /* constructor */, + (uint32_t)(-JS_CLASS_FLOAT64_ARRAY - 1) << 1, + (3 << 1) | (JS_PROP_SPECIAL << 30), + /* class (offset=2384) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2364), + 139, + JS_ROM_VALUE(2374), + JS_ROM_VALUE(2159), + + /* float64 (offset=2389) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x00000000, + 0x7ff00000, + + /* float64 (offset=2392) */ + JS_MB_HEADER_DEF(JS_MTAG_FLOAT64), + 0x00000000, + 0x7ff80000, + + /* properties (offset=2395) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(542) /* log */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 140), + (0 << 1) | (JS_PROP_NORMAL << 30), + /* class (offset=2402) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2395), + -1, + JS_NULL, + JS_NULL, + + /* properties (offset=2407) */ + JS_VALUE_ARRAY_HEADER(6), + 1 << 1, /* n_props */ + 0 << 1, /* hash_mask */ + 3 << 1, + JS_ROM_VALUE(570) /* now */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 141), + (0 << 1) | (JS_PROP_NORMAL << 30), + /* class (offset=2414) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2407), + -1, + JS_NULL, + JS_NULL, + + /* properties (offset=2419) */ + JS_VALUE_ARRAY_HEADER(111), + 31 << 1, /* n_props */ + 15 << 1, /* hash_mask */ + 108 << 1, + 0 << 1, + 57 << 1, + 99 << 1, + 90 << 1, + 78 << 1, + 60 << 1, + 21 << 1, + 93 << 1, + 102 << 1, + 45 << 1, + 72 << 1, + 96 << 1, + 105 << 1, + 75 << 1, + 0 << 1, + JS_ROM_VALUE(783) /* FAST_REFRESH */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 70) /* F */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(790) /* HALF_REFRESH */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 72) /* H */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(797) /* FULL_REFRESH */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 65) /* A */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(804) /* FONT_UI_10 */, + JS_ROM_VALUE(808) /* UI10 */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(811) /* FONT_UI_12 */, + JS_ROM_VALUE(815) /* UI12 */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(818) /* FONT_SMALL */, + JS_ROM_VALUE(822) /* SM */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(824) /* TEXT_REGULAR */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 82) /* R */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(831) /* TEXT_BOLD */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 66) /* B */, + (18 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(837) /* TEXT_ITALIC */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 73) /* I */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(843) /* TEXT_BOLD_ITALIC */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 74) /* J */, + (30 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(851) /* BTN_BACK */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 66) /* B */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(855) /* BTN_CONFIRM */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 67) /* C */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(861) /* BTN_LEFT */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 76) /* L */, + (24 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(867) /* BTN_RIGHT */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 82) /* R */, + (48 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(871) /* BTN_UP */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 85) /* U */, + (51 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(876) /* BTN_DOWN */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, 68) /* D */, + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(882) /* millis */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 142), + (33 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(885) /* btnIsPressed */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 143), + (42 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(890) /* getScreenWidth */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 144), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(895) /* getScreenHeight */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 145), + (39 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(900) /* clearScreen */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 146), + (27 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(904) /* displayBuffer */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 147), + (36 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(909) /* drawLine */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 148), + (54 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(913) /* drawRect */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 149), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(917) /* fillRect */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 150), + (69 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(921) /* drawImage */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 151), + (0 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(925) /* getTextWidth */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 152), + (84 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(930) /* drawCenteredText */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 153), + (66 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(936) /* drawText */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 154), + (81 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(940) /* drawButtonHints */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 155), + (63 << 1) | (JS_PROP_NORMAL << 30), + JS_ROM_VALUE(945) /* drawSideButtonHints */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 156), + (87 << 1) | (JS_PROP_NORMAL << 30), + /* class (offset=2531) */ + JS_MB_HEADER_DEF(JS_MTAG_OBJECT), + JS_ROM_VALUE(2419), + -1, + JS_NULL, + JS_NULL, + + /* global object properties (offset=2536) */ + JS_VALUE_ARRAY_HEADER(90), + JS_ROM_VALUE(224) /* Object */, + JS_ROM_VALUE(1269), + JS_ROM_VALUE(253) /* Function */, + JS_ROM_VALUE(1321), + JS_ROM_VALUE(284) /* Number */, + JS_ROM_VALUE(1416), + JS_ROM_VALUE(342) /* Boolean */, + JS_ROM_VALUE(1435), + JS_ROM_VALUE(345) /* String */, + JS_ROM_VALUE(1542), + JS_ROM_VALUE(427) /* Array */, + JS_ROM_VALUE(1648), + JS_ROM_VALUE(477) /* Math */, + JS_ROM_VALUE(1795), + JS_ROM_VALUE(567) /* Date */, + JS_ROM_VALUE(1817), + JS_ROM_VALUE(572) /* JSON */, + JS_ROM_VALUE(1832), + JS_ROM_VALUE(582) /* RegExp */, + JS_ROM_VALUE(1878), + JS_ROM_VALUE(208) /* Error */, + JS_ROM_VALUE(1918), + JS_ROM_VALUE(633) /* EvalError */, + JS_ROM_VALUE(1940), + JS_ROM_VALUE(637) /* RangeError */, + JS_ROM_VALUE(1962), + JS_ROM_VALUE(641) /* ReferenceError */, + JS_ROM_VALUE(1984), + JS_ROM_VALUE(646) /* SyntaxError */, + JS_ROM_VALUE(2006), + JS_ROM_VALUE(650) /* TypeError */, + JS_ROM_VALUE(2028), + JS_ROM_VALUE(654) /* URIError */, + JS_ROM_VALUE(2050), + JS_ROM_VALUE(658) /* InternalError */, + JS_ROM_VALUE(2072), + JS_ROM_VALUE(663) /* ArrayBuffer */, + JS_ROM_VALUE(2097), + JS_ROM_VALUE(676) /* Uint8ClampedArray */, + JS_ROM_VALUE(2184), + JS_ROM_VALUE(712) /* Int8Array */, + JS_ROM_VALUE(2209), + JS_ROM_VALUE(716) /* Uint8Array */, + JS_ROM_VALUE(2234), + JS_ROM_VALUE(720) /* Int16Array */, + JS_ROM_VALUE(2259), + JS_ROM_VALUE(724) /* Uint16Array */, + JS_ROM_VALUE(2284), + JS_ROM_VALUE(728) /* Int32Array */, + JS_ROM_VALUE(2309), + JS_ROM_VALUE(732) /* Uint32Array */, + JS_ROM_VALUE(2334), + JS_ROM_VALUE(736) /* Float32Array */, + JS_ROM_VALUE(2359), + JS_ROM_VALUE(741) /* Float64Array */, + JS_ROM_VALUE(2384), + JS_ROM_VALUE(287) /* parseInt */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 19), + JS_ROM_VALUE(291) /* parseFloat */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 20), + JS_ROM_VALUE(165) /* eval */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 157), + JS_ROM_VALUE(746) /* isNaN */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 158), + JS_ROM_VALUE(749) /* isFinite */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 159), + JS_ROM_VALUE(197) /* Infinity */, + JS_ROM_VALUE(2389), + JS_ROM_VALUE(195) /* NaN */, + JS_ROM_VALUE(2392), + JS_ROM_VALUE(149) /* undefined */, + JS_UNDEFINED, + JS_ROM_VALUE(753) /* globalThis */, + JS_NULL, + JS_ROM_VALUE(757) /* console */, + JS_ROM_VALUE(2402), + JS_ROM_VALUE(760) /* performance */, + JS_ROM_VALUE(2414), + JS_ROM_VALUE(764) /* print */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 160), + JS_ROM_VALUE(767) /* gc */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 161), + JS_ROM_VALUE(769) /* load */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 162), + JS_ROM_VALUE(772) /* setTimeout */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 163), + JS_ROM_VALUE(776) /* clearTimeout */, + JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, 164), + JS_ROM_VALUE(781) /* CP */, + JS_ROM_VALUE(2531), +}; + +static const JSCFunctionDef js_c_function_table[] = { + { { .generic_params = js_function_bound }, + JS_ROM_VALUE(221) /* bound */, + JS_CFUNC_generic_params, 0, 0 }, + { { .constructor = js_object_constructor }, + JS_ROM_VALUE(224) /* Object */, + JS_CFUNC_constructor, 1, JS_CLASS_OBJECT }, + { { .generic = js_object_defineProperty }, + JS_ROM_VALUE(227) /* defineProperty */, + JS_CFUNC_generic, 3, 0 }, + { { .generic = js_object_getPrototypeOf }, + JS_ROM_VALUE(232) /* getPrototypeOf */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_object_setPrototypeOf }, + JS_ROM_VALUE(237) /* setPrototypeOf */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_object_create }, + JS_ROM_VALUE(242) /* create */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_object_keys }, + JS_ROM_VALUE(245) /* keys */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_object_hasOwnProperty }, + JS_ROM_VALUE(248) /* hasOwnProperty */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_object_toString }, + JS_ROM_VALUE(136) /* toString */, + JS_CFUNC_generic, 0, 0 }, + { { .constructor = js_function_constructor }, + JS_ROM_VALUE(253) /* Function */, + JS_CFUNC_constructor, 1, JS_CLASS_CLOSURE }, + { { .generic = js_function_get_prototype }, + JS_ROM_VALUE(257) /* get prototype */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_function_set_prototype }, + JS_ROM_VALUE(262) /* set prototype */, + JS_CFUNC_generic, 1, 0 }, + { { .generic_magic = js_function_get_length_name }, + JS_ROM_VALUE(276) /* get length */, + JS_CFUNC_generic_magic, 0, 0 }, + { { .generic_magic = js_function_get_length_name }, + JS_ROM_VALUE(280) /* get name */, + JS_CFUNC_generic_magic, 0, 1 }, + { { .generic = js_function_call }, + JS_ROM_VALUE(267) /* call */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_function_apply }, + JS_ROM_VALUE(270) /* apply */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_function_bind }, + JS_ROM_VALUE(273) /* bind */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_function_toString }, + JS_ROM_VALUE(136) /* toString */, + JS_CFUNC_generic, 0, 0 }, + { { .constructor = js_number_constructor }, + JS_ROM_VALUE(284) /* Number */, + JS_CFUNC_constructor, 1, JS_CLASS_NUMBER }, + { { .generic = js_number_parseInt }, + JS_ROM_VALUE(287) /* parseInt */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_number_parseFloat }, + JS_ROM_VALUE(291) /* parseFloat */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_number_toExponential }, + JS_ROM_VALUE(330) /* toExponential */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_number_toFixed }, + JS_ROM_VALUE(335) /* toFixed */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_number_toPrecision }, + JS_ROM_VALUE(338) /* toPrecision */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_number_toString }, + JS_ROM_VALUE(136) /* toString */, + JS_CFUNC_generic, 1, 0 }, + { { .constructor = js_boolean_constructor }, + JS_ROM_VALUE(342) /* Boolean */, + JS_CFUNC_constructor, 1, JS_CLASS_BOOLEAN }, + { { .constructor = js_string_constructor }, + JS_ROM_VALUE(345) /* String */, + JS_CFUNC_constructor, 1, JS_CLASS_STRING }, + { { .generic_magic = js_string_fromCharCode }, + JS_ROM_VALUE(348) /* fromCharCode */, + JS_CFUNC_generic_magic, 1, 0 }, + { { .generic_magic = js_string_fromCharCode }, + JS_ROM_VALUE(353) /* fromCodePoint */, + JS_CFUNC_generic_magic, 1, 1 }, + { { .generic = js_string_get_length }, + JS_ROM_VALUE(276) /* get length */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_string_set_length }, + JS_ROM_VALUE(358) /* set length */, + JS_CFUNC_generic, 1, 0 }, + { { .generic_magic = js_string_charAt }, + JS_ROM_VALUE(362) /* charAt */, + JS_CFUNC_generic_magic, 1, magic_charAt }, + { { .generic_magic = js_string_charAt }, + JS_ROM_VALUE(365) /* charCodeAt */, + JS_CFUNC_generic_magic, 1, magic_charCodeAt }, + { { .generic_magic = js_string_charAt }, + JS_ROM_VALUE(369) /* codePointAt */, + JS_CFUNC_generic_magic, 1, magic_codePointAt }, + { { .generic = js_string_slice }, + JS_ROM_VALUE(373) /* slice */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_string_substring }, + JS_ROM_VALUE(376) /* substring */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_string_concat }, + JS_ROM_VALUE(380) /* concat */, + JS_CFUNC_generic, 1, 0 }, + { { .generic_magic = js_string_indexOf }, + JS_ROM_VALUE(383) /* indexOf */, + JS_CFUNC_generic_magic, 1, 0 }, + { { .generic_magic = js_string_indexOf }, + JS_ROM_VALUE(386) /* lastIndexOf */, + JS_CFUNC_generic_magic, 1, 1 }, + { { .generic = js_string_match }, + JS_ROM_VALUE(390) /* match */, + JS_CFUNC_generic, 1, 0 }, + { { .generic_magic = js_string_replace }, + JS_ROM_VALUE(393) /* replace */, + JS_CFUNC_generic_magic, 2, 0 }, + { { .generic_magic = js_string_replace }, + JS_ROM_VALUE(396) /* replaceAll */, + JS_CFUNC_generic_magic, 2, 1 }, + { { .generic = js_string_search }, + JS_ROM_VALUE(400) /* search */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_string_split }, + JS_ROM_VALUE(403) /* split */, + JS_CFUNC_generic, 2, 0 }, + { { .generic_magic = js_string_toLowerCase }, + JS_ROM_VALUE(406) /* toLowerCase */, + JS_CFUNC_generic_magic, 0, 1 }, + { { .generic_magic = js_string_toLowerCase }, + JS_ROM_VALUE(410) /* toUpperCase */, + JS_CFUNC_generic_magic, 0, 0 }, + { { .generic_magic = js_string_trim }, + JS_ROM_VALUE(414) /* trim */, + JS_CFUNC_generic_magic, 0, 3 }, + { { .generic_magic = js_string_trim }, + JS_ROM_VALUE(417) /* trimEnd */, + JS_CFUNC_generic_magic, 0, 2 }, + { { .generic_magic = js_string_trim }, + JS_ROM_VALUE(420) /* trimStart */, + JS_CFUNC_generic_magic, 0, 1 }, + { { .generic = js_string_toString }, + JS_ROM_VALUE(136) /* toString */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_string_repeat }, + JS_ROM_VALUE(424) /* repeat */, + JS_CFUNC_generic, 1, 0 }, + { { .constructor = js_array_constructor }, + JS_ROM_VALUE(427) /* Array */, + JS_CFUNC_constructor, 1, JS_CLASS_ARRAY }, + { { .generic = js_array_isArray }, + JS_ROM_VALUE(430) /* isArray */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_array_get_length }, + JS_ROM_VALUE(276) /* get length */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_array_set_length }, + JS_ROM_VALUE(358) /* set length */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_array_concat }, + JS_ROM_VALUE(380) /* concat */, + JS_CFUNC_generic, 1, 0 }, + { { .generic_magic = js_array_push }, + JS_ROM_VALUE(433) /* push */, + JS_CFUNC_generic_magic, 1, 0 }, + { { .generic = js_array_pop }, + JS_ROM_VALUE(436) /* pop */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_array_join }, + JS_ROM_VALUE(438) /* join */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_array_toString }, + JS_ROM_VALUE(136) /* toString */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_array_reverse }, + JS_ROM_VALUE(441) /* reverse */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_array_shift }, + JS_ROM_VALUE(444) /* shift */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_array_slice }, + JS_ROM_VALUE(373) /* slice */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_array_splice }, + JS_ROM_VALUE(447) /* splice */, + JS_CFUNC_generic, 2, 0 }, + { { .generic_magic = js_array_push }, + JS_ROM_VALUE(450) /* unshift */, + JS_CFUNC_generic_magic, 1, 1 }, + { { .generic_magic = js_array_indexOf }, + JS_ROM_VALUE(383) /* indexOf */, + JS_CFUNC_generic_magic, 1, 0 }, + { { .generic_magic = js_array_indexOf }, + JS_ROM_VALUE(386) /* lastIndexOf */, + JS_CFUNC_generic_magic, 1, 1 }, + { { .generic_magic = js_array_every }, + JS_ROM_VALUE(453) /* every */, + JS_CFUNC_generic_magic, 1, js_special_every }, + { { .generic_magic = js_array_every }, + JS_ROM_VALUE(456) /* some */, + JS_CFUNC_generic_magic, 1, js_special_some }, + { { .generic_magic = js_array_every }, + JS_ROM_VALUE(459) /* forEach */, + JS_CFUNC_generic_magic, 1, js_special_forEach }, + { { .generic_magic = js_array_every }, + JS_ROM_VALUE(462) /* map */, + JS_CFUNC_generic_magic, 1, js_special_map }, + { { .generic_magic = js_array_every }, + JS_ROM_VALUE(464) /* filter */, + JS_CFUNC_generic_magic, 1, js_special_filter }, + { { .generic_magic = js_array_reduce }, + JS_ROM_VALUE(467) /* reduce */, + JS_CFUNC_generic_magic, 1, js_special_reduce }, + { { .generic_magic = js_array_reduce }, + JS_ROM_VALUE(470) /* reduceRight */, + JS_CFUNC_generic_magic, 1, js_special_reduceRight }, + { { .generic = js_array_sort }, + JS_ROM_VALUE(474) /* sort */, + JS_CFUNC_generic, 1, 0 }, + { { .generic_magic = js_math_min_max }, + JS_ROM_VALUE(480) /* min */, + JS_CFUNC_generic_magic, 2, 0 }, + { { .generic_magic = js_math_min_max }, + JS_ROM_VALUE(482) /* max */, + JS_CFUNC_generic_magic, 2, 1 }, + { { .f_f = js_math_sign }, + JS_ROM_VALUE(484) /* sign */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_fabs }, + JS_ROM_VALUE(487) /* abs */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_floor }, + JS_ROM_VALUE(489) /* floor */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_ceil }, + JS_ROM_VALUE(492) /* ceil */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_round_inf }, + JS_ROM_VALUE(495) /* round */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_sqrt }, + JS_ROM_VALUE(498) /* sqrt */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_sin }, + JS_ROM_VALUE(522) /* sin */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_cos }, + JS_ROM_VALUE(524) /* cos */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_tan }, + JS_ROM_VALUE(526) /* tan */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_asin }, + JS_ROM_VALUE(528) /* asin */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_acos }, + JS_ROM_VALUE(531) /* acos */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_atan }, + JS_ROM_VALUE(534) /* atan */, + JS_CFUNC_f_f, 1, 0 }, + { { .generic = js_math_atan2 }, + JS_ROM_VALUE(537) /* atan2 */, + JS_CFUNC_generic, 2, 0 }, + { { .f_f = js_exp }, + JS_ROM_VALUE(540) /* exp */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_log }, + JS_ROM_VALUE(542) /* log */, + JS_CFUNC_f_f, 1, 0 }, + { { .generic = js_math_pow }, + JS_ROM_VALUE(544) /* pow */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_math_random }, + JS_ROM_VALUE(546) /* random */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_math_imul }, + JS_ROM_VALUE(549) /* imul */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_math_clz32 }, + JS_ROM_VALUE(552) /* clz32 */, + JS_CFUNC_generic, 1, 0 }, + { { .f_f = js_math_fround }, + JS_ROM_VALUE(555) /* fround */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_trunc }, + JS_ROM_VALUE(558) /* trunc */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_log2 }, + JS_ROM_VALUE(561) /* log2 */, + JS_CFUNC_f_f, 1, 0 }, + { { .f_f = js_log10 }, + JS_ROM_VALUE(564) /* log10 */, + JS_CFUNC_f_f, 1, 0 }, + { { .constructor = js_date_constructor }, + JS_ROM_VALUE(567) /* Date */, + JS_CFUNC_constructor, 7, JS_CLASS_DATE }, + { { .generic = js_date_now }, + JS_ROM_VALUE(570) /* now */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_json_parse }, + JS_ROM_VALUE(575) /* parse */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_json_stringify }, + JS_ROM_VALUE(578) /* stringify */, + JS_CFUNC_generic, 3, 0 }, + { { .constructor = js_regexp_constructor }, + JS_ROM_VALUE(582) /* RegExp */, + JS_CFUNC_constructor, 2, JS_CLASS_REGEXP }, + { { .generic = js_regexp_get_lastIndex }, + JS_ROM_VALUE(589) /* get lastIndex */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_regexp_set_lastIndex }, + JS_ROM_VALUE(594) /* set lastIndex */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_regexp_get_source }, + JS_ROM_VALUE(602) /* get source */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_regexp_get_flags }, + JS_ROM_VALUE(609) /* get flags */, + JS_CFUNC_generic, 0, 0 }, + { { .generic_magic = js_regexp_exec }, + JS_ROM_VALUE(613) /* exec */, + JS_CFUNC_generic_magic, 1, 0 }, + { { .generic_magic = js_regexp_exec }, + JS_ROM_VALUE(616) /* test */, + JS_CFUNC_generic_magic, 1, 1 }, + { { .constructor_magic = js_error_constructor }, + JS_ROM_VALUE(208) /* Error */, + JS_CFUNC_constructor_magic, 1, JS_CLASS_ERROR }, + { { .generic_magic = js_error_get_message }, + JS_ROM_VALUE(622) /* get message */, + JS_CFUNC_generic_magic, 0, 0 }, + { { .generic_magic = js_error_get_message }, + JS_ROM_VALUE(629) /* get stack */, + JS_CFUNC_generic_magic, 0, 1 }, + { { .generic = js_error_toString }, + JS_ROM_VALUE(136) /* toString */, + JS_CFUNC_generic, 0, 0 }, + { { .constructor_magic = js_error_constructor }, + JS_ROM_VALUE(633) /* EvalError */, + JS_CFUNC_constructor_magic, 1, JS_CLASS_EVAL_ERROR }, + { { .constructor_magic = js_error_constructor }, + JS_ROM_VALUE(637) /* RangeError */, + JS_CFUNC_constructor_magic, 1, JS_CLASS_RANGE_ERROR }, + { { .constructor_magic = js_error_constructor }, + JS_ROM_VALUE(641) /* ReferenceError */, + JS_CFUNC_constructor_magic, 1, JS_CLASS_REFERENCE_ERROR }, + { { .constructor_magic = js_error_constructor }, + JS_ROM_VALUE(646) /* SyntaxError */, + JS_CFUNC_constructor_magic, 1, JS_CLASS_SYNTAX_ERROR }, + { { .constructor_magic = js_error_constructor }, + JS_ROM_VALUE(650) /* TypeError */, + JS_CFUNC_constructor_magic, 1, JS_CLASS_TYPE_ERROR }, + { { .constructor_magic = js_error_constructor }, + JS_ROM_VALUE(654) /* URIError */, + JS_CFUNC_constructor_magic, 1, JS_CLASS_URI_ERROR }, + { { .constructor_magic = js_error_constructor }, + JS_ROM_VALUE(658) /* InternalError */, + JS_CFUNC_constructor_magic, 1, JS_CLASS_INTERNAL_ERROR }, + { { .constructor = js_array_buffer_constructor }, + JS_ROM_VALUE(663) /* ArrayBuffer */, + JS_CFUNC_constructor, 1, JS_CLASS_ARRAY_BUFFER }, + { { .generic = js_array_buffer_get_byteLength }, + JS_ROM_VALUE(671) /* get byteLength */, + JS_CFUNC_generic, 0, 0 }, + { { .constructor = js_typed_array_base_constructor }, + JS_ROM_VALUE(682) /* TypedArray */, + JS_CFUNC_constructor, 0, JS_CLASS_TYPED_ARRAY }, + { { .generic_magic = js_typed_array_get_length }, + JS_ROM_VALUE(276) /* get length */, + JS_CFUNC_generic_magic, 0, 0 }, + { { .generic_magic = js_typed_array_get_length }, + JS_ROM_VALUE(671) /* get byteLength */, + JS_CFUNC_generic_magic, 0, 1 }, + { { .generic_magic = js_typed_array_get_length }, + JS_ROM_VALUE(690) /* get byteOffset */, + JS_CFUNC_generic_magic, 0, 2 }, + { { .generic_magic = js_typed_array_get_length }, + JS_ROM_VALUE(698) /* get buffer */, + JS_CFUNC_generic_magic, 0, 3 }, + { { .generic = js_typed_array_subarray }, + JS_ROM_VALUE(702) /* subarray */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_typed_array_set }, + JS_ROM_VALUE(177) /* set */, + JS_CFUNC_generic, 1, 0 }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(676) /* Uint8ClampedArray */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_UINT8C_ARRAY }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(712) /* Int8Array */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_INT8_ARRAY }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(716) /* Uint8Array */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_UINT8_ARRAY }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(720) /* Int16Array */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_INT16_ARRAY }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(724) /* Uint16Array */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_UINT16_ARRAY }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(728) /* Int32Array */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_INT32_ARRAY }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(732) /* Uint32Array */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_UINT32_ARRAY }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(736) /* Float32Array */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_FLOAT32_ARRAY }, + { { .constructor_magic = js_typed_array_constructor }, + JS_ROM_VALUE(741) /* Float64Array */, + JS_CFUNC_constructor_magic, 3, JS_CLASS_FLOAT64_ARRAY }, + { { .generic = js_print }, + JS_ROM_VALUE(542) /* log */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_performance_now }, + JS_ROM_VALUE(570) /* now */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_millis }, + JS_ROM_VALUE(882) /* millis */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_btnIsPressed }, + JS_ROM_VALUE(885) /* btnIsPressed */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_getScreenWidth }, + JS_ROM_VALUE(890) /* getScreenWidth */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_getScreenHeight }, + JS_ROM_VALUE(895) /* getScreenHeight */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_clearScreen }, + JS_ROM_VALUE(900) /* clearScreen */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_displayBuffer }, + JS_ROM_VALUE(904) /* displayBuffer */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_drawLine }, + JS_ROM_VALUE(909) /* drawLine */, + JS_CFUNC_generic, 5, 0 }, + { { .generic = js_drawRect }, + JS_ROM_VALUE(913) /* drawRect */, + JS_CFUNC_generic, 5, 0 }, + { { .generic = js_fillRect }, + JS_ROM_VALUE(917) /* fillRect */, + JS_CFUNC_generic, 5, 0 }, + { { .generic = js_drawImage }, + JS_ROM_VALUE(921) /* drawImage */, + JS_CFUNC_generic, 5, 0 }, + { { .generic = js_getTextWidth }, + JS_ROM_VALUE(925) /* getTextWidth */, + JS_CFUNC_generic, 3, 0 }, + { { .generic = js_drawCenteredText }, + JS_ROM_VALUE(930) /* drawCenteredText */, + JS_CFUNC_generic, 5, 0 }, + { { .generic = js_drawText }, + JS_ROM_VALUE(936) /* drawText */, + JS_CFUNC_generic, 6, 0 }, + { { .generic = js_drawButtonHints }, + JS_ROM_VALUE(940) /* drawButtonHints */, + JS_CFUNC_generic, 5, 0 }, + { { .generic = js_drawSideButtonHints }, + JS_ROM_VALUE(945) /* drawSideButtonHints */, + JS_CFUNC_generic, 3, 0 }, + { { .generic = js_global_eval }, + JS_ROM_VALUE(165) /* eval */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_global_isNaN }, + JS_ROM_VALUE(746) /* isNaN */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_global_isFinite }, + JS_ROM_VALUE(749) /* isFinite */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_print }, + JS_ROM_VALUE(764) /* print */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_gc }, + JS_ROM_VALUE(767) /* gc */, + JS_CFUNC_generic, 0, 0 }, + { { .generic = js_load }, + JS_ROM_VALUE(769) /* load */, + JS_CFUNC_generic, 1, 0 }, + { { .generic = js_setTimeout }, + JS_ROM_VALUE(772) /* setTimeout */, + JS_CFUNC_generic, 2, 0 }, + { { .generic = js_clearTimeout }, + JS_ROM_VALUE(776) /* clearTimeout */, + JS_CFUNC_generic, 1, 0 }, +}; + +#ifndef JS_CLASS_COUNT +#define JS_CLASS_COUNT JS_CLASS_USER /* total number of classes */ +#endif + +static const JSCFinalizer js_c_finalizer_table[JS_CLASS_COUNT - JS_CLASS_USER] = { +}; + +const JSSTDLibraryDef js_stdlib = { + js_stdlib_table, + js_c_function_table, + js_c_finalizer_table, + 2627, + 64, + 951, + 2536, + JS_CLASS_COUNT, +}; + diff --git a/lib/mquickjs/cutils.c b/lib/mquickjs/cutils.c new file mode 100644 index 00000000..57a71bde --- /dev/null +++ b/lib/mquickjs/cutils.c @@ -0,0 +1,178 @@ +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include + +#include "cutils.h" + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +int has_suffix(const char *str, const char *suffix) +{ + size_t len = strlen(str); + size_t slen = strlen(suffix); + return (len >= slen && !memcmp(str + len - slen, suffix, slen)); +} + +size_t __unicode_to_utf8(uint8_t *buf, unsigned int c) +{ + uint8_t *q = buf; + + if (c < 0x800) { + *q++ = (c >> 6) | 0xc0; + } else { + if (c < 0x10000) { + *q++ = (c >> 12) | 0xe0; + } else { + if (c < 0x00200000) { + *q++ = (c >> 18) | 0xf0; + } else { + return 0; + } + *q++ = ((c >> 12) & 0x3f) | 0x80; + } + *q++ = ((c >> 6) & 0x3f) | 0x80; + } + *q++ = (c & 0x3f) | 0x80; + return q - buf; +} + +int __unicode_from_utf8(const uint8_t *p, size_t max_len, size_t *plen) +{ + size_t len = 1; + int c; + + c = p[0]; + if (c < 0xc0) { + goto fail; + } else if (c < 0xe0) { + if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80)) + goto fail; + c = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f); + len = 2; + if (unlikely(c < 0x80)) + goto fail; + } else if (c < 0xf0) { + if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80)) + goto fail; + if (unlikely(max_len < 3 || (p[2] & 0xc0) != 0x80)) { + len = 2; + goto fail; + } + c = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); + len = 3; + if (unlikely(c < 0x800)) + goto fail; + } else if (c < 0xf8) { + if (unlikely(max_len < 2 || (p[1] & 0xc0) != 0x80)) + goto fail; + if (unlikely(max_len < 3 || (p[2] & 0xc0) != 0x80)) { + len = 2; + goto fail; + } + if (unlikely(max_len < 4 || (p[3] & 0xc0) != 0x80)) { + len = 3; + goto fail; + } + c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f); + len = 4; + /* We explicitly accept surrogate pairs */ + if (unlikely(c < 0x10000 || c > 0x10ffff)) + goto fail; + } else { + fail: + *plen = len; + return -1; + } + *plen = len; + return c; +} + +int __utf8_get(const uint8_t *p, size_t *plen) +{ + size_t len; + int c; + + c = p[0]; + if (c < 0xc0) { + len = 1; + } else if (c < 0xe0) { + c = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f); + len = 2; + } else if (c < 0xf0) { + c = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); + len = 3; + } else if (c < 0xf8) { + c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f); + len = 4; + } else { + len = 1; + } + *plen = len; + return c; +} diff --git a/lib/mquickjs/cutils.h b/lib/mquickjs/cutils.h new file mode 100644 index 00000000..c0dd0011 --- /dev/null +++ b/lib/mquickjs/cutils.h @@ -0,0 +1,355 @@ +/* + * 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 + +/* return the pointer of type 'type *' containing 'ptr' as field 'member' */ +#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) + +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; +} + +static inline size_t max_size_t(size_t a, size_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline size_t min_size_t(size_t a, size_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)); +} + +static inline uint32_t get_be32(const uint8_t *d) +{ + return bswap32(get_u32(d)); +} + +static inline void put_be32(uint8_t *d, uint32_t v) +{ + put_u32(d, bswap32(v)); +} + +#define UTF8_CHAR_LEN_MAX 4 + +size_t __unicode_to_utf8(uint8_t *buf, unsigned int c); +int __unicode_from_utf8(const uint8_t *p, size_t max_len, size_t *plen); +int __utf8_get(const uint8_t *p, size_t *plen); + +/* Note: at most 21 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes + are output. */ +static inline size_t unicode_to_utf8(uint8_t *buf, unsigned int c) +{ + if (c < 0x80) { + buf[0] = c; + return 1; + } else { + return __unicode_to_utf8(buf, c); + } +} + +/* return -1 in case of error. Surrogates are accepted. max_len must + be >= 1. *plen is set in case of error and always >= 1. */ +static inline int unicode_from_utf8(const uint8_t *buf, size_t max_len, size_t *plen) +{ + if (buf[0] < 0x80) { + *plen = 1; + return buf[0]; + } else { + return __unicode_from_utf8(buf, max_len, plen); + } +} + +/* Warning: no error checking is done so the UTF-8 encoding must be + validated before. */ +static force_inline int utf8_get(const uint8_t *buf, size_t *plen) +{ + if (likely(buf[0] < 0x80)) { + *plen = 1; + return buf[0]; + } else { + return __utf8_get(buf, plen); + } +} + +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; +} + +static inline uint64_t float64_as_uint64(double d) +{ + union { + double d; + uint64_t u64; + } u; + u.d = d; + return u.u64; +} + +static inline double uint64_as_float64(uint64_t u64) +{ + union { + double d; + uint64_t u64; + } u; + u.u64 = u64; + return u.d; +} + +typedef union { + uint32_t u32; + float f; +} f32_union; + +static inline uint32_t float_as_uint(float f) +{ + f32_union u; + u.f = f; + return u.u32; +} + +static inline float uint_as_float(uint32_t v) +{ + f32_union u; + u.u32 = v; + return u.f; +} + +#endif /* CUTILS_H */ diff --git a/lib/mquickjs/dtoa.c b/lib/mquickjs/dtoa.c new file mode 100644 index 00000000..604f3f0c --- /dev/null +++ b/lib/mquickjs/dtoa.c @@ -0,0 +1,1620 @@ +/* + * Tiny float64 printing and parsing library + * + * Copyright (c) 2024-2025 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "dtoa.h" + +/* + TODO: + - test n_digits=101 instead of 100 + - simplify subnormal handling + - reduce max memory usage + - free format: could add shortcut if exact result + - use 64 bit limb_t when possible + - use another algorithm for free format dtoa in base 10 (ryu ?) +*/ + +#define USE_POW5_TABLE +/* use fast path to print small integers in free format */ +#define USE_FAST_INT + +#define LIMB_LOG2_BITS 5 + +#define LIMB_BITS (1 << LIMB_LOG2_BITS) + +typedef int32_t slimb_t; +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; + +#define LIMB_DIGITS 9 + +#define JS_RADIX_MAX 36 + +#define DBIGNUM_LEN_MAX 52 /* ~ 2^(1072+53)*36^100 (dtoa) */ +#define MANT_LEN_MAX 18 /* < 36^100 */ + +typedef intptr_t mp_size_t; + +/* the represented number is sum(i, tab[i]*2^(LIMB_BITS * i)) */ +typedef struct { + int len; /* >= 1 */ + limb_t tab[]; +} mpb_t; + +static limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n) +{ + size_t i; + limb_t k, a; + + k=b; + for(i=0;i> LIMB_BITS; + } + return l; +} + +/* WARNING: d must be >= 2^(LIMB_BITS-1) */ +static inline limb_t udiv1norm_init(limb_t d) +{ + limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, + limb_t d, limb_t d_inv) +{ + limb_t n1m, n_adj, q, r, ah; + dlimb_t a; + n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is between + 0 and d - 1 */ + a = ((dlimb_t)a1 << LIMB_BITS) | a0; + a = a - (dlimb_t)q * d - d; + ah = a >> LIMB_BITS; + q += 1 + ah; + r = (limb_t)a + (ah & d); + *pr = r; + return q; +} + +static limb_t mp_div1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t r) +{ + slimb_t i; + dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + return r; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t high) +{ + mp_size_t i; + limb_t l, a; + + assert(shift >= 1 && shift < LIMB_BITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); + l = a; + } + return l & (((limb_t)1 << shift) - 1); +} + +/* r = (a << shift) + low. 1 <= shift <= LIMB_BITS - 1, 0 <= low < + 2^shift. */ +static limb_t mp_shl(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t low) +{ + mp_size_t i; + limb_t l, a; + + assert(shift >= 1 && shift < LIMB_BITS); + l = low; + for(i = 0; i < n; i++) { + a = tab[i]; + tab_r[i] = (a << shift) | l; + l = (a >> (LIMB_BITS - shift)); + } + return l; +} + +static no_inline limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t r, limb_t b_inv, int shift) +{ + slimb_t i; + + if (shift != 0) { + r = (r << shift) | mp_shl(tabr, taba, n, shift, 0); + } + for(i = n - 1; i >= 0; i--) { + tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); + } + r >>= shift; + return r; +} + +static __maybe_unused void mpb_dump(const char *str, const mpb_t *a) +{ + int i; + + printf("%s= 0x", str); + for(i = a->len - 1; i >= 0; i--) { + printf("%08x", a->tab[i]); + if (i != 0) + printf("_"); + } + printf("\n"); +} + +static void mpb_renorm(mpb_t *r) +{ + while (r->len > 1 && r->tab[r->len - 1] == 0) + r->len--; +} + +#ifdef USE_POW5_TABLE +static const uint32_t pow5_table[17] = { + 0x00000005, 0x00000019, 0x0000007d, 0x00000271, + 0x00000c35, 0x00003d09, 0x0001312d, 0x0005f5e1, + 0x001dcd65, 0x009502f9, 0x02e90edd, 0x0e8d4a51, + 0x48c27395, 0x6bcc41e9, 0x1afd498d, 0x86f26fc1, + 0xa2bc2ec5, +}; + +static const uint8_t pow5h_table[4] = { + 0x00000001, 0x00000007, 0x00000023, 0x000000b1, +}; + +static const uint32_t pow5_inv_table[13] = { + 0x99999999, 0x47ae147a, 0x0624dd2f, 0xa36e2eb1, + 0x4f8b588e, 0x0c6f7a0b, 0xad7f29ab, 0x5798ee23, + 0x12e0be82, 0xb7cdfd9d, 0x5fd7fe17, 0x19799812, + 0xc25c2684, +}; +#endif + +/* return a^b */ +static uint64_t pow_ui(uint32_t a, uint32_t b) +{ + int i, n_bits; + uint64_t r; + if (b == 0) + return 1; + if (b == 1) + return a; +#ifdef USE_POW5_TABLE + if ((a == 5 || a == 10) && b <= 17) { + r = pow5_table[b - 1]; + if (b >= 14) { + r |= (uint64_t)pow5h_table[b - 14] << 32; + } + if (a == 10) + r <<= b; + return r; + } +#endif + r = a; + n_bits = 32 - clz32(b); + for(i = n_bits - 2; i >= 0; i--) { + r *= r; + if ((b >> i) & 1) + r *= a; + } + return r; +} + +static uint32_t pow_ui_inv(uint32_t *pr_inv, int *pshift, uint32_t a, uint32_t b) +{ + uint32_t r_inv, r; + int shift; +#ifdef USE_POW5_TABLE + if (a == 5 && b >= 1 && b <= 13) { + r = pow5_table[b - 1]; + shift = clz32(r); + r <<= shift; + r_inv = pow5_inv_table[b - 1]; + } else +#endif + { + r = pow_ui(a, b); + shift = clz32(r); + r <<= shift; + r_inv = udiv1norm_init(r); + } + *pshift = shift; + *pr_inv = r_inv; + return r; +} + +enum { + JS_RNDN, /* round to nearest, ties to even */ + JS_RNDNA, /* round to nearest, ties away from zero */ + JS_RNDZ, +}; + +static int mpb_get_bit(const mpb_t *r, int k) +{ + int l; + + l = (unsigned)k / LIMB_BITS; + k = k & (LIMB_BITS - 1); + if (l >= r->len) + return 0; + else + return (r->tab[l] >> k) & 1; +} + +/* compute round(r / 2^shift). 'shift' can be negative */ +static void mpb_shr_round(mpb_t *r, int shift, int rnd_mode) +{ + int l, i; + + if (shift == 0) + return; + if (shift < 0) { + shift = -shift; + l = (unsigned)shift / LIMB_BITS; + shift = shift & (LIMB_BITS - 1); + if (shift != 0) { + r->tab[r->len] = mp_shl(r->tab, r->tab, r->len, shift, 0); + r->len++; + mpb_renorm(r); + } + if (l > 0) { + for(i = r->len - 1; i >= 0; i--) + r->tab[i + l] = r->tab[i]; + for(i = 0; i < l; i++) + r->tab[i] = 0; + r->len += l; + } + } else { + limb_t bit1, bit2; + int k, add_one; + + switch(rnd_mode) { + default: + case JS_RNDZ: + add_one = 0; + break; + case JS_RNDN: + case JS_RNDNA: + bit1 = mpb_get_bit(r, shift - 1); + if (bit1) { + if (rnd_mode == JS_RNDNA) { + bit2 = 1; + } else { + /* bit2 = oring of all the bits after bit1 */ + bit2 = 0; + if (shift >= 2) { + k = shift - 1; + l = (unsigned)k / LIMB_BITS; + k = k & (LIMB_BITS - 1); + for(i = 0; i < min_int(l, r->len); i++) + bit2 |= r->tab[i]; + if (l < r->len) + bit2 |= r->tab[l] & (((limb_t)1 << k) - 1); + } + } + if (bit2) { + add_one = 1; + } else { + /* round to even */ + add_one = mpb_get_bit(r, shift); + } + } else { + add_one = 0; + } + break; + } + + l = (unsigned)shift / LIMB_BITS; + shift = shift & (LIMB_BITS - 1); + if (l >= r->len) { + r->len = 1; + r->tab[0] = add_one; + } else { + if (l > 0) { + r->len -= l; + for(i = 0; i < r->len; i++) + r->tab[i] = r->tab[i + l]; + } + if (shift != 0) { + mp_shr(r->tab, r->tab, r->len, shift, 0); + mpb_renorm(r); + } + if (add_one) { + limb_t a; + a = mp_add_ui(r->tab, 1, r->len); + if (a) + r->tab[r->len++] = a; + } + } + } +} + +/* return -1, 0 or 1 */ +static int mpb_cmp(const mpb_t *a, const mpb_t *b) +{ + mp_size_t i; + if (a->len < b->len) + return -1; + else if (a->len > b->len) + return 1; + for(i = a->len - 1; i >= 0; i--) { + if (a->tab[i] != b->tab[i]) { + if (a->tab[i] < b->tab[i]) + return -1; + else + return 1; + } + } + return 0; +} + +static void mpb_set_u64(mpb_t *r, uint64_t m) +{ +#if LIMB_BITS == 64 + r->tab[0] = m; + r->len = 1; +#else + r->tab[0] = m; + r->tab[1] = m >> LIMB_BITS; + if (r->tab[1] == 0) + r->len = 1; + else + r->len = 2; +#endif +} + +static uint64_t mpb_get_u64(mpb_t *r) +{ +#if LIMB_BITS == 64 + return r->tab[0]; +#else + if (r->len == 1) { + return r->tab[0]; + } else { + return r->tab[0] | ((uint64_t)r->tab[1] << LIMB_BITS); + } +#endif +} + +/* floor_log2() = position of the first non zero bit or -1 if zero. */ +static int mpb_floor_log2(mpb_t *a) +{ + limb_t v; + v = a->tab[a->len - 1]; + if (v == 0) + return -1; + else + return a->len * LIMB_BITS - 1 - clz32(v); +} + +#define MUL_LOG2_RADIX_BASE_LOG2 24 + +/* round((1 << MUL_LOG2_RADIX_BASE_LOG2)/log2(i + 2)) */ +static const uint32_t mul_log2_radix_table[JS_RADIX_MAX - 1] = { + 0x000000, 0xa1849d, 0x000000, 0x6e40d2, + 0x6308c9, 0x5b3065, 0x000000, 0x50c24e, + 0x4d104d, 0x4a0027, 0x4768ce, 0x452e54, + 0x433d00, 0x418677, 0x000000, 0x3ea16b, + 0x3d645a, 0x3c43c2, 0x3b3b9a, 0x3a4899, + 0x39680b, 0x3897b3, 0x37d5af, 0x372069, + 0x367686, 0x35d6df, 0x354072, 0x34b261, + 0x342bea, 0x33ac62, 0x000000, 0x32bfd9, + 0x3251dd, 0x31e8d6, 0x318465, +}; + +/* return floor(a / log2(radix)) for -2048 <= a <= 2047 */ +static int mul_log2_radix(int a, int radix) +{ + int radix_bits, mult; + + if ((radix & (radix - 1)) == 0) { + /* if the radix is a power of two better to do it exactly */ + radix_bits = 31 - clz32(radix); + if (a < 0) + a -= radix_bits - 1; + return a / radix_bits; + } else { + mult = mul_log2_radix_table[radix - 2]; + return ((int64_t)a * mult) >> MUL_LOG2_RADIX_BASE_LOG2; + } +} + +#if 0 +static void build_mul_log2_radix_table(void) +{ + int base, radix, mult, col, base_log2; + + base_log2 = 24; + base = 1 << base_log2; + col = 0; + for(radix = 2; radix <= 36; radix++) { + if ((radix & (radix - 1)) == 0) + mult = 0; + else + mult = lrint((double)base / log2(radix)); + printf("0x%06x, ", mult); + if (++col == 4) { + printf("\n"); + col = 0; + } + } + printf("\n"); +} + +static void mul_log2_radix_test(void) +{ + int radix, i, ref, r; + + for(radix = 2; radix <= 36; radix++) { + for(i = -2048; i <= 2047; i++) { + ref = (int)floor((double)i / log2(radix)); + r = mul_log2_radix(i, radix); + if (ref != r) { + printf("ERROR: radix=%d i=%d r=%d ref=%d\n", + radix, i, r, ref); + exit(1); + } + } + } + if (0) + build_mul_log2_radix_table(); +} +#endif + +static void u32toa_len(char *buf, uint32_t n, size_t len) +{ + int digit, i; + for(i = len - 1; i >= 0; i--) { + digit = n % 10; + n = n / 10; + buf[i] = digit + '0'; + } +} + +/* for power of 2 radixes. len >= 1 */ +static void u64toa_bin_len(char *buf, uint64_t n, unsigned int radix_bits, int len) +{ + int digit, i; + unsigned int mask; + + mask = (1 << radix_bits) - 1; + for(i = len - 1; i >= 0; i--) { + digit = n & mask; + n >>= radix_bits; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } +} + +/* len >= 1. 2 <= radix <= 36 */ +static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) +{ + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ +#if LIMB_BITS == 32 + u32toa_len(buf, n, len); +#else + /* XXX: optimize */ + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % 10; + n = (limb_t)n / 10; + buf[i] = digit + '0'; + } +#endif + } else { + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % radix; + n = (limb_t)n / radix; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } + } +} + +size_t u32toa(char *buf, uint32_t n) +{ + char buf1[10], *q; + size_t len; + + q = buf1 + sizeof(buf1); + do { + *--q = n % 10 + '0'; + n /= 10; + } while (n != 0); + len = buf1 + sizeof(buf1) - q; + memcpy(buf, q, len); + return len; +} + +size_t i32toa(char *buf, int32_t n) +{ + if (n >= 0) { + return u32toa(buf, n); + } else { + buf[0] = '-'; + return u32toa(buf + 1, -(uint32_t)n) + 1; + } +} + +#ifdef USE_FAST_INT +size_t u64toa(char *buf, uint64_t n) +{ + if (n < 0x100000000) { + return u32toa(buf, n); + } else { + uint64_t n1; + char *q = buf; + uint32_t n2; + + n1 = n / 1000000000; + n %= 1000000000; + if (n1 >= 0x100000000) { + n2 = n1 / 1000000000; + n1 = n1 % 1000000000; + /* at most two digits */ + if (n2 >= 10) { + *q++ = n2 / 10 + '0'; + n2 %= 10; + } + *q++ = n2 + '0'; + u32toa_len(q, n1, 9); + q += 9; + } else { + q += u32toa(q, n1); + } + u32toa_len(q, n, 9); + q += 9; + return q - buf; + } +} + +size_t i64toa(char *buf, int64_t n) +{ + if (n >= 0) { + return u64toa(buf, n); + } else { + buf[0] = '-'; + return u64toa(buf + 1, -(uint64_t)n) + 1; + } +} + +/* XXX: only tested for 1 <= n < 2^53 */ +size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix) +{ + int radix_bits, l; + if (likely(radix == 10)) + return u64toa(buf, n); + if ((radix & (radix - 1)) == 0) { + radix_bits = 31 - clz32(radix); + if (n == 0) + l = 1; + else + l = (64 - clz64(n) + radix_bits - 1) / radix_bits; + u64toa_bin_len(buf, n, radix_bits, l); + return l; + } else { + char buf1[41], *q; /* maximum length for radix = 3 */ + size_t len; + int digit; + q = buf1 + sizeof(buf1); + do { + digit = n % radix; + n /= radix; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + *--q = digit; + } while (n != 0); + len = buf1 + sizeof(buf1) - q; + memcpy(buf, q, len); + return len; + } +} + +size_t i64toa_radix(char *buf, int64_t n, unsigned int radix) +{ + if (n >= 0) { + return u64toa_radix(buf, n, radix); + } else { + buf[0] = '-'; + return u64toa_radix(buf + 1, -(uint64_t)n, radix) + 1; + } +} +#endif /* USE_FAST_INT */ + +static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = { +#if LIMB_BITS == 32 +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +#else +64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, +#endif +}; + +static const uint32_t radix_base_table[JS_RADIX_MAX - 1] = { + 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395, + 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91, + 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021, + 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571, + 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d, + 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51, + 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899, + 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1, + 0x5c13d840, 0x6d91b519, 0x81bf1000, +}; + +/* XXX: remove the table ? */ +static uint8_t dtoa_max_digits_table[JS_RADIX_MAX - 1] = { + 54, 35, 28, 24, 22, 20, 19, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, +}; + +/* we limit the maximum number of significant digits for atod to about + 128 bits of precision for non power of two bases. The only + requirement for Javascript is at least 20 digits in base 10. For + power of two bases, we do an exact rounding in all the cases. */ +static uint8_t atod_max_digits_table[JS_RADIX_MAX - 1] = { + 64, 80, 32, 55, 49, 45, 21, 40, 38, 37, 35, 34, 33, 32, 16, 31, 30, 30, 29, 29, 28, 28, 27, 27, 27, 26, 26, 26, 26, 25, 12, 25, 25, 24, 24, +}; + +/* if abs(d) >= B^max_exponent, it is an overflow */ +static const int16_t max_exponent[JS_RADIX_MAX - 1] = { + 1024, 647, 512, 442, 397, 365, 342, 324, + 309, 297, 286, 277, 269, 263, 256, 251, + 246, 242, 237, 234, 230, 227, 224, 221, + 218, 216, 214, 211, 209, 207, 205, 203, + 202, 200, 199, +}; + +/* if abs(d) <= B^min_exponent, it is an underflow */ +static const int16_t min_exponent[JS_RADIX_MAX - 1] = { +-1075, -679, -538, -463, -416, -383, -359, -340, + -324, -311, -300, -291, -283, -276, -269, -263, + -258, -254, -249, -245, -242, -238, -235, -232, + -229, -227, -224, -222, -220, -217, -215, -214, + -212, -210, -208, +}; + +#if 0 +void build_tables(void) +{ + int r, j, radix, n, col, i; + + /* radix_base_table */ + for(radix = 2; radix <= 36; radix++) { + r = 1; + for(j = 0; j < digits_per_limb_table[radix - 2]; j++) { + r *= radix; + } + printf(" 0x%08x,", r); + if ((radix % 4) == 1) + printf("\n"); + } + printf("\n"); + + /* dtoa_max_digits_table */ + for(radix = 2; radix <= 36; radix++) { + /* Note: over estimated when the radix is a power of two */ + printf(" %d,", 1 + (int)ceil(53.0 / log2(radix))); + } + printf("\n"); + + /* atod_max_digits_table */ + for(radix = 2; radix <= 36; radix++) { + if ((radix & (radix - 1)) == 0) { + /* 64 bits is more than enough */ + n = (int)floor(64.0 / log2(radix)); + } else { + n = (int)floor(128.0 / log2(radix)); + } + printf(" %d,", n); + } + printf("\n"); + + printf("static const int16_t max_exponent[JS_RADIX_MAX - 1] = {\n"); + col = 0; + for(radix = 2; radix <= 36; radix++) { + printf("%5d, ", (int)ceil(1024 / log2(radix))); + if (++col == 8) { + col = 0; + printf("\n"); + } + } + printf("\n};\n\n"); + + printf("static const int16_t min_exponent[JS_RADIX_MAX - 1] = {\n"); + col = 0; + for(radix = 2; radix <= 36; radix++) { + printf("%5d, ", (int)floor(-1075 / log2(radix))); + if (++col == 8) { + col = 0; + printf("\n"); + } + } + printf("\n};\n\n"); + + printf("static const uint32_t pow5_table[16] = {\n"); + col = 0; + for(i = 2; i <= 17; i++) { + r = 1; + for(j = 0; j < i; j++) { + r *= 5; + } + printf("0x%08x, ", r); + if (++col == 4) { + col = 0; + printf("\n"); + } + } + printf("\n};\n\n"); + + /* high part */ + printf("static const uint8_t pow5h_table[4] = {\n"); + col = 0; + for(i = 14; i <= 17; i++) { + uint64_t r1; + r1 = 1; + for(j = 0; j < i; j++) { + r1 *= 5; + } + printf("0x%08x, ", (uint32_t)(r1 >> 32)); + if (++col == 4) { + col = 0; + printf("\n"); + } + } + printf("\n};\n\n"); +} +#endif + +/* n_digits >= 1. 0 <= dot_pos <= n_digits. If dot_pos == n_digits, + the dot is not displayed. 'a' is modified. */ +static int output_digits(char *buf, + mpb_t *a, int radix, int n_digits1, + int dot_pos) +{ + int n_digits, digits_per_limb, radix_bits, n, len; + + n_digits = n_digits1; + if ((radix & (radix - 1)) == 0) { + /* radix = 2^radix_bits */ + radix_bits = 31 - clz32(radix); + } else { + radix_bits = 0; + } + digits_per_limb = digits_per_limb_table[radix - 2]; + if (radix_bits != 0) { + for(;;) { + n = min_int(n_digits, digits_per_limb); + n_digits -= n; + u64toa_bin_len(buf + n_digits, a->tab[0], radix_bits, n); + if (n_digits == 0) + break; + mpb_shr_round(a, digits_per_limb * radix_bits, JS_RNDZ); + } + } else { + limb_t r; + while (n_digits != 0) { + n = min_int(n_digits, digits_per_limb); + n_digits -= n; + r = mp_div1(a->tab, a->tab, a->len, radix_base_table[radix - 2], 0); + mpb_renorm(a); + limb_to_a(buf + n_digits, r, radix, n); + } + } + + /* add the dot */ + len = n_digits1; + if (dot_pos != n_digits1) { + memmove(buf + dot_pos + 1, buf + dot_pos, n_digits1 - dot_pos); + buf[dot_pos] = '.'; + len++; + } + return len; +} + +/* return (a, e_offset) such that a = a * (radix1*2^radix_shift)^f * + 2^-e_offset. 'f' can be negative. */ +static int mul_pow(mpb_t *a, int radix1, int radix_shift, int f, BOOL is_int, int e) +{ + int e_offset, d, n, n0; + + e_offset = -f * radix_shift; + if (radix1 != 1) { + d = digits_per_limb_table[radix1 - 2]; + if (f >= 0) { + limb_t h, b; + + b = 0; + n0 = 0; + while (f != 0) { + n = min_int(f, d); + if (n != n0) { + b = pow_ui(radix1, n); + n0 = n; + } + h = mp_mul1(a->tab, a->tab, a->len, b, 0); + if (h != 0) { + a->tab[a->len++] = h; + } + f -= n; + } + } else { + int extra_bits, l, shift; + limb_t r, rem, b, b_inv; + + f = -f; + l = (f + d - 1) / d; /* high bound for the number of limbs (XXX: make it better) */ + e_offset += l * LIMB_BITS; + if (!is_int) { + /* at least 'e' bits are needed in the final result for rounding */ + extra_bits = max_int(e - mpb_floor_log2(a), 0); + } else { + /* at least two extra bits are needed in the final result + for rounding */ + extra_bits = max_int(2 + e - e_offset, 0); + } + e_offset += extra_bits; + mpb_shr_round(a, -(l * LIMB_BITS + extra_bits), JS_RNDZ); + + b = 0; + b_inv = 0; + shift = 0; + n0 = 0; + rem = 0; + while (f != 0) { + n = min_int(f, d); + if (n != n0) { + b = pow_ui_inv(&b_inv, &shift, radix1, n); + n0 = n; + } + r = mp_div1norm(a->tab, a->tab, a->len, b, 0, b_inv, shift); + rem |= r; + mpb_renorm(a); + f -= n; + } + /* if the remainder is non zero, use it for rounding */ + a->tab[0] |= (rem != 0); + } + } + return e_offset; +} + +/* tmp1 = round(m*2^e*radix^f). 'tmp0' is a temporary storage */ +static void mul_pow_round(mpb_t *tmp1, uint64_t m, int e, int radix1, int radix_shift, int f, + int rnd_mode) +{ + int e_offset; + + mpb_set_u64(tmp1, m); + e_offset = mul_pow(tmp1, radix1, radix_shift, f, TRUE, e); + mpb_shr_round(tmp1, -e + e_offset, rnd_mode); +} + +/* return round(a*2^e_offset) rounded as a float64. 'a' is modified */ +static uint64_t round_to_d(int *pe, mpb_t *a, int e_offset, int rnd_mode) +{ + int e; + uint64_t m; + + if (a->tab[0] == 0 && a->len == 1) { + /* zero result */ + m = 0; + e = 0; /* don't care */ + } else { + int prec, prec1, e_min; + e = mpb_floor_log2(a) + 1 - e_offset; + prec1 = 53; + e_min = -1021; + if (e < e_min) { + /* subnormal result or zero */ + prec = prec1 - (e_min - e); + } else { + prec = prec1; + } + mpb_shr_round(a, e + e_offset - prec, rnd_mode); + m = mpb_get_u64(a); + m <<= (53 - prec); + /* mantissa overflow due to rounding */ + if (m >= (uint64_t)1 << 53) { + m >>= 1; + e++; + } + } + *pe = e; + return m; +} + +/* return (m, e) such that m*2^(e-53) = round(a * radix^f) with 2^52 + <= m < 2^53 or m = 0. + 'a' is modified. */ +static uint64_t mul_pow_round_to_d(int *pe, mpb_t *a, + int radix1, int radix_shift, int f, int rnd_mode) +{ + int e_offset; + + e_offset = mul_pow(a, radix1, radix_shift, f, FALSE, 55); + return round_to_d(pe, a, e_offset, rnd_mode); +} + +#ifdef JS_DTOA_DUMP_STATS +static int out_len_count[17]; + +void js_dtoa_dump_stats(void) +{ + int i, sum; + sum = 0; + for(i = 0; i < 17; i++) + sum += out_len_count[i]; + for(i = 0; i < 17; i++) { + printf("%2d %8d %5.2f%%\n", + i + 1, out_len_count[i], (double)out_len_count[i] / sum * 100); + } +} +#endif + +/* return a maximum bound of the string length. The bound depends on + 'd' only if format = JS_DTOA_FORMAT_FRAC or if JS_DTOA_EXP_DISABLED + is enabled. */ +int js_dtoa_max_len(double d, int radix, int n_digits, int flags) +{ + int fmt = flags & JS_DTOA_FORMAT_MASK; + int n, e; + uint64_t a; + + if (fmt != JS_DTOA_FORMAT_FRAC) { + if (fmt == JS_DTOA_FORMAT_FREE) { + n = dtoa_max_digits_table[radix - 2]; + } else { + n = n_digits; + } + if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_DISABLED) { + /* no exponential */ + a = float64_as_uint64(d); + e = (a >> 52) & 0x7ff; + if (e == 0x7ff) { + /* NaN, Infinity */ + n = 0; + } else { + e -= 1023; + /* XXX: adjust */ + n += 10 + abs(mul_log2_radix(e - 1, radix)); + } + } else { + /* extra: sign, 1 dot and exponent "e-1000" */ + n += 1 + 1 + 6; + } + } else { + a = float64_as_uint64(d); + e = (a >> 52) & 0x7ff; + if (e == 0x7ff) { + /* NaN, Infinity */ + n = 0; + } else { + /* high bound for the integer part */ + e -= 1023; + /* x < 2^(e + 1) */ + if (e < 0) { + n = 1; + } else { + n = 2 + mul_log2_radix(e - 1, radix); + } + /* sign, extra digit, 1 dot */ + n += 1 + 1 + 1 + n_digits; + } + } + return max_int(n, 9); /* also include NaN and [-]Infinity */ +} + +#if defined(__SANITIZE_ADDRESS__) && 0 +static void *dtoa_malloc(uint64_t **pptr, size_t size) +{ + return malloc(size); +} +static void dtoa_free(void *ptr) +{ + free(ptr); +} +#else +static void *dtoa_malloc(uint64_t **pptr, size_t size) +{ + void *ret; + ret = *pptr; + *pptr += (size + 7) / 8; + return ret; +} + +static void dtoa_free(void *ptr) +{ +} +#endif + +/* return the length */ +int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, + JSDTOATempMem *tmp_mem) +{ + uint64_t a, m, *mptr = tmp_mem->mem; + int e, sgn, l, E, P, i, E_max, radix1, radix_shift; + char *q; + mpb_t *tmp1, *mant_max; + int fmt = flags & JS_DTOA_FORMAT_MASK; + + tmp1 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX); + mant_max = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * MANT_LEN_MAX); + assert((mptr - tmp_mem->mem) <= sizeof(JSDTOATempMem) / sizeof(mptr[0])); + + radix_shift = ctz32(radix); + radix1 = radix >> radix_shift; + a = float64_as_uint64(d); + sgn = a >> 63; + e = (a >> 52) & 0x7ff; + m = a & (((uint64_t)1 << 52) - 1); + q = buf; + if (e == 0x7ff) { + if (m == 0) { + if (sgn) + *q++ = '-'; + memcpy(q, "Infinity", 8); + q += 8; + } else { + memcpy(q, "NaN", 3); + q += 3; + } + goto done; + } else if (e == 0) { + if (m == 0) { + tmp1->len = 1; + tmp1->tab[0] = 0; + E = 1; + if (fmt == JS_DTOA_FORMAT_FREE) + P = 1; + else if (fmt == JS_DTOA_FORMAT_FRAC) + P = n_digits + 1; + else + P = n_digits; + /* "-0" is displayed as "0" if JS_DTOA_MINUS_ZERO is not present */ + if (sgn && (flags & JS_DTOA_MINUS_ZERO)) + *q++ = '-'; + goto output; + } + /* denormal number: convert to a normal number */ + l = clz64(m) - 11; + e -= l - 1; + m <<= l; + } else { + m |= (uint64_t)1 << 52; + } + if (sgn) + *q++ = '-'; + /* remove the bias */ + e -= 1022; + /* d = 2^(e-53)*m */ + // printf("m=0x%016" PRIx64 " e=%d\n", m, e); +#ifdef USE_FAST_INT + if (fmt == JS_DTOA_FORMAT_FREE && + e >= 1 && e <= 53 && + (m & (((uint64_t)1 << (53 - e)) - 1)) == 0 && + (flags & JS_DTOA_EXP_MASK) != JS_DTOA_EXP_ENABLED) { + m >>= 53 - e; + /* 'm' is never zero */ + q += u64toa_radix(q, m, radix); + goto done; + } +#endif + + /* this choice of E implies F=round(x*B^(P-E) is such as: + B^(P-1) <= F < 2.B^P. */ + E = 1 + mul_log2_radix(e - 1, radix); + + if (fmt == JS_DTOA_FORMAT_FREE) { + int P_max, E0, e1, E_found, P_found; + uint64_t m1, mant_found, mant, mant_max1; + /* P_max is guaranteed to work by construction */ + P_max = dtoa_max_digits_table[radix - 2]; + E0 = E; + E_found = 0; + P_found = 0; + mant_found = 0; + /* find the minimum number of digits by successive tries */ + P = P_max; /* P_max is guaranteed to work */ + for(;;) { + /* mant_max always fits on 64 bits */ + mant_max1 = pow_ui(radix, P); + /* compute the mantissa in base B */ + E = E0; + for(;;) { + /* XXX: add inexact flag */ + mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDN); + mant = mpb_get_u64(tmp1); + if (mant < mant_max1) + break; + E++; /* at most one iteration is possible */ + } + /* remove useless trailing zero digits */ + while ((mant % radix) == 0) { + mant /= radix; + P--; + } + /* guaranteed to work for P = P_max */ + if (P_found == 0) + goto prec_found; + /* convert back to base 2 */ + mpb_set_u64(tmp1, mant); + m1 = mul_pow_round_to_d(&e1, tmp1, radix1, radix_shift, E - P, JS_RNDN); + // printf("P=%2d: m=0x%016" PRIx64 " e=%d m1=0x%016" PRIx64 " e1=%d\n", P, m, e, m1, e1); + /* Note: (m, e) is never zero here, so the exponent for m1 + = 0 does not matter */ + if (m1 == m && e1 == e) { + prec_found: + P_found = P; + E_found = E; + mant_found = mant; + if (P == 1) + break; + P--; /* try lower exponent */ + } else { + break; + } + } + P = P_found; + E = E_found; + mpb_set_u64(tmp1, mant_found); +#ifdef JS_DTOA_DUMP_STATS + if (radix == 10) { + out_len_count[P - 1]++; + } +#endif + } else if (fmt == JS_DTOA_FORMAT_FRAC) { + int len; + + assert(n_digits >= 0 && n_digits <= JS_DTOA_MAX_DIGITS); + /* P = max_int(E, 1) + n_digits; */ + /* frac is rounded using RNDNA */ + mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, n_digits, JS_RNDNA); + + /* we add one extra digit on the left and remove it if needed + to avoid testing if the result is < radix^P */ + len = output_digits(q, tmp1, radix, max_int(E + 1, 1) + n_digits, + max_int(E + 1, 1)); + if (q[0] == '0' && len >= 2 && q[1] != '.') { + len--; + memmove(q, q + 1, len); + } + q += len; + goto done; + } else { + int pow_shift; + assert(n_digits >= 1 && n_digits <= JS_DTOA_MAX_DIGITS); + P = n_digits; + /* mant_max = radix^P */ + mant_max->len = 1; + mant_max->tab[0] = 1; + pow_shift = mul_pow(mant_max, radix1, radix_shift, P, FALSE, 0); + mpb_shr_round(mant_max, pow_shift, JS_RNDZ); + + for(;;) { + /* fixed and frac are rounded using RNDNA */ + mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDNA); + if (mpb_cmp(tmp1, mant_max) < 0) + break; + E++; /* at most one iteration is possible */ + } + } + output: + if (fmt == JS_DTOA_FORMAT_FIXED) + E_max = n_digits; + else + E_max = dtoa_max_digits_table[radix - 2] + 4; + if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_ENABLED || + ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_AUTO && (E <= -6 || E > E_max))) { + q += output_digits(q, tmp1, radix, P, 1); + E--; + if (radix == 10) { + *q++ = 'e'; + } else if (radix1 == 1 && radix_shift <= 4) { + E *= radix_shift; + *q++ = 'p'; + } else { + *q++ = '@'; + } + if (E < 0) { + *q++ = '-'; + E = -E; + } else { + *q++ = '+'; + } + q += u32toa(q, E); + } else if (E <= 0) { + *q++ = '0'; + *q++ = '.'; + for(i = 0; i < -E; i++) + *q++ = '0'; + q += output_digits(q, tmp1, radix, P, P); + } else { + q += output_digits(q, tmp1, radix, P, min_int(P, E)); + for(i = 0; i < E - P; i++) + *q++ = '0'; + } + done: + *q = '\0'; + dtoa_free(mant_max); + dtoa_free(tmp1); + return q - buf; +} + +static inline int to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* r = r * radix_base + a. radix_base = 0 means radix_base = 2^32 */ +static void mpb_mul1_base(mpb_t *r, limb_t radix_base, limb_t a) +{ + int i; + if (r->tab[0] == 0 && r->len == 1) { + r->tab[0] = a; + } else { + if (radix_base == 0) { + for(i = r->len; i >= 0; i--) { + r->tab[i + 1] = r->tab[i]; + } + r->tab[0] = a; + } else { + r->tab[r->len] = mp_mul1(r->tab, r->tab, r->len, + radix_base, a); + } + r->len++; + mpb_renorm(r); + } +} + +/* XXX: add fast path for small integers */ +double js_atod(const char *str, const char **pnext, int radix, int flags, + JSATODTempMem *tmp_mem) +{ + uint64_t *mptr = tmp_mem->mem; + const char *p, *p_start; + limb_t cur_limb, radix_base, extra_digits; + int is_neg, digit_count, limb_digit_count, digits_per_limb, sep, radix1, radix_shift; + int radix_bits, expn, e, max_digits, expn_offset, dot_pos, sig_pos, pos; + mpb_t *tmp0; + double dval; + BOOL is_bin_exp, is_zero, expn_overflow; + uint64_t m, a; + + tmp0 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX); + assert((mptr - tmp_mem->mem) <= sizeof(JSATODTempMem) / sizeof(mptr[0])); + /* optional separator between digits */ + sep = (flags & JS_ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + + p = str; + is_neg = 0; + if (p[0] == '+') { + p++; + p_start = p; + } else if (p[0] == '-') { + is_neg = 1; + p++; + p_start = p; + } else { + p_start = p; + } + + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16)) { + p += 2; + radix = 16; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) { + p += 2; + radix = 2; + } else if ((p[1] >= '0' && p[1] <= '9') && + radix == 0 && (flags & JS_ATOD_ACCEPT_LEGACY_OCTAL)) { + int i; + sep = 256; + for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) + continue; + if (p[i] == '8' || p[i] == '9') + goto no_prefix; + p += 1; + radix = 8; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (to_digit((uint8_t)*p) >= radix) + goto fail; + no_prefix: ; + } else { + if (!(flags & JS_ATOD_INT_ONLY) && strstart(p, "Infinity", &p)) + goto overflow; + } + if (radix == 0) + radix = 10; + + cur_limb = 0; + expn_offset = 0; + digit_count = 0; + limb_digit_count = 0; + max_digits = atod_max_digits_table[radix - 2]; + digits_per_limb = digits_per_limb_table[radix - 2]; + radix_base = radix_base_table[radix - 2]; + radix_shift = ctz32(radix); + radix1 = radix >> radix_shift; + if (radix1 == 1) { + /* radix = 2^radix_bits */ + radix_bits = radix_shift; + } else { + radix_bits = 0; + } + tmp0->len = 1; + tmp0->tab[0] = 0; + extra_digits = 0; + pos = 0; + dot_pos = -1; + /* skip leading zeros */ + for(;;) { + if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) && + !(flags & JS_ATOD_INT_ONLY)) { + if (*p == sep) + goto fail; + if (dot_pos >= 0) + break; + dot_pos = pos; + p++; + } + if (*p == sep && p > p_start && p[1] == '0') + p++; + if (*p != '0') + break; + p++; + pos++; + } + + sig_pos = pos; + for(;;) { + limb_t c; + if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) && + !(flags & JS_ATOD_INT_ONLY)) { + if (*p == sep) + goto fail; + if (dot_pos >= 0) + break; + dot_pos = pos; + p++; + } + if (*p == sep && p > p_start && to_digit(p[1]) < radix) + p++; + c = to_digit(*p); + if (c >= radix) + break; + p++; + pos++; + if (digit_count < max_digits) { + /* XXX: could be faster when radix_bits != 0 */ + cur_limb = cur_limb * radix + c; + limb_digit_count++; + if (limb_digit_count == digits_per_limb) { + mpb_mul1_base(tmp0, radix_base, cur_limb); + cur_limb = 0; + limb_digit_count = 0; + } + digit_count++; + } else { + extra_digits |= c; + } + } + if (limb_digit_count != 0) { + mpb_mul1_base(tmp0, pow_ui(radix, limb_digit_count), cur_limb); + } + if (digit_count == 0) { + is_zero = TRUE; + expn_offset = 0; + } else { + is_zero = FALSE; + if (dot_pos < 0) + dot_pos = pos; + expn_offset = sig_pos + digit_count - dot_pos; + } + + /* Use the extra digits for rounding if the base is a power of + two. Otherwise they are just truncated. */ + if (radix_bits != 0 && extra_digits != 0) { + tmp0->tab[0] |= 1; + } + + /* parse the exponent, if any */ + expn = 0; + expn_overflow = FALSE; + is_bin_exp = FALSE; + if (!(flags & JS_ATOD_INT_ONLY) && + ((radix == 10 && (*p == 'e' || *p == 'E')) || + (radix != 10 && (*p == '@' || + (radix_bits >= 1 && radix_bits <= 4 && (*p == 'p' || *p == 'P'))))) && + p > p_start) { + BOOL exp_is_neg; + int c; + is_bin_exp = (*p == 'p' || *p == 'P'); + p++; + exp_is_neg = 0; + if (*p == '+') { + p++; + } else if (*p == '-') { + exp_is_neg = 1; + p++; + } + c = to_digit(*p); + if (c >= 10) + goto fail; /* XXX: could stop before the exponent part */ + expn = c; + p++; + for(;;) { + if (*p == sep && to_digit(p[1]) < 10) + p++; + c = to_digit(*p); + if (c >= 10) + break; + if (!expn_overflow) { + if (unlikely(expn > ((INT32_MAX - 2 - 9) / 10))) { + expn_overflow = TRUE; + } else { + expn = expn * 10 + c; + } + } + p++; + } + if (exp_is_neg) + expn = -expn; + /* if zero result, the exponent can be arbitrarily large */ + if (!is_zero && expn_overflow) { + if (exp_is_neg) + a = 0; + else + a = (uint64_t)0x7ff << 52; /* infinity */ + goto done; + } + } + + if (p == p_start) + goto fail; + + if (is_zero) { + a = 0; + } else { + int expn1; + if (radix_bits != 0) { + if (!is_bin_exp) + expn *= radix_bits; + expn -= expn_offset * radix_bits; + expn1 = expn + digit_count * radix_bits; + if (expn1 >= 1024 + radix_bits) + goto overflow; + else if (expn1 <= -1075) + goto underflow; + m = round_to_d(&e, tmp0, -expn, JS_RNDN); + } else { + expn -= expn_offset; + expn1 = expn + digit_count; + if (expn1 >= max_exponent[radix - 2] + 1) + goto overflow; + else if (expn1 <= min_exponent[radix - 2]) + goto underflow; + m = mul_pow_round_to_d(&e, tmp0, radix1, radix_shift, expn, JS_RNDN); + } + if (m == 0) { + underflow: + a = 0; + } else if (e > 1024) { + overflow: + /* overflow */ + a = (uint64_t)0x7ff << 52; + } else if (e < -1073) { + /* underflow */ + /* XXX: check rounding */ + a = 0; + } else if (e < -1021) { + /* subnormal */ + a = m >> (-e - 1021); + } else { + a = ((uint64_t)(e + 1022) << 52) | (m & (((uint64_t)1 << 52) - 1)); + } + } + done: + a |= (uint64_t)is_neg << 63; + dval = uint64_as_float64(a); + done1: + if (pnext) + *pnext = p; + dtoa_free(tmp0); + return dval; + fail: + dval = NAN; + goto done1; +} diff --git a/lib/mquickjs/dtoa.h b/lib/mquickjs/dtoa.h new file mode 100644 index 00000000..91b025b4 --- /dev/null +++ b/lib/mquickjs/dtoa.h @@ -0,0 +1,83 @@ +/* + * Tiny float64 printing and parsing library + * + * Copyright (c) 2024-2025 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +//#define JS_DTOA_DUMP_STATS + +/* maximum number of digits for fixed and frac formats */ +#define JS_DTOA_MAX_DIGITS 101 + +/* radix != 10 is only supported with flags = JS_DTOA_FORMAT_FREE */ +/* use as many digits as necessary */ +#define JS_DTOA_FORMAT_FREE (0 << 0) +/* use n_digits significant digits (1 <= n_digits <= JS_DTOA_MAX_DIGITS) */ +#define JS_DTOA_FORMAT_FIXED (1 << 0) +/* force fractional format: [-]dd.dd with n_digits fractional digits. + 0 <= n_digits <= JS_DTOA_MAX_DIGITS */ +#define JS_DTOA_FORMAT_FRAC (2 << 0) +#define JS_DTOA_FORMAT_MASK (3 << 0) + +/* select exponential notation either in fixed or free format */ +#define JS_DTOA_EXP_AUTO (0 << 2) +#define JS_DTOA_EXP_ENABLED (1 << 2) +#define JS_DTOA_EXP_DISABLED (2 << 2) +#define JS_DTOA_EXP_MASK (3 << 2) + +#define JS_DTOA_MINUS_ZERO (1 << 4) /* show the minus sign for -0 */ + +/* only accepts integers (no dot, no exponent) */ +#define JS_ATOD_INT_ONLY (1 << 0) +/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ +#define JS_ATOD_ACCEPT_BIN_OCT (1 << 1) +/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ +#define JS_ATOD_ACCEPT_LEGACY_OCTAL (1 << 2) +/* accept _ between digits as a digit separator */ +#define JS_ATOD_ACCEPT_UNDERSCORES (1 << 3) + +typedef struct { + uint64_t mem[37]; +} JSDTOATempMem; + +typedef struct { + uint64_t mem[27]; +} JSATODTempMem; + +/* return a maximum bound of the string length */ +int js_dtoa_max_len(double d, int radix, int n_digits, int flags); +/* return the string length */ +int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, + JSDTOATempMem *tmp_mem); +double js_atod(const char *str, const char **pnext, int radix, int flags, + JSATODTempMem *tmp_mem); + +#ifdef JS_DTOA_DUMP_STATS +void js_dtoa_dump_stats(void); +#endif + +/* additional exported functions */ +size_t u32toa(char *buf, uint32_t n); +size_t i32toa(char *buf, int32_t n); +size_t u64toa(char *buf, uint64_t n); +size_t i64toa(char *buf, int64_t n); +size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix); +size_t i64toa_radix(char *buf, int64_t n, unsigned int radix); diff --git a/lib/mquickjs/libm.c b/lib/mquickjs/libm.c new file mode 100644 index 00000000..bff8ed1c --- /dev/null +++ b/lib/mquickjs/libm.c @@ -0,0 +1,2260 @@ +/* + * Tiny Math Library + * + * Copyright (c) 2024 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ +#include +#include +#include +#define NDEBUG +#include + +#include "cutils.h" +#include "libm.h" + +/* define to enable softfloat support */ +//#define USE_SOFTFLOAT +/* use less code for tan() but currently less precise */ +#define USE_TAN_SHORTCUT + +/* + TODO: + - smaller scalbn implementation ? + - add all ES6 math functions +*/ +/* + tc32: + - base: size libm+libgcc: 21368 + - size libm+libgcc: 11832 + + x86: + - size libm softfp: 18510 + - size libm hardfp: 10051 + + TODO: + - unify i32 bit and i64 bit conversions + - unify comparisons operations +*/ + +typedef enum { + RM_RNE, /* Round to Nearest, ties to Even */ + RM_RTZ, /* Round towards Zero */ + RM_RDN, /* Round Down (must be even) */ + RM_RUP, /* Round Up (must be odd) */ + RM_RMM, /* Round to Nearest, ties to Max Magnitude */ + RM_RMMUP, /* only for rint_sf64(): round to nearest, ties to +inf (must be odd) */ +} RoundingModeEnum; + +#define FFLAG_INVALID_OP (1 << 4) +#define FFLAG_DIVIDE_ZERO (1 << 3) +#define FFLAG_OVERFLOW (1 << 2) +#define FFLAG_UNDERFLOW (1 << 1) +#define FFLAG_INEXACT (1 << 0) + +typedef enum { + FMINMAX_PROP, /* min(1, qNaN/sNaN) -> qNaN */ + FMINMAX_IEEE754_2008, /* min(1, qNaN) -> 1, min(1, sNaN) -> qNaN */ + FMINMAX_IEEE754_201X, /* min(1, qNaN/sNaN) -> 1 */ +} SoftFPMinMaxTypeEnum; + +typedef uint32_t sfloat32; +typedef uint64_t sfloat64; + +#define F_STATIC static __maybe_unused +#define F_USE_FFLAGS 0 + +#define F_SIZE 32 +#define F_NORMALIZE_ONLY +#include "softfp_template.h" + +#define F_SIZE 64 +#include "softfp_template.h" + +#ifdef USE_SOFTFLOAT + +/* wrappers */ +double __adddf3(double a, double b) +{ + return uint64_as_float64(add_sf64(float64_as_uint64(a), + float64_as_uint64(b), RM_RNE)); +} + +double __subdf3(double a, double b) +{ + return uint64_as_float64(sub_sf64(float64_as_uint64(a), + float64_as_uint64(b), RM_RNE)); +} + +double __muldf3(double a, double b) +{ + return uint64_as_float64(mul_sf64(float64_as_uint64(a), + float64_as_uint64(b), RM_RNE)); +} + +double __divdf3(double a, double b) +{ + return uint64_as_float64(div_sf64(float64_as_uint64(a), + float64_as_uint64(b), RM_RNE)); +} + +/* comparisons */ + +int __eqdf2(double a, double b) +{ + int ret = cmp_sf64(float64_as_uint64(a), + float64_as_uint64(b)); + return ret; +} + +/* NaN: return 0 */ +int __nedf2(double a, double b) +{ + int ret = cmp_sf64(float64_as_uint64(a), + float64_as_uint64(b)); + if (unlikely(ret == 2)) + return 0; + else + return ret; +} + +int __ledf2(double a, double b) +{ + int ret = cmp_sf64(float64_as_uint64(a), + float64_as_uint64(b)); + return ret; +} + +int __ltdf2(double a, double b) +{ + int ret = cmp_sf64(float64_as_uint64(a), + float64_as_uint64(b)); + return ret; +} + +int __gedf2(double a, double b) +{ + int ret = cmp_sf64(float64_as_uint64(a), + float64_as_uint64(b)); + if (unlikely(ret == 2)) + return -1; + else + return ret; +} + +int __gtdf2(double a, double b) +{ + int ret = cmp_sf64(float64_as_uint64(a), + float64_as_uint64(b)); + if (unlikely(ret == 2)) + return -1; + else + return ret; +} + +int __unorddf2(double a, double b) +{ + return isnan_sf64(float64_as_uint64(a)) || + isnan_sf64(float64_as_uint64(b)); +} + +/* conversions */ +double __floatsidf(int32_t a) +{ + return uint64_as_float64(cvt_i32_sf64(a, RM_RNE)); +} + +double __floatdidf(int64_t a) +{ + return uint64_as_float64(cvt_i64_sf64(a, RM_RNE)); +} + +double __floatunsidf(unsigned int a) +{ + return uint64_as_float64(cvt_u32_sf64(a, RM_RNE)); +} + +int32_t __fixdfsi(double a) +{ + return cvt_sf64_i32(float64_as_uint64(a), RM_RTZ); +} + +double __extendsfdf2(float a) +{ + return uint64_as_float64(cvt_sf32_sf64(float_as_uint(a))); +} + +float __truncdfsf2(double a) +{ + return uint_as_float(cvt_sf64_sf32(float64_as_uint64(a), RM_RNE)); +} + +double js_sqrt(double a) +{ + return uint64_as_float64(sqrt_sf64(float64_as_uint64(a), RM_RNE)); +} + +#if defined(__tc32__) +/* XXX: check */ +int __fpclassifyd(double a) +{ + uint64_t u = float64_as_uint64(a); + uint32_t h = u >> 32; + uint32_t l = u; + + h &= 0x7fffffff; + if (h >= 0x7ff00000) { + if (h == 0x7ff00000 && l == 0) + return FP_INFINITE; + else + return FP_NAN; + } else if (h < 0x00100000) { + if (h == 0 && l == 0) + return FP_ZERO; + else + return FP_SUBNORMAL; + } else { + return FP_NORMAL; + } +} +#endif + +#endif /* USE_SOFTFLOAT */ + +int32_t js_lrint(double a) +{ + return cvt_sf64_i32(float64_as_uint64(a), RM_RNE); +} + +double js_fmod(double a, double b) +{ + return uint64_as_float64(fmod_sf64(float64_as_uint64(a), float64_as_uint64(b))); +} + +/* supported rounding modes: RM_UP, RM_DN, RM_RTZ, RM_RMMUP, RM_RMM */ +static double rint_sf64(double a, RoundingModeEnum rm) +{ + uint64_t u = float64_as_uint64(a); + uint64_t frac_mask, one, m, addend; + int e; + unsigned int s; + + e = ((u >> 52) & 0x7ff) - 0x3ff; + s = u >> 63; + if (e < 0) { + m = u & (((uint64_t)1 << 52) - 1); + if (e == -0x3ff && m == 0) { + /* zero: nothing to do */ + } else { + /* abs(a) < 1 */ + s = u >> 63; + one = (uint64_t)0x3ff << 52; + u = 0; + switch(rm) { + case RM_RUP: + case RM_RDN: + if (s ^ (rm & 1)) + u = one; + break; + default: + case RM_RMM: + case RM_RMMUP: + if (e == -1 && (m != 0 || (m == 0 && (!s || rm == RM_RMM)))) + u = one; + break; + case RM_RTZ: + break; + } + u |= (uint64_t)s << 63; + } + } else if (e < 52) { + one = (uint64_t)1 << (52 - e); + frac_mask = one - 1; + addend = 0; + switch(rm) { + case RM_RMMUP: + addend = (one >> 1) - s; + break; + default: + case RM_RMM: + addend = (one >> 1); + break; + case RM_RTZ: + break; + case RM_RUP: + case RM_RDN: + if (s ^ (rm & 1)) + addend = one - 1; + break; + } + u += addend; + u &= ~frac_mask; /* truncate to an integer */ + } + /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */ + return uint64_as_float64(u); +} + +double js_floor(double x) +{ + return rint_sf64(x, RM_RDN); +} + +double js_ceil(double x) +{ + return rint_sf64(x, RM_RUP); +} + +double js_trunc(double x) +{ + return rint_sf64(x, RM_RTZ); +} + +double js_round_inf(double x) +{ + return rint_sf64(x, RM_RMMUP); +} + +double js_fabs(double x) +{ + uint64_t a = float64_as_uint64(x); + return uint64_as_float64(a & 0x7fffffffffffffff); +} + +/************************************************************/ +/* libm */ + +#define EXTRACT_WORDS(ix0,ix1,d) \ + do { \ + uint64_t __u = float64_as_uint64(d); \ + (ix0) = (uint32_t)(__u >> 32); \ + (ix1) = (uint32_t)__u; \ + } while (0) + +static uint32_t get_high_word(double d) +{ + return float64_as_uint64(d) >> 32; +} + +static double set_high_word(double d, uint32_t h) +{ + uint64_t u = float64_as_uint64(d); + u = (u & 0xffffffff) | ((uint64_t)h << 32); + return uint64_as_float64(u); +} + +static uint32_t get_low_word(double d) +{ + return float64_as_uint64(d); +} + +/* set the low 32 bits to zero */ +static double zero_low(double x) +{ + uint64_t u = float64_as_uint64(x); + u &= 0xffffffff00000000; + return uint64_as_float64(u); +} + +static double float64_from_u32(uint32_t h, uint32_t l) +{ + return uint64_as_float64(((uint64_t)h << 32) | l); +} + +static const double zero = 0.0; +static const double one = 1.0; +static const double half = 5.00000000000000000000e-01; +static const double tiny = 1.0e-300; +static const double huge = 1.0e300; + +/* @(#)s_scalbn.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * scalbn (double x, int n) + * scalbn(x,n) returns x* 2**n computed by exponent + * manipulation rather than by actually performing an + * exponentiation or a multiplication. + */ + +static const double + two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ + twom54 = 5.55111512312578270212e-17; /* 0x3C900000, 0x00000000 */ + +double js_scalbn(double x, int n) +{ + int k,hx,lx; + EXTRACT_WORDS(hx, lx, x); + k = (hx&0x7ff00000)>>20; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ + x *= two54; + hx = get_high_word(x); + k = ((hx&0x7ff00000)>>20) - 54; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0x7ff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0x7fe) return huge*copysign(huge,x); /* overflow */ + if (k > 0) /* normal result */ + {x = set_high_word(x, (hx&0x800fffff)|(k<<20)); return x;} + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge*copysign(huge,x); /*overflow*/ + else + return tiny*copysign(tiny,x); /*underflow*/ + } + k += 54; /* subnormal result */ + x = set_high_word(x, (hx&0x800fffff)|(k<<20)); + return x*twom54; +} + +#ifndef USE_SOFTFLOAT +/* @(#)e_sqrt.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_sqrt(x) + * Return correctly rounded sqrt. + * ------------------------------------------ + * | Use the hardware sqrt if you have one | + * ------------------------------------------ + * Method: + * Bit by bit method using integer arithmetic. (Slow, but portable) + * 1. Normalization + * Scale x to y in [1,4) with even powers of 2: + * find an integer k such that 1 <= (y=x*2^(2k)) < 4, then + * sqrt(x) = 2^k * sqrt(y) + * 2. Bit by bit computation + * Let q = sqrt(y) truncated to i bit after binary point (q = 1), + * i 0 + * i+1 2 + * s = 2*q , and y = 2 * ( y - q ). (1) + * i i i i + * + * To compute q from q , one checks whether + * i+1 i + * + * -(i+1) 2 + * (q + 2 ) <= y. (2) + * i + * -(i+1) + * If (2) is false, then q = q ; otherwise q = q + 2 . + * i+1 i i+1 i + * + * With some algebraic manipulation, it is not difficult to see + * that (2) is equivalent to + * -(i+1) + * s + 2 <= y (3) + * i i + * + * The advantage of (3) is that s and y can be computed by + * i i + * the following recurrence formula: + * if (3) is false + * + * s = s , y = y ; (4) + * i+1 i i+1 i + * + * otherwise, + * -i -(i+1) + * s = s + 2 , y = y - s - 2 (5) + * i+1 i i+1 i i + * + * One may easily use induction to prove (4) and (5). + * Note. Since the left hand side of (3) contain only i+2 bits, + * it does not necessary to do a full (53-bit) comparison + * in (3). + * 3. Final rounding + * After generating the 53 bits result, we compute one more bit. + * Together with the remainder, we can decide whether the + * result is exact, bigger than 1/2ulp, or less than 1/2ulp + * (it will never equal to 1/2ulp). + * The rounding mode can be detected by checking whether + * huge + tiny is equal to huge, and whether huge - tiny is + * equal to huge for some floating point number "huge" and "tiny". + * + * Special cases: + * sqrt(+-0) = +-0 ... exact + * sqrt(inf) = inf + * sqrt(-ve) = NaN ... with invalid signal + * sqrt(NaN) = NaN ... with invalid signal for signaling NaN + * + * Other methods : see the appended file at the end of the program below. + *--------------- + */ + +#if defined(__aarch64__) || defined(__x86_64__) || defined(__i386__) +/* hardware sqrt is available */ +double js_sqrt(double x) +{ + return sqrt(x); +} +#else +double js_sqrt(double x) +{ + double z; + int sign = (int)0x80000000; + unsigned r,t1,s1,ix1,q1; + int ix0,s0,q,m,t,i; + + EXTRACT_WORDS(ix0, ix1, x); + + /* take care of Inf and NaN */ + if((ix0&0x7ff00000)==0x7ff00000) { + return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if(ix0<=0) { + if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */ + else if(ix0<0) + return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix0>>20); + if(m==0) { /* subnormal x */ + while(ix0==0) { + m -= 21; + ix0 |= (ix1>>11); ix1 <<= 21; + } + for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1; + m -= i-1; + ix0 |= (ix1>>(32-i)); + ix1 <<= i; + } + m -= 1023; /* unbias exponent */ + ix0 = (ix0&0x000fffff)|0x00100000; + if(m&1){ /* odd m, double x to make it even */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + } + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ + r = 0x00200000; /* r = moving bit from right to left */ + + while(r!=0) { + t = s0+r; + if(t<=ix0) { + s0 = t+r; + ix0 -= t; + q += r; + } + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + r>>=1; + } + + r = sign; + while(r!=0) { + t1 = s1+r; + t = s0; + if((t>31); + ix1 += ix1; + r>>=1; + } + + /* use floating add to find out rounding direction */ + if((ix0|ix1)!=0) { + z = one-tiny; /* trigger inexact flag */ + if (z>=one) { + z = one+tiny; + if (q1==(unsigned)0xffffffff) { q1=0; q += 1;} + else if (z>one) { + if (q1==(unsigned)0xfffffffe) q+=1; + q1+=2; + } else + q1 += (q1&1); + } + } + ix0 = (q>>1)+0x3fe00000; + ix1 = q1>>1; + if ((q&1)==1) ix1 |= sign; + ix0 += (m <<20); + return float64_from_u32(ix0, ix1); +} +#endif /* !hardware sqrt */ +#endif /* USE_SOFTFLOAT */ + +/* to have smaller code */ +/* n >= 1 */ +/* return sum(x^i*coefs[i] with i = 0 ... n - 1 and n >= 1 using + Horner algorithm. */ +static double eval_poly(double x, const double *coefs, int n) +{ + double r; + int i; + r = coefs[n - 1]; + for(i = n - 2; i >= 0; i--) { + r = r * x + coefs[i]; + } + return r; +} + +/* @(#)k_sin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __kernel_sin( x, y, iy) + * kernel sin function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input iy indicates whether y is 0. (if iy=0, y assume to be 0). + * + * Algorithm + * 1. Since sin(-x) = -sin(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return x with inexact if x!=0. + * 3. sin(x) is approximated by a polynomial of degree 13 on + * [0,pi/4] + * 3 13 + * sin(x) ~ x + S1*x + ... + S6*x + * where + * + * |sin(x) 2 4 6 8 10 12 | -58 + * |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 + * | x | + * + * 4. sin(x+y) = sin(x) + sin'(x')*y + * ~ sin(x) + (1-x*x/2)*y + * For better accuracy, let + * 3 2 2 2 2 + * r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) + * then 3 2 + * sin(x) = x + (S1*x + (x *(r-y/2)+y)) + */ + +static const double +S1 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */ +static const double S_tab[] = { + /* S2 */ 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */ + /* S3 */ -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */ + /* S4 */ 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */ + /* S5 */ -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */ + /* S6 */ 1.58969099521155010221e-10, /* 0x3DE5D93A, 0x5ACFD57C */ +}; + +/* iy=0 if y is zero */ +static double __kernel_sin(double x, double y, int iy) +{ + double z,r,v; + int ix; + ix = get_high_word(x)&0x7fffffff; /* high word of x */ + if(ix<0x3e400000) /* |x| < 2**-27 */ + {if((int)x==0) return x;} /* generate inexact */ + z = x*x; + v = z*x; + r = eval_poly(z, S_tab, 5); + if(iy==0) return x+v*(S1+z*r); + else return x-((z*(half*y-v*r)-y)-v*S1); +} + + +/* @(#)k_cos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * __kernel_cos( x, y ) + * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * + * Algorithm + * 1. Since cos(-x) = cos(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. + * 3. cos(x) is approximated by a polynomial of degree 14 on + * [0,pi/4] + * 4 14 + * cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x + * where the remez error is + * + * | 2 4 6 8 10 12 14 | -58 + * |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 + * | | + * + * 4 6 8 10 12 14 + * 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then + * cos(x) = 1 - x*x/2 + r + * since cos(x+y) ~ cos(x) - sin(x)*y + * ~ cos(x) - x*y, + * a correction term is necessary in cos(x) and hence + * cos(x+y) = 1 - (x*x/2 - (r - x*y)) + * For better accuracy when x > 0.3, let qx = |x|/4 with + * the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125. + * Then + * cos(x+y) = (1-qx) - ((x*x/2-qx) - (r-x*y)). + * Note that 1-qx and (x*x/2-qx) is EXACT here, and the + * magnitude of the latter is at least a quarter of x*x/2, + * thus, reducing the rounding error in the subtraction. + */ + +static const double C_tab[] = { + /* C1 */ 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */ + /* C2 */ -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */ + /* C3 */ 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */ + /* C4 */ -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */ + /* C5 */ 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */ + /* C6 */ -1.13596475577881948265e-11, /* 0xBDA8FAE9, 0xBE8838D4 */ +}; + +static double __kernel_cos(double x, double y) +{ + double a,hz,z,r,qx; + int ix; + ix = get_high_word(x)&0x7fffffff; /* ix = |x|'s high word*/ + if(ix<0x3e400000) { /* if x < 2**27 */ + if(((int)x)==0) return one; /* generate inexact */ + } + z = x*x; + r = z * eval_poly(z, C_tab, 6); + if(ix < 0x3FD33333) /* if |x| < 0.3 */ + return one - (0.5*z - (z*r - x*y)); + else { + if(ix > 0x3fe90000) { /* x > 0.78125 */ + qx = 0.28125; + } else { + qx = float64_from_u32(ix-0x00200000, 0); /* x/4 */ + } + hz = 0.5*z-qx; + a = one-qx; + return a - (hz - (z*r-x*y)); + } +} + +/* rem_pio2 */ + +#define T_LEN 19 + +/* T[i] = floor(2^(64*(T_LEN - i))/2pi) mod 2^64 */ +static const uint64_t T[T_LEN] = { + 0x1580cc11bf1edaea, + 0x9afed7ec47e35742, + 0xcf41ce7de294a4ba, + 0x5d49eeb1faf97c5e, + 0xd3d18fd9a797fa8b, + 0xdb4d9fb3c9f2c26d, + 0xfbcbc462d6829b47, + 0xc7fe25fff7816603, + 0x272117e2ef7e4a0e, + 0x4e64758e60d4ce7d, + 0x3a671c09ad17df90, + 0xba208d7d4baed121, + 0x3f877ac72c4a69cf, + 0x01924bba82746487, + 0x6dc91b8e909374b8, + 0x7f9458eaf7aef158, + 0x36d8a5664f10e410, + 0x7f09d5f47d4d3770, + 0x28be60db9391054a, /* high part */ +}; + +/* PIO2[i] = floor(2^(64*(2 - i))*PI/4) mod 2^64 */ +static const uint64_t PIO4[2] = { + 0xc4c6628b80dc1cd1, + 0xc90fdaa22168c234, +}; + +static uint64_t get_u64_at_bit(const uint64_t *tab, uint32_t tab_len, + uint32_t pos) +{ + uint64_t v; + uint32_t p = pos / 64; + int shift = pos % 64; + v = tab[p] >> shift; + if (shift != 0 && (p + 1) < tab_len) + v |= tab[p + 1] << (64 - shift); + return v; +} + +/* return n = round(x/(pi/2)) (only low 2 bits are valid) and + (y[0], y[1]) = x - (pi/2) * n. + 'x' must be finite and such as abs(x) >= PI/4. + The initial algorithm comes from the CORE-MATH project. +*/ +static int rem_pio2_large(double x, double *y) +{ + uint64_t m; + int e, sgn, n, rnd, j, i, y_sgn; + uint64_t c[2], d[3]; + uint64_t r0, r1; + uint32_t carry, carry1; + + m = float64_as_uint64(x); + sgn = m >> 63; + e = (m >> 52) & 0x7ff; + /* 1022 <= e <= 2047 */ + m = (m & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + + /* multiply m by T[j:j+192] */ + j = T_LEN * 64 - (e - 1075) - 192; + /* 53 <= j <= 1077 */ + // printf("m=0x%016" PRIx64 " e=%d j=%d\n", m, e, j); + for(i = 0; i < 3; i++) { + d[i] = get_u64_at_bit(T, T_LEN, j + i * 64); + } + r1 = mul_u64(&r0, m, d[0]); + c[0] = r1; + r1 = mul_u64(&r0, m, d[1]); + c[0] += r0; + carry = c[0] < r0; + c[1] = r1 + carry; + mul_u64(&r0, m, d[2]); + c[1] += r0; + + // printf("c0=%016" PRIx64 " %016" PRIx64 "\n", c[1], c[0]); + + /* n = round(c[1]/2^62) */ + n = c[1] >> 62; + rnd = (c[1] >> 61) & 1; + n += rnd; + /* c = c * 4 - n */ + c[1] = (c[1] << 2) | (c[0] >> 62); + c[0] = (c[0] << 2); + y_sgn = sgn; + if (rnd) { + /* 'y' sign change */ + y_sgn ^= 1; + c[0] = ~c[0]; + c[1] = ~c[1]; + if (++c[0] == 0) + c[1]++; + } + // printf("c1=%016" PRIx64 " %016" PRIx64 " n=%d sgn=%d\n", c[1], c[0], n, sgn); + + /* c = c * (PI/2) (high 128 bits of the product) */ + r1 = mul_u64(&r0, c[0], PIO4[1]); + d[0] = r0; + d[1] = r1; + + r1 = mul_u64(&r0, c[1], PIO4[0]); + d[0] += r0; + carry = d[0] < r0; + d[1] += r1; + carry1 = d[1] < r1; + d[1] += carry; + carry1 |= (d[1] < carry); + d[2] = carry1; + + r1 = mul_u64(&r0, c[1], PIO4[1]); + d[1] += r0; + carry = d[1] < r0; + d[2] += r1 + carry; + + /* convert d to two float64 */ + // printf("d=%016" PRIx64 " %016" PRIx64 "\n", d[2], d[1]); + if (d[2] == 0) { + /* should never happen (see ARGUMENT REDUCTION FOR HUGE + ARGUMENTS: Good to the Last Bit, K. C. Ng and the members + of the FP group of SunPro */ + y[0] = y[1] = 0; + } else { + uint64_t m0, m1; + int e1; + + e = clz64(d[2]); + d[2] = (d[2] << e) | (d[1] >> (64 - e)); + d[1] = (d[1] << e); + // printf("d=%016" PRIx64 " %016" PRIx64 " e=%d\n", d[2], d[1], e); + m0 = (d[2] >> 11) & (((uint64_t)1 << 52) - 1); + m1 = ((d[2] & 0x7ff) << 42) | (d[1] >> (64 - 42)); + y[0] = uint64_as_float64(((uint64_t)y_sgn << 63) | + ((uint64_t)(1023 - e) << 52) | + m0); + if (m1 == 0) { + y[1] = 0; + } else { + e1 = clz64(m1) - 11; + m1 = (m1 << e1) & (((uint64_t)1 << 52) - 1); + y[1] = uint64_as_float64(((uint64_t)y_sgn << 63) | + ((uint64_t)(1023 - e - 53 - e1) << 52) | + m1); + } + } + if (sgn) + n = -n; + return n; +} + +#ifdef USE_SOFTFLOAT +/* when using softfloat, the FP reduction should be not much faster + than the generic one */ +int js_rem_pio2(double x, double *y) +{ + int ix,hx; + + hx = get_high_word(x); /* high word of x */ + ix = hx&0x7fffffff; + if(ix<=0x3fe921fb) { + /* |x| ~<= pi/4 , no need for reduction */ + y[0] = x; + y[1] = 0; + return 0; + } + /* + * all other (large) arguments + */ + if(ix>=0x7ff00000) { /* x is inf or NaN */ + y[0]=y[1]=x-x; + return 0; + } + + return rem_pio2_large(x, y); +} +#else +/* + * invpio2: 53 bits of 2/pi + * pio2_1: first 33 bit of pi/2 + * pio2_1t: pi/2 - pio2_1 + * pio2_2: second 33 bit of pi/2 + * pio2_2t: pi/2 - (pio2_1+pio2_2) + * pio2_3: third 33 bit of pi/2 + * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) + */ + +static const double +invpio2 = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */ +static const double pio2_tab[3] = { + /* pio2_1 */ 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */ + /* pio2_2 */ 6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */ + /* pio2_3 */ 2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */ +}; +static const double pio2_t_tab[3] = { + /* pio2_1t */ 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */ + /* pio2_2t */ 2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */ + /* pio2_3t */ 8.47842766036889956997e-32, /* 0x397B839A, 0x252049C1 */ +}; +static uint8_t rem_pio2_emax[2] = { 16, 49 }; + +int js_rem_pio2(double x, double *y) +{ + double w,t,r,fn; + int i,j,n,ix,hx,it; + + hx = get_high_word(x); /* high word of x */ + ix = hx&0x7fffffff; + if(ix<=0x3fe921fb) { + /* |x| ~<= pi/4 , no need for reduction */ + y[0] = x; + y[1] = 0; + return 0; + } + if(ix<=0x413921fb) { /* |x| ~<= 2^19*(pi/2), medium size */ + t = fabs(x); + if (ix<0x4002d97c) { + /* |x| < 3pi/4, special case with n=+-1 */ + n = 1; + fn = 1; + } else { + n = (int) (t*invpio2+half); + fn = (double)n; + } + + it = 0; + for(;;) { + /* 1st round good to 85 bit */ + /* 2nd iteration needed, good to 118 */ + /* 3rd iteration need, 151 bits acc */ + r = t-fn*pio2_tab[it]; + w = fn*pio2_t_tab[it]; + y[0] = r-w; + j = ix>>20; + i = j-(((get_high_word(y[0]))>>20)&0x7ff); + if (it == 2 || i <= rem_pio2_emax[it]) + break; + t = r; + it++; + } + y[1] = (r-y[0])-w; + if (hx<0) { + y[0] = -y[0]; + y[1] = -y[1]; + return -n; + } else { + return n; + } + } + /* + * all other (large) arguments + */ + if(ix>=0x7ff00000) { /* x is inf or NaN */ + y[0]=y[1]=x-x; + return 0; + } + + return rem_pio2_large(x, y); +} +#endif /* !USE_SOFTFLOAT */ + +/* @(#)s_sin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* sin(x) + * Return sine function of x. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cose function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +/* flag = 0: sin() + flag = 1: cos() + flag = 3: tan() +*/ +static double js_sin_cos(double x, int flag) +{ + double y[2], z, s, c; + int ix; + uint32_t n; + + /* High word of x. */ + ix = get_high_word(x); + + /* sin(Inf or NaN) is NaN */ + if (ix>=0x7ff00000) + return x-x; + + n = js_rem_pio2(x,y); + s = c = 0; /* avoid warning */ + if (flag == 3 || (n & 1) == flag) { + s = __kernel_sin(y[0],y[1],1); + if (flag != 3) + goto done; + } + if (flag == 3 || (n & 1) != flag) { + c = __kernel_cos(y[0],y[1]); + if (flag != 3) { + s = c; + goto done; + } + } + if (n & 1) + z = -c / s; + else + z = s / c; + return z; +done: + if ((n + flag) & 2) + s = -s; + return s; +} + +double js_sin(double x) +{ + return js_sin_cos(x, 0); +} + +double js_cos(double x) +{ + return js_sin_cos(x, 1); +} + +#ifdef USE_TAN_SHORTCUT +double js_tan(double x) +{ + return js_sin_cos(x, 3); +} +#endif + +#ifndef USE_TAN_SHORTCUT +/* + * ==================================================== + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* INDENT OFF */ +/* __kernel_tan( x, y, k ) + * kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input k indicates whether tan (if k = 1) or -1/tan (if k = -1) is returned. + * + * Algorithm + * 1. Since tan(-x) = -tan(x), we need only to consider positive x. + * 2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0. + * 3. tan(x) is approximated by a odd polynomial of degree 27 on + * [0,0.67434] + * 3 27 + * tan(x) ~ x + T1*x + ... + T13*x + * where + * + * |tan(x) 2 4 26 | -59.2 + * |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 + * | x | + * + * Note: tan(x+y) = tan(x) + tan'(x)*y + * ~ tan(x) + (1+x*x)*y + * Therefore, for better accuracy in computing tan(x+y), let + * 3 2 2 2 2 + * r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) + * then + * 3 2 + * tan(x+y) = x + (T1*x + (x *(r+y)+y)) + * + * 4. For x in [0.67434,pi/4], let y = pi/4 - x, then + * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) + * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) + */ + +static const double T0 = 3.33333333333334091986e-01; /* 3FD55555, 55555563 */ +static const double T_even[] = { + 5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */ + 8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */ + 1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */ + 2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */ + 7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */ + 2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */ +}; +static const double T_odd[] = { + 1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */ + 2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */ + 3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */ + 5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */ + 7.81794442939557092300e-05, /* 3F147E88, A03792A6 */ + -1.85586374855275456654e-05, /* BEF375CB, DB605373 */ +}; +static const double pio4 = 7.85398163397448278999e-01, /* 3FE921FB, 54442D18 */ + pio4lo = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */ + +/* compute -1 / (x+y) carefully */ +static double minus_inv(double x, double y) +{ + double a, t, z, v, s, w; + + w = x + y; + z = zero_low(w); + v = y - (z - x); + a = -one / w; + t = zero_low(a); + s = one + t * z; + return t + a * (s + t * v); +} + +static double +__kernel_tan(double x, double y, int iy) { + double z, r, v, w, s; + int ix, hx; + + hx = get_high_word(x); /* high word of x */ + ix = hx & 0x7fffffff; /* high word of |x| */ + if (ix < 0x3e300000) { /* x < 2**-28 */ + if ((int) x == 0) { /* generate inexact */ + if (((ix | get_low_word(x)) | (iy + 1)) == 0) + return one / fabs(x); + else { + if (iy == 1) + return x; + else + return minus_inv(x, y); + } + } + } + if (ix >= 0x3FE59428) { /* |x| >= 0.6744 */ + if (hx < 0) { + x = -x; + y = -y; + } + z = pio4 - x; + w = pio4lo - y; + x = z + w; + y = 0.0; + } + z = x * x; + w = z * z; + /* + * Break x^5*(T[1]+x^2*T[2]+...) into + * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + + * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) + */ + r = eval_poly(w, T_odd, 6); + v = z * eval_poly(w, T_even, 6); + s = z * x; + r = y + z * (s * (r + v) + y); + r += T0 * s; + w = x + r; + if (ix >= 0x3FE59428) { + v = (double) iy; + return (double) (1 - ((hx >> 30) & 2)) * + (v - 2.0 * (x - (w * w / (w + v) - r))); + } + if (iy == 1) { + return w; + } else { + /* + * if allow error up to 2 ulp, simply return + * -1.0 / (x+r) here + */ + return minus_inv(x, r); + } +} + +/* @(#)s_tan.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* tan(x) + * Return tangent function of x. + * + * kernel function: + * __kernel_tan ... tangent function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +double js_tan(double x) +{ + double y[2],z=0.0; + int n, ix; + + /* High word of x. */ + ix = get_high_word(x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_tan(x,z,1); + + /* tan(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; /* NaN */ + + /* argument reduction needed */ + else { + n = js_rem_pio2(x,y); + return __kernel_tan(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even + -1 -- n odd */ + } +} +#endif + +/* @(#)e_asin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_asin(x) + * Method : + * Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ... + * we approximate asin(x) on [0,0.5] by + * asin(x) = x + x*x^2*R(x^2) + * where + * R(x^2) is a rational approximation of (asin(x)-x)/x^3 + * and its remez error is bounded by + * |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75) + * + * For x in [0.5,1] + * asin(x) = pi/2-2*asin(sqrt((1-x)/2)) + * Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2; + * then for x>0.98 + * asin(x) = pi/2 - 2*(s+s*z*R(z)) + * = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo) + * For x<=0.98, let pio4_hi = pio2_hi/2, then + * f = hi part of s; + * c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z) + * and + * asin(x) = pi/2 - 2*(s+s*z*R(z)) + * = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo) + * = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c)) + * + * Special cases: + * if x is NaN, return x itself; + * if |x|>1, return NaN with invalid signal. + * + */ + + +static const double +pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */ + pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */ + pio4_hi = 7.85398163397448278999e-01; /* 0x3FE921FB, 0x54442D18 */ +/* coefficient for R(x^2) */ +static const double pS[] = { + /* pS0 */ 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ + /* pS1 */-3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ + /* pS2 */ 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ + /* pS3 */ -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ + /* pS4 */ 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ + /* pS5 */ 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ +}; + +static const double qS[] = { + /* qS1 */ -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ + /* qS2 */ 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ + /* qS3 */ -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ + /* qS4 */ 7.70381505559019352791e-02, /* 0x3FB3B8C5, 0xB12E9282 */ +}; + +static double R(double t) +{ + double p, q, w; + p = t * eval_poly(t, pS, 6); + q = one + t * eval_poly(t, qS, 4); + w = p/q; + return w; +} + +double js_asin(double x) +{ + double t,w,p,q,c,r,s; + int hx,ix; + hx = get_high_word(x); + ix = hx&0x7fffffff; + if(ix>= 0x3ff00000) { /* |x|>= 1 */ + if(((ix-0x3ff00000)|get_low_word(x))==0) + /* asin(1)=+-pi/2 with inexact */ + return x*pio2_hi+x*pio2_lo; + return (x-x)/(x-x); /* asin(|x|>1) is NaN */ + } else if (ix<0x3fe00000) { /* |x|<0.5 */ + if(ix<0x3e400000) { /* if |x| < 2**-27 */ + if(huge+x>one) return x;/* return x with inexact if x!=0*/ + } else { + t = x*x; + w = R(t); + return x+x*w; + } + } + /* 1> |x|>= 0.5 */ + w = one-fabs(x); + t = w*0.5; + r = R(t); + s = js_sqrt(t); + if(ix>=0x3FEF3333) { /* if |x| > 0.975 */ + w = r; + t = pio2_hi-(2.0*(s+s*w)-pio2_lo); + } else { + w = zero_low(s); + c = (t-w*w)/(s+w); + p = 2.0*s*r-(pio2_lo-2.0*c); + q = pio4_hi-2.0*w; + t = pio4_hi-(p-q); + } + if(hx>0) return t; else return -t; +} + + +/* @(#)e_acos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_acos(x) + * Method : + * acos(x) = pi/2 - asin(x) + * acos(-x) = pi/2 + asin(x) + * For |x|<=0.5 + * acos(x) = pi/2 - (x + x*x^2*R(x^2)) (see asin.c) + * For x>0.5 + * acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2))) + * = 2asin(sqrt((1-x)/2)) + * = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z) + * = 2f + (2c + 2s*z*R(z)) + * where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term + * for f so that f+c ~ sqrt(z). + * For x<-0.5 + * acos(x) = pi - 2asin(sqrt((1-|x|)/2)) + * = pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z) + * + * Special cases: + * if x is NaN, return x itself; + * if |x|>1, return NaN with invalid signal. + * + * Function needed: sqrt + */ + +static const double +pi = 3.14159265358979311600e+00; /* 0x400921FB, 0x54442D18 */ + +double js_acos(double x) +{ + double z,r,w,s,c,df; + int hx,ix; + hx = get_high_word(x); + ix = hx&0x7fffffff; + if(ix>=0x3ff00000) { /* |x| >= 1 */ + if(((ix-0x3ff00000)|get_low_word(x))==0) { /* |x|==1 */ + if(hx>0) return 0.0; /* acos(1) = 0 */ + else return pi+2.0*pio2_lo; /* acos(-1)= pi */ + } + return (x-x)/(x-x); /* acos(|x|>1) is NaN */ + } + if(ix<0x3fe00000) { /* |x| < 0.5 */ + if(ix<=0x3c600000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/ + z = x*x; + r = R(z); + return pio2_hi - (x - (pio2_lo-x*r)); + } else { + z = (one-fabs(x))*0.5; + r = R(z); + s = js_sqrt(z); + if (hx<0) { /* x < -0.5 */ + w = r*s-pio2_lo; + return pi - 2.0*(s+w); + } else { /* x > 0.5 */ + df = zero_low(s); + c = (z-df*df)/(s+df); + w = r*s+c; + return 2.0*(df+w); + } + } +} + + +/* @(#)s_atan.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* atan(x) + * Method + * 1. Reduce x to positive by atan(x) = -atan(-x). + * 2. According to the integer k=4t+0.25 chopped, t=x, the argument + * is further reduced to one of the following intervals and the + * arctangent of t is evaluated by the corresponding formula: + * + * [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...) + * [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) ) + * [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) ) + * [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) ) + * [39/16,INF] atan(x) = atan(INF) + atan( -1/t ) + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +static const double atanhi[] = { + 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */ + 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */ + 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */ + 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */ +}; + +static const double atanlo[] = { + 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */ + 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */ + 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */ + 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */ +}; + +static const double aT_even[] = { + 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */ + 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */ + 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */ + 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */ + 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */ + 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */ +}; +static const double aT_odd[] = { + -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */ + -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */ + -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */ + -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */ + -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */ +}; + +double js_atan(double x) +{ + double w,s1,s2,z; + int ix,hx,id; + + hx = get_high_word(x); + ix = hx&0x7fffffff; + if(ix>=0x44100000) { /* if |x| >= 2^66 */ + if(ix>0x7ff00000|| + (ix==0x7ff00000&&(get_low_word(x)!=0))) + return x+x; /* NaN */ + if(hx>0) return atanhi[3]+atanlo[3]; + else return -atanhi[3]-atanlo[3]; + } if (ix < 0x3fdc0000) { /* |x| < 0.4375 */ + if (ix < 0x3e200000) { /* |x| < 2^-29 */ + if(huge+x>one) return x; /* raise inexact */ + } + id = -1; + } else { + x = fabs(x); + if (ix < 0x3ff30000) { /* |x| < 1.1875 */ + if (ix < 0x3fe60000) { /* 7/16 <=|x|<11/16 */ + id = 0; x = (2.0*x-one)/(2.0+x); + } else { /* 11/16<=|x|< 19/16 */ + id = 1; x = (x-one)/(x+one); + } + } else { + if (ix < 0x40038000) { /* |x| < 2.4375 */ + id = 2; x = (x-1.5)/(one+1.5*x); + } else { /* 2.4375 <= |x| < 2^66 */ + id = 3; x = -1.0/x; + } + }} + /* end of argument reduction */ + z = x*x; + w = z*z; + /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ + s1 = z*eval_poly(w, aT_even, 6); + s2 = w*eval_poly(w, aT_odd, 5); + if (id<0) return x - x*(s1+s2); + else { + z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); + return (hx<0)? -z:z; + } +} + +/* @(#)e_atan2.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_atan2(y,x) + * Method : + * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x). + * 2. Reduce x to positive by (if x and y are unexceptional): + * ARG (x+iy) = arctan(y/x) ... if x > 0, + * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, + * + * Special cases: + * + * ATAN2((anything), NaN ) is NaN; + * ATAN2(NAN , (anything) ) is NaN; + * ATAN2(+-0, +(anything but NaN)) is +-0 ; + * ATAN2(+-0, -(anything but NaN)) is +-pi ; + * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; + * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; + * ATAN2(+-(anything but INF and NaN), -INF) is +-pi; + * ATAN2(+-INF,+INF ) is +-pi/4 ; + * ATAN2(+-INF,-INF ) is +-3pi/4; + * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +static const double +pi_o_4 = 7.8539816339744827900E-01, /* 0x3FE921FB, 0x54442D18 */ +pi_o_2 = 1.5707963267948965580E+00, /* 0x3FF921FB, 0x54442D18 */ +pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ + +double js_atan2(double y, double x) +{ + double z; + int k,m,hx,hy,ix,iy; + unsigned lx,ly; + + EXTRACT_WORDS(hx, lx, x); + EXTRACT_WORDS(hy, ly, y); + ix = hx&0x7fffffff; + iy = hy&0x7fffffff; + if(((ix|((lx|-lx)>>31))>0x7ff00000)|| + ((iy|((ly|-ly)>>31))>0x7ff00000)) /* x or y is NaN */ + return x+y; + if(((hx-0x3ff00000)|lx)==0) + return js_atan(y); /* x=1.0 */ + m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if((iy|ly)==0) { + z = 0; + goto done; + } + /* when x = 0 */ + if((ix|lx)==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* when x is INF */ + if(ix==0x7ff00000) { + if(iy==0x7ff00000) { + z = pi_o_4; + } else { + z = 0; + } + goto done; + } + /* when y is INF */ + if(iy==0x7ff00000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* compute y/x */ + k = (iy-ix)>>20; + if(k > 60) { + z=pi_o_2+0.5*pi_lo; /* |y/x| > 2**60 */ + } else if(hx<0&&k<-60) { + z=0.0; /* |y|/x < -2**60 */ + } else { + z=js_atan(fabs(y/x)); /* safe to do y/x */ + } + done: + switch (m) { + case 0: + return z ; /* atan(+,+) */ + case 1: + z = set_high_word(z, get_high_word(z) ^ 0x80000000); + return z ; /* atan(-,+) */ + case 2: + return pi-(z-pi_lo);/* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo)-pi;/* atan(-,-) */ + } +} + +/* @(#)e_exp.c 1.6 04/04/22 */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_exp(x) + * Returns the exponential of x. + * + * Method + * 1. Argument reduction: + * Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658. + * Given x, find r and integer k such that + * + * x = k*ln2 + r, |r| <= 0.5*ln2. + * + * Here r will be represented as r = hi-lo for better + * accuracy. + * + * 2. Approximation of exp(r) by a special rational function on + * the interval [0,0.34658]: + * Write + * R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ... + * We use a special Remes algorithm on [0,0.34658] to generate + * a polynomial of degree 5 to approximate R. The maximum error + * of this polynomial approximation is bounded by 2**-59. In + * other words, + * R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5 + * (where z=r*r, and the values of P1 to P5 are listed below) + * and + * | 5 | -59 + * | 2.0+P1*z+...+P5*z - R(z) | <= 2 + * | | + * The computation of exp(r) thus becomes + * 2*r + * exp(r) = 1 + ------- + * R - r + * r*R1(r) + * = 1 + r + ----------- (for better accuracy) + * 2 - R1(r) + * where + * 2 4 10 + * R1(r) = r - (P1*r + P2*r + ... + P5*r ). + * + * 3. Scale back to obtain exp(x): + * From step 1, we have + * exp(x) = 2^k * exp(r) + * + * Special cases: + * exp(INF) is INF, exp(NaN) is NaN; + * exp(-INF) is 0, and + * for finite argument, only exp(0)=1 is exact. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Misc. info. + * For IEEE double + * if x > 7.09782712893383973096e+02 then exp(x) overflow + * if x < -7.45133219101941108420e+02 then exp(x) underflow + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +static const double +two = 2.0, +halF[2] = {0.5,-0.5,}, +twom1000= 9.33263618503218878990e-302, /* 2**-1000=0x01700000,0*/ +o_threshold= 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */ +u_threshold= -7.45133219101941108420e+02, /* 0xc0874910, 0xD52D3051 */ +ln2HI[2] ={ 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */ + -6.93147180369123816490e-01,},/* 0xbfe62e42, 0xfee00000 */ +ln2LO[2] ={ 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */ + -1.90821492927058770002e-10,},/* 0xbdea39ef, 0x35793c76 */ + invln2 = 1.44269504088896338700e+00; /* 0x3ff71547, 0x652b82fe */ +static const double P[] = { + /* P1 */ 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ + /* P2 */ -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ + /* P3 */ 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ + /* P4 */ -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ + /* P5 */ 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */ +}; + +/* compute exp(z+w)*2^n */ +static double kernel_exp(double z, double w, double lo, double hi, int n) +{ + int j; + double t, t1, r; + t = z*z; + t1 = z - t*eval_poly(t, P, 5); + r = (z*t1)/(t1-two) - (w+z*w); + z = one-((lo + r)-hi); + j = get_high_word(z); + j += (n<<20); + if((j>>20)<=0) + z = js_scalbn(z,n); /* subnormal output */ + else + z = set_high_word(z, get_high_word(z) + (n<<20)); + return z; +} + +double js_exp(double x) +{ + double hi,lo,t; + int k,xsb; + unsigned hx; + + hx = get_high_word(x); /* high word of x */ + xsb = (hx>>31)&1; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out non-finite argument */ + if(hx >= 0x40862E42) { /* if |x|>=709.78... */ + if(hx>=0x7ff00000) { + if(((hx&0xfffff)|get_low_word(x))!=0) + return x+x; /* NaN */ + else return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */ + } + if(x > o_threshold) return huge*huge; /* overflow */ + if(x < u_threshold) return twom1000*twom1000; /* underflow */ + } + + /* argument reduction */ + if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ + hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb; + } else { + k = (int)(invln2*x+halF[xsb]); + t = k; + hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + lo = t*ln2LO[0]; + } + x = hi - lo; + } + else if(hx < 0x3e300000) { /* when |x|<2**-28 */ + if(huge+x>one) return one+x;/* trigger inexact */ + k = 0; /* avoid warning */ + } + else k = 0; + + /* x is now in primary range */ + if (k == 0) { + lo = 0; + hi = x; + } + return kernel_exp(x, 0, lo, hi, k); +} + + +/* @(#)e_pow.c 1.5 04/04/22 SMI */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_pow(x,y) return x**y + * + * n + * Method: Let x = 2 * (1+f) + * 1. Compute and return log2(x) in two pieces: + * log2(x) = w1 + w2, + * where w1 has 53-24 = 29 bit trailing zeros. + * 2. Perform y*log2(x) = n+y' by simulating multi-precision + * arithmetic, where |y'|<=0.5. + * 3. Return x**y = 2**n*exp(y'*log2) + * + * Special cases: + * 1. (anything) ** 0 is 1 + * 2. (anything) ** 1 is itself + * 3. (anything) ** NAN is NAN + * 4. NAN ** (anything except 0) is NAN + * 5. +-(|x| > 1) ** +INF is +INF + * 6. +-(|x| > 1) ** -INF is +0 + * 7. +-(|x| < 1) ** +INF is +0 + * 8. +-(|x| < 1) ** -INF is +INF + * 9. +-1 ** +-INF is NAN + * 10. +0 ** (+anything except 0, NAN) is +0 + * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 + * 12. +0 ** (-anything except 0, NAN) is +INF + * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF + * 14. -0 ** (odd integer) = -( +0 ** (odd integer) ) + * 15. +INF ** (+anything except 0,NAN) is +INF + * 16. +INF ** (-anything except 0,NAN) is +0 + * 17. -INF ** (anything) = -0 ** (-anything) + * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) + * 19. (-anything except 0 and inf) ** (non-integer) is NAN + * + * Accuracy: + * pow(x,y) returns x**y nearly rounded. In particular + * pow(integer,integer) + * always returns the correct integer provided it is + * representable. + * + * Constants : + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +static const double +bp[] = {1.0, 1.5,}, +dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */ +dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */ +two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ +lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ +lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ +ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */ +cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ +cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ +cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ +ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ +ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ +ivln2_l = 1.92596299112661746887e-08, /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ +ivlg10b2 = 0.3010299956639812, /* 0x3fd34413509f79ff 1/log2(10) */ +ivlg10b2_h = 0.30102992057800293, /* 0x3fd3441300000000 1/log2(10) high */ +ivlg10b2_l = 7.508597826552624e-8; /* 0x3e7427de7fbcc47c 1/log2(10) low */ + +static const double L_tab[] = { + /* L1 */ 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ + /* L2 */ 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ + /* L3 */ 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ + /* L4 */ 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ + /* L5 */ 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ + /* L6 */ 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ +}; + +/* compute (t1, t2) = log2(ax). is_small_ax is true if abs(ax)<= 2**-20 */ +static void kernel_log2(double *pt1, double *pt2, double ax) +{ + double t, u, v, t1, t2, r; + int n, j, ix, k; + double ss, s2, s_h, s_l, t_h, t_l, p_l, p_h, z_h, z_l; + + n = 0; + ix = get_high_word(ax); + /* take care subnormal number */ + if(ix<0x00100000) + {ax *= two53; n -= 53; ix = get_high_word(ax); } + n += ((ix)>>20)-0x3ff; + j = ix&0x000fffff; + /* determine interval */ + ix = j|0x3ff00000; /* normalize ix */ + if(j<=0x3988E) k=0; /* |x|>1)|0x20000000)+0x00080000+(k<<18)); + t_l = ax - (t_h-bp[k]); + s_l = v*((u-s_h*t_h)-s_h*t_l); + /* compute log(ax) */ + s2 = ss*ss; + r = s2*s2*eval_poly(s2, L_tab, 6); + r += s_l*(s_h+ss); + s2 = s_h*s_h; + t_h = zero_low(3.0+s2+r); + t_l = r-((t_h-3.0)-s2); + /* u+v = ss*(1+...) */ + u = s_h*t_h; + v = s_l*t_h+t_l*ss; + /* 2/(3log2)*(ss+...) */ + p_h = zero_low(u+v); + p_l = v-(p_h-u); + z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l*p_h+p_l*cp+dp_l[k]; + /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (double)n; + t1 = zero_low(((z_h+z_l)+dp_h[k])+t); + t2 = z_l-(((t1-t)-dp_h[k])-z_h); + + *pt1 = t1; + *pt2 = t2; +} + +/* flag = 0: log2() + flag = 1: log() + flag = 2: log10() +*/ +static double js_log_internal(double x, int flag) +{ + double p_h, p_l, t, u, v; + int hx, lx; + + EXTRACT_WORDS(hx, lx, x); + if (hx <= 0) { + if (((hx&0x7fffffff)|lx)==0) + return -INFINITY; /* log(+-0)=-inf */ + if (hx<0) + return NAN; /* log(-#) = NaN */ + } else if (hx >= 0x7ff00000) { + /* log(inf) = inf, log(nan) = nan */ + return x+x; + } + kernel_log2(&p_h, &p_l, x); + + t = p_h + p_l; + if (flag == 0) { + return t; + } else { + t = zero_low(t); + if (flag == 1) { + /* multiply (p_l+p_h) by lg2 */ + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + } else { + /* mutiply (p_l+p_h) by 1/log2(10) */ + u = t*ivlg10b2_h; + v = (p_l-(t-p_h))*ivlg10b2+t*ivlg10b2_l; + } + return u+v; + } +} + +double js_log2(double x) +{ + return js_log_internal(x, 0); +} + +double js_log(double x) +{ + return js_log_internal(x, 1); +} + +double js_log10(double x) +{ + return js_log_internal(x, 2); +} + +double js_pow(double x, double y) +{ + double z,ax,p_h,p_l; + double y1,t1,t2,s,t,u,v,w; + int i,j,k,yisint,n; + int hx,hy,ix,iy; + unsigned lx,ly; + + EXTRACT_WORDS(hx, lx, x); + EXTRACT_WORDS(hy, ly, y); + ix = hx&0x7fffffff; iy = hy&0x7fffffff; + + /* y==zero: x**0 = 1 */ + if((iy|ly)==0) return one; + + /* +-NaN return x+y */ + if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) || + iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0))) + return x+y; + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if(hx<0) { + if(iy>=0x43400000) yisint = 2; /* even integer y */ + else if(iy>=0x3ff00000) { + k = (iy>>20)-0x3ff; /* exponent */ + if(k>20) { + j = ly>>(52-k); + if((j<<(52-k))==ly) yisint = 2-(j&1); + } else if(ly==0) { + j = iy>>(20-k); + if((j<<(20-k))==iy) yisint = 2-(j&1); + } + } + } + + /* special value of y */ + if(ly==0) { + if (iy==0x7ff00000) { /* y is +-inf */ + if(((ix-0x3ff00000)|lx)==0) + return y - y; /* inf**+-1 is NaN */ + else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */ + return (hy>=0)? y: zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy<0)?-y: zero; + } + if(iy==0x3ff00000) { /* y is +-1 */ + if(hy<0) return one/x; else return x; + } + if(hy==0x40000000) return x*x; /* y is 2 */ + if(hy==0x3fe00000) { /* y is 0.5 */ + if(hx>=0) /* x >= +0 */ + return js_sqrt(x); + } + } + + ax = fabs(x); + /* special value of x */ + if(lx==0) { + if(ix==0x7ff00000||ix==0||ix==0x3ff00000){ + z = ax; /*x is +-0,+-inf,+-1*/ + if(hy<0) z = one/z; /* z = (1/|x|) */ + if(hx<0) { + if(((ix-0x3ff00000)|yisint)==0) { + z = (z-z)/(z-z); /* (-1)**non-int is NaN */ + } else if(yisint==1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + } + + n = (hx>>31)+1; + + /* (x<0)**(non-int) is NaN */ + if((n|yisint)==0) return (x-x)/(x-x); + + s = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if((n|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */ + + /* |y| is huge */ + if(iy>0x41e00000) { /* if |y| > 2**31 */ + if(iy>0x43f00000){ /* if |y| > 2**64, must o/uflow */ + if(ix<=0x3fefffff) return (hy<0)? huge*huge:tiny*tiny; + if(ix>=0x3ff00000) return (hy>0)? huge*huge:tiny*tiny; + } + /* over/underflow if x is not close to one */ + if(ix<0x3fefffff) return (hy<0)? s*huge*huge:s*tiny*tiny; + if(ix>0x3ff00000) return (hy>0)? s*huge*huge:s*tiny*tiny; + t = ax-one; /* t has 20 trailing zeros */ + w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25)); + u = ivln2_h*t; /* ivln2_h has 21 sig. bits */ + v = t*ivln2_l-w*ivln2; + t1 = zero_low(u+v); + t2 = v-(t1-u); + } else { + kernel_log2(&t1, &t2, ax); + } + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + y1 = zero_low(y); + p_l = (y-y1)*t1+y*t2; + p_h = y1*t1; + z = p_l+p_h; + EXTRACT_WORDS(j, i, z); + if (j>=0x40900000) { /* z >= 1024 */ + if(((j-0x40900000)|i)!=0) /* if z > 1024 */ + return s*huge*huge; /* overflow */ + else { + if(p_l+ovt>z-p_h) return s*huge*huge; /* overflow */ + } + } else if((j&0x7fffffff)>=0x4090cc00 ) { /* z <= -1075 */ + if(((j-0xc090cc00)|i)!=0) /* z < -1075 */ + return s*tiny*tiny; /* underflow */ + else { + if(p_l<=z-p_h) return s*tiny*tiny; /* underflow */ + } + } + /* + * compute 2**(p_h+p_l) + */ + i = j&0x7fffffff; + k = (i>>20)-0x3ff; + n = 0; + if(i>0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j+(0x00100000>>(k+1)); + k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */ + t = zero; + t = set_high_word(t, n&~(0x000fffff>>k)); + n = ((n&0x000fffff)|0x00100000)>>(20-k); + if(j<0) n = -n; + p_h -= t; + } + /* multiply (p_l+p_h) by lg2 */ + t = zero_low(p_l+p_h); + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + z = u+v; + w = v-(z-u); + return s * kernel_exp(z, w, 0, z, n); +} + diff --git a/lib/mquickjs/libm.h b/lib/mquickjs/libm.h new file mode 100644 index 00000000..17e94e0e --- /dev/null +++ b/lib/mquickjs/libm.h @@ -0,0 +1,46 @@ +/* + * Tiny Math Library + * + * Copyright (c) 2024 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +double js_scalbn(double x, int n); +double js_floor(double x); +double js_ceil(double x); +double js_trunc(double x); +double js_round_inf(double a); +double js_fabs(double x); +double js_sqrt(double x); +int32_t js_lrint(double a); +double js_fmod(double x, double y); +double js_sin(double x); +double js_cos(double x); +double js_tan(double x); +double js_acos(double x); +double js_asin(double x); +double js_atan(double x); +double js_atan2(double y, double x); +double js_exp(double x); +double js_log(double x); +double js_log2(double x); +double js_log10(double x); +double js_pow(double x, double y); +/* exported only for tests */ +int js_rem_pio2(double x, double *y); diff --git a/lib/mquickjs/list.h b/lib/mquickjs/list.h new file mode 100644 index 00000000..5c182340 --- /dev/null +++ b/lib/mquickjs/list.h @@ -0,0 +1,99 @@ +/* + * Linux klist like system + * + * Copyright (c) 2016-2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIST_H +#define LIST_H + +#ifndef NULL +#include +#endif + +struct list_head { + struct list_head *prev; + struct list_head *next; +}; + +#define LIST_HEAD_INIT(el) { &(el), &(el) } + +/* return the pointer of type 'type *' containing 'el' as field 'member' */ +#define list_entry(el, type, member) container_of(el, type, member) + +static inline void init_list_head(struct list_head *head) +{ + head->prev = head; + head->next = head; +} + +/* insert 'el' between 'prev' and 'next' */ +static inline void __list_add(struct list_head *el, + struct list_head *prev, struct list_head *next) +{ + prev->next = el; + el->prev = prev; + el->next = next; + next->prev = el; +} + +/* add 'el' at the head of the list 'head' (= after element head) */ +static inline void list_add(struct list_head *el, struct list_head *head) +{ + __list_add(el, head, head->next); +} + +/* add 'el' at the end of the list 'head' (= before element head) */ +static inline void list_add_tail(struct list_head *el, struct list_head *head) +{ + __list_add(el, head->prev, head); +} + +static inline void list_del(struct list_head *el) +{ + struct list_head *prev, *next; + prev = el->prev; + next = el->next; + prev->next = next; + next->prev = prev; + el->prev = NULL; /* fail safe */ + el->next = NULL; /* fail safe */ +} + +static inline int list_empty(struct list_head *el) +{ + return el->next == el; +} + +#define list_for_each(el, head) \ + for(el = (head)->next; el != (head); el = el->next) + +#define list_for_each_safe(el, el1, head) \ + for(el = (head)->next, el1 = el->next; el != (head); \ + el = el1, el1 = el->next) + +#define list_for_each_prev(el, head) \ + for(el = (head)->prev; el != (head); el = el->prev) + +#define list_for_each_prev_safe(el, el1, head) \ + for(el = (head)->prev, el1 = el->prev; el != (head); \ + el = el1, el1 = el->prev) + +#endif /* LIST_H */ diff --git a/lib/mquickjs/mquickjs.c b/lib/mquickjs/mquickjs.c new file mode 100644 index 00000000..a950f3c3 --- /dev/null +++ b/lib/mquickjs/mquickjs.c @@ -0,0 +1,18324 @@ +/* + * Micro QuickJS Javascript Engine + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "dtoa.h" +#include "mquickjs_priv.h" + +/* + TODO: + - regexp: better error position info + - use a specific MTAG for short functions instead of an immediate value + - use hash table for atoms + - set the length accessors as non configurable so that the + 'get_length' instruction optimizations are always safe. + - memory: + - fix stack_bottom logic + - launch gc at regular intervals + - only launch compaction when needed (handle free blocks in malloc()) + - avoid pass to rehash the properties + - ensure no undefined bytes (e.g. at end of JSString) in + saved bytecode ? + - reduced memory usage: + - reduce JSFunctionBytecode size (remove source_pos) + - do not explicitly store function names for get/set/bound + - use JSSTDLibraryDef fields instead of copying them to JSContext ? +*/ + +#define __exception __attribute__((warn_unused_result)) + +#define JS_STACK_SLACK 16 /* additional free space on the stack */ +/* min free size in bytes between heap_free and the bottom of the stack */ +#define JS_MIN_FREE_SIZE 512 +/* minimum free size in bytes to create the out of memory object */ +#define JS_MIN_CRITICAL_FREE_SIZE (JS_MIN_FREE_SIZE - 256) +#define JS_MAX_LOCAL_VARS 65535 +#define JS_MAX_FUNC_STACK_SIZE 65535 +#define JS_MAX_ARGC 65535 +/* maximum number of recursing JS_Call() */ +#define JS_MAX_CALL_RECURSE 8 + + +#define JS_VALUE_IS_BOTH_INT(a, b) ((((a) | (b)) & 1) == 0) +#define JS_VALUE_IS_BOTH_SHORT_FLOAT(a, b) (((((a) - JS_TAG_SHORT_FLOAT) | ((b) - JS_TAG_SHORT_FLOAT)) & 7) == 0) + +static __maybe_unused const char *js_mtag_name[JS_MTAG_COUNT] = { + "free", + "object", + "float64", + "string", + "func_bytecode", + "value_array", + "byte_array", + "varref", +}; + +/* function call flags (max 31 bits) */ +#define FRAME_CF_ARGC_MASK 0xffff +/* FRAME_CF_CTOR */ +#define FRAME_CF_POP_RET (1 << 17) /* pop the return value */ +#define FRAME_CF_PC_ADD1 (1 << 18) /* increment the PC by 1 instead of 3 */ + +#define JS_MB_PAD(n) (JSW * 8 - (n)) + +typedef struct { + JS_MB_HEADER; + JSWord dummy: JS_MB_PAD(JS_MTAG_BITS); +} JSMemBlockHeader; + +typedef struct { + JS_MB_HEADER; + /* in JSWords excluding the header. Free blocks of JSW bytes + are only generated by js_shrink() and may not be always + compacted */ + JSWord size: JS_MB_PAD(JS_MTAG_BITS); +} JSFreeBlock; + +#if JSW == 8 +#define JS_STRING_LEN_MAX 0x7ffffffe +#else +#define JS_STRING_LEN_MAX ((1 << (32 - JS_MTAG_BITS - 3)) - 1) +#endif + +typedef struct { + JS_MB_HEADER; + JSWord is_unique: 1; + JSWord is_ascii: 1; + /* true if the string content represents a number, only meaningful + is is_unique = true */ + JSWord is_numeric: 1; + JSWord len: JS_MB_PAD(JS_MTAG_BITS + 3); + uint8_t buf[]; +} JSString; + +typedef struct { + JSWord string_buf[sizeof(JSString) / sizeof(JSWord)]; /* for JSString */ + uint8_t buf[5]; +} JSStringCharBuf; + +#define JS_BYTE_ARRAY_SIZE_MAX ((1 << (32 - JS_MTAG_BITS)) - 1) + +typedef struct { + JS_MB_HEADER; + JSWord size: JS_MB_PAD(JS_MTAG_BITS); + uint8_t buf[]; +} JSByteArray; + +#define JS_VALUE_ARRAY_SIZE_MAX ((1 << (32 - JS_MTAG_BITS)) - 1) + +typedef struct { + JS_MB_HEADER; + JSWord size: JS_MB_PAD(JS_MTAG_BITS); + JSValue arr[]; +} JSValueArray; + +typedef struct JSVarRef { + JS_MB_HEADER; + JSWord is_detached : 1; + JSWord dummy: JS_MB_PAD(JS_MTAG_BITS + 1); + union { + JSValue value; /* is_detached = true */ + struct { + JSValue next; /* is_detached = false: JS_NULL or JSVarRef, + must be at the same address as 'value' */ + JSValue *pvalue; + }; + } u; +} JSVarRef; + +typedef struct { + JS_MB_HEADER; + JSWord dummy: JS_MB_PAD(JS_MTAG_BITS); +#ifdef JS_PTR64 + struct { + double dval; + } u; +#else + /* unaligned 64 bit access in 32-bit mode */ + struct __attribute__((packed)) { + double dval; + } u; +#endif +} JSFloat64; + +typedef struct JSROMClass { + JS_MB_HEADER; + JSWord dummy: JS_MB_PAD(JS_MTAG_BITS); + JSValue props; + int32_t ctor_idx; /* -1 if defining a normal object */ + JSValue proto_props; + JSValue parent_class; /* JSROMClass or JS_NULL */ +} JSROMClass; + +#define N_ROM_ATOM_TABLES_MAX 2 + +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define JS_INTERRUPT_COUNTER_INIT 10000 + +#define JS_STRING_POS_CACHE_SIZE 2 +#define JS_STRING_POS_CACHE_MIN_LEN 16 + +typedef enum { + POS_TYPE_UTF8, + POS_TYPE_UTF16, +} StringPosTypeEnum; + +typedef struct { + JSValue str; /* JS_NULL or weak reference to a JSString. It + contains at least JS_STRING_POS_CACHE_MIN_LEN + bytes and is a non ascii string */ + uint32_t str_pos[2]; /* 0 = UTF-8 pos (in bytes), 1 = UTF-16 pos */ +} JSStringPosCacheEntry; + +struct JSContext { + /* memory map: + Stack + Free area + Heap + JSContext + */ + uint8_t *heap_base; + uint8_t *heap_free; /* first free area */ + uint8_t *stack_top; + JSValue *stack_bottom; /* sp must always be higher than stack_bottom */ + JSValue *sp; /* current stack pointer */ + JSValue *fp; /* current frame pointer, stack_top if none */ + uint32_t min_free_size; /* min free size between heap_free and the + bottom of the stack */ + BOOL in_out_of_memory : 8; /* != 0 if generating the out of memory object */ + uint8_t n_rom_atom_tables; + uint8_t string_pos_cache_counter; /* used for string_pos_cache[] update */ + uint16_t class_count; /* number of classes including user classes */ + int16_t interrupt_counter; + BOOL current_exception_is_uncatchable : 8; + struct JSParseState *parse_state; /* != NULL during JS_Eval() */ + int unique_strings_len; + int js_call_rec_count; /* number of recursing JS_Call() */ + JSGCRef *top_gc_ref; /* used to reference temporary GC roots (stack top) */ + JSGCRef *last_gc_ref; /* used to reference temporary GC roots (list) */ + const JSWord *atom_table; /* constant atom table */ + /* 'n_rom_atom_tables' atom tables from code loaded from rom */ + const JSValueArray *rom_atom_tables[N_ROM_ATOM_TABLES_MAX]; + const JSCFunctionDef *c_function_table; + const JSCFinalizer *c_finalizer_table; + uint64_t random_state; + JSInterruptHandler *interrupt_handler; + JSWriteFunc *write_func; /* for the various dump functions */ + void *opaque; + JSValue *class_obj; /* same as class_proto + class_count */ + JSStringPosCacheEntry string_pos_cache[JS_STRING_POS_CACHE_SIZE]; + + /* must only contain JSValue from this point (see JS_GC()) */ + JSValue unique_strings; /* JSValueArray of sorted strings or JS_NULL */ + + JSValue current_exception; /* currently pending exception, must + come after unique_strings */ +#ifdef DEBUG_GC + JSValue dummy_block; /* dummy memory block near the start of the memory */ +#endif + JSValue empty_props; /* empty prop list, for objects with no properties */ + JSValue global_obj; + JSValue minus_zero; /* minus zero float64 value */ + JSValue class_proto[]; /* prototype for each class (class_count + element, then class_count elements for + class_obj */ +}; + +typedef enum { + JS_VARREF_KIND_ARG, /* var_idx is an argument of the parent function */ + JS_VARREF_KIND_VAR, /* var_idx is a local variable of the parent function */ + JS_VARREF_KIND_VAR_REF, /* var_idx is a var ref of the parent function */ + JS_VARREF_KIND_GLOBAL, /* to debug */ +} JSVarRefKindEnum; + +typedef struct JSObject JSObject; + +typedef struct { + /* string, short integer or JS_UNINITIALIZED if no property. If + the last property is uninitialized, hash_next = 2 * + first_free. */ + JSValue key; + /* JS_PROP_GETSET: JSValueArray of two elements + JS_PROP_VARREF: JSVarRef */ + JSValue value; + /* XXX: when JSW = 8, could use 32 bits for hash_next (faster) */ + uint32_t hash_next : 30; /* low bit at zero */ + uint32_t prop_type : 2; +} JSProperty; + +typedef struct { + JSValue func_bytecode; /* JSFunctionBytecode */ + JSValue var_refs[]; /* JSValueArray */ +} JSClosureData; + +typedef struct { + uint32_t idx; + JSValue params; /* optional associated parameters */ +} JSCFunctionData; + +typedef struct { + JSValue tab; /* JS_NULL or JSValueArray */ + uint32_t len; /* maximum value: 2^30-1 */ +} JSArrayData; + +typedef struct { + JSValue message; /* string or JS_NULL */ + JSValue stack; /* string or JS_NULL */ +} JSErrorData; + +typedef struct { + JSValue byte_buffer; /* JSByteBuffer */ +} JSArrayBuffer; + +typedef struct { + JSValue buffer; /* corresponding array buffer */ + uint32_t len; /* in elements */ + uint32_t offset; /* in elements */ +} JSTypedArray; + +typedef struct { + JSValue source; + JSValue byte_code; + int last_index; +} JSRegExp; + +typedef struct { + void *opaque; +} JSObjectUserData; + +struct JSObject { + JS_MB_HEADER; + JSWord class_id: 8; + JSWord extra_size: JS_MB_PAD(JS_MTAG_BITS + 8); /* object additional size, in JSValue */ + + JSValue proto; /* JSObject or JS_NULL */ + /* JSValueArray. structure: + prop_count (number of properties excluding deleted ones) + hash_mask (= hash_size - 1) + hash_table[hash_size] (0 = end of list or offset in array) + JSProperty props[] + */ + JSValue props; + /* number of additional fields depends on the object */ + union { + JSClosureData closure; + JSCFunctionData cfunc; + JSArrayData array; + JSErrorData error; + JSArrayBuffer array_buffer; + JSTypedArray typed_array; + JSRegExp regexp; + JSObjectUserData user; + } u; +}; + +typedef struct JSFunctionBytecode { + JS_MB_HEADER; + JSWord has_arguments : 1; /* only used during parsing */ + JSWord has_local_func_name : 1; /* only used during parsing */ + JSWord has_column : 1; /* column debug info is present */ + /* during parse: variable index + 1 of hoisted function, 0 otherwise */ + JSWord arg_count : 16; + JSWord dummy: JS_MB_PAD(JS_MTAG_BITS + 3 + 16); + + JSValue func_name; /* JS_NULL if anonymous function */ + JSValue byte_code; /* JS_NULL if the function is not parsed yet */ + JSValue cpool; /* constant pool */ + JSValue vars; /* only for debug */ + JSValue ext_vars; /* records of (var_name, var_kind (2 bits) var_idx (16 bits)) */ + uint16_t stack_size; /* maximum stack size */ + uint16_t ext_vars_len; /* XXX: only used during parsing */ + JSValue filename; /* filename in which the function is defined */ + JSValue pc2line; /* JSByteArray or JS_NULL if not initialized */ + uint32_t source_pos; /* only used during parsing (XXX: shrink) */ +} JSFunctionBytecode; + +static JSValue js_resize_value_array(JSContext *ctx, JSValue val, int new_size); +static int get_mblock_size(const void *ptr); +static JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto, int class_id, int extra_size); +static void js_shrink_byte_array(JSContext *ctx, JSValue *pval, int new_size); +static void build_backtrace(JSContext *ctx, JSValue error_obj, + const char *filename, int line_num, int col_num, int skip_level); +static JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val); +static JSByteArray *js_alloc_byte_array(JSContext *ctx, int size); +static JSValue js_new_c_function_proto(JSContext *ctx, int func_idx, JSValue proto, BOOL has_params, + JSValue params); +static int JS_ToUint8Clamp(JSContext *ctx, int *pres, JSValue val); +static JSValue js_set_prototype_internal(JSContext *ctx, JSValue obj, JSValue proto); +static JSValue js_resize_byte_array(JSContext *ctx, JSValue val, int new_size); +static JSValueArray *js_alloc_props(JSContext *ctx, int n); + +typedef enum OPCodeFormat { +#define FMT(f) OP_FMT_ ## f, +#define DEF(id, size, n_pop, n_push, f) +#include "mquickjs_opcode.h" +#undef DEF +#undef FMT +} OPCodeFormat; + +typedef enum OPCodeEnum { +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "mquickjs_opcode.h" +#undef def +#undef DEF +#undef FMT + OP_COUNT, +} OPCodeEnum; + +typedef struct { +#ifdef DUMP_BYTECODE + const char *name; +#endif + uint8_t size; /* in bytes */ + /* the opcodes remove n_pop items from the top of the stack, then + pushes n_pusch items */ + uint8_t n_pop; + uint8_t n_push; + uint8_t fmt; +} JSOpCode; + +static __maybe_unused const JSOpCode opcode_info[OP_COUNT] = { +#define FMT(f) +#ifdef DUMP_BYTECODE +#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, +#else +#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, +#endif +#include "mquickjs_opcode.h" +#undef DEF +#undef FMT +}; + +#include "mquickjs_atom.h" + +JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref) +{ + ref->prev = ctx->top_gc_ref; + ctx->top_gc_ref = ref; + ref->val = JS_UNDEFINED; + return &ref->val; +} + +JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref) +{ + ctx->top_gc_ref = ref->prev; + return ref->val; +} + +JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref) +{ + ref->prev = ctx->last_gc_ref; + ctx->last_gc_ref = ref; + ref->val = JS_UNDEFINED; + return &ref->val; +} + +void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref) +{ + JSGCRef **pref, *ref1; + pref = &ctx->last_gc_ref; + for(;;) { + ref1 = *pref; + if (ref1 == NULL) + abort(); + if (ref1 == ref) { + *pref = ref1->prev; + break; + } + pref = &ref1->prev; + } +} + +#undef JS_PUSH_VALUE +#undef JS_POP_VALUE + +#define JS_PUSH_VALUE(ctx, v) do { \ + v ## _ref.prev = ctx->top_gc_ref; \ + ctx->top_gc_ref = &v ## _ref; \ + v ## _ref.val = v; \ + } while (0) + +#define JS_POP_VALUE(ctx, v) do { \ + v = v ## _ref.val; \ + ctx->top_gc_ref = v ## _ref.prev; \ + } while (0) + +static JSValue js_get_atom(JSContext *ctx, int a) +{ + return JS_VALUE_FROM_PTR(&ctx->atom_table[a]); +} + +static force_inline JSValue JS_NewTailCall(int val) +{ + return JS_VALUE_MAKE_SPECIAL(JS_TAG_EXCEPTION, JS_EX_CALL + val); +} + +static inline JS_BOOL JS_IsExceptionOrTailCall(JSValue v) +{ + return JS_VALUE_GET_SPECIAL_TAG(v) == JS_TAG_EXCEPTION; +} + +static int js_get_mtag(void *ptr) +{ + return ((JSMemBlockHeader *)ptr)->mtag; +} + +static int check_free_mem(JSContext *ctx, JSValue *stack_bottom, uint32_t size) +{ +#ifdef DEBUG_GC + assert(ctx->sp >= stack_bottom); + /* don't start the GC before dummy_block is allocated */ + if (JS_IsPtr(ctx->dummy_block)) { + JS_GC(ctx); + } +#endif + if (((uint8_t *)stack_bottom - ctx->heap_free) < size + ctx->min_free_size) { + JS_GC(ctx); + if (((uint8_t *)stack_bottom - ctx->heap_free) < size + ctx->min_free_size) { + JS_ThrowOutOfMemory(ctx); + return -1; + } + } + return 0; +} + +/* check that 'len' values can be pushed on the stack. Return 0 if OK, + -1 if not enough space. May trigger a GC(). */ +int JS_StackCheck(JSContext *ctx, uint32_t len) +{ + JSValue *new_stack_bottom; + + len += JS_STACK_SLACK; + new_stack_bottom = ctx->sp - len; + if (check_free_mem(ctx, new_stack_bottom, len * sizeof(JSValue))) + return -1; + ctx->stack_bottom = new_stack_bottom; + return 0; +} + +static void *js_malloc(JSContext *ctx, uint32_t size, int mtag) +{ + JSMemBlockHeader *p; + + if (size == 0) + return NULL; + size = (size + JSW - 1) & ~(JSW - 1); + + if (check_free_mem(ctx, ctx->stack_bottom, size)) + return NULL; + + p = (JSMemBlockHeader *)ctx->heap_free; + ctx->heap_free += size; + + p->mtag = mtag; + p->gc_mark = 0; + p->dummy = 0; + return p; +} + +static void *js_mallocz(JSContext *ctx, uint32_t size, int mtag) +{ + uint8_t *ptr; + ptr = js_malloc(ctx, size, mtag); + if (!ptr) + return NULL; + if (size > sizeof(uint32_t)) { + memset(ptr + sizeof(uint32_t), 0, size - sizeof(uint32_t)); + } + return ptr; +} + +/* currently only free the last element */ +static void js_free(JSContext *ctx, void *ptr) +{ + uint8_t *ptr1; + if (!ptr) + return; + ptr1 = ptr; + ptr1 += get_mblock_size(ptr1); + if (ptr1 == ctx->heap_free) + ctx->heap_free = ptr; +} + +/* 'size' is in bytes and must be multiple of JSW and > 0 */ +static void set_free_block(void *ptr, uint32_t size) +{ + JSFreeBlock *p; + p = (JSFreeBlock *)ptr; + p->mtag = JS_MTAG_FREE; + p->gc_mark = 0; + p->size = (size - sizeof(JSFreeBlock)) / sizeof(JSWord); +} + +/* 'ptr' must be != NULL. new_size must be less or equal to the + current block size. */ +static void *js_shrink(JSContext *ctx, void *ptr, uint32_t new_size) +{ + uint32_t old_size; + uint32_t diff; + + new_size = (new_size + (JSW - 1)) & ~(JSW - 1); + + if (new_size == 0) { + js_free(ctx, ptr); + return NULL; + } + old_size = get_mblock_size(ptr); + assert(new_size <= old_size); + diff = old_size - new_size; + if (diff == 0) + return ptr; + set_free_block((uint8_t *)ptr + new_size, diff); + /* add a new free block after 'ptr' */ + return ptr; +} + +JSValue JS_Throw(JSContext *ctx, JSValue obj) +{ + ctx->current_exception = obj; + ctx->current_exception_is_uncatchable = FALSE; + return JS_EXCEPTION; +} + +/* return the byte length. 'buf' must contain UTF8_CHAR_LEN_MAX + 1 bytes */ +static int get_short_string(uint8_t *buf, JSValue val) +{ + int len; + len = unicode_to_utf8(buf, JS_VALUE_GET_SPECIAL_VALUE(val)); + buf[len] = '\0'; + return len; +} + +/* printf utility */ + +#define PF_ZERO_PAD (1 << 0) /* 0 */ +#define PF_ALT_FORM (1 << 1) /* # */ +#define PF_MARK_POS (1 << 2) /* + */ +#define PF_LEFT_ADJ (1 << 3) /* - */ +#define PF_PAD_POS (1 << 4) /* ' ' */ +#define PF_INT64 (1 << 5) /* l/ll */ + +static BOOL is_digit(int c) +{ + return (c >= '0' && c <= '9'); +} + +/* pad with chars 'c' */ +static void pad(JSWriteFunc *write_func, void *opaque, char c, + int width, int len) +{ + char buf[16]; + int l; + if (len >= width) + return; + width -= len; + memset(buf, c, min_int(sizeof(buf), width)); + while (width != 0) { + l = min_int(width, sizeof(buf)); + write_func(opaque, buf, l); + width -= l; + } +} + +/* The 'o' format can be used to print a JSValue. Only short int, + bool, null, undefined and string types are supported. */ +static void js_vprintf(JSWriteFunc *write_func, void *opaque, const char *fmt, va_list ap) +{ + const char *p; + int width, prec, flags, c; + char tmp_buf[32], *buf; + size_t len; + + while (*fmt != '\0') { + p = fmt; + while (*fmt != '%' && *fmt != '\0') + fmt++; + if (fmt > p) + write_func(opaque, p, fmt - p); + if (*fmt == '\0') + break; + fmt++; + /* get the flags */ + flags = 0; + for(;;) { + c = *fmt; + if (c == '0') { + flags |= PF_ZERO_PAD; + } else if (c == '#') { + flags |= PF_ALT_FORM; + } else if (c == '+') { + flags |= PF_MARK_POS; + } else if (c == '-') { + flags |= PF_LEFT_ADJ; + } else if (c == ' ') { + flags |= PF_MARK_POS; + } else { + break; + } + fmt++; + } + width = 0; + if (*fmt == '*') { + width = va_arg(ap, int); + } else { + while (is_digit(*fmt)) { + width = width * 10 + *fmt - '0'; + fmt++; + } + } + prec = 0; + if (*fmt == '.') { + fmt++; + if (*fmt == '*') { + prec = va_arg(ap, int); + } else { + while (is_digit(*fmt)) { + prec = prec * 10 + *fmt - '0'; + fmt++; + } + } + } + /* modifiers */ + for(;;) { + c = *fmt; + if (c == 'l') { + if (sizeof(long) == sizeof(int64_t) || fmt[-1] == 'l') + flags |= PF_INT64; + } else + if (c == 'z' || c == 't') { + if (sizeof(size_t) == sizeof(uint64_t)) + flags |= PF_INT64; + } else { + break; + } + fmt++; + } + + c = *fmt++; + /* XXX: not complete, just enough for our needs */ + buf = tmp_buf; + len = 0; + switch(c) { + case '%': + write_func(opaque, fmt - 1, 1); + break; + case 'c': + buf[0] = va_arg(ap, int); + len = 1; + flags &= ~PF_ZERO_PAD; + break; + case 's': + buf = va_arg(ap, char *); + if (!buf) + buf = "null"; + len = strlen(buf); + flags &= ~PF_ZERO_PAD; + break; + case 'd': + if (flags & PF_INT64) + len = i64toa(buf, va_arg(ap, int64_t)); + else + len = i32toa(buf, va_arg(ap, int32_t)); + break; + case 'u': + if (flags & PF_INT64) + len = u64toa(buf, va_arg(ap, uint64_t)); + else + len = u32toa(buf, va_arg(ap, uint32_t)); + break; + case 'x': + if (flags & PF_INT64) + len = u64toa_radix(buf, va_arg(ap, uint64_t), 16); + else + len = u64toa_radix(buf, va_arg(ap, uint32_t), 16); + break; + case 'p': + buf[0] = '0'; + buf[1] = 'x'; + len = u64toa_radix(buf + 2, (uintptr_t)va_arg(ap, void *), 16); + len += 2; + break; + case 'o': + { + JSValue val = (flags & PF_INT64) ? va_arg(ap, uint64_t) : va_arg(ap, uint32_t); + if (JS_IsInt(val)) { + len = i32toa(buf, JS_VALUE_GET_INT(val)); + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_IsShortFloat(val)) { + /* XXX: print it */ + buf = "[short_float]"; + goto do_strlen; + } else +#endif + if (!JS_IsPtr(val)) { + switch(JS_VALUE_GET_SPECIAL_TAG(val)) { + case JS_TAG_NULL: + buf = "null"; + goto do_strlen; + case JS_TAG_UNDEFINED: + buf = "undefined"; + goto do_strlen; + case JS_TAG_UNINITIALIZED: + buf = "uninitialized"; + goto do_strlen; + case JS_TAG_BOOL: + buf = JS_VALUE_GET_SPECIAL_VALUE(val) ? "true" : "false"; + goto do_strlen; + case JS_TAG_STRING_CHAR: + len = get_short_string((uint8_t *)buf, val); + break; + default: + buf = "[tag]"; + goto do_strlen; + } + } else { + void *ptr = JS_VALUE_TO_PTR(val); + int mtag = ((JSMemBlockHeader *)ptr)->mtag; + switch(mtag) { + case JS_MTAG_STRING: + { + JSString *p = ptr; + buf = (char *)p->buf; + len = p->len; + } + break; + default: + buf = "[mtag]"; + do_strlen: + len = strlen(buf); + break; + } + } + /* remove the trailing '\n' if any (used in error output) */ + if ((flags & PF_ALT_FORM) && len > 0 && buf[len - 1] == '\n') + len--; + flags &= ~PF_ZERO_PAD; + } + break; + default: + goto error; + } + if (flags & PF_ZERO_PAD) { + /* XXX: incorrect with prefix */ + pad(write_func, opaque, '0', width, len); + } else { + if (!(flags & PF_LEFT_ADJ)) + pad(write_func, opaque, ' ', width, len); + } + write_func(opaque, buf, len); + if (flags & PF_LEFT_ADJ) + pad(write_func, opaque, ' ', width, len); + } + return; + error: + return; +} + +/* used for the debug output */ +static void __js_printf_like(2, 3) js_printf(JSContext *ctx, + const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + js_vprintf(ctx->write_func, ctx->opaque, fmt, ap); + va_end(ap); +} + +static __maybe_unused void js_putchar(JSContext *ctx, uint8_t c) +{ + ctx->write_func(ctx->opaque, &c, 1); +} + +typedef struct { + char *ptr; + char *buf_end; + int len; +} SNPrintfState; + +static void snprintf_write_func(void *opaque, const void *buf, size_t buf_len) +{ + SNPrintfState *s = opaque; + size_t l; + s->len += buf_len; + l = min_size_t(buf_len, s->buf_end - s->ptr); + if (l != 0) { + memcpy(s->ptr, buf, l); + s->ptr += l; + } +} + +static int js_vsnprintf(char *buf, size_t buf_size, const char *fmt, va_list ap) +{ + SNPrintfState ss, *s = &ss; + s->ptr = buf; + s->buf_end = buf + max_size_t(buf_size, 1) - 1; + s->len = 0; + js_vprintf(snprintf_write_func, s, fmt, ap); + if (buf_size > 0) + *s->ptr = '\0'; + return s->len; +} + +static int __maybe_unused __js_printf_like(3, 4) js_snprintf(char *buf, size_t buf_size, const char *fmt, ...) +{ + va_list ap; + int ret; + va_start(ap, fmt); + ret = js_vsnprintf(buf, buf_size, fmt, ap); + va_end(ap); + return ret; +} + +JSValue __js_printf_like(3, 4) JS_ThrowError(JSContext *ctx, JSObjectClassEnum error_num, + const char *fmt, ...) +{ + JSObject *p; + va_list ap; + char buf[128]; + JSValue msg, error_obj; + JSGCRef msg_ref, error_obj_ref; + + va_start(ap, fmt); + js_vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + msg = JS_NewString(ctx, buf); + + JS_PUSH_VALUE(ctx, msg); + error_obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[error_num], JS_CLASS_ERROR, + sizeof(JSErrorData)); + JS_POP_VALUE(ctx, msg); + if (JS_IsException(error_obj)) + return error_obj; + + p = JS_VALUE_TO_PTR(error_obj); + p->u.error.message = msg; + p->u.error.stack = JS_NULL; + + /* in case of syntax error, the backtrace is added later */ + if (error_num != JS_CLASS_SYNTAX_ERROR) { + JS_PUSH_VALUE(ctx, error_obj); + build_backtrace(ctx, error_obj, NULL, 0, 0, 0); + JS_POP_VALUE(ctx, error_obj); + } + + return JS_Throw(ctx, error_obj); +} + +JSValue JS_ThrowOutOfMemory(JSContext *ctx) +{ + JSValue val; + if (ctx->in_out_of_memory) + return JS_Throw(ctx, JS_NULL); + ctx->in_out_of_memory = TRUE; + ctx->min_free_size = JS_MIN_CRITICAL_FREE_SIZE; + val = JS_ThrowInternalError(ctx, "out of memory"); + ctx->in_out_of_memory = FALSE; + ctx->min_free_size = JS_MIN_FREE_SIZE; + return val; +} + +#define JS_SHORTINT_MIN (-(1 << 30)) +#define JS_SHORTINT_MAX ((1 << 30) - 1) + +#ifdef JS_USE_SHORT_FLOAT + +#define JS_FLOAT64_VALUE_EXP_MIN (1023 - 127) +#define JS_FLOAT64_VALUE_ADDEND ((uint64_t)(JS_FLOAT64_VALUE_EXP_MIN - (JS_TAG_SHORT_FLOAT << 8)) << 52) + +/* 1 <= n <= 63 */ +static inline uint64_t rotl64(uint64_t a, int n) +{ + return (a << n) | (a >> (64 - n)); +} + +static double js_get_short_float(JSValue v) +{ + return uint64_as_float64(rotl64(v, 60) + JS_FLOAT64_VALUE_ADDEND); +} + +static JSValue js_to_short_float(double d) +{ + return rotl64(float64_as_uint64(d) - JS_FLOAT64_VALUE_ADDEND, 4); +} + +#endif /* JS_USE_SHORT_FLOAT */ + +static JSValue js_alloc_float64(JSContext *ctx, double d) +{ + JSFloat64 *f; + f = js_malloc(ctx, sizeof(JSFloat64), JS_MTAG_FLOAT64); + if (!f) + return JS_EXCEPTION; + f->u.dval = d; + return JS_VALUE_FROM_PTR(f); +} + +/* create a new float64 value which is known not to be a short integer */ +static JSValue __JS_NewFloat64(JSContext *ctx, double d) +{ + if (float64_as_uint64(d) == 0x8000000000000000) { + /* minus zero often happens, so it is worth having a constant + value */ + return ctx->minus_zero; + } else +#ifdef JS_USE_SHORT_FLOAT + /* Note: this test is false for NaN */ + if (fabs(d) >= 0x1p-127 && fabs(d) <= 0x1p+128) { + return js_to_short_float(d); + } else +#endif + { + return js_alloc_float64(ctx, d); + } +} + +static inline JSValue JS_NewShortInt(int32_t val) +{ + return JS_TAG_INT + (val << 1); +} + +#if defined(USE_SOFTFLOAT) +JSValue JS_NewFloat64(JSContext *ctx, double d) +{ + uint64_t a, m; + int e, b, shift; + JSValue v; + + a = float64_as_uint64(d); + if (a == 0) { + v = JS_NewShortInt(0); + } else { + e = (a >> 52) & 0x7ff; + if (e >= 1023 && e <= 1023 + 30 - 1) { + m = (a & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + shift = 52 - (e - 1023); + /* test if exact integer */ + if ((m & (((uint64_t)1 << shift) - 1)) != 0) + goto not_int; + b = m >> shift; + if (a >> 63) + b = -b; + v = JS_NewShortInt(b); + } else if (a == 0xc1d0000000000000) { + v = JS_NewShortInt(-(1 << 30)); + } else { + not_int: + v = __JS_NewFloat64(ctx, d); + } + } + return v; +} +#else +JSValue JS_NewFloat64(JSContext *ctx, double d) +{ + int32_t val; + if (d >= JS_SHORTINT_MIN && d <= JS_SHORTINT_MAX) { + val = (int32_t)d; + /* -0 cannot be represented as integer, so we compare the bit + representation */ + if (float64_as_uint64(d) == float64_as_uint64((double)val)) + return JS_NewShortInt(val); + } + return __JS_NewFloat64(ctx, d); +} +#endif + +static inline BOOL int64_is_short_int(int64_t val) +{ + return val >= JS_SHORTINT_MIN && val <= JS_SHORTINT_MAX; +} + +JSValue JS_NewInt64(JSContext *ctx, int64_t val) +{ + JSValue v; + if (likely(int64_is_short_int(val))) { + v = JS_NewShortInt(val); + } else { + v = __JS_NewFloat64(ctx, val); + } + return v; +} + +JSValue JS_NewInt32(JSContext *ctx, int32_t val) +{ + return JS_NewInt64(ctx, val); +} + +JSValue JS_NewUint32(JSContext *ctx, uint32_t val) +{ + return JS_NewInt64(ctx, val); +} + +static BOOL JS_IsPrimitive(JSContext *ctx, JSValue val) +{ + if (!JS_IsPtr(val)) { + return JS_VALUE_GET_SPECIAL_TAG(val) != JS_TAG_SHORT_FUNC; + } else { + return (js_get_mtag(JS_VALUE_TO_PTR(val)) != JS_MTAG_OBJECT); + } +} + +/* Note: short functions are not considered as objects by this function */ +static BOOL JS_IsObject(JSContext *ctx, JSValue val) +{ + if (!JS_IsPtr(val)) { + return FALSE; + } else { + JSObject *p = JS_VALUE_TO_PTR(val); + return (p->mtag == JS_MTAG_OBJECT); + } +} + +/* return -1 if not an object */ +int JS_GetClassID(JSContext *ctx, JSValue val) +{ + if (!JS_IsPtr(val)) { + return -1; + } else { + JSObject *p = JS_VALUE_TO_PTR(val); + if (p->mtag != JS_MTAG_OBJECT) + return -1; + else + return p->class_id; + } +} + +void JS_SetOpaque(JSContext *ctx, JSValue val, void *opaque) +{ + JSObject *p; + assert(JS_IsPtr(val)); + p = JS_VALUE_TO_PTR(val); + assert(p->mtag == JS_MTAG_OBJECT); + assert(p->class_id >= JS_CLASS_USER); + p->u.user.opaque = opaque; +} + +void *JS_GetOpaque(JSContext *ctx, JSValue val) +{ + JSObject *p; + assert(JS_IsPtr(val)); + p = JS_VALUE_TO_PTR(val); + assert(p->mtag == JS_MTAG_OBJECT); + assert(p->class_id >= JS_CLASS_USER); + return p->u.user.opaque; +} + +static JSObject *js_get_object_class(JSContext *ctx, JSValue val, int class_id) +{ + if (!JS_IsPtr(val)) { + return NULL; + } else { + JSObject *p = JS_VALUE_TO_PTR(val); + if (p->mtag != JS_MTAG_OBJECT || p->class_id != class_id) + return NULL; + else + return p; + } +} + +BOOL JS_IsFunction(JSContext *ctx, JSValue val) +{ + if (!JS_IsPtr(val)) { + return JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_SHORT_FUNC; + } else { + JSObject *p = JS_VALUE_TO_PTR(val); + return (p->mtag == JS_MTAG_OBJECT && + (p->class_id == JS_CLASS_CLOSURE || + p->class_id == JS_CLASS_C_FUNCTION)); + } +} + +static BOOL JS_IsFunctionObject(JSContext *ctx, JSValue val) +{ + if (!JS_IsPtr(val)) { + return FALSE; + } else { + JSObject *p = JS_VALUE_TO_PTR(val); + return (p->mtag == JS_MTAG_OBJECT && + (p->class_id == JS_CLASS_CLOSURE || + p->class_id == JS_CLASS_C_FUNCTION)); + } +} + +BOOL JS_IsError(JSContext *ctx, JSValue val) +{ + if (!JS_IsPtr(val)) { + return FALSE; + } else { + JSObject *p = JS_VALUE_TO_PTR(val); + return (p->mtag == JS_MTAG_OBJECT && p->class_id == JS_CLASS_ERROR); + } +} + +static force_inline BOOL JS_IsIntOrShortFloat(JSValue val) +{ +#ifdef JS_USE_SHORT_FLOAT + return JS_IsInt(val) || JS_IsShortFloat(val); +#else + return JS_IsInt(val); +#endif +} + +BOOL JS_IsNumber(JSContext *ctx, JSValue val) +{ + if (JS_IsIntOrShortFloat(val)) { + return TRUE; + } else if (JS_IsPtr(val)) { + void *ptr = JS_VALUE_TO_PTR(val); + return (js_get_mtag(ptr) == JS_MTAG_FLOAT64); + } else { + return FALSE; + } +} + +BOOL JS_IsString(JSContext *ctx, JSValue val) +{ + if (!JS_IsPtr(val)) { + return JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR; + } else { + void *ptr = JS_VALUE_TO_PTR(val); + return (js_get_mtag(ptr) == JS_MTAG_STRING); + } +} + +static JSString *js_alloc_string(JSContext *ctx, uint32_t buf_len) +{ + JSString *p; + + if (buf_len > JS_STRING_LEN_MAX) { + JS_ThrowInternalError(ctx, "string too long"); + return NULL; + } + p = js_malloc(ctx, sizeof(JSString) + buf_len + 1, JS_MTAG_STRING); + if (!p) + return NULL; + p->is_unique = FALSE; + p->is_ascii = FALSE; + p->is_numeric = FALSE; + p->len = buf_len; + p->buf[buf_len] = '\0'; + return p; +} + +/* 0 <= c <= 0x10ffff */ +static inline JSValue JS_NewStringChar(uint32_t c) +{ + return JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, c); +} + +static force_inline int utf8_char_len(int c) +{ + int l; + if (c < 0x80) { + l = 1; + } else if (c < 0xc0) { + l = 1; + } else if (c < 0xe0) { + l = 2; + } else if (c < 0xf0) { + l = 3; + } else if (c < 0xf8) { + l = 4; + } else { + l = 1; + } + return l; +} + +static BOOL is_ascii_string(const char *buf, size_t len) +{ + size_t i; + for(i = 0; i < len; i++) { + if ((uint8_t)buf[i] > 0x7f) + return FALSE; + } + return TRUE; +} + +static JSString *get_string_ptr(JSContext *ctx, JSStringCharBuf *buf, + JSValue val) +{ + if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) { + JSString *p = (JSString *)buf; + p->is_unique = FALSE; + p->is_ascii = JS_VALUE_GET_SPECIAL_VALUE(val) <= 0x7f; + p->len = get_short_string(p->buf, val); + return p; + } else { + return JS_VALUE_TO_PTR(val); + } +} + +static JSValue js_sub_string_utf8(JSContext *ctx, JSValue val, + uint32_t start0, uint32_t end0) +{ + JSString *p, *p1; + int len, start, end, c; + BOOL start_surrogate, end_surrogate; + JSStringCharBuf buf; + JSGCRef val_ref; + const uint8_t *ptr; + size_t clen; + + if (end0 - start0 == 0) { + return js_get_atom(ctx, JS_ATOM_empty); + } + start_surrogate = start0 & 1; + end_surrogate = end0 & 1; + start = start0 >> 1; + end = end0 >> 1; + len = end - start; + p1 = get_string_ptr(ctx, &buf, val); + ptr = p1->buf; + if (!start_surrogate && !end_surrogate && utf8_char_len(ptr[start]) == len) { + c = utf8_get(ptr + start, &clen); + return JS_NewStringChar(c); + } + + JS_PUSH_VALUE(ctx, val); + p = js_alloc_string(ctx, len - start_surrogate + (end_surrogate ? 3 : 0)); + JS_POP_VALUE(ctx, val); + if (!p) + return JS_EXCEPTION; + p1 = get_string_ptr(ctx, &buf, val); + ptr = p1->buf; + if (unlikely(start_surrogate || end_surrogate)) { + uint8_t *q = p->buf; + p->is_ascii = FALSE; + if (start_surrogate) { + c = utf8_get(ptr + start, &clen); + c = 0xdc00 + ((c - 0x10000) & 0x3ff); /* right surrogate */ + q += unicode_to_utf8(q, c); + start += 4; + } + memcpy(q, ptr + start, end - start); + q += end - start; + if (end_surrogate) { + c = utf8_get(ptr + end, &clen); + c = 0xd800 + ((c - 0x10000) >> 10); /* left surrogate */ + q += unicode_to_utf8(q, c); + } + assert((q - p->buf) == p->len); + } else { + p->is_ascii = p1->is_ascii ? TRUE : is_ascii_string((const char *)(ptr + start), len); + memcpy(p->buf, ptr + start, len); + } + return JS_VALUE_FROM_PTR(p); +} + +/* Warning: the string must be a valid WTF-8 string (= UTF-8 + + unpaired surrogates). */ +JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t len) +{ + JSString *p; + + if (len == 0) { + return js_get_atom(ctx, JS_ATOM_empty); + } else { + if (utf8_char_len(buf[0]) == len) { + size_t clen; + int c; + c = utf8_get((const uint8_t *)buf, &clen); + return JS_NewStringChar(c); + } + } + p = js_alloc_string(ctx, len); + if (!p) + return JS_EXCEPTION; + p->is_ascii = is_ascii_string((const char *)buf, len); + memcpy(p->buf, buf, len); + return JS_VALUE_FROM_PTR(p); +} + +/* Warning: the string must be a valid UTF-8 string. */ +JSValue JS_NewString(JSContext *ctx, const char *buf) +{ + return JS_NewStringLen(ctx, buf, strlen(buf)); +} + +/* the byte array must be zero terminated. */ +static JSValue js_byte_array_to_string(JSContext *ctx, JSValue val, int len, BOOL is_ascii) +{ + JSByteArray *arr = JS_VALUE_TO_PTR(val); + JSString *p; + + assert(len + 1 <= arr->size); + if (len == 0) { + return js_get_atom(ctx, JS_ATOM_empty); + } else if (utf8_char_len(arr->buf[0]) == len) { + size_t clen; + return JS_NewStringChar(utf8_get(arr->buf, &clen)); + } else { + js_shrink_byte_array(ctx, &val, len + 1); + p = (JSString *)arr; + p->mtag = JS_MTAG_STRING; + p->is_ascii = is_ascii; + p->is_unique = FALSE; + p->is_numeric = FALSE; + p->len = len; + return val; + } +} + +/* in bytes */ +static __maybe_unused int js_string_byte_len(JSContext *ctx, JSValue val) +{ + if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) { + int c = JS_VALUE_GET_SPECIAL_VALUE(val); + if (c < 0x80) + return 1; + else if (c < 0x800) + return 2; + else if (c < 0x10000) + return 3; + else + return 4; + } else { + JSString *p = JS_VALUE_TO_PTR(val); + return p->len; + } +} + +/* assuming that utf8_next() returns 4, validate the corresponding UTF-8 sequence */ +static BOOL is_valid_len4_utf8(const uint8_t *buf) +{ + return (((buf[0] & 0xf) << 6) | (buf[1] & 0x3f)) >= 0x10; +} + +static __maybe_unused void dump_string_pos_cache(JSContext *ctx) +{ + int i; + JSStringPosCacheEntry *ce; + for(i = 0; i < JS_STRING_POS_CACHE_SIZE; i++) { + ce = &ctx->string_pos_cache[i]; + printf("%d: ", i); + if (ce->str == JS_NULL) { + printf("\n"); + } else { + JSString *p = JS_VALUE_TO_PTR(ce->str); + printf(" utf8_pos=%u/%u utf16_pos=%u\n", + ce->str_pos[POS_TYPE_UTF8], (int)p->len, ce->str_pos[POS_TYPE_UTF16]); + } + } +} + +/* an UTF-8 position is the byte position multiplied by 2. One is + added when the corresponding UTF-16 character represents the right + surrogate if the code is >= 0x10000. +*/ +static uint32_t js_string_convert_pos(JSContext *ctx, JSValue val, uint32_t pos, + StringPosTypeEnum pos_type) +{ + JSStringCharBuf buf; + JSString *p; + size_t i, clen, len, start; + uint32_t d_min, d, j; + JSStringPosCacheEntry *ce, *ce1; + uint32_t surrogate_flag, has_surrogate, limit; + int ce_idx; + + p = get_string_ptr(ctx, &buf, val); + len = p->len; + if (p->is_ascii) { + if (pos_type == POS_TYPE_UTF8) + return min_int(len, pos / 2); + else + return min_int(len, pos) * 2; + } + + if (pos_type == POS_TYPE_UTF8) { + has_surrogate = pos & 1; + pos >>= 1; + } else { + has_surrogate = 0; + } + + ce = NULL; + if (len < JS_STRING_POS_CACHE_MIN_LEN) { + j = 0; + i = 0; + goto uncached; + } + + d_min = pos; + for(ce_idx = 0; ce_idx < JS_STRING_POS_CACHE_SIZE; ce_idx++) { + ce1 = &ctx->string_pos_cache[ce_idx]; + if (ce1->str == val) { + d = ce1->str_pos[pos_type]; + d = d >= pos ? d - pos : pos - d; + if (d < d_min) { + d_min = d; + ce = ce1; + } + } + } + if (!ce) { + /* "random" replacement */ + ce = &ctx->string_pos_cache[ctx->string_pos_cache_counter]; + if (++ctx->string_pos_cache_counter == JS_STRING_POS_CACHE_SIZE) + ctx->string_pos_cache_counter = 0; + ce->str = val; + ce->str_pos[POS_TYPE_UTF8] = 0; + ce->str_pos[POS_TYPE_UTF16] = 0; + } + + i = ce->str_pos[POS_TYPE_UTF8]; + j = ce->str_pos[POS_TYPE_UTF16]; + if (ce->str_pos[pos_type] <= pos) { + uncached: + surrogate_flag = 0; + if (pos_type == POS_TYPE_UTF8) { + limit = INT32_MAX; + len = pos; + } else { + limit = pos; + } + for(; i < len; i += clen) { + if (j == limit) + break; + clen = utf8_char_len(p->buf[i]); + if (clen == 4 && is_valid_len4_utf8(p->buf + i)) { + if ((j + 1) == limit) { + surrogate_flag = 1; + break; + } + j += 2; + } else { + j++; + } + } + } else { + surrogate_flag = 0; + if (pos_type == POS_TYPE_UTF8) { + start = pos; + limit = INT32_MAX; + } else { + limit = pos; + start = 0; + } + while (i > start) { + size_t i0 = i; + i--; + while ((p->buf[i] & 0xc0) == 0x80) + i--; + clen = i0 - i; + if (clen == 4 && is_valid_len4_utf8(p->buf + i)) { + j -= 2; + if ((j + 1) == limit) { + surrogate_flag = 1; + break; + } + } else { + j--; + } + if (j == limit) + break; + } + } + if (ce) { + ce->str_pos[POS_TYPE_UTF8] = i; + ce->str_pos[POS_TYPE_UTF16] = j; + } + if (pos_type == POS_TYPE_UTF8) + return j + has_surrogate; + else + return i * 2 + surrogate_flag; +} + +static uint32_t js_string_utf16_to_utf8_pos(JSContext *ctx, JSValue val, uint32_t utf16_pos) +{ + return js_string_convert_pos(ctx, val, utf16_pos, POS_TYPE_UTF16); +} + +static uint32_t js_string_utf8_to_utf16_pos(JSContext *ctx, JSValue val, uint32_t utf8_pos) +{ + return js_string_convert_pos(ctx, val, utf8_pos, POS_TYPE_UTF8); +} + +/* Testing the third byte is not needed as the UTF-8 encoding must be + correct */ +static BOOL is_utf8_left_surrogate(const uint8_t *p) +{ + return p[0] == 0xed && (p[1] >= 0xa0 && p[1] <= 0xaf); +} + +static BOOL is_utf8_right_surrogate(const uint8_t *p) +{ + return p[0] == 0xed && (p[1] >= 0xb0 && p[1] <= 0xbf); +} + +typedef struct { + JSGCRef buffer_ref; /* string, JSByteBuffer or JS_EXCEPTION */ + int len; /* current string length (in bytes) */ + BOOL is_ascii; +} StringBuffer; + +/* return 0 if OK, -1 in case of exception (exception possible if len > 0) */ +static int string_buffer_push(JSContext *ctx, StringBuffer *s, int len) +{ + s->len = 0; + s->is_ascii = TRUE; + if (len > 0) { + JSByteArray *arr; + arr = js_alloc_byte_array(ctx, len); + if (!arr) + return -1; + s->buffer_ref.val = JS_VALUE_FROM_PTR(arr); + } else { + s->buffer_ref.val = js_get_atom(ctx, JS_ATOM_empty); + } + s->buffer_ref.prev = ctx->top_gc_ref; + ctx->top_gc_ref = &s->buffer_ref; + return 0; +} + +/* val2 must be a string. Return 0 if OK, -1 in case of exception */ +static int string_buffer_concat_str(JSContext *ctx, StringBuffer *s, JSValue val2) +{ + JSStringCharBuf buf1, buf2; + JSByteArray *arr; + JSString *p1, *p2; + int len, len1, len2; + JSValue val1; + uint8_t *q; + + if (JS_IsException(s->buffer_ref.val)) + return -1; + p2 = get_string_ptr(ctx, &buf2, val2); + len2 = p2->len; + if (len2 == 0) + return 0; + if (JS_IsString(ctx, s->buffer_ref.val)) { + p1 = get_string_ptr(ctx, &buf1, s->buffer_ref.val); + len1 = p1->len; + if (len1 == 0) { + /* empty string in buffer: just keep 'val2' */ + s->buffer_ref.val = val2; + return 0; + } + arr = NULL; + val1 = s->buffer_ref.val; + s->buffer_ref.val = JS_NULL; + } else { + arr = JS_VALUE_TO_PTR(s->buffer_ref.val); + len1 = s->len; + val1 = JS_NULL; + } + + len = len1 + len2; + if (len > JS_STRING_LEN_MAX) { + s->buffer_ref.val = JS_ThrowInternalError(ctx, "string too long"); + return -1; + } + + if (!arr || (len + 1) > arr->size) { + JSGCRef val1_ref, val2_ref; + + JS_PUSH_VALUE(ctx, val1); + JS_PUSH_VALUE(ctx, val2); + s->buffer_ref.val = js_resize_byte_array(ctx, s->buffer_ref.val, len + 1); + JS_POP_VALUE(ctx, val2); + JS_POP_VALUE(ctx, val1); + if (JS_IsException(s->buffer_ref.val)) + return -1; + arr = JS_VALUE_TO_PTR(s->buffer_ref.val); + if (val1 != JS_NULL) { + p1 = get_string_ptr(ctx, &buf1, val1); + s->is_ascii = p1->is_ascii; + memcpy(arr->buf, p1->buf, len1); + } + p2 = get_string_ptr(ctx, &buf2, val2); + } + + q = arr->buf + len1; + if (len2 >= 3 && unlikely(is_utf8_right_surrogate(p2->buf)) && + len1 >= 3 && is_utf8_left_surrogate(q - 3)) { + size_t clen; + int c; + /* contract the two surrogates to 4 bytes */ + c = (utf8_get(q - 3, &clen) & 0x3ff) << 10; + c |= (utf8_get(p2->buf, &clen) & 0x3ff); + c += 0x10000; + len -= 2; + len2 -= 3; + q -= 3; + q += unicode_to_utf8(q, c); + s->is_ascii = FALSE; + } + memcpy(q, p2->buf + p2->len - len2, len2); + s->len = len; + s->is_ascii &= p2->is_ascii; + return 0; +} + +/* 'str' must be a string */ +static int string_buffer_concat_utf8(JSContext *ctx, StringBuffer *s, JSValue str, + uint32_t start, uint32_t end) +{ + JSValue val2; + + if (end <= start) + return 0; + /* XXX: avoid explicitly constructing the substring */ + val2 = js_sub_string_utf8(ctx, str, start, end); + if (JS_IsException(val2)) { + s->buffer_ref.val = JS_EXCEPTION; + return -1; + } + return string_buffer_concat_str(ctx, s, val2); +} + +static int string_buffer_concat_utf16(JSContext *ctx, StringBuffer *s, JSValue str, + uint32_t start, uint32_t end) +{ + uint32_t start_utf8, end_utf8; + if (end <= start) + return 0; + start_utf8 = js_string_utf16_to_utf8_pos(ctx, str, start); + end_utf8 = js_string_utf16_to_utf8_pos(ctx, str, end); + return string_buffer_concat_utf8(ctx, s, str, start_utf8, end_utf8); +} + +static int string_buffer_concat(JSContext *ctx, StringBuffer *s, JSValue val2) +{ + val2 = JS_ToString(ctx, val2); + if (JS_IsException(val2)) { + s->buffer_ref.val = JS_EXCEPTION; + return -1; + } + return string_buffer_concat_str(ctx, s, val2); +} + +/* XXX: could optimize */ +static int string_buffer_putc(JSContext *ctx, StringBuffer *s, int c) +{ + return string_buffer_concat_str(ctx, s, JS_NewStringChar(c)); +} + +static int string_buffer_puts(JSContext *ctx, StringBuffer *s, const char *str) +{ + JSValue val; + + /* XXX: avoid this allocation */ + val = JS_NewString(ctx, str); + if (JS_IsException(val)) + return -1; + return string_buffer_concat_str(ctx, s, val); +} + +static JSValue string_buffer_pop(JSContext *ctx, StringBuffer *s) +{ + JSValue res; + if (JS_IsException(s->buffer_ref.val) || + JS_IsString(ctx, s->buffer_ref.val)) { + res = s->buffer_ref.val; + } else { + if (s->len != 0) { + /* add the trailing '\0' */ + JSByteArray *arr = JS_VALUE_TO_PTR(s->buffer_ref.val); + arr->buf[s->len] = '\0'; + } + res = js_byte_array_to_string(ctx, s->buffer_ref.val, s->len, s->is_ascii); + } + ctx->top_gc_ref = s->buffer_ref.prev; + return res; +} + +/* val1 and val2 must be strings or exception */ +static JSValue JS_ConcatString(JSContext *ctx, JSValue val1, JSValue val2) +{ + StringBuffer b_s, *b = &b_s; + + if (JS_IsException(val1) || + JS_IsException(val2)) + return JS_EXCEPTION; + + string_buffer_push(ctx, b, 0); + string_buffer_concat_str(ctx, b, val1); /* no memory allocation */ + string_buffer_concat_str(ctx, b, val2); + return string_buffer_pop(ctx, b); +} + +static BOOL js_string_eq(JSContext *ctx, JSValue val1, JSValue val2) +{ + JSStringCharBuf buf1, buf2; + JSString *p1, *p2; + + p1 = get_string_ptr(ctx, &buf1, val1); + p2 = get_string_ptr(ctx, &buf2, val2); + if (p1->len != p2->len) + return FALSE; + return !memcmp(p1->buf, p2->buf, p1->len); +} + +/* Return the unicode character containing the byte at position + 'i'. Return -1 in case of error. */ +static int string_get_cp(const uint8_t *p) +{ + size_t clen; + while ((*p & 0xc0) == 0x80) + p--; + return utf8_get(p, &clen); +} + +static int js_string_compare(JSContext *ctx, JSValue val1, JSValue val2) +{ + JSStringCharBuf buf1, buf2; + int len, i, res; + JSString *p1, *p2; + + p1 = get_string_ptr(ctx, &buf1, val1); + p2 = get_string_ptr(ctx, &buf2, val2); + len = min_int(p1->len, p2->len); + for(i = 0; i < len; i++) { + if (p1->buf[i] != p2->buf[i]) + break; + } + if (i != len) { + int c1, c2; + /* if valid UTF-8, the strings cannot be equal at this point */ + /* Note: UTF-16 does not preserve unicode order like UTF-8 */ + c1 = string_get_cp(p1->buf + i); + c2 = string_get_cp(p2->buf + i); + if ((c1 < 0x10000 && c2 < 0x10000) || + (c1 >= 0x10000 && c2 >= 0x10000)) { + if (c1 < c2) + res = -1; + else + res = 1; + } else if (c1 < 0x10000) { + /* p1 < p2 if same first UTF-16 char */ + c2 = 0xd800 + ((c2 - 0x10000) >> 10); + if (c1 <= c2) + res = -1; + else + res = 1; + } else { + /* p1 > p2 if same first UTF-16 char */ + c1 = 0xd800 + ((c1 - 0x10000) >> 10); + if (c1 < c2) + res = -1; + else + res = 1; + } + } else { + if (p1->len == p2->len) + res = 0; + else if (p1->len < p2->len) + res = -1; + else + res = 1; + } + return res; +} + +/* return the string length in UTF16 characters. 'val' must be a + string char or a string */ +static int js_string_len(JSContext *ctx, JSValue val) +{ + if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) { + return JS_VALUE_GET_SPECIAL_VALUE(val) >= 0x10000 ? 2 : 1; + } else { + JSString *p; + p = JS_VALUE_TO_PTR(val); + if (p->is_ascii) + return p->len; + else + return js_string_utf8_to_utf16_pos(ctx, val, p->len * 2); + } +} + +/* return the UTF-16 code or the unicode character at a given UTF-8 + position or -1 if outside the string */ +static int string_getcp(JSContext *ctx, JSValue str, uint32_t utf16_pos, BOOL is_codepoint) +{ + JSString *p; + JSStringCharBuf buf; + uint32_t surrogate_flag, c, utf8_pos; + size_t clen; + + utf8_pos = js_string_utf16_to_utf8_pos(ctx, str, utf16_pos); + surrogate_flag = utf8_pos & 1; + utf8_pos >>= 1; + p = get_string_ptr(ctx, &buf, str); + if (utf8_pos >= p->len) + return -1; + c = utf8_get(p->buf + utf8_pos, &clen); + if (c < 0x10000 || (!surrogate_flag && is_codepoint)) { + return c; + } else { + c -= 0x10000; + if (!surrogate_flag) + return 0xd800 + (c >> 10); /* left surrogate */ + else + return 0xdc00 + (c & 0x3ff); /* right surrogate */ + } +} + +static int string_getc(JSContext *ctx, JSValue str, uint32_t utf16_pos) +{ + return string_getcp(ctx, str, utf16_pos, FALSE); +} + +/* precondition: 0 <= start <= end <= string length */ +static JSValue js_sub_string(JSContext *ctx, JSValue val, int start, int end) +{ + uint32_t start_utf8, end_utf8; + + if (end <= start) + return js_get_atom(ctx, JS_ATOM_empty); + start_utf8 = js_string_utf16_to_utf8_pos(ctx, val, start); + end_utf8 = js_string_utf16_to_utf8_pos(ctx, val, end); + return js_sub_string_utf8(ctx, val, start_utf8, end_utf8); +} + +static inline int is_num(int c) +{ + return c >= '0' && c <= '9'; +} + +/* return TRUE if the property 'val' represents a numeric property. -1 + is returned in case of exception. 'val' must be a string. It is + assumed that NaN and infinities have already been handled. */ +static int js_is_numeric_string(JSContext *ctx, JSValue val) +{ + int c, len; + double d; + const char *r, *q; + JSString *p; + JSByteArray *tmp_arr; + JSGCRef val_ref; + char buf[32]; /* enough for js_dtoa() */ + + p = JS_VALUE_TO_PTR(val); + /* the fast case is when the string is not a number */ + if (p->len == 0 || !p->is_ascii) + return FALSE; + q = (const char *)p->buf; + c = *q; + if (c == '-') { + if (p->len == 1) + return FALSE; + q++; + c = *q; + } + if (!is_num(c)) + return FALSE; + + JS_PUSH_VALUE(ctx, val); + tmp_arr = js_alloc_byte_array(ctx, max_int(sizeof(JSATODTempMem), + sizeof(JSDTOATempMem))); + JS_POP_VALUE(ctx, val); + if (!tmp_arr) + return -1; + p = JS_VALUE_TO_PTR(val); + d = js_atod((char *)p->buf, &r, 10, 0, (JSATODTempMem *)tmp_arr->buf); + if ((r - (char *)p->buf) != p->len) { + js_free(ctx, tmp_arr); + return FALSE; + } + len = js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE, (JSDTOATempMem *)tmp_arr->buf); + js_free(ctx, tmp_arr); + return (p->len == len && !memcmp(buf, p->buf, len)); +} + +/* return JS_NULL if not found */ +static JSValue find_atom(JSContext *ctx, int *pidx, const JSValueArray *arr, int len, JSValue val) +{ + int a, b, m, r; + JSValue val1; + + a = 0; + b = len - 1; + while (a <= b) { + m = (a + b) >> 1; + val1 = arr->arr[m]; + r = js_string_compare(ctx, val, val1); + if (r == 0) { + /* found */ + *pidx = m; + return val1; + } else if (r < 0) { + b = m - 1; + } else { + a = m + 1; + } + } + *pidx = a; + return JS_NULL; +} + +/* if 'val' is not a string, it is returned */ +/* XXX: use hash table */ +static JSValue JS_MakeUniqueString(JSContext *ctx, JSValue val) +{ + JSString *p; + int a, is_numeric, i; + JSValueArray *arr; + const JSValueArray *arr1; + JSValue val1, new_tab; + JSGCRef val_ref; + + if (!JS_IsPtr(val)) + return val; + p = JS_VALUE_TO_PTR(val); + if (p->mtag != JS_MTAG_STRING || p->is_unique) + return val; + + /* not unique: find it in the ROM or RAM sorted unique string table */ + for(i = 0; i < ctx->n_rom_atom_tables; i++) { + arr1 = ctx->rom_atom_tables[i]; + if (arr1) { + val1 = find_atom(ctx, &a, arr1, arr1->size, val); + if (!JS_IsNull(val1)) + return val1; + } + } + + arr = JS_VALUE_TO_PTR( ctx->unique_strings); + val1 = find_atom(ctx, &a, arr, ctx->unique_strings_len, val); + if (!JS_IsNull(val1)) + return val1; + + JS_PUSH_VALUE(ctx, val); + is_numeric = js_is_numeric_string(ctx, val); + JS_POP_VALUE(ctx, val); + if (is_numeric < 0) + return JS_EXCEPTION; + + /* not found: add it in the table */ + JS_PUSH_VALUE(ctx, val); + new_tab = js_resize_value_array(ctx, ctx->unique_strings, + ctx->unique_strings_len + 1); + JS_POP_VALUE(ctx, val); + if (JS_IsException(new_tab)) + return JS_EXCEPTION; + ctx->unique_strings = new_tab; + arr = JS_VALUE_TO_PTR( ctx->unique_strings); + memmove(&arr->arr[a + 1], &arr->arr[a], + sizeof(arr->arr[0]) * (ctx->unique_strings_len - a)); + arr->arr[a] = val; + p = JS_VALUE_TO_PTR(val); + p->is_unique = TRUE; + p->is_numeric = is_numeric; + ctx->unique_strings_len++; + return val; +} + +static int JS_ToBool(JSContext *ctx, JSValue val) +{ + if (JS_IsInt(val)) { + return JS_VALUE_GET_INT(val) != 0; + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_IsShortFloat(val)) { + double d; + d = js_get_short_float(val); + return !isnan(d) && d != 0; + } else +#endif + if (!JS_IsPtr(val)) { + switch(JS_VALUE_GET_SPECIAL_TAG(val)) { + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_VALUE_GET_SPECIAL_VALUE(val); + case JS_TAG_SHORT_FUNC: + case JS_TAG_STRING_CHAR: + return TRUE; + default: + return FALSE; + } + } else { + JSMemBlockHeader *h = JS_VALUE_TO_PTR(val); + switch(h->mtag) { + case JS_MTAG_STRING: + { + JSString *p = (JSString *)h; + return p->len != 0; + } + case JS_MTAG_FLOAT64: + { + JSFloat64 *p = (JSFloat64 *)h; + return !isnan(p->u.dval) && p->u.dval != 0; + } + default: + case JS_MTAG_OBJECT: + return TRUE; + } + } +} + +/* plen can be NULL. No memory allocation is done if 'val' already is + a string. */ +const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValue val, + JSCStringBuf *buf) +{ + const char *p; + int len; + + val = JS_ToString(ctx, val); + if (JS_IsException(val)) + return NULL; + if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) { + len = get_short_string(buf->buf, val); + p = (const char *)buf->buf; + } else { + JSString *r; + r = JS_VALUE_TO_PTR(val); + p = (const char *)r->buf; + len = r->len; + } + if (plen) + *plen = len; + return p; +} + +const char *JS_ToCString(JSContext *ctx, JSValue val, JSCStringBuf *buf) +{ + return JS_ToCStringLen(ctx, NULL, val, buf); +} + +JSValue JS_GetException(JSContext *ctx) +{ + JSValue obj; + obj = ctx->current_exception; + ctx->current_exception = JS_UNDEFINED; + return obj; +} + +static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValue val) +{ + if (val == JS_NULL || val == JS_UNDEFINED) + return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); + return JS_ToString(ctx, val); +} + +static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not an object"); +} + +/* 'val' must be a string. return TRUE if the string represents a + short integer */ +static inline BOOL is_num_string(JSContext *ctx, int32_t *pval, JSValue val) +{ + JSStringCharBuf buf; + uint32_t n; + uint64_t n64; + JSString *p1; + int c, is_neg; + const uint8_t *p, *p_end; + + p1 = get_string_ptr(ctx, &buf, val); + if (p1->len == 0 || p1->len > 11 || !p1->is_ascii) + return FALSE; + p = p1->buf; + p_end = p + p1->len; + c = *p++; + is_neg = 0; + if (c == '-') { + if (p >= p_end) + return FALSE; + is_neg = 1; + c = *p++; + } + if (!is_num(c)) + return FALSE; + if (c == '0') { + if (p != p_end || is_neg) + return FALSE; + n = 0; + } else { + n = c - '0'; + while (p < p_end) { + c = *p++; + if (!is_num(c)) + return FALSE; + /* XXX: simplify ? */ + n64 = (uint64_t)n * 10 + (c - '0'); + if (n64 > (JS_SHORTINT_MAX + is_neg)) + return FALSE; + n = n64; + } + if (is_neg) + n = -n; + } + *pval = n; + return TRUE; +} + +/* return TRUE if the property 'val' represent a numeric property. It + is assumed that the shortint case has been tested before */ +static BOOL JS_IsNumericProperty(JSContext *ctx, JSValue val) +{ + JSString *p; + if (!JS_IsPtr(val)) + return FALSE; /* JS_TAG_STRING_CHAR */ + p = JS_VALUE_TO_PTR(val); + return p->is_numeric; +} + +static JSValueArray *js_alloc_value_array(JSContext *ctx, int init_base, int new_size) +{ + JSValueArray *arr; + int i; + + if (new_size > JS_VALUE_ARRAY_SIZE_MAX) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + arr = js_malloc(ctx, sizeof(JSValueArray) + new_size * sizeof(JSValue), JS_MTAG_VALUE_ARRAY); + if (!arr) + return NULL; + arr->size = new_size; + for(i = init_base; i < new_size; i++) + arr->arr[i] = JS_UNDEFINED; + return arr; +} + +/* val can be JS_NULL (zero size). 'prop_base' is non zero only when + * resizing the property arrays so that the property array has a size + * which is a multiple of 3 */ +static JSValue js_resize_value_array2(JSContext *ctx, JSValue val, int new_size, int prop_base) +{ + JSValueArray *slots, *new_slots; + int old_size, new_size1; + JSGCRef val_ref; + + if (val == JS_NULL) { + slots = NULL; + old_size = 0; + } else { + slots = JS_VALUE_TO_PTR(val); + old_size = slots->size; + } + if (unlikely(new_size > old_size)) { + new_size1 = old_size + old_size / 2; + if (new_size1 > new_size) { + new_size = new_size1; + /* ensure that the property array has a size which is a + * multiple of 3 */ + if (prop_base != 0) { + int align = (new_size - prop_base) % 3; + if (align != 0) + new_size += 3 - align; + } + } + new_size = max_int(new_size, old_size + old_size / 2); + JS_PUSH_VALUE(ctx, val); + new_slots = js_alloc_value_array(ctx, old_size, new_size); + JS_POP_VALUE(ctx, val); + if (!new_slots) + return JS_EXCEPTION; + if (old_size > 0) { + slots = JS_VALUE_TO_PTR(val); + memcpy(new_slots->arr, slots->arr, old_size * sizeof(JSValue)); + } + val = JS_VALUE_FROM_PTR(new_slots); + } + return val; +} + +static JSValue js_resize_value_array(JSContext *ctx, JSValue val, int new_size) +{ + return js_resize_value_array2(ctx, val, new_size, 0); +} + +/* no allocation is done */ +static void js_shrink_value_array(JSContext *ctx, JSValue *pval, int new_size) +{ + JSValueArray *arr; + if (*pval == JS_NULL) + return; + arr = JS_VALUE_TO_PTR(*pval); + assert(new_size <= arr->size); + if (new_size == 0) { + js_free(ctx, arr); + *pval = JS_NULL; + } else { + arr = js_shrink(ctx, arr, sizeof(JSValueArray) + new_size * sizeof(JSValue)); + arr->size = new_size; + } +} + +static JSByteArray *js_alloc_byte_array(JSContext *ctx, int size) +{ + JSByteArray *arr; + + if (size > JS_BYTE_ARRAY_SIZE_MAX) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + arr = js_malloc(ctx, sizeof(JSByteArray) + size, JS_MTAG_BYTE_ARRAY); + if (!arr) + return NULL; + arr->size = size; + return arr; +} + +static JSValue js_resize_byte_array(JSContext *ctx, JSValue val, int new_size) +{ + JSByteArray *arr, *new_arr; + int old_size; + JSGCRef val_ref; + + if (val == JS_NULL) { + arr = NULL; + old_size = 0; + } else { + arr = JS_VALUE_TO_PTR(val); + old_size = arr->size; + } + if (unlikely(new_size > old_size)) { + new_size = max_int(new_size, old_size + old_size / 2); + JS_PUSH_VALUE(ctx, val); + new_arr = js_alloc_byte_array(ctx, new_size); + JS_POP_VALUE(ctx, val); + if (!new_arr) + return JS_EXCEPTION; + if (old_size > 0) { + arr = JS_VALUE_TO_PTR(val); + memcpy(new_arr->buf, arr->buf, old_size); + } + val = JS_VALUE_FROM_PTR(new_arr); + } + return val; +} + +static void js_shrink_byte_array(JSContext *ctx, JSValue *pval, int new_size) +{ + JSByteArray *arr; + if (*pval == JS_NULL) + return; + arr = JS_VALUE_TO_PTR(*pval); + assert(new_size <= arr->size); + if (new_size == 0) { + js_free(ctx, arr); + *pval = JS_NULL; + } else { + arr = js_shrink(ctx, arr, sizeof(JSByteArray) + new_size); + arr->size = new_size; + } +} + +/* extra_size is in bytes */ +static JSObject *JS_NewObjectProtoClass1(JSContext *ctx, JSValue proto, + int class_id, int extra_size) +{ + JSObject *p; + JSGCRef proto_ref; + extra_size = (unsigned)(extra_size + JSW - 1) / JSW; + JS_PUSH_VALUE(ctx, proto); + p = js_malloc(ctx, offsetof(JSObject, u) + extra_size * JSW, JS_MTAG_OBJECT); + JS_POP_VALUE(ctx, proto); + if (!p) + return NULL; + p->class_id = class_id; + p->extra_size = extra_size; + p->proto = proto; + p->props = ctx->empty_props; + return p; +} + +static JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto, int class_id, int extra_size) +{ + JSObject *p; + p = JS_NewObjectProtoClass1(ctx, proto, class_id, extra_size); + if (!p) + return JS_EXCEPTION; + else + return JS_VALUE_FROM_PTR(p); +} + +static JSValue JS_NewObjectClass(JSContext *ctx, int class_id, int extra_size) +{ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id, extra_size); +} + +JSValue JS_NewObjectClassUser(JSContext *ctx, int class_id) +{ + JSObject *p; + assert(class_id >= JS_CLASS_USER); + p = JS_NewObjectProtoClass1(ctx, ctx->class_proto[class_id], class_id, + sizeof(JSObjectUserData)); + if (!p) + return JS_EXCEPTION; + p->u.user.opaque = NULL; + return JS_VALUE_FROM_PTR(p); +} + +JSValue JS_NewObject(JSContext *ctx) +{ + return JS_NewObjectClass(ctx, JS_CLASS_OBJECT, 0); +} + +/* same as JS_NewObject() but preallocate for 'n' properties */ +JSValue JS_NewObjectPrealloc(JSContext *ctx, int n) +{ + JSValue obj; + JSValueArray *arr; + JSObject *p; + JSGCRef obj_ref; + + obj = JS_NewObjectClass(ctx, JS_CLASS_OBJECT, 0); + if (JS_IsException(obj) || n <= 0) + return obj; + JS_PUSH_VALUE(ctx, obj); + arr = js_alloc_props(ctx, n); + JS_POP_VALUE(ctx, obj); + if (!arr) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(obj); + p->props = JS_VALUE_FROM_PTR(arr); + return obj; +} + +JSValue JS_NewArray(JSContext *ctx, int initial_len) +{ + JSObject *p; + JSValue val; + JSGCRef val_ref; + + val = JS_NewObjectClass(ctx, JS_CLASS_ARRAY, sizeof(JSArrayData)); + if (JS_IsException(val)) + return val; + p = JS_VALUE_TO_PTR(val); + p->u.array.tab = JS_NULL; + p->u.array.len = 0; + if (initial_len > 0) { + JSValueArray *arr; + JS_PUSH_VALUE(ctx, val); + arr = js_alloc_value_array(ctx, 0, initial_len); + JS_POP_VALUE(ctx, val); + if (!arr) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(val); + p->u.array.tab = JS_VALUE_FROM_PTR(arr); + p->u.array.len = initial_len; + } + return val; +} + +static inline uint32_t hash_prop(JSValue prop) +{ + return (prop / JSW) ^ (prop % JSW); /* XXX: improve */ +} + +/* return NULL if not found */ +static force_inline JSProperty *find_own_property_inlined(JSContext *ctx, + JSObject *p, JSValue prop) +{ + JSValueArray *arr; + JSProperty *pr; + uint32_t hash_mask, h, idx; + + arr = JS_VALUE_TO_PTR(p->props); + hash_mask = JS_VALUE_GET_INT(arr->arr[1]); + h = hash_prop(prop) & hash_mask; + idx = arr->arr[2 + h]; /* JSValue, hence idx * 2 */ + while (idx != 0) { + pr = (JSProperty *)((uint8_t *)arr->arr + idx * (sizeof(JSValue) / 2)); + if (pr->key == prop) + return pr; + idx = pr->hash_next; /* JSValue, hence idx * 2 */ + } + return NULL; +} + +static inline JSProperty *find_own_property(JSContext *ctx, + JSObject *p, JSValue prop) +{ + return find_own_property_inlined(ctx, p, prop); +} + +static JSValue get_special_prop(JSContext *ctx, JSValue val) +{ + int idx; + /* 'prototype' or 'constructor' property in ROM */ + idx = JS_VALUE_GET_INT(val); + if (idx >= 0) + return ctx->class_proto[idx]; + else + return ctx->class_obj[-idx - 1]; +} + +/* return the value or: + - exception + - tail call : returned in case of getter and handle_getset = + true. The function is put on the stack +*/ +static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValue obj, JSValue prop, + BOOL allow_tail_call) +{ + JSObject *p; + JSValue proto; + JSProperty *pr; + + if (unlikely(!JS_IsPtr(obj))) { + if (JS_IsIntOrShortFloat(obj)) { + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_NUMBER]); + } else { + switch(JS_VALUE_GET_SPECIAL_TAG(obj)) { + case JS_TAG_BOOL: + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_BOOLEAN]); + break; + case JS_TAG_SHORT_FUNC: + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_CLOSURE]); + break; + case JS_TAG_STRING_CHAR: + goto string_proto; + case JS_TAG_NULL: + return JS_ThrowTypeError(ctx, "cannot read property '%"JSValue_PRI"' of null", prop); + case JS_TAG_UNDEFINED: + return JS_ThrowTypeError(ctx, "cannot read property '%"JSValue_PRI"' of undefined", prop); + default: + goto no_prop; + } + } + } else { + p = JS_VALUE_TO_PTR(obj); + } + if (unlikely(p->mtag != JS_MTAG_OBJECT)) { + switch(p->mtag) { + case JS_MTAG_FLOAT64: + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_NUMBER]); + break; + case JS_MTAG_STRING: + string_proto: + { + if (JS_IsInt(prop)) { + JSValue ret; + ret = js_string_charAt(ctx, &obj, 1, &prop, magic_internalAt); + if (!JS_IsUndefined(ret)) + return ret; + } + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_STRING]); + } + break; + default: + no_prop: + return JS_ThrowTypeError(ctx, "cannot read property '%"JSValue_PRI"' of value", prop); + } + } + + for(;;) { + if (p->class_id == JS_CLASS_ARRAY) { + if (JS_IsInt(prop)) { + uint32_t idx = JS_VALUE_GET_INT(prop); + if (idx < p->u.array.len) { + JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab); + return arr->arr[idx]; + } + } else if (JS_IsNumericProperty(ctx, prop)) { + return JS_UNDEFINED; + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + if (JS_IsInt(prop)) { + uint32_t idx = JS_VALUE_GET_INT(prop); + JSObject *pbuffer; + JSByteArray *arr; + if (idx < p->u.typed_array.len) { + idx += p->u.typed_array.offset; + pbuffer = JS_VALUE_TO_PTR(p->u.typed_array.buffer); + arr = JS_VALUE_TO_PTR(pbuffer->u.array_buffer.byte_buffer); + switch(p->class_id) { + default: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + return JS_NewShortInt(*((uint8_t *)arr->buf + idx)); + case JS_CLASS_INT8_ARRAY: + return JS_NewShortInt(*((int8_t *)arr->buf + idx)); + case JS_CLASS_INT16_ARRAY: + return JS_NewShortInt(*((int16_t *)arr->buf + idx)); + case JS_CLASS_UINT16_ARRAY: + return JS_NewShortInt(*((uint16_t *)arr->buf + idx)); + case JS_CLASS_INT32_ARRAY: + return JS_NewInt32(ctx, *((int32_t *)arr->buf + idx)); + case JS_CLASS_UINT32_ARRAY: + return JS_NewUint32(ctx, *((uint32_t *)arr->buf + idx)); + case JS_CLASS_FLOAT32_ARRAY: + return JS_NewFloat64(ctx, *((float *)arr->buf + idx)); + case JS_CLASS_FLOAT64_ARRAY: + return JS_NewFloat64(ctx, *((double *)arr->buf + idx)); + } + } + } else if (JS_IsNumericProperty(ctx, prop)) { + return JS_UNDEFINED; + } + } + + pr = find_own_property(ctx, p, prop); + if (pr) { + if (likely(pr->prop_type == JS_PROP_NORMAL)) { + return pr->value; + } else if (pr->prop_type == JS_PROP_VARREF) { + JSVarRef *pv = JS_VALUE_TO_PTR(pr->value); + /* always detached */ + return pv->u.value; + } else if (pr->prop_type == JS_PROP_SPECIAL) { + return get_special_prop(ctx, pr->value); + } else { + JSValueArray *arr = JS_VALUE_TO_PTR(pr->value); + JSValue getter = arr->arr[0]; + if (getter == JS_UNDEFINED) + return JS_UNDEFINED; + if (allow_tail_call) { + /* It is assumed 'this_obj' is on the stack and + that the stack has some slack to add one element. */ + ctx->sp[-1] = ctx->sp[0]; + ctx->sp[0] = getter; + ctx->sp--; + return JS_NewTailCall(0); + } else { + JSGCRef getter_ref, obj_ref; + int err; + JS_PUSH_VALUE(ctx, getter); + JS_PUSH_VALUE(ctx, obj); + err = JS_StackCheck(ctx, 2); + JS_POP_VALUE(ctx, obj); + JS_POP_VALUE(ctx, getter); + if (err) + return JS_EXCEPTION; + JS_PushArg(ctx, getter); + JS_PushArg(ctx, obj); + return JS_Call(ctx, 0); + } + } + } + /* look in the prototype */ + proto = p->proto; + if (proto == JS_NULL) + break; + p = JS_VALUE_TO_PTR(proto); + } + return JS_UNDEFINED; +} + +static JSValue JS_GetProperty(JSContext *ctx, JSValue obj, JSValue prop) +{ + return JS_GetPropertyInternal(ctx, obj, prop, FALSE); +} + +JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, const char *str) +{ + JSValue prop; + JSGCRef this_obj_ref; + + JS_PUSH_VALUE(ctx, this_obj); + prop = JS_NewString(ctx, str); + if (!JS_IsException(prop)) { + prop = JS_ToPropertyKey(ctx, prop); + } + JS_POP_VALUE(ctx, this_obj); + if (JS_IsException(prop)) + return prop; + return JS_GetProperty(ctx, this_obj, prop); +} + +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue obj, uint32_t idx) +{ + if (idx > JS_SHORTINT_MAX) + return JS_ThrowRangeError(ctx, "invalid array index"); + return JS_GetProperty(ctx, obj, JS_NewInt32(ctx, idx)); +} + +static BOOL JS_HasProperty(JSContext *ctx, JSValue obj, JSValue prop) +{ + JSObject *p; + JSProperty *pr; + + if (!JS_IsPtr(obj)) + return FALSE; + p = JS_VALUE_TO_PTR(obj); + if (p->mtag != JS_MTAG_OBJECT) + return FALSE; + for(;;) { + pr = find_own_property(ctx, p, prop); + if (pr) + return TRUE; + obj = p->proto; + if (obj == JS_NULL) + break; + p = JS_VALUE_TO_PTR(obj); + } + return FALSE; +} + +static int get_prop_hash_size_log2(int prop_count) +{ + /* XXX: adjust ? */ + if (prop_count <= 1) + return 0; + else + return (32 - clz32(prop_count - 1)) - 1; +} + +/* allocate 'n' properties, assuming n >= 1 */ +static JSValueArray *js_alloc_props(JSContext *ctx, int n) +{ + int hash_size_log2, hash_mask, size, i, first_free; + JSValueArray *arr; + JSProperty *pr; + + hash_size_log2 = get_prop_hash_size_log2(n); + hash_mask = (1 << hash_size_log2) - 1; + first_free = 2 + hash_mask + 1; + size = first_free + 3 * n; + arr = js_alloc_value_array(ctx, 0, size); + if (!arr) + return NULL; + arr->arr[0] = JS_NewShortInt(0); /* no property is allocated yet */ + arr->arr[1] = JS_NewShortInt(hash_mask); + for(i = 0; i <= hash_mask; i++) + arr->arr[2 + i] = 0; + pr = NULL; /* avoid warning */ + for(i = 0; i < n; i++) { + pr = (JSProperty *)&arr->arr[2 + hash_mask + 1 + 3 * i]; + pr->key = JS_UNINITIALIZED; + } + /* last property */ + pr->hash_next = first_free << 1; + return arr; +} + +static void js_rehash_props(JSContext *ctx, JSObject *p, BOOL gc_rehash) +{ + JSValueArray *arr; + int prop_count, hash_mask, h, idx, i, j; + JSProperty *pr; + + arr = JS_VALUE_TO_PTR(p->props); + if (JS_IS_ROM_PTR(ctx, arr)) + return; + hash_mask = JS_VALUE_GET_INT(arr->arr[1]); + if (hash_mask == 0 && gc_rehash) + return; /* no need to rehash if single hash entry */ + prop_count = JS_VALUE_GET_INT(arr->arr[0]); + for(i = 0; i <= hash_mask; i++) { + arr->arr[2 + i] = JS_NewShortInt(0); + } + for(i = 0, j = 0; j < prop_count; i++) { + idx = 2 + (hash_mask + 1) + 3 * i; + pr = (JSProperty *)&arr->arr[idx]; + if (pr->key != JS_UNINITIALIZED) { + h = hash_prop(pr->key) & hash_mask; + pr->hash_next = arr->arr[2 + h]; + arr->arr[2 + h] = JS_NewShortInt(idx); + j++; + } + } +} + +/* Compact the properties. No memory allocation is done */ +static void js_compact_props(JSContext *ctx, JSObject *p) +{ + JSValueArray *arr; + int prop_count, hash_mask, i, j, hash_size_log2; + int new_size, new_hash_mask; + JSProperty *pr, *pr1; + + arr = JS_VALUE_TO_PTR(p->props); + prop_count = JS_VALUE_GET_INT(arr->arr[0]); + + /* no property */ + if (prop_count == 0) { + if (p->props != ctx->empty_props) { + //js_free(ctx, p->props); + p->props = ctx->empty_props; + } + return; + } + + hash_mask = JS_VALUE_GET_INT(arr->arr[1]); + hash_size_log2 = get_prop_hash_size_log2(prop_count); + new_hash_mask = min_int(hash_mask, (1 << hash_size_log2) - 1); + new_size = 2 + new_hash_mask + 1 + 3 * prop_count; + if (new_size >= arr->size) + return; /* nothing to do */ + // printf("compact_props: new_size=%d size=%d hash=%d\n", new_size, arr->size, new_hash_mask); + + arr->arr[1] = JS_NewShortInt(new_hash_mask); + + /* move the properties, skipping the deleted ones */ + for(i = 0, j = 0; j < prop_count; i++) { + pr = (JSProperty *)&arr->arr[2 + (hash_mask + 1) + 3 * i]; + if (pr->key != JS_UNINITIALIZED) { + pr1 = (JSProperty *)&arr->arr[2 + (new_hash_mask + 1) + 3 * j]; + *pr1 = *pr; + j++; + } + } + + js_shrink_value_array(ctx, &p->props, new_size); + + js_rehash_props(ctx, p, FALSE); +} + +/* if the existing properties are in ROM, copy them to RAM. Return non zero if error */ +static int js_update_props(JSContext *ctx, JSValue obj) +{ + JSObject *p; + JSValueArray *arr, *arr1; + JSGCRef obj_ref; + int i, idx, prop_count, hash_mask; + JSProperty *pr; + + p = JS_VALUE_TO_PTR(obj); + arr = JS_VALUE_TO_PTR(p->props); + if (!JS_IS_ROM_PTR(ctx, arr)) + return 0; + JS_PUSH_VALUE(ctx, obj); + arr1 = js_alloc_value_array(ctx, 0, arr->size); + JS_POP_VALUE(ctx, obj); + if (!arr1) + return -1; + /* no rehashing is needed because all the atoms are in ROM */ + memcpy(arr1->arr, arr->arr, arr->size * sizeof(JSValue)); + prop_count = JS_VALUE_GET_INT(arr1->arr[0]); + hash_mask = JS_VALUE_GET_INT(arr1->arr[1]); + /* no deleted properties in ROM */ + assert(arr1->size == 2 + (hash_mask + 1) + 3 * prop_count); + /* convert JS_PROP_SPECIAL properties ("prototype" and "constructor") */ + for(i = 0; i < prop_count; i++) { + idx = 2 + (hash_mask + 1) + 3 * i; + pr = (JSProperty *)&arr1->arr[idx]; + if (pr->prop_type == JS_PROP_SPECIAL) { + pr->value = get_special_prop(ctx, pr->value); + pr->prop_type = JS_PROP_NORMAL; + } + } + + p = JS_VALUE_TO_PTR(obj); + p->props = JS_VALUE_FROM_PTR(arr1); + return 0; +} + +/* compute 'first_free' in a property list */ +static int get_first_free(JSValueArray *arr) +{ + JSProperty *pr1; + int first_free; + + pr1 = (JSProperty *)&arr->arr[arr->size - 3]; + if (pr1->key == JS_UNINITIALIZED) + first_free = pr1->hash_next >> 1; + else + first_free = arr->size; + return first_free; +} + +/* It is assumed that the property does not already exists. */ +static JSProperty *js_create_property(JSContext *ctx, JSValue obj, + JSValue prop) +{ + JSObject *p; + JSValueArray *arr; + int prop_count, hash_mask, new_size, h, first_free, new_hash_mask; + JSProperty *pr, *pr1; + JSValue new_props; + JSGCRef obj_ref, prop_ref; + + p = JS_VALUE_TO_PTR(obj); + arr = JS_VALUE_TO_PTR(p->props); + + // JS_DumpValue(ctx, "create", prop); + prop_count = JS_VALUE_GET_INT(arr->arr[0]); + hash_mask = JS_VALUE_GET_INT(arr->arr[1]); + /* extend the array if no space left (this single test is valid + even if the property list is empty) */ + pr1 = (JSProperty *)&arr->arr[arr->size - 3]; + if (pr1->key != JS_UNINITIALIZED) { + if (p->props == ctx->empty_props) { + /* XXX: remove and move empty_props to ROM */ + JS_PUSH_VALUE(ctx, obj); + JS_PUSH_VALUE(ctx, prop); + arr = js_alloc_props(ctx, 1); + JS_POP_VALUE(ctx, prop); + JS_POP_VALUE(ctx, obj); + if (!arr) + return NULL; + p = JS_VALUE_TO_PTR(obj); + p->props = JS_VALUE_FROM_PTR(arr); + first_free = 3; + } else { + first_free = arr->size; + new_size = first_free + 3; + new_hash_mask = hash_mask; + if ((prop_count + 1) > 2 * (hash_mask + 1)) { + /* resize the hash table if too many properties */ + new_hash_mask = 2 * (hash_mask + 1) - 1; + new_size += new_hash_mask - hash_mask; + } + JS_PUSH_VALUE(ctx, obj); + JS_PUSH_VALUE(ctx, prop); + // printf("resize_props: new_size=%d hash=%d %d\n", new_size, new_hash_mask, hash_mask); + new_props = js_resize_value_array2(ctx, p->props, new_size, 2 + new_hash_mask + 1); + JS_POP_VALUE(ctx, prop); + JS_POP_VALUE(ctx, obj); + if (JS_IsException(new_props)) + return NULL; + p = JS_VALUE_TO_PTR(obj); + p->props = new_props; + arr = JS_VALUE_TO_PTR(p->props); + if (new_hash_mask != hash_mask) { + /* rebuild the hash table */ + memmove(&arr->arr[2 + (new_hash_mask + 1)], + &arr->arr[2 + (hash_mask + 1)], + (first_free - (2 + hash_mask + 1)) * sizeof(JSValue)); + first_free += new_hash_mask - hash_mask; + hash_mask = new_hash_mask; + arr->arr[1] = JS_NewShortInt(hash_mask); + js_rehash_props(ctx, p, FALSE); + } + } + /* ensure the last element is marked as uninitialized to store 'first_free' */ + pr1 = (JSProperty *)&arr->arr[arr->size - 3]; + pr1->key = JS_UNINITIALIZED; + } else { + first_free = pr1->hash_next >> 1; + } + + pr = (JSProperty *)&arr->arr[first_free]; + pr->key = prop; + pr->value = JS_UNDEFINED; + pr->prop_type = JS_PROP_NORMAL; + h = hash_prop(prop) & hash_mask; + pr->hash_next = arr->arr[2 + h]; + arr->arr[2 + h] = JS_NewShortInt(first_free); + arr->arr[0] = JS_NewShortInt(prop_count + 1); + /* update first_free */ + first_free += 3; + if (first_free < arr->size) { + pr1 = (JSProperty *)&arr->arr[arr->size - 3]; + pr1->hash_next = first_free << 1; + } + + return pr; +} + +/* don't do property lookup if not present */ +#define JS_DEF_PROP_LOOKUP (1 << 0) +/* return the raw property value */ +#define JS_DEF_PROP_RET_VAL (1 << 1) +#define JS_DEF_PROP_HAS_VALUE (1 << 2) +#define JS_DEF_PROP_HAS_GET (1 << 3) +#define JS_DEF_PROP_HAS_SET (1 << 4) + +/* XXX: handle arrays and typed arrays */ +static JSValue JS_DefinePropertyInternal(JSContext *ctx, JSValue obj, + JSValue prop, JSValue val, + JSValue setter, int flags) +{ + JSProperty *pr; + JSValueArray *arr; + JSGCRef obj_ref, prop_ref, val_ref, setter_ref; + int ret, prop_type; + + /* move to RAM if needed */ + JS_PUSH_VALUE(ctx, obj); + JS_PUSH_VALUE(ctx, prop); + JS_PUSH_VALUE(ctx, val); + JS_PUSH_VALUE(ctx, setter); + ret = js_update_props(ctx, obj); + JS_POP_VALUE(ctx, setter); + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, prop); + JS_POP_VALUE(ctx, obj); + if (ret) + return JS_EXCEPTION; + + if (flags & JS_DEF_PROP_LOOKUP) { + pr = find_own_property(ctx, JS_VALUE_TO_PTR(obj), prop); + if (pr) { + if (flags & JS_DEF_PROP_HAS_VALUE) { + if (pr->prop_type == JS_PROP_NORMAL) { + pr->value = val; + } else if (pr->prop_type == JS_PROP_VARREF) { + JSVarRef *pv = JS_VALUE_TO_PTR(pr->value); + pv->u.value = val; + } else { + goto error_modify; + } + } else if (flags & (JS_DEF_PROP_HAS_GET | JS_DEF_PROP_HAS_SET)) { + if (pr->prop_type != JS_PROP_GETSET) { + error_modify: + return JS_ThrowTypeError(ctx, "cannot modify getter/setter/value kind"); + } + arr = JS_VALUE_TO_PTR(pr->value); + if (unlikely(JS_IS_ROM_PTR(ctx, arr))) { + /* move to RAM */ + JSValueArray *arr2; + JS_PUSH_VALUE(ctx, obj); + JS_PUSH_VALUE(ctx, prop); + JS_PUSH_VALUE(ctx, val); + JS_PUSH_VALUE(ctx, setter); + arr2 = js_alloc_value_array(ctx, 0, 2); + JS_POP_VALUE(ctx, setter); + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, prop); + JS_POP_VALUE(ctx, obj); + if (!arr2) + return JS_EXCEPTION; + pr = find_own_property(ctx, JS_VALUE_TO_PTR(obj), prop); + arr = JS_VALUE_TO_PTR(pr->value); + arr2->arr[0] = arr->arr[0]; + arr2->arr[1] = arr->arr[1]; + pr->value = JS_VALUE_FROM_PTR(arr2); + arr = arr2; + } + if (flags & JS_DEF_PROP_HAS_GET) + arr->arr[0] = val; + if (flags & JS_DEF_PROP_HAS_SET) + arr->arr[1] = setter; + } + goto done; + } + } + + if (flags & (JS_DEF_PROP_HAS_GET | JS_DEF_PROP_HAS_SET)) { + prop_type = JS_PROP_GETSET; + JS_PUSH_VALUE(ctx, obj); + JS_PUSH_VALUE(ctx, prop); + JS_PUSH_VALUE(ctx, val); + JS_PUSH_VALUE(ctx, setter); + arr = js_alloc_value_array(ctx, 0, 2); + JS_POP_VALUE(ctx, setter); + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, prop); + JS_POP_VALUE(ctx, obj); + if (!arr) + return JS_EXCEPTION; + arr->arr[0] = val; + arr->arr[1] = setter; + val = JS_VALUE_FROM_PTR(arr); + } else if (obj == ctx->global_obj) { + JSVarRef *pv; + + prop_type = JS_PROP_VARREF; + JS_PUSH_VALUE(ctx, obj); + JS_PUSH_VALUE(ctx, prop); + JS_PUSH_VALUE(ctx, val); + pv = js_malloc(ctx, sizeof(JSVarRef) - sizeof(JSValue), JS_MTAG_VARREF); + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, prop); + JS_POP_VALUE(ctx, obj); + if (!pv) + return JS_EXCEPTION; + pv->is_detached = TRUE; + pv->u.value = val; + val = JS_VALUE_FROM_PTR(pv); + } else { + prop_type = JS_PROP_NORMAL; + } + JS_PUSH_VALUE(ctx, val); + pr = js_create_property(ctx, obj, prop); + JS_POP_VALUE(ctx, val); + if (!pr) + return JS_EXCEPTION; + pr->prop_type = prop_type; + pr->value = val; + done: + if (flags & JS_DEF_PROP_RET_VAL) { + return pr->value; + } else { + return JS_UNDEFINED; + } +} + +static JSValue JS_DefinePropertyValue(JSContext *ctx, JSValue obj, + JSValue prop, JSValue val) +{ + return JS_DefinePropertyInternal(ctx, obj, prop, val, JS_NULL, + JS_DEF_PROP_LOOKUP | JS_DEF_PROP_HAS_VALUE); +} + +static JSValue JS_DefinePropertyGetSet(JSContext *ctx, JSValue obj, + JSValue prop, JSValue getter, + JSValue setter, int flags) +{ + return JS_DefinePropertyInternal(ctx, obj, prop, getter, setter, + JS_DEF_PROP_LOOKUP | flags); +} + +/* return a JSVarRef or an exception. */ +static JSValue add_global_var(JSContext *ctx, JSValue prop, BOOL define_flag) +{ + JSObject *p; + JSProperty *pr; + + p = JS_VALUE_TO_PTR(ctx->global_obj); + pr = find_own_property(ctx, p, prop); + if (pr) { + if (pr->prop_type != JS_PROP_VARREF) + return JS_ThrowReferenceError(ctx, "global variable '%"JSValue_PRI"' must be a reference", prop); + if (define_flag) { + JSVarRef *pv = JS_VALUE_TO_PTR(pr->value); + /* define the variable if needed */ + if (pv->u.value == JS_UNINITIALIZED) + pv->u.value = JS_UNDEFINED; + } + return pr->value; + } + return JS_DefinePropertyInternal(ctx, ctx->global_obj, prop, + define_flag ? JS_UNDEFINED : JS_UNINITIALIZED, JS_NULL, + JS_DEF_PROP_RET_VAL | JS_DEF_PROP_HAS_VALUE); +} + +/* return JS_UNDEFINED in the normal case. Otherwise: + - exception + - tail call : returned in case of getter and handle_getset = + true. The function is put on the stack +*/ +static JSValue JS_SetPropertyInternal(JSContext *ctx, JSValue this_obj, + JSValue prop, JSValue val, + BOOL allow_tail_call) +{ + JSValue proto; + JSObject *p; + JSProperty *pr; + BOOL is_obj; + + if (unlikely(!JS_IsPtr(this_obj))) { + is_obj = FALSE; + if (JS_IsIntOrShortFloat(this_obj)) { + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_NUMBER]); + goto prototype_lookup; + } else { + switch(JS_VALUE_GET_SPECIAL_TAG(this_obj)) { + case JS_TAG_BOOL: + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_BOOLEAN]); + goto prototype_lookup; + case JS_TAG_SHORT_FUNC: + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_CLOSURE]); + goto prototype_lookup; + case JS_TAG_STRING_CHAR: + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_STRING]); + goto prototype_lookup; + case JS_TAG_NULL: + return JS_ThrowTypeError(ctx, "cannot set property '%"JSValue_PRI"' of null", prop); + case JS_TAG_UNDEFINED: + return JS_ThrowTypeError(ctx, "cannot set property '%"JSValue_PRI"' of undefined", prop); + default: + goto no_prop; + } + } + } else { + is_obj = TRUE; + p = JS_VALUE_TO_PTR(this_obj); + } + if (unlikely(p->mtag != JS_MTAG_OBJECT)) { + is_obj = FALSE; + switch(p->mtag) { + case JS_MTAG_FLOAT64: + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_NUMBER]); + goto prototype_lookup; + case JS_MTAG_STRING: + p = JS_VALUE_TO_PTR(ctx->class_proto[JS_CLASS_STRING]); + goto prototype_lookup; + default: + no_prop: + return JS_ThrowTypeError(ctx, "cannot set property '%"JSValue_PRI"' of value", prop); + } + } + + /* search if the property is already present */ + if (p->class_id == JS_CLASS_ARRAY) { + if (JS_IsInt(prop)) { + JSValueArray *arr; + uint32_t idx = JS_VALUE_GET_INT(prop); + /* not standard: we refuse to add properties to object + except at the last position */ + if (idx < p->u.array.len) { + arr = JS_VALUE_TO_PTR(p->u.array.tab); + arr->arr[idx] = val; + return JS_UNDEFINED; + } else if (idx == p->u.array.len) { + JSValue new_tab; + JSGCRef this_obj_ref, val_ref; + + JS_PUSH_VALUE(ctx, this_obj); + JS_PUSH_VALUE(ctx, val); + new_tab = js_resize_value_array(ctx, p->u.array.tab, idx + 1); + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, this_obj); + if (JS_IsException(new_tab)) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(this_obj); + p->u.array.tab = new_tab; + arr = JS_VALUE_TO_PTR(p->u.array.tab); + arr->arr[idx] = val; + p->u.array.len++; + return JS_UNDEFINED; + } else { + goto invalid_array_subscript; + } + } else if (JS_IsNumericProperty(ctx, prop)) { + goto invalid_array_subscript; + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + if (JS_IsInt(prop)) { + uint32_t idx = JS_VALUE_GET_INT(prop); + int v, conv_ret; + double d; + JSObject *pbuffer; + JSByteArray *arr; + JSGCRef val_ref, this_obj_ref; + + JS_PUSH_VALUE(ctx, this_obj); + JS_PUSH_VALUE(ctx, val); + switch(p->class_id) { + case JS_CLASS_UINT8C_ARRAY: + conv_ret = JS_ToUint8Clamp(ctx, &v, val); + break; + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + conv_ret = JS_ToNumber(ctx, &d, val); + break; + default: + conv_ret = JS_ToInt32(ctx, &v, val); + break; + } + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, this_obj); + if (conv_ret) + return JS_EXCEPTION; + + p = JS_VALUE_TO_PTR(this_obj); + if (idx >= p->u.typed_array.len) + goto invalid_array_subscript; + idx += p->u.typed_array.offset; + pbuffer = JS_VALUE_TO_PTR(p->u.typed_array.buffer); + arr = JS_VALUE_TO_PTR(pbuffer->u.array_buffer.byte_buffer); + switch(p->class_id) { + default: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + *((uint8_t *)arr->buf + idx) = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + *((uint16_t *)arr->buf + idx) = v; + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + *((uint32_t *)arr->buf + idx) = v; + break; + case JS_CLASS_FLOAT32_ARRAY: + *((float *)arr->buf + idx) = d; + break; + case JS_CLASS_FLOAT64_ARRAY: + *((double *)arr->buf + idx) = d; + break; + } + return JS_UNDEFINED; + } else if (JS_IsNumericProperty(ctx, prop)) { + invalid_array_subscript: + return JS_ThrowTypeError(ctx, "invalid array subscript"); + } + } + + redo: + pr = find_own_property(ctx, p, prop); + if (pr) { + if (likely(pr->prop_type == JS_PROP_NORMAL)) { + if (unlikely(JS_IS_ROM_PTR(ctx, pr))) + goto convert_to_ram; + pr->value = val; + return JS_UNDEFINED; + } else if (pr->prop_type == JS_PROP_VARREF) { + JSVarRef *pv = JS_VALUE_TO_PTR(pr->value); + /* always detached */ + pv->u.value = val; + return JS_UNDEFINED; + } else if (pr->prop_type == JS_PROP_SPECIAL) { + JSGCRef val_ref, prop_ref, this_obj_ref; + int err; + convert_to_ram: + JS_PUSH_VALUE(ctx, this_obj); + JS_PUSH_VALUE(ctx, prop); + JS_PUSH_VALUE(ctx, val); + err = js_update_props(ctx, this_obj); + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, prop); + JS_POP_VALUE(ctx, this_obj); + if (err) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(this_obj); + goto redo; + } else { + goto getset; + } + } + + /* search in the prototype chain (getter/setters) */ + for(;;) { + proto = p->proto; + if (proto == JS_NULL) + break; + p = JS_VALUE_TO_PTR(proto); + prototype_lookup: + pr = find_own_property(ctx, p, prop); + if (pr) { + if (unlikely(pr->prop_type == JS_PROP_GETSET)) { + JSValueArray *arr; + JSValue setter; + getset: + arr = JS_VALUE_TO_PTR(pr->value); + setter = arr->arr[1]; + if (allow_tail_call) { + /* It is assumed "this_obj" already is on the stack + and that the stack has some slack to add one + element. */ + ctx->sp[-2] = ctx->sp[0]; + ctx->sp[-1] = setter; + ctx->sp[0] = val; + ctx->sp -= 2; + return JS_NewTailCall(1 | FRAME_CF_POP_RET); + } else { + JSGCRef val_ref, setter_ref, this_obj_ref; + int err; + JS_PUSH_VALUE(ctx, val); + JS_PUSH_VALUE(ctx, setter); + JS_PUSH_VALUE(ctx, this_obj); + err = JS_StackCheck(ctx, 3); + JS_POP_VALUE(ctx, this_obj); + JS_POP_VALUE(ctx, setter); + JS_POP_VALUE(ctx, val); + if (err) + return JS_EXCEPTION; + JS_PushArg(ctx, val); + JS_PushArg(ctx, setter); + JS_PushArg(ctx, this_obj); + return JS_Call(ctx, 1); + } + } else { + /* stop prototype chain lookup */ + break; + } + } + } + + /* add the property in the object */ + if (!is_obj) + return JS_ThrowTypeErrorNotAnObject(ctx); + return JS_DefinePropertyInternal(ctx, this_obj, prop, val, JS_UNDEFINED, + JS_DEF_PROP_HAS_VALUE); +} + +JSValue JS_SetPropertyStr(JSContext *ctx, JSValue this_obj, + const char *str, JSValue val) +{ + JSValue prop; + JSGCRef this_obj_ref, val_ref; + + JS_PUSH_VALUE(ctx, this_obj); + JS_PUSH_VALUE(ctx, val); + prop = JS_NewString(ctx, str); + if (!JS_IsException(prop)) { + prop = JS_ToPropertyKey(ctx, prop); + } + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, this_obj); + if (JS_IsException(prop)) + return prop; + return JS_SetPropertyInternal(ctx, this_obj, prop, val, FALSE); +} + +JSValue JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx, JSValue val) +{ + if (idx > JS_SHORTINT_MAX) + return JS_ThrowRangeError(ctx, "invalid array index"); + return JS_SetPropertyInternal(ctx, this_obj, JS_NewShortInt(idx), val, FALSE); +} + +/* return JS_FALSE, JS_TRUE or JS_EXCEPTION. Return false only if the + property is not configurable which is never the case here. */ +static JSValue JS_DeleteProperty(JSContext *ctx, JSValue this_obj, + JSValue prop) +{ + JSObject *p; + JSProperty *pr, *pr1; + JSValueArray *arr; + int h, idx, hash_mask, last_idx, prop_count, first_free; + JSGCRef this_obj_ref; + + JS_PUSH_VALUE(ctx, this_obj); + prop = JS_ToPropertyKey(ctx, prop); + JS_POP_VALUE(ctx, this_obj); + if (JS_IsException(prop)) + return prop; + + /* XXX: check return value */ + if (!JS_IsPtr(this_obj)) + return JS_TRUE; + p = JS_VALUE_TO_PTR(this_obj); + if (p->mtag != JS_MTAG_OBJECT) + return JS_TRUE; + + arr = JS_VALUE_TO_PTR(p->props); + hash_mask = JS_VALUE_GET_INT(arr->arr[1]); + h = hash_prop(prop) & hash_mask; + idx = JS_VALUE_GET_INT(arr->arr[2 + h]); + last_idx = -1; + while (idx != 0) { + pr = (JSProperty *)(arr->arr + idx); + if (pr->key == prop) { + if (JS_IS_ROM_PTR(ctx, arr)) { + JSGCRef this_obj_ref; + int ret; + JS_PUSH_VALUE(ctx, this_obj); + ret = js_update_props(ctx, this_obj); + JS_POP_VALUE(ctx, this_obj); + if (ret) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(this_obj); + arr = JS_VALUE_TO_PTR(p->props); + pr = (JSProperty *)(arr->arr + idx); + } + /* found: remove it */ + if (last_idx >= 0) { + JSProperty *lpr = (JSProperty *)(arr->arr + last_idx); + lpr->hash_next = pr->hash_next; + } else { + arr->arr[2 + h] = pr->hash_next; + } + first_free = get_first_free(arr); + + prop_count = JS_VALUE_GET_INT(arr->arr[0]); + arr->arr[0] = JS_NewShortInt(prop_count - 1); + pr->prop_type = JS_PROP_NORMAL; + pr->key = JS_UNINITIALIZED; + pr->value = JS_UNDEFINED; + + /* update first_free if needed */ + while (first_free > 2 + hash_mask + 1) { + pr1 = (JSProperty *)&arr->arr[first_free - 3]; + if (pr1->key != JS_UNINITIALIZED) + break; + first_free -= 3; + } + + /* update first_free */ + if (first_free < arr->size) { + pr1 = (JSProperty *)&arr->arr[arr->size - 3]; + pr1->hash_next = first_free << 1; + } + + /* compact the properties if needed */ + if ((2 + hash_mask + 1 + 3 * prop_count) < arr->size / 2) + js_compact_props(ctx, p); + return JS_TRUE; + } + last_idx = idx; + idx = pr->hash_next >> 1; + } + /* not found */ + return JS_TRUE; +} + +static JSValue stdlib_init_class(JSContext *ctx, const JSROMClass *class_def) +{ + JSValue obj, proto, parent_class, parent_proto; + JSGCRef parent_class_ref; + JSObject *p; + int ctor_idx = class_def->ctor_idx; + + if (ctor_idx >= 0) { + int class_id = ctx->c_function_table[ctor_idx].magic; + obj = ctx->class_obj[class_id]; + if (!JS_IsNull(obj)) + return obj; /* already defined */ + + /* initialize the parent class if necessary */ + if (!JS_IsNull(class_def->parent_class)) { + JSROMClass *parent_class_def = JS_VALUE_TO_PTR(class_def->parent_class); + int parent_class_id; + parent_class = stdlib_init_class(ctx, parent_class_def); + parent_class_id = ctx->c_function_table[parent_class_def->ctor_idx].magic; + parent_proto = ctx->class_proto[parent_class_id]; + } else { + parent_class = JS_NULL; + parent_proto = ctx->class_proto[JS_CLASS_OBJECT]; + } + /* initialize the prototype before. It is already defined only + for Object and Function */ + proto = ctx->class_proto[class_id]; + if (JS_IsNull(proto)) { + JS_PUSH_VALUE(ctx, parent_class); + proto = JS_NewObjectProtoClass(ctx, parent_proto, JS_CLASS_OBJECT, 0); + JS_POP_VALUE(ctx, parent_class); + ctx->class_proto[class_id] = proto; + } + p = JS_VALUE_TO_PTR(proto); + if (!JS_IsNull(class_def->proto_props)) + p->props = class_def->proto_props; + + if (JS_IsNull(parent_class)) + parent_class = ctx->class_proto[JS_CLASS_CLOSURE]; + obj = js_new_c_function_proto(ctx, ctor_idx, parent_class, FALSE, JS_NULL); + ctx->class_obj[class_id] = obj; + } else { + /* normal object */ + obj = JS_NewObject(ctx); + } + p = JS_VALUE_TO_PTR(obj); + if (!JS_IsNull(class_def->props)) { + /* set the properties from the ROM. They are copied to RAM + when modified */ + p->props = class_def->props; + } + return obj; +} + +static void stdlib_init(JSContext *ctx, const JSValueArray *arr) +{ + JSValue name, val; + int i; + + for(i = 0; i < arr->size; i += 2) { + name = arr->arr[i]; + val = arr->arr[i + 1]; + if (JS_IsObject(ctx, val)) { + val = stdlib_init_class(ctx, JS_VALUE_TO_PTR(val)); + } else if (val == JS_NULL) { + val = ctx->global_obj; + } + JS_DefinePropertyInternal(ctx, ctx->global_obj, name, + val, JS_NULL, + JS_DEF_PROP_HAS_VALUE); + } +} + +static void dummy_write_func(void *opaque, const void *buf, size_t buf_len) +{ + // fwrite(buf, 1, buf_len, stdout); +} + +/* if prepare_compilation is true, the context will be used to compile + to a binary file. It is not expected to be used in the embedded + version */ +JSContext *JS_NewContext2(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def, BOOL prepare_compilation) +{ + JSContext *ctx; + JSValueArray *arr; + int i, mem_align; + +#ifdef JS_PTR64 + mem_align = 8; +#else + mem_align = 4; +#endif + mem_size = mem_size & ~(mem_align - 1); + assert(mem_size >= 1024); + assert(((uintptr_t)mem_start & (mem_align - 1)) == 0); + + ctx = mem_start; + memset(ctx, 0, sizeof(*ctx)); + ctx->class_count = stdlib_def->class_count; + ctx->class_obj = ctx->class_proto + ctx->class_count; + ctx->heap_base = (void *)(ctx->class_proto + 2 * ctx->class_count); + ctx->heap_free = ctx->heap_base; + ctx->stack_top = mem_start + mem_size; + ctx->sp = (JSValue *)ctx->stack_top; + ctx->stack_bottom = ctx->sp; + ctx->fp = ctx->sp; + ctx->min_free_size = JS_MIN_FREE_SIZE; +#ifdef DEBUG_GC + ctx->dummy_block = JS_NULL; + ctx->unique_strings = JS_NULL; +#endif + ctx->random_state = 1; + ctx->write_func = dummy_write_func; + for(i = 0; i < JS_STRING_POS_CACHE_SIZE; i++) + ctx->string_pos_cache[i].str = JS_NULL; + + if (prepare_compilation) { + int atom_table_len; + JSValueArray *arr, *arr1; + uint8_t *ptr; + + /* for compilation, no stdlib is needed. Only the atoms + corresponding to JS_ATOM_x are needed and they are stored + in RAM. */ + /* copy the atoms to a fixed location at the beginning of the + heap */ + ctx->atom_table = (JSWord *)ctx->heap_free; + atom_table_len = stdlib_def->sorted_atoms_offset; + memcpy(ctx->heap_free, stdlib_def->stdlib_table, + atom_table_len * sizeof(JSWord)); + ctx->heap_free += atom_table_len * sizeof(JSWord); + + /* allocate the sorted atom table and populate it */ + arr1 = (JSValueArray *)(stdlib_def->stdlib_table + atom_table_len); + arr = js_alloc_value_array(ctx, 0, arr1->size); + ctx->unique_strings = JS_VALUE_FROM_PTR(arr); + for(i = 0; i < arr1->size; i++) { + ptr = JS_VALUE_TO_PTR(arr1->arr[i]); + ptr = ptr - (uint8_t *)stdlib_def->stdlib_table + + (uint8_t *)ctx->atom_table; + arr->arr[i] = JS_VALUE_FROM_PTR(ptr); + } + ctx->unique_strings_len = arr1->size; + } else { + ctx->atom_table = stdlib_def->stdlib_table; + ctx->rom_atom_tables[0] = (JSValueArray *)(stdlib_def->stdlib_table + + stdlib_def->sorted_atoms_offset); + ctx->n_rom_atom_tables = 1; + ctx->c_function_table = stdlib_def->c_function_table; + ctx->c_finalizer_table = stdlib_def->c_finalizer_table; + ctx->unique_strings = JS_NULL; + ctx->unique_strings_len = 0; + } + + + ctx->current_exception = JS_UNDEFINED; +#ifdef DEBUG_GC + /* set the dummy block at the start of the memory */ + { + JSByteArray *barr; + barr = js_alloc_byte_array(ctx, (min_int(mem_size / 2, 1 << 17)) & ~(JSW - 1)); + ctx->dummy_block = JS_VALUE_FROM_PTR(barr); + } +#endif + + arr = js_alloc_value_array(ctx, 0, 3); + arr->arr[0] = JS_NewShortInt(0); /* prop_count */ + arr->arr[1] = JS_NewShortInt(0); /* hash_mark */ + arr->arr[2] = JS_NewShortInt(0); /* hash_table[1] */ + ctx->empty_props = JS_VALUE_FROM_PTR(arr); + for(i = 0; i < ctx->class_count; i++) + ctx->class_proto[i] = JS_NULL; + for(i = 0; i < ctx->class_count; i++) + ctx->class_obj[i] = JS_NULL; + /* must be done first so that the prototype of Object.prototype is + JS_NULL */ + ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObject(ctx); + /* must be done for proper function init */ + ctx->class_proto[JS_CLASS_CLOSURE] = JS_NewObject(ctx); + + ctx->global_obj = JS_NewObject(ctx); + ctx->minus_zero = js_alloc_float64(ctx, -0.0); /* XXX: use a ROM value instead */ + + if (!prepare_compilation) { + stdlib_init(ctx, (JSValueArray *)(stdlib_def->stdlib_table + stdlib_def->global_object_offset)); + } + + return ctx; +} + +JSContext *JS_NewContext(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def) +{ + return JS_NewContext2(mem_start, mem_size, stdlib_def, FALSE); +} + +void JS_FreeContext(JSContext *ctx) +{ + uint8_t *ptr; + int size; + JSObject *p; + + /* call the user C finalizers */ + /* XXX: could disable it when prepare_compilation = true */ + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + size = get_mblock_size(ptr); + p = (JSObject *)ptr; + if (p->mtag == JS_MTAG_OBJECT && p->class_id >= JS_CLASS_USER && + ctx->c_finalizer_table[p->class_id - JS_CLASS_USER] != NULL) { + ctx->c_finalizer_table[p->class_id - JS_CLASS_USER](ctx, p->u.user.opaque); + } + ptr += size; + } +} + +void JS_SetContextOpaque(JSContext *ctx, void *opaque) +{ + ctx->opaque = opaque; +} + +void JS_SetInterruptHandler(JSContext *ctx, JSInterruptHandler *interrupt_handler) +{ + ctx->interrupt_handler = interrupt_handler; +} + +void JS_SetLogFunc(JSContext *ctx, JSWriteFunc *write_func) +{ + ctx->write_func = write_func; +} + +void JS_SetRandomSeed(JSContext *ctx, uint64_t seed) +{ + ctx->random_state = seed; +} + +JSValue JS_GetGlobalObject(JSContext *ctx) +{ + return ctx->global_obj; +} + +static JSValue get_var_ref(JSContext *ctx, JSValue *pfirst_var_ref, JSValue *pval) +{ + JSValue val; + JSVarRef *p; + + val = *pfirst_var_ref; + for(;;) { + if (val == JS_NULL) + break; + p = JS_VALUE_TO_PTR(val); + assert(!p->is_detached); + if (p->u.pvalue == pval) + return val; + val = p->u.next; + } + + p = js_malloc(ctx, sizeof(JSVarRef), JS_MTAG_VARREF); + if (!p) + return JS_EXCEPTION; + p->is_detached = FALSE; + p->u.pvalue = pval; + p->u.next = *pfirst_var_ref; + val = JS_VALUE_FROM_PTR(p); + *pfirst_var_ref = val; + return val; +} + +#define FRAME_OFFSET_ARG0 4 +#define FRAME_OFFSET_FUNC_OBJ 3 +#define FRAME_OFFSET_THIS_OBJ 2 +#define FRAME_OFFSET_CALL_FLAGS 1 +#define FRAME_OFFSET_SAVED_FP 0 +#define FRAME_OFFSET_CUR_PC (-1) /* current pc_offset */ +#define FRAME_OFFSET_FIRST_VARREF (-2) +#define FRAME_OFFSET_VAR0 (-3) + +/* stack layout: + + padded_args (padded_argc - argc) + args (argc) + func_obj fp[3] + this_obj fp[2] + call_flags (int) fp[1] + saved_fp (int) fp[0] + cur_pc (int) fp[-1] + first_var_ref (val) fp[-2] + vars (var_count) + temp stack (pointed by sp) +*/ + +#define SP_TO_VALUE(ctx, fp) JS_NewShortInt((uint8_t *)(fp) - (uint8_t *)ctx) +#define VALUE_TO_SP(ctx, val) (void *)((uint8_t *)ctx + JS_VALUE_GET_INT(val)) + +/* buf_end points to the end of the buffer (after the final '\0') */ +static __js_printf_like(3, 4) void cprintf(char **pp, char *buf_end, const char *fmt, ...) +{ + char *p; + va_list ap; + + p = *pp; + if ((p + 1) >= buf_end) + return; + va_start(ap, fmt); + js_vsnprintf(p, buf_end - p, fmt, ap); + va_end(ap); + p += strlen(p); + *pp = p; +} + +static JSValue reloc_c_func_name(JSContext *ctx, JSValue val) +{ + return val; +} + +/* no memory allocation is done */ +/* XXX: handle bound functions */ +static JSValue js_function_get_length_name1(JSContext *ctx, JSValue *this_val, + int is_name, JSFunctionBytecode **pb) +{ + int short_func_idx; + const JSCFunctionDef *fd; + JSValue ret; + + if (!JS_IsPtr(*this_val)) { + if (JS_VALUE_GET_SPECIAL_TAG(*this_val) != JS_TAG_SHORT_FUNC) + goto fail; + short_func_idx = JS_VALUE_GET_SPECIAL_VALUE(*this_val); + goto short_func; + } else { + JSObject *p = JS_VALUE_TO_PTR(*this_val); + JSFunctionBytecode *b; + if (p->mtag != JS_MTAG_OBJECT) + goto fail; + if (p->class_id == JS_CLASS_CLOSURE) { + b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode); + if (is_name) { + /* XXX: directly set func_name to the empty string ? */ + if (b->func_name == JS_NULL) + ret = js_get_atom(ctx, JS_ATOM_empty); + else + ret = b->func_name; + } else { + ret = JS_NewShortInt(b->arg_count); + } + *pb = b; + return ret; + } else if (p->class_id == JS_CLASS_C_FUNCTION) { + short_func_idx = p->u.cfunc.idx; + short_func: + fd = &ctx->c_function_table[short_func_idx]; + if (is_name) { + ret = reloc_c_func_name(ctx, fd->name); + } else { + ret = JS_NewShortInt(fd->arg_count); + } + *pb = NULL; + return ret; + } else { + fail: + *pb = NULL; + return JS_NULL; + } + } +} + +static uint32_t get_bit(const uint8_t *buf, uint32_t index) +{ + return (buf[index >> 3] >> (7 - (index & 7))) & 1; +} + +static uint32_t get_bits_slow(const uint8_t *buf, uint32_t index, int n) +{ + int i; + uint32_t val; + val = 0; + for(i = 0; i < n; i++) + val |= get_bit(buf, index + i) << (n - 1 - i); + return val; +} + +static uint32_t get_bits(const uint8_t *buf, uint32_t buf_len, + uint32_t index, int n) +{ + uint32_t val, pos; + + pos = index >> 3; + if (unlikely(n > 25 || (pos + 3) >= buf_len)) { + /* slow case */ + return get_bits_slow(buf, index, n); + } else { + /* fast case */ + val = get_be32(buf + pos); + return (val >> (32 - (index & 7) - n)) & ((1 << n) - 1); + } +} + +static uint32_t get_ugolomb(const uint8_t *buf, uint32_t buf_len, + uint32_t *pindex) +{ + uint32_t index = *pindex; + int i; + uint32_t v; + + i = 0; + for(;;) { + if (get_bit(buf, index++)) + break; + i++; + if (i == 32) { + /* error */ + *pindex = index; + return 0xffffffff; + } + } + if (i == 0) { + v = 0; + } else { + v = ((1 << i) | get_bits(buf, buf_len, index, i)) - 1; + index += i; + } + *pindex = index; + // printf("get_ugolomb: v=%d\n", v); + return v; +} + +static int32_t get_sgolomb(const uint8_t *buf, uint32_t buf_len, + uint32_t *pindex) +{ + uint32_t val; + val = get_ugolomb(buf, buf_len, pindex); + return (val >> 1) ^ -(val & 1); +} + +static int get_pc2line_hoisted_code_len(const uint8_t *buf, size_t buf_len) +{ + size_t i = buf_len; + int v = 0; + while (i > 0) { + i--; + v = (v << 7) | (buf[i] & 0x7f); + if ((buf[i] & 0x80) == 0) + break; + } + return v; +} + +/* line_num, col_num and index are updated */ +static void get_pc2line(int *pline_num, int *pcol_num, const uint8_t *buf, + uint32_t buf_len, uint32_t *pindex, BOOL has_column) +{ + int line_delta, line_num, col_num, col_delta; + + line_num = *pline_num; + col_num = *pcol_num; + + line_delta = get_sgolomb(buf, buf_len, pindex); + line_num += line_delta; + if (has_column) { + if (line_delta == 0) { + col_delta = get_sgolomb(buf, buf_len, pindex); + col_num += col_delta; + } else { + col_num = get_ugolomb(buf, buf_len, pindex) + 1; + } + } else { + col_num = 0; + } + *pline_num = line_num; + *pcol_num = col_num; +} + +/* return 0 if line/col number info */ +static int find_line_col(int *pcol_num, JSFunctionBytecode *b, uint32_t pc) +{ + JSByteArray *arr, *pc2line; + int pos, op, line_num, col_num; + uint32_t pc2line_pos; + + if (b->pc2line == JS_NULL) + goto fail; + arr = JS_VALUE_TO_PTR(b->byte_code); + pc2line = JS_VALUE_TO_PTR(b->pc2line); + + /* skip the hoisted code */ + pos = get_pc2line_hoisted_code_len(pc2line->buf, pc2line->size); + if (pc < pos) + pc = pos; + pc2line_pos = 0; + line_num = 1; + col_num = 1; + while (pos < arr->size) { + get_pc2line(&line_num, &col_num, pc2line->buf, pc2line->size, + &pc2line_pos, b->has_column); + if (pos == pc) { + *pcol_num = col_num; + return line_num; + } + op = arr->buf[pos]; + pos += opcode_info[op].size; + } + fail: + *pcol_num = 0; + return 0; +} + +static const char *get_func_name(JSContext *ctx, JSValue func_obj, + JSCStringBuf *str_buf, JSFunctionBytecode **pb) +{ + JSValue val; + val = js_function_get_length_name1(ctx, &func_obj, 1, pb); + if (JS_IsNull(val)) + return NULL; + return JS_ToCString(ctx, val, str_buf); +} + +static void build_backtrace(JSContext *ctx, JSValue error_obj, + const char *filename, int line_num, int col_num, int skip_level) +{ + JSObject *p1; + char buf[128], *p, *buf_end, *line_start; + const char *str; + JSValue *fp, stack_str; + JSCStringBuf str_buf; + JSFunctionBytecode *b; + int level; + JSGCRef error_obj_ref; + + if (!JS_IsError(ctx, error_obj)) + return; + p = buf; + buf_end = buf + sizeof(buf); + p[0] = '\0'; + if (filename) { + cprintf(&p, buf_end, " at %s:%d:%d\n", filename, line_num, col_num); + } + fp = ctx->fp; + level = 0; + while (fp != (JSValue *)ctx->stack_top && level < 10) { + if (skip_level != 0) { + skip_level--; + } else { + line_start = p; + str = get_func_name(ctx, fp[FRAME_OFFSET_FUNC_OBJ], &str_buf, &b); + if (!str) + str = ""; + cprintf(&p, buf_end, " at %s", str); + if (b) { + int pc, line_num, col_num; + const char *filename; + filename = JS_ToCString(ctx, b->filename, &str_buf); + pc = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CUR_PC]) - 1; + line_num = find_line_col(&col_num, b, pc); + cprintf(&p, buf_end, " (%s", filename); + if (line_num != 0) { + cprintf(&p, buf_end, ":%d", line_num); + if (col_num != 0) + cprintf(&p, buf_end, ":%d", col_num); + } + cprintf(&p, buf_end, ")"); + } else { + cprintf(&p, buf_end, " (native)"); + } + cprintf(&p, buf_end, "\n"); + /* if truncated line, remove it and stop */ + if ((p + 1) >= buf_end) { + *line_start = '\0'; + break; + } + level++; + } + fp = VALUE_TO_SP(ctx, fp[FRAME_OFFSET_SAVED_FP]); + } + + JS_PUSH_VALUE(ctx, error_obj); + stack_str = JS_NewString(ctx, buf); + JS_POP_VALUE(ctx, error_obj); + p1 = JS_VALUE_TO_PTR(error_obj); + p1->u.error.stack = stack_str; +} + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE HINT_NUMBER + +static JSValue JS_ToPrimitive(JSContext *ctx, JSValue val, int hint) +{ + int i, atom; + JSValue method, ret; + JSGCRef val_ref, method_ref; + + if (JS_IsPrimitive(ctx, val)) + return val; + for(i = 0; i < 2; i++) { + if ((i ^ hint) == 0) { + atom = JS_ATOM_toString; + } else { + atom = JS_ATOM_valueOf; + } + JS_PUSH_VALUE(ctx, val); + method = JS_GetProperty(ctx, val, js_get_atom(ctx, atom)); + JS_POP_VALUE(ctx, val); + if (JS_IsException(method)) + return method; + if (JS_IsFunction(ctx, method)) { + int err; + JS_PUSH_VALUE(ctx, method); + JS_PUSH_VALUE(ctx, val); + err = JS_StackCheck(ctx, 2); + JS_POP_VALUE(ctx, val); + JS_POP_VALUE(ctx, method); + if (err) + return JS_EXCEPTION; + + JS_PushArg(ctx, method); + JS_PushArg(ctx, val); + JS_PUSH_VALUE(ctx, val); + ret = JS_Call(ctx, 0); + JS_POP_VALUE(ctx, val); + if (JS_IsException(ret)) + return ret; + if (!JS_IsObject(ctx, ret)) + return ret; + } + } + return JS_ThrowTypeError(ctx, "toPrimitive"); +} + +/* return a string or an exception */ +static JSValue js_dtoa2(JSContext *ctx, double d, int radix, int n_digits, int flags) +{ + int len_max, len; + JSValue str; + JSGCRef str_ref; + JSByteArray *tmp_arr, *p; + + len_max = js_dtoa_max_len(d, radix, n_digits, flags); + p = js_alloc_byte_array(ctx, len_max + 1); + if (!p) + return JS_EXCEPTION; + /* allocate the temporary buffer */ + str = JS_VALUE_FROM_PTR(p); + JS_PUSH_VALUE(ctx, str); + tmp_arr = js_alloc_byte_array(ctx, sizeof(JSDTOATempMem)); + JS_POP_VALUE(ctx, str); + if (!tmp_arr) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(str); + /* Note: tmp_arr->buf is always 32 bit aligned */ + len = js_dtoa((char *)p->buf, d, radix, n_digits, flags, (JSDTOATempMem *)tmp_arr->buf); + js_free(ctx, tmp_arr); + return js_byte_array_to_string(ctx, str, len, TRUE); +} + +JSValue JS_ToString(JSContext *ctx, JSValue val) +{ + char buf[128]; + int atom; + const char *str; + + redo: + if (JS_IsInt(val)) { + int len; + len = i32toa(buf, JS_VALUE_GET_INT(val)); + buf[len] = '\0'; + goto ret_buf; + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_IsShortFloat(val)) { + return js_dtoa2(ctx, js_get_short_float(val), 10, 0, JS_DTOA_FORMAT_FREE); + } else +#endif + if (JS_IsPtr(val)) { + void *ptr = JS_VALUE_TO_PTR(val); + int mtag = js_get_mtag(ptr); + switch(mtag) { + case JS_MTAG_OBJECT: + to_primitive: + val = JS_ToPrimitive(ctx, val, HINT_STRING); + if (JS_IsException(val)) + return val; + goto redo; + case JS_MTAG_STRING: + return val; + case JS_MTAG_FLOAT64: + { + JSFloat64 *p = ptr; + return js_dtoa2(ctx, p->u.dval, 10, 0, JS_DTOA_FORMAT_FREE); + } + default: + js_snprintf(buf, sizeof(buf), "[mtag %d]", mtag); + goto ret_buf; + } + } else { + switch(JS_VALUE_GET_SPECIAL_TAG(val)) { + case JS_TAG_NULL: + atom = JS_ATOM_null; + goto ret_atom; + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + goto ret_atom; + case JS_TAG_BOOL: + if (JS_VALUE_GET_SPECIAL_VALUE(val)) + atom = JS_ATOM_true; + else + atom = JS_ATOM_false; + ret_atom: + return js_get_atom(ctx, atom); + case JS_TAG_STRING_CHAR: + return val; + case JS_TAG_SHORT_FUNC: + goto to_primitive; + default: + str = "?"; + goto ret_str; + ret_buf: + str = buf; + ret_str: + return JS_NewString(ctx, str); + } + } +} + +/* return either a unique string or an integer. Strings representing + a short integer are converted to short integer */ +static JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val) +{ + int32_t n; + if (JS_IsInt(val)) + return val; + val = JS_ToString(ctx, val); + if (JS_IsException(val)) + return val; + if (is_num_string(ctx, &n, val)) + return JS_NewShortInt(n); + else + return JS_MakeUniqueString(ctx, val); +} + +static int skip_spaces(const char *p1) +{ + const char *p = p1; + int c; + for(;;) { + c = *p; + if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) + break; + p++; + } + return p - p1; +} + +/* JS_ToString() specific behaviors */ +#define JS_ATOD_TOSTRING (1 << 8) + +/* 'val' must be a string */ +static int js_atod1(JSContext *ctx, double *pres, JSValue val, + int radix, int flags) +{ + JSString *p; + JSByteArray *tmp_arr; + double d; + JSGCRef val_ref; + const char *p1; + + if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) { + int c = JS_VALUE_GET_SPECIAL_VALUE(val); + if (c >= '0' && c <= '9') { + *pres = c - '0'; + } else { + *pres = NAN; + } + return 0; + } + + JS_PUSH_VALUE(ctx, val); + tmp_arr = js_alloc_byte_array(ctx, sizeof(JSATODTempMem)); + JS_POP_VALUE(ctx, val); + if (!tmp_arr) { + *pres = NAN; + return -1; + } + p = JS_VALUE_TO_PTR(val); + p1 = (char *)p->buf; + p1 += skip_spaces(p1); + if ((p1 - (char *)p->buf) == p->len) { + if (flags & JS_ATOD_TOSTRING) + d = 0; + else + d = NAN; + goto done; + } + d = js_atod(p1, &p1, radix, flags, (JSATODTempMem *)tmp_arr->buf); + js_free(ctx, tmp_arr); + if (flags & JS_ATOD_TOSTRING) { + p1 += skip_spaces(p1); + if ((p1 - (char *)p->buf) < p->len) + d = NAN; + } + done: + *pres = d; + return 0; +} + +/* Note: can fail due to memory allocation even if primitive type */ +int JS_ToNumber(JSContext *ctx, double *pres, JSValue val) +{ + redo: + if (JS_IsInt(val)) { + *pres = (double)JS_VALUE_GET_INT(val); + return 0; + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_IsShortFloat(val)) { + *pres = js_get_short_float(val); + return 0; + } else +#endif + if (JS_IsPtr(val)) { + void *ptr = JS_VALUE_TO_PTR(val); + switch(js_get_mtag(ptr)) { + case JS_MTAG_STRING: + goto atod; + case JS_MTAG_FLOAT64: + { + JSFloat64 *p = ptr; + *pres = p->u.dval; + return 0; + } + case JS_MTAG_OBJECT: + val = JS_ToPrimitive(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) { + *pres = NAN; + return -1; + } + goto redo; + default: + *pres = NAN; + return 0; + } + } else { + switch(JS_VALUE_GET_SPECIAL_TAG(val)) { + case JS_TAG_NULL: + case JS_TAG_BOOL: + *pres = (double)JS_VALUE_GET_SPECIAL_VALUE(val); + return 0; + case JS_TAG_UNDEFINED: + *pres = NAN; + return 0; + case JS_TAG_STRING_CHAR: + atod: + return js_atod1(ctx, pres, val, 0, + JS_ATOD_ACCEPT_BIN_OCT | JS_ATOD_TOSTRING); + default: + *pres = NAN; + return 0; + } + } +} + +static int JS_ToInt32Internal(JSContext *ctx, int *pres, JSValue val, BOOL sat_flag) +{ + int32_t ret; + double d; + + if (JS_IsInt(val)) { + ret = JS_VALUE_GET_INT(val); + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_IsShortFloat(val)) { + d = js_get_short_float(val); + goto handle_float64; + } else +#endif + if (JS_IsPtr(val)) { + uint64_t u; + int e; + + handle_number: + if (JS_ToNumber(ctx, &d, val)) { + *pres = 0; + return -1; + } +#ifdef JS_USE_SHORT_FLOAT + handle_float64: +#endif + u = float64_as_uint64(d); + e = (u >> 52) & 0x7ff; + if (likely(e <= (1023 + 30))) { + /* fast case */ + ret = (int32_t)d; + } else if (!sat_flag) { + if (e <= (1023 + 30 + 53)) { + uint64_t v; + /* remainder modulo 2^32 */ + v = (u & (((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 >> 63) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } else { + if (e == 2047 && (u & (((uint64_t)1 << 52) - 1)) != 0) { + /* nan */ + ret = 0; + } else { + /* take the sign into account */ + if (u >> 63) + ret = 0x80000000; + else + ret = 0x7fffffff; + } + } + } else { + switch(JS_VALUE_GET_SPECIAL_TAG(val)) { + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_SPECIAL_VALUE(val); + break; + default: + goto handle_number; + } + } + *pres = ret; + return 0; +} + +int JS_ToInt32(JSContext *ctx, int *pres, JSValue val) +{ + return JS_ToInt32Internal(ctx, pres, val, FALSE); +} + +int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValue val) +{ + return JS_ToInt32Internal(ctx, (int *)pres, val, FALSE); +} + +int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValue val) +{ + return JS_ToInt32Internal(ctx, pres, val, TRUE); +} + +static int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValue val, + int min, int max, int min_offset) +{ + int res = JS_ToInt32Sat(ctx, pres, val); + if (res == 0) { + if (*pres < min) { + *pres += min_offset; + if (*pres < min) + *pres = min; + } else { + if (*pres > max) + *pres = max; + } + } + return res; +} + +static int JS_ToUint8Clamp(JSContext *ctx, int *pres, JSValue val) +{ + int32_t ret; + double d; + + if (JS_IsInt(val)) { + ret = JS_VALUE_GET_INT(val); + if (ret < 0) + ret = 0; + else if (ret > 255) + ret = 255; + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_IsShortFloat(val)) { + d = js_get_short_float(val); + goto handle_float64; + } else +#endif + if (JS_IsPtr(val)) { + handle_number: + if (JS_ToNumber(ctx, &d, val)) { + *pres = 0; + return -1; + } +#ifdef JS_USE_SHORT_FLOAT + handle_float64: +#endif + if (d < 0 || isnan(d)) + ret = 0; + else if (d > 255) + ret = 255; + else + ret = js_lrint(d); + } else { + switch(JS_VALUE_GET_SPECIAL_TAG(val)) { + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_SPECIAL_VALUE(val); + break; + default: + goto handle_number; + } + } + *pres = ret; + return 0; +} + +static int js_get_length32(JSContext *ctx, uint32_t *pres, JSValue obj) +{ + JSValue len_val; + len_val = JS_GetProperty(ctx, obj, js_get_atom(ctx, JS_ATOM_length)); + if (JS_IsException(len_val)) { + *pres = 0; + return -1; + } + return JS_ToUint32(ctx, pres, len_val); +} + +static no_inline JSValue js_add_slow(JSContext *ctx) +{ + JSValue *op1, *op2; + + op1 = &ctx->sp[1]; + op2 = &ctx->sp[0]; + *op1 = JS_ToPrimitive(ctx, *op1, HINT_NONE); + if (JS_IsException(*op1)) + return JS_EXCEPTION; + *op2 = JS_ToPrimitive(ctx, *op2, HINT_NONE); + if (JS_IsException(*op2)) + return JS_EXCEPTION; + if (JS_IsString(ctx, *op1) || JS_IsString(ctx, *op2)) { + *op1 = JS_ToString(ctx, *op1); + if (JS_IsException(*op1)) + return JS_EXCEPTION; + *op2 = JS_ToString(ctx, *op2); + if (JS_IsException(*op2)) + return JS_EXCEPTION; + return JS_ConcatString(ctx, *op1, *op2); + } else { + double d1, d2, r; + /* cannot fail */ + if (JS_ToNumber(ctx, &d1, *op1)) + return JS_EXCEPTION; + if (JS_ToNumber(ctx, &d2, *op2)) + return JS_EXCEPTION; + r = d1 + d2; + return JS_NewFloat64(ctx, r); + } +} + +static no_inline JSValue js_binary_arith_slow(JSContext *ctx, OPCodeEnum op) +{ + double d1, d2, r; + + if (JS_ToNumber(ctx, &d1, ctx->sp[1])) + return JS_EXCEPTION; + + if (JS_ToNumber(ctx, &d2, ctx->sp[0])) + return JS_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 = js_fmod(d1, d2); + break; + case OP_pow: + r = js_pow(d1, d2); + break; + default: + abort(); + } + return JS_NewFloat64(ctx, r); +} + +static no_inline JSValue js_unary_arith_slow(JSContext *ctx, OPCodeEnum op) +{ + double d; + + if (JS_ToNumber(ctx, &d, ctx->sp[0])) + return JS_EXCEPTION; + + switch(op) { + case OP_inc: + d++; + break; + case OP_dec: + d--; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); + } + return JS_NewFloat64(ctx, d); +} + +/* specific case necessary for correct return value semantics */ +static no_inline JSValue js_post_inc_slow(JSContext *ctx, OPCodeEnum op) +{ + JSValue val; + double d, r; + + if (JS_ToNumber(ctx, &d, ctx->sp[0])) + return JS_EXCEPTION; + r = d + 2 * (op - OP_post_dec) - 1; + val = JS_NewFloat64(ctx, d); + if (JS_IsException(val)) + return val; + ctx->sp[0] = val; + return JS_NewFloat64(ctx, r); +} + +static no_inline JSValue js_binary_logic_slow(JSContext *ctx, OPCodeEnum op) +{ + uint32_t v1, v2, r; + + if (JS_ToUint32(ctx, &v1, ctx->sp[1])) + return JS_EXCEPTION; + if (JS_ToUint32(ctx, &v2, ctx->sp[0])) + return JS_EXCEPTION; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_shr: + r = v1 >> (v2 & 0x1f); + return JS_NewUint32(ctx, r); + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + return JS_NewInt32(ctx, r); +} + +static no_inline JSValue js_not_slow(JSContext *ctx) +{ + uint32_t r; + + if (JS_ToUint32(ctx, &r, ctx->sp[0])) + return JS_EXCEPTION; + return JS_NewInt32(ctx, ~r); +} + +static no_inline JSValue js_relational_slow(JSContext *ctx, OPCodeEnum op) +{ + JSValue *op1, *op2; + int res; + double d1, d2; + + op1 = &ctx->sp[1]; + op2 = &ctx->sp[0]; + *op1 = JS_ToPrimitive(ctx, *op1, HINT_NUMBER); + if (JS_IsException(*op1)) + return JS_EXCEPTION; + *op2 = JS_ToPrimitive(ctx, *op2, HINT_NUMBER); + if (JS_IsException(*op2)) + return JS_EXCEPTION; + if (JS_IsString(ctx, *op1) && JS_IsString(ctx, *op2)) { + res = js_string_compare(ctx, *op1, *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 { + if (JS_ToNumber(ctx, &d1, *op1)) + return JS_EXCEPTION; + if (JS_ToNumber(ctx, &d2, *op2)) + return JS_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; + } + } + return JS_NewBool(res); +} + +static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) +{ + BOOL res; + + if (JS_IsNumber(ctx, op1)) { + if (!JS_IsNumber(ctx, op2)) { + res = FALSE; + } else { + double d1, d2; + /* cannot fail */ + JS_ToNumber(ctx, &d1, op1); + JS_ToNumber(ctx, &d2, op2); + res = (d1 == d2); /* if NaN return false */ + } + } else if (JS_IsString(ctx, op1)) { + if (!JS_IsString(ctx, op2)) { + res = FALSE; + } else { + res = js_string_eq(ctx, op1, op2); + } + } else { + /* special value or object */ + res = (op1 == op2); + } + return res; +} + +static JSValue js_strict_eq_slow(JSContext *ctx, BOOL is_neq) +{ + BOOL res; + res = js_strict_eq(ctx, ctx->sp[1], ctx->sp[0]); + return JS_NewBool(res ^ is_neq); +} + +enum { + /* special tags to simplify the comparison */ + JS_ETAG_NUMBER = JS_TAG_SPECIAL | (8 << 2), + JS_ETAG_STRING = JS_TAG_SPECIAL | (9 << 2), + JS_ETAG_OBJECT = JS_TAG_SPECIAL | (10 << 2), +}; + +static int js_eq_get_type(JSContext *ctx, JSValue val) +{ + if (JS_IsIntOrShortFloat(val)) { + return JS_ETAG_NUMBER; + } else if (JS_IsPtr(val)) { + void *ptr = JS_VALUE_TO_PTR(val); + switch(js_get_mtag(ptr)) { + case JS_MTAG_FLOAT64: + return JS_ETAG_NUMBER; + case JS_MTAG_STRING: + return JS_ETAG_STRING; + default: + case JS_MTAG_OBJECT: + return JS_ETAG_OBJECT; + } + } else { + int tag = JS_VALUE_GET_SPECIAL_TAG(val); + switch(tag) { + case JS_TAG_STRING_CHAR: + return JS_ETAG_STRING; + case JS_TAG_SHORT_FUNC: + return JS_ETAG_OBJECT; + default: + return tag; + } + } +} + +static no_inline JSValue js_eq_slow(JSContext *ctx, BOOL is_neq) +{ + JSValue op1, op2; + int tag1, tag2; + BOOL res; + + redo: + op1 = ctx->sp[1]; + op2 = ctx->sp[0]; + tag1 = js_eq_get_type(ctx, op1); + tag2 = js_eq_get_type(ctx, op2); + if (tag1 == tag2) { + 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_ETAG_STRING && tag2 == JS_ETAG_NUMBER) || + (tag2 == JS_ETAG_STRING && tag1 == JS_ETAG_NUMBER)) { + double d1; + double d2; + if (JS_ToNumber(ctx, &d1, ctx->sp[1])) + return JS_EXCEPTION; + if (JS_ToNumber(ctx, &d2, ctx->sp[0])) + return JS_EXCEPTION; + res = (d1 == d2); + } else if (tag1 == JS_TAG_BOOL) { + ctx->sp[1] = JS_NewShortInt(JS_VALUE_GET_SPECIAL_VALUE(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + ctx->sp[0] = JS_NewShortInt(JS_VALUE_GET_SPECIAL_VALUE(op2)); + goto redo; + } else if (tag1 == JS_ETAG_OBJECT && + (tag2 == JS_ETAG_NUMBER || tag2 == JS_ETAG_STRING)) { + ctx->sp[1] = JS_ToPrimitive(ctx, op1, HINT_NONE); + if (JS_IsException(ctx->sp[1])) + return JS_EXCEPTION; + goto redo; + } else if (tag2 == JS_ETAG_OBJECT && + (tag1 == JS_ETAG_NUMBER || tag1 == JS_ETAG_STRING)) { + ctx->sp[0] = JS_ToPrimitive(ctx, op2, HINT_NONE); + if (JS_IsException(ctx->sp[0])) + return JS_EXCEPTION; + goto redo; + } else { + res = FALSE; + } + return JS_NewBool(res ^ is_neq); +} + +static JSValue js_operator_in(JSContext *ctx) +{ + JSValue prop; + int res; + + if (js_eq_get_type(ctx, ctx->sp[0]) != JS_ETAG_OBJECT) + return JS_ThrowTypeError(ctx, "invalid 'in' operand"); + prop = JS_ToPropertyKey(ctx, ctx->sp[1]); + if (JS_IsException(prop)) + return prop; + res = JS_HasProperty(ctx, ctx->sp[0], prop); + return JS_NewBool(res); +} + +static JSValue js_operator_instanceof(JSContext *ctx) +{ + JSValue op1, op2, proto; + JSObject *p; + + op1 = ctx->sp[1]; + op2 = ctx->sp[0]; + if (!JS_IsFunctionObject(ctx, op2)) + return JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand"); + proto = JS_GetProperty(ctx, op2, js_get_atom(ctx, JS_ATOM_prototype)); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(ctx, op1)) + return JS_NewBool(FALSE); + p = JS_VALUE_TO_PTR(op1); + for(;;) { + if (p->proto == JS_NULL) + return JS_NewBool(FALSE); + if (p->proto == proto) + return JS_NewBool(TRUE); + p = JS_VALUE_TO_PTR(p->proto); + } + return JS_NewBool(FALSE); +} + +static JSValue js_operator_typeof(JSContext *ctx, JSValue val) +{ + int tag, atom; + tag = js_eq_get_type(ctx, val); + switch(tag) { + case JS_ETAG_NUMBER: + atom = JS_ATOM_number; + break; + case JS_ETAG_STRING: + atom = JS_ATOM_string; + break; + case JS_TAG_BOOL: + atom = JS_ATOM_boolean; + break; + case JS_ETAG_OBJECT: + if (JS_IsFunction(ctx, val)) + atom = JS_ATOM_function; + else + atom = JS_ATOM_object; + break; + case JS_TAG_NULL: + atom = JS_ATOM_object; + break; + default: + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + break; + } + return js_get_atom(ctx, atom); +} + +static void js_reverse_val(JSValue *tab, int n) +{ + int i; + JSValue tmp; + + for(i = 0; i < n / 2; i++) { + tmp = tab[i]; + tab[i] = tab[n - 1 - i]; + tab[n - 1 - i] = tmp; + } +} + +static JSValue js_closure(JSContext *ctx, JSValue bfunc, JSValue *fp) +{ + JSFunctionBytecode *b; + JSObject *p; + JSGCRef bfunc_ref, closure_ref; + JSValueArray *ext_vars; + JSValue closure; + int ext_vars_len; + + b = JS_VALUE_TO_PTR(bfunc); + if (b->ext_vars != JS_NULL) { + ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + ext_vars_len = ext_vars->size / 2; + } else { + ext_vars_len = 0; + } + + JS_PUSH_VALUE(ctx, bfunc); + closure = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_CLOSURE], JS_CLASS_CLOSURE, + sizeof(JSClosureData) + ext_vars_len * sizeof(JSValue)); + JS_POP_VALUE(ctx, bfunc); + if (JS_IsException(closure)) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(closure); + p->u.closure.func_bytecode = bfunc; + + if (ext_vars_len > 0) { + JSValue *pfirst_var_ref, val; + int i, var_idx, var_kind, decl; + + /* initialize the var_refs in case of exception */ + memset(p->u.closure.var_refs, 0, sizeof(JSValue) * ext_vars_len); + if (fp) { + pfirst_var_ref = &fp[FRAME_OFFSET_FIRST_VARREF]; + } else { + pfirst_var_ref = NULL; /* not used */ + } + for(i = 0; i < ext_vars_len; i++) { + b = JS_VALUE_TO_PTR(bfunc); + ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + decl = JS_VALUE_GET_INT(ext_vars->arr[2 * i + 1]); + var_kind = decl >> 16; + var_idx = decl & 0xffff; + JS_PUSH_VALUE(ctx, bfunc); + JS_PUSH_VALUE(ctx, closure); + switch(var_kind) { + case JS_VARREF_KIND_ARG: + val = get_var_ref(ctx, pfirst_var_ref, + &fp[FRAME_OFFSET_ARG0 + var_idx]); + break; + case JS_VARREF_KIND_VAR: + val = get_var_ref(ctx, pfirst_var_ref, + &fp[FRAME_OFFSET_VAR0 - var_idx]); + break; + case JS_VARREF_KIND_VAR_REF: + { + JSObject *p; + p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]); + val = p->u.closure.var_refs[var_idx]; + } + break; + case JS_VARREF_KIND_GLOBAL: + /* only for eval code */ + val = add_global_var(ctx, ext_vars->arr[2 * i], (var_idx != 0)); + break; + default: + abort(); + } + JS_POP_VALUE(ctx, closure); + JS_POP_VALUE(ctx, bfunc); + if (JS_IsException(val)) + return val; + p = JS_VALUE_TO_PTR(closure); + p->u.closure.var_refs[i] = val; + } + } + return closure; +} + +static JSValue js_for_of_start(JSContext *ctx, BOOL is_for_in) +{ + JSValueArray *arr; + + if (is_for_in) { + /* XXX: not spec compliant and slow. We return only the own + object keys. */ + ctx->sp[0] = js_object_keys(ctx, NULL, 1, &ctx->sp[0]); + if (JS_IsException(ctx->sp[0])) + return JS_EXCEPTION; + } + + if (!js_get_object_class(ctx, ctx->sp[0], JS_CLASS_ARRAY)) + return JS_ThrowTypeError(ctx, "unsupported type in for...of"); + + arr = js_alloc_value_array(ctx, 0, 2); + if (!arr) + return JS_EXCEPTION; + arr->arr[0] = ctx->sp[0]; + arr->arr[1] = JS_NewShortInt(0); + return JS_VALUE_FROM_PTR(arr); +} + +static JSValue js_for_of_next(JSContext *ctx) +{ + JSValueArray *arr, *arr1; + JSObject *p; + int pos; + + arr = JS_VALUE_TO_PTR(ctx->sp[0]); + pos = JS_VALUE_GET_INT(arr->arr[1]); + p = JS_VALUE_TO_PTR(arr->arr[0]); + if (pos >= p->u.array.len) { + ctx->sp[-2] = JS_TRUE; + ctx->sp[-1] = JS_UNDEFINED; + } else { + ctx->sp[-2] = JS_FALSE; + arr1 = JS_VALUE_TO_PTR(p->u.array.tab); + ctx->sp[-1] = arr1->arr[pos]; + arr->arr[1] = JS_NewShortInt(pos + 1); + } + return JS_UNDEFINED; +} + +static JSValue js_new_c_function_proto(JSContext *ctx, int func_idx, JSValue proto, BOOL has_params, + JSValue params) +{ + JSObject *p; + JSGCRef params_ref; + + JS_PUSH_VALUE(ctx, params); + p = JS_NewObjectProtoClass1(ctx, proto, JS_CLASS_C_FUNCTION, + sizeof(JSCFunctionData) - (!has_params ? sizeof(JSValue) : 0)); + JS_POP_VALUE(ctx, params); + if (!p) + return JS_EXCEPTION; + p->u.cfunc.idx = func_idx; + if (has_params) + p->u.cfunc.params = params; + return JS_VALUE_FROM_PTR(p); +} + +JSValue JS_NewCFunctionParams(JSContext *ctx, int func_idx, JSValue params) +{ + return js_new_c_function_proto(ctx, func_idx, ctx->class_proto[JS_CLASS_CLOSURE], TRUE, params); +} + +static JSValue js_call_constructor_start(JSContext *ctx, JSValue func) +{ + JSValue proto; + proto = JS_GetProperty(ctx, func, js_get_atom(ctx, JS_ATOM_prototype)); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(ctx, proto)) + proto = ctx->class_proto[JS_CLASS_OBJECT]; + return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT, 0); +} + +#define SAVE() do { \ + fp[FRAME_OFFSET_CUR_PC] = JS_NewShortInt(pc - ((JSByteArray *)JS_VALUE_TO_PTR(b->byte_code))->buf); \ + ctx->sp = sp; \ + ctx->fp = fp; \ + } while (0) + +/* only need to restore PC */ +#define RESTORE() do { \ + b = JS_VALUE_TO_PTR(((JSObject *)JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]))->u.closure.func_bytecode); \ + pc = ((JSByteArray *)JS_VALUE_TO_PTR(b->byte_code))->buf + JS_VALUE_GET_INT(fp[FRAME_OFFSET_CUR_PC]); \ + } while (0) + +static JSValue __js_poll_interrupt(JSContext *ctx) +{ + ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; + if (ctx->interrupt_handler && ctx->interrupt_handler(ctx, ctx->opaque)) { + JS_ThrowInternalError(ctx, "interrupted"); + ctx->current_exception_is_uncatchable = TRUE; + return JS_EXCEPTION; + } + return JS_UNDEFINED; +} + +/* handle user interruption */ +#define POLL_INTERRUPT() do { \ + if (unlikely(--ctx->interrupt_counter <= 0)) { \ + SAVE(); \ + val = __js_poll_interrupt(ctx); \ + RESTORE(); \ + if (JS_IsException(val)) \ + goto exception; \ + } \ + } while(0) + +/* must use JS_StackCheck() before using it */ +void JS_PushArg(JSContext *ctx, JSValue val) +{ +#ifdef DEBUG_GC + assert((ctx->sp - 1) >= ctx->stack_bottom); +#endif + *--ctx->sp = val; +} + +/* Usage: + if (JS_StackCheck(ctx, n + 2)) ... + JS_PushArg(ctx, arg[n - 1]); + ... + JS_PushArg(ctx, arg[0]); + JS_PushArg(ctx, func); + JS_PushArg(ctx, this_obj); + res = JS_Call(ctx, n); +*/ +JSValue JS_Call(JSContext *ctx, int call_flags) +{ + JSValue *fp, *sp, val = JS_UNDEFINED, *initial_fp; + uint8_t *pc; + /* temporary variables */ + int opcode = OP_invalid, i; + JSFunctionBytecode *b; +#ifdef JS_USE_SHORT_FLOAT + double dr; +#endif + + if (ctx->js_call_rec_count >= JS_MAX_CALL_RECURSE) + return JS_ThrowInternalError(ctx, "C stack overflow"); + ctx->js_call_rec_count++; + + sp = ctx->sp; + fp = ctx->fp; + initial_fp = fp; + b = NULL; + pc = NULL; + goto function_call; + +#define CASE(op) case op +#define DEFAULT default +#define BREAK break + + for(;;) { + opcode = *pc++; +#ifdef DUMP_EXEC + { + JSByteArray *arr; + arr = JS_VALUE_TO_PTR(b->byte_code); + js_printf(ctx, " sp=%d\n", (int)(sp - fp)); + js_printf(ctx, "%4d: %s\n", (int)(pc - arr->buf - 1), + opcode_info[opcode].name); + } +#endif + switch(opcode) { + 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_NewShortInt(opcode - OP_push_0); + BREAK; + CASE(OP_push_i8): + *--sp = JS_NewShortInt(get_i8(pc)); + pc += 1; + BREAK; + CASE(OP_push_i16): + *--sp = JS_NewShortInt(get_i16(pc)); + pc += 2; + BREAK; + CASE(OP_push_value): + *--sp = get_u32(pc); + pc += 4; + BREAK; + CASE(OP_push_const): + { + JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool); + *--sp = cpool->arr[get_u16(pc)]; + pc += 2; + } + BREAK; + CASE(OP_undefined): + *--sp = JS_UNDEFINED; + BREAK; + CASE(OP_null): + *--sp = JS_NULL; + BREAK; + CASE(OP_push_this): + *--sp = fp[FRAME_OFFSET_THIS_OBJ]; + BREAK; + CASE(OP_push_false): + *--sp = JS_FALSE; + BREAK; + CASE(OP_push_true): + *--sp = JS_TRUE; + BREAK; + CASE(OP_object): + { + int n = get_u16(pc); + SAVE(); + val = JS_NewObjectPrealloc(ctx, n); + RESTORE(); + if (JS_IsException(val)) + goto exception; + *--sp = val; + pc += 2; + } + BREAK; + CASE(OP_regexp): + { + JSObject *p; + SAVE(); + val = JS_NewObjectClass(ctx, JS_CLASS_REGEXP, sizeof(JSRegExp)); + RESTORE(); + if (JS_IsException(val)) + goto exception; + p = JS_VALUE_TO_PTR(val); + p->u.regexp.source = sp[1]; + p->u.regexp.byte_code = sp[0]; + p->u.regexp.last_index = 0; + sp[1] = val; + sp++; + } + BREAK; + CASE(OP_array_from): + { + JSObject *p; + JSValueArray *arr; + int i, argc; + + argc = get_u16(pc); + SAVE(); + val = JS_NewArray(ctx, argc); + RESTORE(); + if (JS_IsException(val)) + goto exception; + pc += 2; + p = JS_VALUE_TO_PTR(val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + for(i = 0; i < argc; i++) { + arr->arr[i] = sp[argc - 1 - i]; + } + sp += argc; + *--sp = val; + } + BREAK; + CASE(OP_this_func): + *--sp = fp[FRAME_OFFSET_FUNC_OBJ]; + BREAK; + CASE(OP_arguments): + { + JSObject *p; + JSValueArray *arr; + int i, argc; + + argc = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CALL_FLAGS]) & FRAME_CF_ARGC_MASK; + SAVE(); + val = JS_NewArray(ctx, argc); + RESTORE(); + if (JS_IsException(val)) + goto exception; + p = JS_VALUE_TO_PTR(val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + for(i = 0; i < argc; i++) { + arr->arr[i] = fp[FRAME_OFFSET_ARG0 + i]; + } + *--sp = val; + } + BREAK; + CASE(OP_new_target): + call_flags = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CALL_FLAGS]); + if (call_flags & FRAME_CF_CTOR) { + *--sp = fp[FRAME_OFFSET_FUNC_OBJ]; + } else { + *--sp = JS_UNDEFINED; + } + BREAK; + CASE(OP_drop): + sp++; + BREAK; + CASE(OP_nip): + sp[1] = sp[0]; + sp++; + BREAK; + CASE(OP_dup): + sp--; + sp[0] = sp[1]; + BREAK; + CASE(OP_dup2): + sp -= 2; + sp[0] = sp[2]; + sp[1] = sp[3]; + BREAK; + CASE(OP_insert2): + sp[-1] = sp[0]; + sp[0] = sp[1]; + sp[1] = sp[-1]; + sp--; + BREAK; + CASE(OP_insert3): + sp[-1] = sp[0]; + sp[0] = sp[1]; + sp[1] = sp[2]; + sp[2] = sp[-1]; + sp--; + BREAK; + CASE(OP_perm3): /* obj a b -> a obj b (213) */ + { + JSValue tmp; + tmp = sp[1]; + sp[1] = sp[2]; + sp[2] = tmp; + } + BREAK; + CASE(OP_rot3l): /* x a b -> a b x (231) */ + { + JSValue tmp; + tmp = sp[2]; + sp[2] = sp[1]; + sp[1] = sp[0]; + sp[0] = tmp; + } + BREAK; + CASE(OP_perm4): /* obj prop a b -> a obj prop b */ + { + JSValue tmp; + tmp = sp[1]; + sp[1] = sp[2]; + sp[2] = sp[3]; + sp[3] = tmp; + } + BREAK; + CASE(OP_swap): /* a b -> b a */ + { + JSValue tmp; + tmp = sp[1]; + sp[1] = sp[0]; + sp[0] = tmp; + } + BREAK; + + CASE(OP_fclosure): + { + int idx; + JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool); + idx = get_u16(pc); + SAVE(); + val = js_closure(ctx, cpool->arr[idx], fp); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + pc += 2; + *--sp = val; + } + BREAK; + + CASE(OP_call_constructor): + call_flags = get_u16(pc) | FRAME_CF_CTOR; + goto global_function_call; + CASE(OP_call): + call_flags = get_u16(pc); + global_function_call: + js_reverse_val(sp, (call_flags & FRAME_CF_ARGC_MASK) + 1); + *--sp = JS_UNDEFINED; + goto generic_function_call; + CASE(OP_call_method): + { + int n, argc, short_func_idx; + JSValue func_obj; + JSObject *p; + JSByteArray *byte_code; + + call_flags = get_u16(pc); + + n = (call_flags & FRAME_CF_ARGC_MASK) + 2; + js_reverse_val(sp, n); + + generic_function_call: + POLL_INTERRUPT(); + byte_code = JS_VALUE_TO_PTR(b->byte_code); + /* save pc + 1 of the current call */ + fp[FRAME_OFFSET_CUR_PC] = JS_NewShortInt(pc - byte_code->buf); + function_call: + *--sp = JS_NewShortInt(call_flags); + *--sp = SP_TO_VALUE(ctx, fp); + + func_obj = sp[FRAME_OFFSET_FUNC_OBJ]; +#if defined(DUMP_EXEC) + JS_DumpValue(ctx, "calling", func_obj); +#endif + if (!JS_IsPtr(func_obj)) { + if (JS_VALUE_GET_SPECIAL_TAG(func_obj) != JS_TAG_SHORT_FUNC) + goto not_a_function; + short_func_idx = JS_VALUE_GET_SPECIAL_VALUE(func_obj); + p = NULL; + goto c_function; + } else { + p = JS_VALUE_TO_PTR(func_obj); + if (p->mtag != JS_MTAG_OBJECT) + goto not_a_function; + if (p->class_id == JS_CLASS_C_FUNCTION) { + const JSCFunctionDef *fd; + int pushed_argc; + short_func_idx = p->u.cfunc.idx; + c_function: + fd = &ctx->c_function_table[short_func_idx]; + /* add undefined arguments if the caller did not + provide enough arguments */ + call_flags = JS_VALUE_GET_INT(sp[FRAME_OFFSET_CALL_FLAGS]); + if ((call_flags & FRAME_CF_CTOR) && + (fd->def_type != JS_CFUNC_constructor && + fd->def_type != JS_CFUNC_constructor_magic)) { + sp += 2; /* go back to the caller frame */ + ctx->sp = sp; + ctx->fp = fp; + val = JS_ThrowTypeError(ctx, "not a constructor"); + goto call_exception; + } + + argc = call_flags & FRAME_CF_ARGC_MASK; + /* JS_StackCheck may trigger a gc */ + ctx->sp = sp; + ctx->fp = fp; + n = JS_StackCheck(ctx, max_int(fd->arg_count - argc, 0)); + if (n) { + sp += 2; /* go back to the caller frame */ + val = JS_EXCEPTION; + goto call_exception; + } + pushed_argc = argc; + if (fd->arg_count > argc) { + n = fd->arg_count - argc; + sp -= n; + for(i = 0; i < FRAME_OFFSET_ARG0 + argc; i++) + sp[i] = sp[i + n]; + for(i = 0; i < n; i++) + sp[FRAME_OFFSET_ARG0 + argc + i] = JS_UNDEFINED; + pushed_argc = fd->arg_count; + } + fp = sp; + ctx->sp = sp; + ctx->fp = fp; + switch(fd->def_type) { + case JS_CFUNC_generic: + case JS_CFUNC_constructor: + val = fd->func.generic(ctx, &fp[FRAME_OFFSET_THIS_OBJ], + call_flags & (FRAME_CF_CTOR | FRAME_CF_ARGC_MASK), + fp + FRAME_OFFSET_ARG0); + break; + case JS_CFUNC_generic_magic: + case JS_CFUNC_constructor_magic: + val = fd->func.generic_magic(ctx, &fp[FRAME_OFFSET_THIS_OBJ], + call_flags & (FRAME_CF_CTOR | FRAME_CF_ARGC_MASK), + fp + FRAME_OFFSET_ARG0, fd->magic); + break; + case JS_CFUNC_generic_params: + p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]); + val = fd->func.generic_params(ctx, &fp[FRAME_OFFSET_THIS_OBJ], + call_flags & (FRAME_CF_CTOR | FRAME_CF_ARGC_MASK), + fp + FRAME_OFFSET_ARG0, p->u.cfunc.params); + break; + case JS_CFUNC_f_f: + { + double d; + if (JS_ToNumber(ctx, &d, fp[FRAME_OFFSET_ARG0])) { + val = JS_EXCEPTION; + } else { + d = fd->func.f_f(d); + } + val = JS_NewFloat64(ctx, d); + } + break; + default: + assert(0); + } + if (JS_IsExceptionOrTailCall(val) && + JS_VALUE_GET_SPECIAL_VALUE(val) >= JS_EX_CALL) { + JSValue *fp1, *sp1; + /* tail call: equivalent to calling the + function after the C function */ + /* XXX: handle the call flags of the caller ? */ + call_flags = JS_VALUE_GET_SPECIAL_VALUE(val) - JS_EX_CALL; + sp = ctx->sp; + /* pop the frame */ + fp1 = VALUE_TO_SP(ctx, fp[FRAME_OFFSET_SAVED_FP]); + /* move the new arguments at the correct stack position */ + argc = (call_flags & FRAME_CF_ARGC_MASK) + 2; + sp1 = fp + FRAME_OFFSET_ARG0 + pushed_argc - argc; + memmove(sp1, sp, sizeof(*sp) * (argc)); + sp = sp1; + fp = fp1; + goto function_call; + } else { + sp = fp + FRAME_OFFSET_ARG0 + pushed_argc; + goto return_call; + } + } else if (p->class_id == JS_CLASS_CLOSURE) { + int n_vars; + call_flags = JS_VALUE_GET_INT(sp[FRAME_OFFSET_CALL_FLAGS]); + if (call_flags & FRAME_CF_CTOR) { + ctx->sp = sp; + ctx->fp = fp; + /* Note: can recurse at this point */ + val = js_call_constructor_start(ctx, func_obj); + if (JS_IsException(val)) + goto call_exception; + sp[FRAME_OFFSET_THIS_OBJ] = val; + func_obj = sp[FRAME_OFFSET_FUNC_OBJ]; + p = JS_VALUE_TO_PTR(func_obj); + } + b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode); + if (b->vars != JS_NULL) { + JSValueArray *vars = JS_VALUE_TO_PTR(b->vars); + n_vars = vars->size - b->arg_count; + } else { + n_vars = 0; + } + argc = call_flags & FRAME_CF_ARGC_MASK; + /* JS_StackCheck may trigger a gc */ + ctx->sp = sp; + ctx->fp = fp; + n = JS_StackCheck(ctx, max_int(b->arg_count - argc, 0) + 2 + n_vars + + b->stack_size); + if (n) { + val = JS_EXCEPTION; + goto call_exception; + } + func_obj = sp[FRAME_OFFSET_FUNC_OBJ]; + p = JS_VALUE_TO_PTR(func_obj); + b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode); + /* add undefined arguments if the caller did not + provide enough arguments */ + if (unlikely(b->arg_count > argc)) { + n = b->arg_count - argc; + sp -= n; + for(i = 0; i < FRAME_OFFSET_ARG0 + argc; i++) + sp[i] = sp[i + n]; + for(i = 0; i < n; i++) + sp[FRAME_OFFSET_ARG0 + argc + i] = JS_UNDEFINED; + } + fp = sp; + *--sp = JS_NewShortInt(0); /* FRAME_OFFSET_CUR_PC */ + *--sp = JS_NULL; /* FRAME_OFFSET_FIRST_VARREF */ + sp -= n_vars; + for(i = 0; i < n_vars; i++) + sp[i] = JS_UNDEFINED; + byte_code = JS_VALUE_TO_PTR(b->byte_code); + pc = byte_code->buf; + } else { + not_a_function: + sp += 2; /* go back to the caller frame */ + ctx->sp = sp; + ctx->fp = fp; + val = JS_ThrowTypeError(ctx, "not a function"); + call_exception: + if (!pc) { + goto done; + } else { + RESTORE(); + goto exception; + } + } + } + } + BREAK; + + exception: + /* 'val' must contain the exception */ + { + JSValue *stack_top, val2; + JSValueArray *vars; + int v; + /* exception before entering in the first function ? + (XXX: remove this test) */ + if (!pc) + goto done; + v = JS_VALUE_GET_SPECIAL_VALUE(val); + if (v >= JS_EX_CALL) { + /* tail call */ + call_flags = JS_VALUE_GET_SPECIAL_VALUE(val) - JS_EX_CALL; + /* the opcode has only one byte, hence the PC must + be updated accordingly after the function + returns */ + if (opcode == OP_get_length || + opcode == OP_get_length2 || + opcode == OP_get_array_el || + opcode == OP_get_array_el2 || + opcode == OP_put_array_el) { + call_flags |= FRAME_CF_PC_ADD1; + } + // js_printf(ctx, "tail call: 0x%x\n", call_flags); + goto generic_function_call; + } + /* XXX: start gc in case of JS_EXCEPTION_MEM */ + stack_top = fp + FRAME_OFFSET_VAR0 + 1; + if (b->vars != JS_NULL) { + vars = JS_VALUE_TO_PTR(b->vars); + stack_top -= (vars->size - b->arg_count); + } + if (ctx->current_exception_is_uncatchable) { + sp = stack_top; + } else { + while (sp < stack_top) { + val2 = *sp++; + if (JS_VALUE_GET_SPECIAL_TAG(val2) == JS_TAG_CATCH_OFFSET) { + JSByteArray *byte_code; + /* exception caught by a 'catch' in the + current function */ + *--sp = ctx->current_exception; + ctx->current_exception = JS_NULL; + byte_code = JS_VALUE_TO_PTR(b->byte_code); + pc = byte_code->buf + JS_VALUE_GET_SPECIAL_VALUE(val2); + goto restart; + } + } + } + } + goto generic_return; + + CASE(OP_return_undef): + val = JS_UNDEFINED; + goto generic_return; + + CASE(OP_return): + val = sp[0]; + generic_return: + { + JSObject *p; + int argc, pc_offset; + JSValue val2; + JSVarRef *pv; + JSByteArray *byte_code; + + /* detach the variable references */ + val2 = fp[FRAME_OFFSET_FIRST_VARREF]; + while (val2 != JS_NULL) { + pv = JS_VALUE_TO_PTR(val2); + val2 = pv->u.next; + assert(!pv->is_detached); + pv->u.value = *pv->u.pvalue; + pv->is_detached = TRUE; + /* shrink 'pv' */ + set_free_block((uint8_t *)pv + sizeof(JSVarRef) - sizeof(JSValue), sizeof(JSValue)); + } + + call_flags = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CALL_FLAGS]); + if (unlikely(call_flags & FRAME_CF_CTOR)) { + if (!JS_IsException(val) && !JS_IsObject(ctx, val)) { + val = fp[FRAME_OFFSET_THIS_OBJ]; + } + } + argc = call_flags & FRAME_CF_ARGC_MASK; + argc = max_int(argc, b->arg_count); + sp = fp + FRAME_OFFSET_ARG0 + argc; + return_call: + call_flags = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CALL_FLAGS]); + /* XXX: restore stack_bottom to reduce memory usage */ + fp = VALUE_TO_SP(ctx, fp[FRAME_OFFSET_SAVED_FP]); + if (fp == initial_fp) + goto done; + pc_offset = JS_VALUE_GET_INT(fp[FRAME_OFFSET_CUR_PC]); + p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]); + b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode); + byte_code = JS_VALUE_TO_PTR(b->byte_code); + pc = byte_code->buf + pc_offset; + /* now we are in the calling function */ + if (JS_IsException(val)) + goto exception; + if (!(call_flags & FRAME_CF_POP_RET)) + *--sp = val; + /* Note: if variable size call, can add a flag in call_flags */ + if (!(call_flags & FRAME_CF_PC_ADD1)) + pc += 2; /* skip the call arg or get_field/put_field arg */ + } + BREAK; + + CASE(OP_catch): + { + int32_t diff; + JSByteArray *byte_code = JS_VALUE_TO_PTR(b->byte_code); + diff = get_u32(pc); + *--sp = JS_VALUE_MAKE_SPECIAL(JS_TAG_CATCH_OFFSET, pc + diff - byte_code->buf); + pc += 4; + } + BREAK; + CASE(OP_throw): + val = *sp++; + SAVE(); + val = JS_Throw(ctx, val); + RESTORE(); + goto exception; + CASE(OP_gosub): + { + int32_t diff; + JSByteArray *byte_code = JS_VALUE_TO_PTR(b->byte_code); + diff = get_u32(pc); + *--sp = JS_NewShortInt(pc + 4 - byte_code->buf); + pc += diff; + } + BREAK; + CASE(OP_ret): + { + JSByteArray *byte_code = JS_VALUE_TO_PTR(b->byte_code); + uint32_t pos; + if (unlikely(!JS_IsInt(sp[0]))) + goto ret_fail; + pos = JS_VALUE_GET_INT(sp[0]); + if (unlikely(pos >= byte_code->size)) { + ret_fail: + SAVE(); + val = JS_ThrowInternalError(ctx, "invalid ret value"); + RESTORE(); + goto exception; + } + sp++; + pc = byte_code->buf + pos; + } + BREAK; + + CASE(OP_get_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + *--sp = fp[FRAME_OFFSET_VAR0 - idx]; + } + BREAK; + CASE(OP_put_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + fp[FRAME_OFFSET_VAR0 - idx] = sp[0]; + sp++; + } + BREAK; + CASE(OP_get_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + *--sp = fp[FRAME_OFFSET_ARG0 + idx]; + } + BREAK; + CASE(OP_put_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + fp[FRAME_OFFSET_ARG0 + idx] = sp[0]; + sp++; + } + BREAK; + + CASE(OP_get_loc0): *--sp = fp[FRAME_OFFSET_VAR0 - 0]; BREAK; + CASE(OP_get_loc1): *--sp = fp[FRAME_OFFSET_VAR0 - 1]; BREAK; + CASE(OP_get_loc2): *--sp = fp[FRAME_OFFSET_VAR0 - 2]; BREAK; + CASE(OP_get_loc3): *--sp = fp[FRAME_OFFSET_VAR0 - 3]; BREAK; + CASE(OP_get_loc8): *--sp = fp[FRAME_OFFSET_VAR0 - *pc++]; BREAK; + + CASE(OP_put_loc0): fp[FRAME_OFFSET_VAR0 - 0] = *sp++; BREAK; + CASE(OP_put_loc1): fp[FRAME_OFFSET_VAR0 - 1] = *sp++; BREAK; + CASE(OP_put_loc2): fp[FRAME_OFFSET_VAR0 - 2] = *sp++; BREAK; + CASE(OP_put_loc3): fp[FRAME_OFFSET_VAR0 - 3] = *sp++; BREAK; + CASE(OP_put_loc8): fp[FRAME_OFFSET_VAR0 - *pc++] = *sp++; BREAK; + + CASE(OP_get_arg0): *--sp = fp[FRAME_OFFSET_ARG0 + 0]; BREAK; + CASE(OP_get_arg1): *--sp = fp[FRAME_OFFSET_ARG0 + 1]; BREAK; + CASE(OP_get_arg2): *--sp = fp[FRAME_OFFSET_ARG0 + 2]; BREAK; + CASE(OP_get_arg3): *--sp = fp[FRAME_OFFSET_ARG0 + 3]; BREAK; + + CASE(OP_put_arg0): fp[FRAME_OFFSET_ARG0 + 0] = *sp++; BREAK; + CASE(OP_put_arg1): fp[FRAME_OFFSET_ARG0 + 1] = *sp++; BREAK; + CASE(OP_put_arg2): fp[FRAME_OFFSET_ARG0 + 2] = *sp++; BREAK; + CASE(OP_put_arg3): fp[FRAME_OFFSET_ARG0 + 3] = *sp++; BREAK; + + CASE(OP_get_var_ref): + CASE(OP_get_var_ref_nocheck): + { + int idx; + JSObject *p; + JSVarRef *pv; + idx = get_u16(pc); + p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]); + pv = JS_VALUE_TO_PTR(p->u.closure.var_refs[idx]); + if (pv->is_detached) + val = pv->u.value; + else + val = *pv->u.pvalue; + if (unlikely(val == JS_TAG_UNINITIALIZED) && + opcode == OP_get_var_ref) { + JSValueArray *ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + SAVE(); + val = JS_ThrowReferenceError(ctx, "variable '%"JSValue_PRI"' is not defined", ext_vars->arr[2 * idx]); + RESTORE(); + goto exception; + } + pc += 2; + *--sp = val; + } + BREAK; + CASE(OP_put_var_ref): + CASE(OP_put_var_ref_nocheck): + { + int idx; + JSObject *p; + JSVarRef *pv; + JSValue *pval; + idx = get_u16(pc); + p = JS_VALUE_TO_PTR(fp[FRAME_OFFSET_FUNC_OBJ]); + pv = JS_VALUE_TO_PTR(p->u.closure.var_refs[idx]); + if (pv->is_detached) + pval = &pv->u.value; + else + pval = pv->u.pvalue; + if (unlikely(*pval == JS_TAG_UNINITIALIZED) && + opcode == OP_put_var_ref) { + JSValueArray *ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + SAVE(); + val = JS_ThrowReferenceError(ctx, "variable '%"JSValue_PRI"' is not defined", ext_vars->arr[2 * idx]); + RESTORE(); + goto exception; + } + *pval = *sp++; + pc += 2; + } + BREAK; + + CASE(OP_goto): + pc += (int32_t)get_u32(pc); + POLL_INTERRUPT(); + BREAK; + CASE(OP_if_false): + CASE(OP_if_true): + { + int res; + + pc += 4; + + res = JS_ToBool(ctx, *sp++); + if (res ^ (OP_if_true - opcode)) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + POLL_INTERRUPT(); + } + BREAK; + + CASE(OP_lnot): + { + int res; + res = JS_ToBool(ctx, sp[0]); + sp[0] = JS_NewBool(!res); + } + BREAK; + + CASE(OP_get_field2): + sp--; + sp[0] = sp[1]; + goto get_field_common; + CASE(OP_get_field): + get_field_common: + { + int idx; + JSValue prop, obj; + JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool); + idx = get_u16(pc); + prop = cpool->arr[idx]; + obj = sp[0]; + if (likely(JS_IsPtr(obj))) { + /* fast case */ + JSObject *p = JS_VALUE_TO_PTR(obj); + JSProperty *pr; + if (unlikely(p->mtag != JS_MTAG_OBJECT)) + goto get_field_slow; + for(;;) { + /* no array check is necessary because 'prop' is + guaranteed not to be a numeric property */ + /* XXX: slow due to short ints */ + pr = find_own_property_inlined(ctx, p, prop); + if (pr) { + if (unlikely(pr->prop_type != JS_PROP_NORMAL)) { + /* sp[0] is this_obj, obj is the current + object */ + goto get_field_slow; + } else { + val = pr->value; + break; + } + } + obj = p->proto; + if (obj == JS_NULL) { + val = JS_UNDEFINED; + break; + } + p = JS_VALUE_TO_PTR(obj); + } + } else { + get_field_slow: + SAVE(); + val = JS_GetPropertyInternal(ctx, obj, prop, TRUE); + RESTORE(); + if (unlikely(JS_IsExceptionOrTailCall(val))) { + sp = ctx->sp; + goto exception; + } + } + pc += 2; + sp[0] = val; + } + BREAK; + + CASE(OP_get_length2): + sp--; + sp[0] = sp[1]; + goto get_length_common; + + CASE(OP_get_length): + get_length_common: + { + JSValue obj; + obj = sp[0]; + if (likely(JS_IsPtr(obj))) { + /* fast case */ + JSObject *p = JS_VALUE_TO_PTR(obj); + if (p->mtag == JS_MTAG_OBJECT) { + if (p->class_id == JS_CLASS_ARRAY) { + if (unlikely(p->proto != ctx->class_proto[JS_CLASS_ARRAY] || + p->props != ctx->empty_props)) + goto get_length_slow; + val = JS_NewShortInt(p->u.array.len); + } else { + goto get_length_slow; + } + } else if (p->mtag == JS_MTAG_STRING) { + JSString *ps = (JSString *)p; + if (likely(ps->is_ascii)) + val = JS_NewShortInt(ps->len); + else + val = JS_NewShortInt(js_string_utf8_to_utf16_pos(ctx, obj, ps->len * 2)); + } else { + goto get_length_slow; + } + } else if (JS_VALUE_GET_SPECIAL_TAG(val) == JS_TAG_STRING_CHAR) { + val = JS_NewShortInt(JS_VALUE_GET_SPECIAL_VALUE(val) >= 0x10000 ? 2 : 1); + } else { + get_length_slow: + SAVE(); + val = JS_GetPropertyInternal(ctx, obj, js_get_atom(ctx, JS_ATOM_length), TRUE); + RESTORE(); + if (unlikely(JS_IsExceptionOrTailCall(val))) { + sp = ctx->sp; + goto exception; + } + } + sp[0] = val; + } + BREAK; + + CASE(OP_put_field): + { + int idx; + JSValue prop, obj; + JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool); + + idx = get_u16(pc); + prop = cpool->arr[idx]; + obj = sp[1]; + if (likely(JS_IsPtr(obj))) { + /* fast case */ + JSObject *p = JS_VALUE_TO_PTR(obj); + JSProperty *pr; + if (unlikely(p->mtag != JS_MTAG_OBJECT)) + goto put_field_slow; + /* no array check is necessary because 'prop' is + guaranteed not to be a numeric property */ + /* XXX: slow due to short ints */ + pr = find_own_property_inlined(ctx, p, prop); + if (unlikely(!pr)) + goto put_field_slow; + if (unlikely(pr->prop_type != JS_PROP_NORMAL)) + goto put_field_slow; + /* XXX: slow */ + if (unlikely(JS_IS_ROM_PTR(ctx, pr))) + goto put_field_slow; + pr->value = sp[0]; + sp += 2; + } else { + put_field_slow: + val = *sp++; + SAVE(); + val = JS_SetPropertyInternal(ctx, sp[0], prop, val, TRUE); + RESTORE(); + if (unlikely(JS_IsExceptionOrTailCall(val))) { + sp = ctx->sp; + goto exception; + } + sp++; + } + pc += 2; + } + BREAK; + + CASE(OP_get_array_el2): + val = sp[0]; + sp[0] = sp[1]; + goto get_array_el_common; + CASE(OP_get_array_el): + val = sp[0]; + sp++; + get_array_el_common: + { + JSValue prop = val, obj; + obj = sp[0]; + if (JS_IsPtr(obj) && JS_IsInt(prop)) { + /* fast case with array */ + /* XXX: optimize typed arrays too ? */ + JSObject *p = JS_VALUE_TO_PTR(obj); + uint32_t idx; + JSValueArray *arr; + if (unlikely(p->mtag != JS_MTAG_OBJECT)) + goto get_array_el_slow; + if (unlikely(p->class_id != JS_CLASS_ARRAY)) + goto get_array_el_slow; + idx = JS_VALUE_GET_INT(prop); + if (unlikely(idx >= p->u.array.len)) + goto get_array_el_slow; + + arr = JS_VALUE_TO_PTR(p->u.array.tab); + val = arr->arr[idx]; + } else { + get_array_el_slow: + SAVE(); + prop = JS_ToPropertyKey(ctx, prop); + RESTORE(); + if (JS_IsException(prop)) { + val = prop; + goto exception; + } + SAVE(); + val = JS_GetPropertyInternal(ctx, sp[0], prop, TRUE); + RESTORE(); + if (unlikely(JS_IsExceptionOrTailCall(val))) { + sp = ctx->sp; + goto exception; + } + } + sp[0] = val; + } + BREAK; + + CASE(OP_put_array_el): + { + JSValue prop, obj; + obj = sp[2]; + prop = sp[1]; + if (JS_IsPtr(obj) && JS_IsInt(prop)) { + /* fast case with array */ + /* XXX: optimize typed arrays too ? */ + JSObject *p = JS_VALUE_TO_PTR(obj); + uint32_t idx; + JSValueArray *arr; + if (unlikely(p->mtag != JS_MTAG_OBJECT)) + goto put_array_el_slow; + if (unlikely(p->class_id != JS_CLASS_ARRAY)) + goto put_array_el_slow; + idx = JS_VALUE_GET_INT(prop); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + if (unlikely(idx >= p->u.array.len)) { + if (idx == p->u.array.len && + p->u.array.tab != JS_NULL && + idx < arr->size) { + arr->arr[idx] = sp[0]; + p->u.array.len = idx + 1; + } else { + goto put_array_el_slow; + } + } else { + arr->arr[idx] = sp[0]; + } + sp += 3; + } else { + put_array_el_slow: + SAVE(); + sp[1] = JS_ToPropertyKey(ctx, sp[1]); + RESTORE(); + if (JS_IsException(sp[1])) { + val = sp[1]; + goto exception; + } + val = *sp++; + prop = *sp++; + SAVE(); + val = JS_SetPropertyInternal(ctx, sp[0], prop, val, TRUE); + RESTORE(); + if (unlikely(JS_IsExceptionOrTailCall(val))) { + sp = ctx->sp; + goto exception; + } + sp++; + } + } + BREAK; + + CASE(OP_define_field): + CASE(OP_define_getter): + CASE(OP_define_setter): + { + int idx; + JSValue prop; + JSValueArray *cpool = JS_VALUE_TO_PTR(b->cpool); + + idx = get_u16(pc); + prop = cpool->arr[idx]; + + SAVE(); + if (opcode == OP_define_field) { + val = JS_DefinePropertyValue(ctx, sp[1], prop, sp[0]); + } else if (opcode == OP_define_getter) + val = JS_DefinePropertyGetSet(ctx, sp[1], prop, sp[0], JS_UNDEFINED, JS_DEF_PROP_HAS_GET); + else + val = JS_DefinePropertyGetSet(ctx, sp[1], prop, JS_UNDEFINED, sp[0], JS_DEF_PROP_HAS_SET); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + pc += 2; + sp++; + } + BREAK; + + CASE(OP_set_proto): + { + if (JS_IsObject(ctx, sp[0]) || JS_IsNull(sp[0])) { + SAVE(); + val = js_set_prototype_internal(ctx, sp[1], sp[0]); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + } + sp++; + } + BREAK; + + CASE(OP_add): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int r; + if (unlikely(__builtin_add_overflow((int)op1, (int)op2, &r))) + goto add_slow; + sp[1] = (uint32_t)r; + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_VALUE_IS_BOTH_SHORT_FLOAT(op1, op2)) { + double d1, d2; + d1 = js_get_short_float(op1); + d2 = js_get_short_float(op2); + dr = d1 + d2; + sp++; + goto float_result; + } else +#endif + { + add_slow: + SAVE(); + val = js_add_slow(ctx); + RESTORE(); + if (JS_IsException(val)) + goto exception; + sp[1] = val; + } + sp++; + } + BREAK; + CASE(OP_sub): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int r; + if (unlikely(__builtin_sub_overflow((int)op1, (int)op2, &r))) + goto binary_arith_slow; + sp[1] = (uint32_t)r; + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_VALUE_IS_BOTH_SHORT_FLOAT(op1, op2)) { + double d1, d2; + d1 = js_get_short_float(op1); + d2 = js_get_short_float(op2); + dr = d1 - d2; + sp++; + goto float_result; + } else +#endif + { + goto binary_arith_slow; + } + sp++; + } + BREAK; + CASE(OP_mul): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + int64_t r; + v1 = (int)op1; + v2 = (int)op2 >> 1; + r = (int64_t)v1 * (int64_t)v2; + if (unlikely(r != (int)r)) { +#if defined(JS_USE_SHORT_FLOAT) + dr = (double)(r >> 1); + sp++; + goto float_result; +#else + goto binary_arith_slow; +#endif + } + /* -0 case */ + if (unlikely(r == 0 && (v1 | v2) < 0)) { + sp[1] = ctx->minus_zero; + } else { + sp[1] = (uint32_t)r; + } + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_VALUE_IS_BOTH_SHORT_FLOAT(op1, op2)) { + double d1, d2; + d1 = js_get_short_float(op1); + d2 = js_get_short_float(op2); + dr = d1 * d2; + sp++; + goto float_result; + } else +#endif + { + goto binary_arith_slow; + } + sp++; + } + BREAK; + CASE(OP_div): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + SAVE(); + val = JS_NewFloat64(ctx, (double)v1 / (double)v2); + RESTORE(); + if (JS_IsException(val)) + goto exception; + sp[1] = val; + sp++; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mod): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + 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); + if (unlikely(v1 < 0 || v2 <= 0)) + goto binary_arith_slow; + r = v1 % v2; + sp[1] = JS_NewShortInt(r); + sp++; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_pow): + binary_arith_slow: + SAVE(); + val = js_binary_arith_slow(ctx, opcode); + RESTORE(); + if (JS_IsException(val)) + goto exception; + sp[1] = val; + sp++; + BREAK; + CASE(OP_plus): + { + JSValue op1; + op1 = sp[0]; + if (JS_IsIntOrShortFloat(op1) || + (JS_IsPtr(op1) && js_get_mtag(JS_VALUE_TO_PTR(op1)) == JS_MTAG_FLOAT64)) { + } else { + goto unary_arith_slow; + } + } + BREAK; + CASE(OP_neg): + { + JSValue op1; + int v1; + op1 = sp[0]; + if (JS_IsInt(op1)) { + v1 = op1; + if (v1 == 0) { + sp[0] = ctx->minus_zero; + } else if (v1 == INT32_MIN) { +#if defined(JS_USE_SHORT_FLOAT) + dr = -(double)JS_SHORTINT_MIN; + goto float_result; +#else + goto unary_arith_slow; +#endif + } else { + sp[0] = -v1; + } + } else +#if defined(JS_USE_SHORT_FLOAT) + if (JS_IsShortFloat(op1)) { + dr = -js_get_short_float(op1); + float_result: + /* for efficiency, we don't try to store it as a short integer */ + if (likely(fabs(dr) >= 0x1p-127 && fabs(dr) <= 0x1p+128)) { + val = js_to_short_float(dr); + } else if (dr == 0.0) { + if (float64_as_uint64(dr) != 0) { + /* minus zero often happens, so it is worth having a constant + value */ + val = ctx->minus_zero; + } else { + /* XXX: could have a short float + representation for zero and minus zero + so that the float fast case is still + used when they happen */ + val = JS_NewShortInt(0); + } + } else { + /* slow case: need to allocate it */ + SAVE(); + val = js_alloc_float64(ctx, dr); + RESTORE(); + if (JS_IsException(val)) + goto exception; + } + sp[0] = val; + } else +#endif + { + goto unary_arith_slow; + } + } + BREAK; + CASE(OP_inc): + { + JSValue op1; + int v1; + op1 = sp[0]; + if (JS_IsInt(op1)) { + v1 = JS_VALUE_GET_INT(op1); + if (unlikely(v1 == JS_SHORTINT_MAX)) + goto unary_arith_slow; + sp[0] = JS_NewShortInt(v1 + 1); + } else { + goto unary_arith_slow; + } + } + BREAK; + CASE(OP_dec): + { + JSValue op1; + int v1; + op1 = sp[0]; + if (JS_IsInt(op1)) { + v1 = JS_VALUE_GET_INT(op1); + if (unlikely(v1 == JS_SHORTINT_MIN)) + goto unary_arith_slow; + sp[0] = JS_NewShortInt(v1 - 1); + } else { + unary_arith_slow: + SAVE(); + val = js_unary_arith_slow(ctx, opcode); + RESTORE(); + if (JS_IsException(val)) + goto exception; + sp[0] = val; + } + } + BREAK; + CASE(OP_post_inc): + CASE(OP_post_dec): + { + JSValue op1; + int v1; + op1 = sp[0]; + if (JS_IsInt(op1)) { + v1 = JS_VALUE_GET_INT(op1) + 2 * (opcode - OP_post_dec) - 1; + if (v1 < JS_SHORTINT_MIN || v1 > JS_SHORTINT_MAX) + goto slow_post_inc_dec; + val = JS_NewShortInt(v1); + } else { + slow_post_inc_dec: + SAVE(); + val = js_post_inc_slow(ctx, opcode); + RESTORE(); + if (JS_IsException(val)) + goto exception; + } + *--sp = val; + } + BREAK; + + CASE(OP_not): + { + JSValue op1; + op1 = sp[0]; + if (JS_IsInt(op1)) { + sp[0] = (~op1) & (~1); + } else { + SAVE(); + val = js_not_slow(ctx); + RESTORE(); + if (JS_IsException(val)) + goto exception; + sp[0] = val; + } + } + BREAK; + + CASE(OP_shl): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int32_t r; + r = JS_VALUE_GET_INT(op1) << (JS_VALUE_GET_INT(op2) & 0x1f); + if (unlikely(r < JS_SHORTINT_MIN || r > JS_SHORTINT_MAX)) { +#if defined(JS_USE_SHORT_FLOAT) + dr = (double)r; + sp++; + goto float_result; +#else + goto binary_logic_slow; +#endif + } + sp[1] = JS_NewShortInt(r); + sp++; + } else { + goto binary_logic_slow; + } + } + BREAK; + CASE(OP_shr): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t r; + r = (uint32_t)JS_VALUE_GET_INT(op1) >> + ((uint32_t)JS_VALUE_GET_INT(op2) & 0x1f); + if (unlikely(r > JS_SHORTINT_MAX)) { +#if defined(JS_USE_SHORT_FLOAT) + dr = (double)r; + sp++; + goto float_result; +#else + goto binary_logic_slow; +#endif + } + sp[1] = JS_NewShortInt(r); + sp++; + } else { + goto binary_logic_slow; + } + } + BREAK; + CASE(OP_sar): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[1] = ((int)op1 >> ((uint32_t)JS_VALUE_GET_INT(op2) & 0x1f)) & ~1; + sp++; + } else { + goto binary_logic_slow; + } + } + BREAK; + CASE(OP_and): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[1] = op1 & op2; + sp++; + } else { + goto binary_logic_slow; + } + } + BREAK; + CASE(OP_or): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[1] = op1 | op2; + sp++; + } else { + goto binary_logic_slow; + } + } + BREAK; + CASE(OP_xor): + { + JSValue op1, op2; + op1 = sp[1]; + op2 = sp[0]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[1] = op1 ^ op2; + sp++; + } else { + binary_logic_slow: + SAVE(); + val = js_binary_logic_slow(ctx, opcode); + RESTORE(); + if (JS_IsException(val)) + goto exception; + sp[1] = val; + sp++; + } + } + BREAK; + + +#define OP_CMP(opcode, binary_op, slow_call) \ + CASE(opcode): \ + { \ + JSValue op1, op2; \ + op1 = sp[1]; \ + op2 = sp[0]; \ + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ + sp[1] = JS_NewBool(JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ + sp++; \ + } else { \ + SAVE(); \ + val = slow_call; \ + RESTORE(); \ + if (JS_IsException(val)) \ + goto exception; \ + sp[1] = val; \ + sp++; \ + } \ + } \ + BREAK; + + OP_CMP(OP_lt, <, js_relational_slow(ctx, opcode)); + OP_CMP(OP_lte, <=, js_relational_slow(ctx, opcode)); + OP_CMP(OP_gt, >, js_relational_slow(ctx, opcode)); + OP_CMP(OP_gte, >=, js_relational_slow(ctx, opcode)); + OP_CMP(OP_eq, ==, js_eq_slow(ctx, 0)); + OP_CMP(OP_neq, !=, js_eq_slow(ctx, 1)); + OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, 0)); + OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, 1)); + CASE(OP_in): + SAVE(); + val = js_operator_in(ctx); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + sp[1] = val; + sp++; + BREAK; + CASE(OP_instanceof): + SAVE(); + val = js_operator_instanceof(ctx); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + sp[1] = val; + sp++; + BREAK; + CASE(OP_typeof): + SAVE(); + val = js_operator_typeof(ctx, sp[0]); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + sp[0] = val; + BREAK; + CASE(OP_delete): + SAVE(); + val = JS_DeleteProperty(ctx, sp[1], sp[0]); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + sp[1] = val; + sp++; + BREAK; + CASE(OP_for_in_start): + CASE(OP_for_of_start): + SAVE(); + val = js_for_of_start(ctx, (opcode == OP_for_in_start)); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + sp[0] = val; + BREAK; + CASE(OP_for_of_next): + SAVE(); + val = js_for_of_next(ctx); + RESTORE(); + if (unlikely(JS_IsException(val))) + goto exception; + sp -= 2; + BREAK; + default: + { + JSByteArray *byte_code = JS_VALUE_TO_PTR(b->byte_code); + SAVE(); + val = JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", + (int)(pc - byte_code->buf - 1), opcode); + RESTORE(); + } + goto exception; + } + restart: ; + } /* switch */ + done: + ctx->sp = sp; + ctx->fp = fp; + ctx->js_call_rec_count--; + return val; +} + +#undef SAVE +#undef RESTORE + +static inline int is_ident_first(int c) +{ + return (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '_' || c == '$'; +} + +static inline int is_ident_next(int c) +{ + return is_ident_first(c) || is_num(c); +} + +/**********************************************************************/ +/* dump utilities */ + +#ifdef JS_DUMP + +static void js_dump_array(JSContext *ctx, JSValueArray *arr, int len) +{ + int i; + + js_printf(ctx, "[ "); + for(i = 0; i < len; i++) { + if (i != 0) + js_printf(ctx, ", "); + JS_PrintValue(ctx, arr->arr[i]); + } + js_printf(ctx, " ]"); +} + +/* put constructors into a separate table */ +/* XXX: improve by using a table */ +static JSValue js_find_class_name(JSContext *ctx, int class_id) +{ + const JSCFunctionDef *fd; + fd = ctx->c_function_table; + while ((fd->def_type != JS_CFUNC_constructor_magic && + fd->def_type != JS_CFUNC_constructor) || + fd->magic != class_id) { + fd++; + } + return reloc_c_func_name(ctx, fd->name); +} + +static void js_dump_float64(JSContext *ctx, double d) +{ + char buf[32]; + JSDTOATempMem tmp_mem; /* XXX: potentially large stack size */ + js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE | JS_DTOA_MINUS_ZERO, &tmp_mem); + js_printf(ctx, "%s", buf); +} + +static void dump_regexp(JSContext *ctx, JSObject *p); + +static void js_dump_error(JSContext *ctx, JSObject *p) +{ + JSObject *p1; + JSProperty *pr; + JSValue name; + + /* find the error name without side effect */ + p1 = p; + if (p->proto != JS_NULL) + p1 = JS_VALUE_TO_PTR(p->proto); + pr = find_own_property(ctx, p1, js_get_atom(ctx, JS_ATOM_name)); + if (!pr || !JS_IsString(ctx, pr->value)) + name = js_get_atom(ctx, JS_ATOM_Error); + else + name = pr->value; + js_printf(ctx, "%" JSValue_PRI, name); + if (p->u.error.message != JS_NULL) { + js_printf(ctx, ": %" JSValue_PRI, p->u.error.message); + } + if (p->u.error.stack != JS_NULL) { + /* remove the trailing '\n' if any */ + js_printf(ctx, "\n%#" JSValue_PRI, p->u.error.stack); + } +} + +static void js_dump_object(JSContext *ctx, JSObject *p, int flags) +{ + if (flags & JS_DUMP_LONG) { + switch(p->class_id) { + case JS_CLASS_CLOSURE: + { + JSFunctionBytecode *b = JS_VALUE_TO_PTR(p->u.closure.func_bytecode); + js_printf(ctx, "function "); + JS_PrintValueF(ctx, b->func_name, JS_DUMP_NOQUOTE); + js_printf(ctx, "()"); + } + break; + case JS_CLASS_C_FUNCTION: + js_printf(ctx, "function "); + JS_PrintValueF(ctx, reloc_c_func_name(ctx, ctx->c_function_table[p->u.cfunc.idx].name), JS_DUMP_NOQUOTE); + js_printf(ctx, "()"); + break; + case JS_CLASS_ERROR: + js_dump_error(ctx, p); + break; + case JS_CLASS_REGEXP: + dump_regexp(ctx, p); + break; + default: + case JS_CLASS_ARRAY: + case JS_CLASS_OBJECT: + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + int i, idx; + uint32_t v; + double d; + JSObject *pbuffer; + JSByteArray *arr; + JS_PrintValueF(ctx, js_find_class_name(ctx, p->class_id), + JS_DUMP_NOQUOTE); + js_printf(ctx, "([ "); + pbuffer = JS_VALUE_TO_PTR(p->u.typed_array.buffer); + arr = JS_VALUE_TO_PTR(pbuffer->u.array_buffer.byte_buffer); + for(i = 0; i < p->u.typed_array.len; i++) { + if (i != 0) + js_printf(ctx, ", "); + idx = i + p->u.typed_array.offset; + switch(p->class_id) { + default: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + v = *((uint8_t *)arr->buf + idx); + goto ta_i32; + case JS_CLASS_INT8_ARRAY: + v = *((int8_t *)arr->buf + idx); + goto ta_i32; + case JS_CLASS_INT16_ARRAY: + v = *((int16_t *)arr->buf + idx); + goto ta_i32; + case JS_CLASS_UINT16_ARRAY: + v = *((uint16_t *)arr->buf + idx); + goto ta_i32; + case JS_CLASS_INT32_ARRAY: + v = *((int32_t *)arr->buf + idx); + ta_i32: + js_printf(ctx, "%d", v); + break; + case JS_CLASS_UINT32_ARRAY: + v = *((uint32_t *)arr->buf + idx); + js_printf(ctx, "%u", v); + break; + case JS_CLASS_FLOAT32_ARRAY: + d = *((float *)arr->buf + idx); + goto ta_d; + case JS_CLASS_FLOAT64_ARRAY: + d = *((double *)arr->buf + idx); + ta_d: + js_dump_float64(ctx, d); + break; + } + } + js_printf(ctx, " ])"); + } else { + int i, j, prop_count, hash_mask; + JSProperty *pr; + JSValueArray *arr; + BOOL is_first = TRUE; + + arr = JS_VALUE_TO_PTR(p->props); + prop_count = JS_VALUE_GET_INT(arr->arr[0]); + hash_mask = JS_VALUE_GET_INT(arr->arr[1]); + if (p->class_id == JS_CLASS_ARRAY) { + JSValueArray *tab = JS_VALUE_TO_PTR(p->u.array.tab); + js_printf(ctx, "[ "); + for(i = 0; i < p->u.array.len; i++) { + if (!is_first) + js_printf(ctx, ", "); + JS_PrintValue(ctx, tab->arr[i]); + is_first = FALSE; + } + } else { + if (p->class_id != JS_CLASS_OBJECT) { + JSValue class_name = js_find_class_name(ctx, p->class_id); + if (!JS_IsNull(class_name)) + JS_PrintValueF(ctx, class_name, JS_DUMP_NOQUOTE); + js_putchar(ctx, ' '); + } + js_printf(ctx, "{ "); + } + for(i = 0, j = 0; j < prop_count; i++) { + pr = (JSProperty *)&arr->arr[2 + (hash_mask + 1) + 3 * i]; + if (pr->key != JS_UNINITIALIZED) { + if (!is_first) + js_printf(ctx, ", "); + JS_PrintValueF(ctx, pr->key, JS_DUMP_NOQUOTE); + js_printf(ctx, ": "); + if (!(flags & JS_DUMP_RAW) && pr->prop_type == JS_PROP_SPECIAL) { + JS_PrintValue(ctx, get_special_prop(ctx, pr->value)); + } else { + JS_PrintValue(ctx, pr->value); + } + is_first = FALSE; + j++; + } + } + js_printf(ctx, " %c", + p->class_id == JS_CLASS_ARRAY ? ']' : '}'); + } + break; + } + } else { + const char *str; + if (p->class_id == JS_CLASS_ARRAY) + str = "Array"; + else if (p->class_id == JS_CLASS_ERROR) + str = "Error"; + else if (p->class_id == JS_CLASS_CLOSURE || + p->class_id == JS_CLASS_C_FUNCTION) { + str = "Function"; + } else { + str = "Object"; + } + js_printf(ctx, "[object %s]", str); + } +} + +static void dump_string(JSContext *ctx, int sep, const uint8_t *buf, size_t len, + int flags) +{ + BOOL use_quote; + const uint8_t *p, *p_end; + size_t i, clen; + int c; + + use_quote = TRUE; + if (flags & JS_DUMP_NOQUOTE) { + if (len >= 1 && is_ident_first(buf[0])) { + for(i = 1; i < len; i++) { + if (!is_ident_next(buf[i])) + goto need_quote; + } + use_quote = FALSE; + } + need_quote: ; + } + + if (!(flags & JS_DUMP_RAW)) + sep = '"'; + if (use_quote) + js_putchar(ctx, sep); + p = buf; + p_end = buf + len; + while (p < p_end) { + c = utf8_get(p, &clen); + 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: + js_putchar(ctx, '\\'); + js_putchar(ctx, c); + break; + default: + if (c < 32 || (c >= 0xd800 && c < 0xe000)) { + js_printf(ctx, "\\u%04x", c); + } else { + ctx->write_func(ctx->opaque, p, clen); + } + break; + } + p += clen; + } + if (use_quote) + js_putchar(ctx, sep); +} + +void JS_PrintValueF(JSContext *ctx, JSValue val, int flags) +{ + if (JS_IsInt(val)) { + js_printf(ctx, "%d", JS_VALUE_GET_INT(val)); + } else +#ifdef JS_USE_SHORT_FLOAT + if (JS_IsShortFloat(val)) { + js_dump_float64(ctx, js_get_short_float(val)); + } else +#endif + if (!JS_IsPtr(val)) { + switch(JS_VALUE_GET_SPECIAL_TAG(val)) { + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + case JS_TAG_UNINITIALIZED: + case JS_TAG_BOOL: + js_printf(ctx, "%"JSValue_PRI"", val); + break; + case JS_TAG_EXCEPTION: + js_printf(ctx, "[exception %d]", JS_VALUE_GET_SPECIAL_VALUE(val)); + break; + case JS_TAG_CATCH_OFFSET: + js_printf(ctx, "[catch_offset %d]", JS_VALUE_GET_SPECIAL_VALUE(val)); + break; + case JS_TAG_SHORT_FUNC: + { + int idx = JS_VALUE_GET_SPECIAL_VALUE(val); + js_printf(ctx, "function "); + JS_PrintValueF(ctx, reloc_c_func_name(ctx, ctx->c_function_table[idx].name), JS_DUMP_NOQUOTE); + js_printf(ctx, "()"); + } + break; + case JS_TAG_STRING_CHAR: + { + uint8_t buf[UTF8_CHAR_LEN_MAX + 1]; + int len; + len = get_short_string(buf, val); + dump_string(ctx, '`', buf, len, flags); + } + break; + default: + js_printf(ctx, "[tag %d]", (int)JS_VALUE_GET_SPECIAL_TAG(val)); + break; + } + } else { + void *ptr = JS_VALUE_TO_PTR(val); + int mtag = ((JSMemBlockHeader *)ptr)->mtag; + switch(mtag) { + case JS_MTAG_FLOAT64: + { + JSFloat64 *p = ptr; + js_dump_float64(ctx, p->u.dval); + } + break; + case JS_MTAG_OBJECT: + js_dump_object(ctx, ptr, flags); + break; + case JS_MTAG_STRING: + { + JSString *p = ptr; + int sep; + sep = p->is_unique ? '\'' : '\"'; + dump_string(ctx, sep, p->buf, p->len, flags); + } + break; + case JS_MTAG_VALUE_ARRAY: + { + JSValueArray *arr = ptr; + js_dump_array(ctx, arr, arr->size); + } + break; + case JS_MTAG_BYTE_ARRAY: + { + JSByteArray *arr = ptr; + js_printf(ctx, "byte_array(%" PRIu64 ")", (uint64_t)arr->size); + } + break; + case JS_MTAG_FUNCTION_BYTECODE: + { + JSFunctionBytecode *b = ptr; + js_printf(ctx, "bytecode_function "); + JS_PrintValueF(ctx, b->func_name, JS_DUMP_NOQUOTE); + js_printf(ctx, "()"); + } + break; + case JS_MTAG_VARREF: + { + JSVarRef *pv = ptr; + js_printf(ctx, "var_ref("); + if (pv->is_detached) + JS_PrintValue(ctx, pv->u.value); + else + JS_PrintValue(ctx, *pv->u.pvalue); + js_printf(ctx, ")"); + } + break; + default: + js_printf(ctx, "[mtag %d]", mtag); + break; + } + } +} + +void JS_PrintValue(JSContext *ctx, JSValue val) +{ + return JS_PrintValueF(ctx, val, 0); +} + +static const char *get_mtag_name(unsigned int mtag) +{ + if (mtag >= countof(js_mtag_name)) + return "?"; + else + return js_mtag_name[mtag]; +} + +static uint32_t val_to_offset(JSContext *ctx, JSValue val) +{ + if (!JS_IsPtr(val)) + return 0; + else + return (uint8_t *)JS_VALUE_TO_PTR(val) - ctx->heap_base; +} + +void JS_DumpMemory(JSContext *ctx, BOOL is_long) +{ + uint8_t *ptr; + uint32_t mtag_mem_size[JS_MTAG_COUNT]; + uint32_t mtag_count[JS_MTAG_COUNT]; + uint32_t tot_size, i; + if (is_long) { + js_printf(ctx, "%10s %s %8s %15s %10s %10s %s\n", "OFFSET", "M", "SIZE", "TAG", "PROTO", "PROPS", "EXTRA"); + } + for(i = 0; i < JS_MTAG_COUNT; i++) { + mtag_mem_size[i] = 0; + mtag_count[i] = 0; + } + tot_size = 0; + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + int mtag, size, gc_mark; + mtag = ((JSMemBlockHeader *)ptr)->mtag; + gc_mark = ((JSMemBlockHeader *)ptr)->gc_mark; + size = get_mblock_size(ptr); + mtag_mem_size[mtag] += size; + mtag_count[mtag]++; + tot_size += size; + if (is_long) { + js_printf(ctx, "0x%08x %c %8u %15s", + (unsigned int)((uint8_t *)ptr - ctx->heap_base), + gc_mark ? '*' : ' ', + size, + get_mtag_name(mtag)); + if (mtag != JS_MTAG_FREE) { + if (mtag == JS_MTAG_OBJECT) { + JSObject *p = (JSObject *)ptr; + js_printf(ctx, " 0x%08x 0x%08x", + val_to_offset(ctx, p->proto), val_to_offset(ctx, p->props)); + } else { + js_printf(ctx, " %10s %10s", "", ""); + } + js_printf(ctx, " "); + JS_PrintValueF(ctx, JS_VALUE_FROM_PTR(ptr), JS_DUMP_RAW); + } + js_printf(ctx, "\n"); + } + ptr += size; + } + + js_printf(ctx, "%15s %8s %8s %8s %8s\n", "TAG", "COUNT", "AVG_SIZE", "SIZE", "RATIO"); + for(i = 0; i < JS_MTAG_COUNT; i++) { + if (mtag_count[i] != 0) { + js_printf(ctx, "%15s %8u %8d %8u %7d%%\n", + get_mtag_name(i), + (unsigned int)mtag_count[i], + (int)js_lrint((double)mtag_mem_size[i] / (double)mtag_count[i]), + (unsigned int)mtag_mem_size[i], + (int)js_lrint((double)mtag_mem_size[i] / (double)tot_size * 100.0)); + } + } + js_printf(ctx, "heap size=%u/%u stack_size=%u\n", + (unsigned int)(ctx->heap_free - ctx->heap_base), + (unsigned int)(ctx->stack_top - ctx->heap_base), + (unsigned int)(ctx->stack_top - (uint8_t *)ctx->sp)); +} + +static __maybe_unused void JS_DumpUniqueStrings(JSContext *ctx) +{ + int i; + JSValueArray *arr; + + arr = JS_VALUE_TO_PTR( ctx->unique_strings); + js_printf(ctx, "%5s %s\n", "N", "UNIQUE_STRING"); + for(i = 0; i < ctx->unique_strings_len; i++) { + js_printf(ctx, "%5d ", i); + JS_PrintValue(ctx, arr->arr[i]); + js_printf(ctx, "\n"); + } +} +#else +void JS_PrintValueF(JSContext *ctx, JSValue val, int flags) +{ +} +void JS_PrintValue(JSContext *ctx, JSValue val) +{ + return JS_PrintValueF(ctx, val, 0); +} +void JS_DumpMemory(JSContext *ctx, BOOL is_long) +{ +} +static __maybe_unused void JS_DumpUniqueStrings(JSContext *ctx) +{ +} +#endif + +void JS_DumpValueF(JSContext *ctx, const char *str, + JSValue val, int flags) +{ + js_printf(ctx, "%s=", str); + JS_PrintValueF(ctx, val, flags); + js_printf(ctx, "\n"); +} + +void JS_DumpValue(JSContext *ctx, const char *str, + JSValue val) +{ + JS_DumpValueF(ctx, str, val, 0); +} + + +/**************************************************/ +/* JS parser */ + +enum { + TOK_NUMBER = 128, + TOK_STRING, + TOK_IDENT, + TOK_REGEXP, + /* warning: order matters (see js_parse_assign_expr) */ + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SAR_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, + TOK_POW_ASSIGN, + TOK_DEC, + TOK_INC, + TOK_SHL, + TOK_SAR, + TOK_SHR, + TOK_LT, + TOK_LTE, + TOK_GT, + TOK_GTE, + TOK_EQ, + TOK_STRICT_EQ, + TOK_NEQ, + TOK_STRICT_NEQ, + TOK_LAND, + TOK_LOR, + TOK_POW, + TOK_EOF, + /* keywords */ + TOK_FIRST_KEYWORD, + TOK_NULL = TOK_FIRST_KEYWORD + JS_ATOM_null, + TOK_FALSE = TOK_FIRST_KEYWORD + JS_ATOM_false, + TOK_TRUE = TOK_FIRST_KEYWORD + JS_ATOM_true, + TOK_IF = TOK_FIRST_KEYWORD + JS_ATOM_if, + TOK_ELSE = TOK_FIRST_KEYWORD + JS_ATOM_else, + TOK_RETURN = TOK_FIRST_KEYWORD + JS_ATOM_return, + TOK_VAR = TOK_FIRST_KEYWORD + JS_ATOM_var, + TOK_THIS = TOK_FIRST_KEYWORD + JS_ATOM_this, + TOK_DELETE = TOK_FIRST_KEYWORD + JS_ATOM_delete, + TOK_VOID = TOK_FIRST_KEYWORD + JS_ATOM_void, + TOK_TYPEOF = TOK_FIRST_KEYWORD + JS_ATOM_typeof, + TOK_NEW = TOK_FIRST_KEYWORD + JS_ATOM_new, + TOK_IN = TOK_FIRST_KEYWORD + JS_ATOM_in, + TOK_INSTANCEOF = TOK_FIRST_KEYWORD + JS_ATOM_instanceof, + TOK_DO = TOK_FIRST_KEYWORD + JS_ATOM_do, + TOK_WHILE = TOK_FIRST_KEYWORD + JS_ATOM_while, + TOK_FOR = TOK_FIRST_KEYWORD + JS_ATOM_for, + TOK_BREAK = TOK_FIRST_KEYWORD + JS_ATOM_break, + TOK_CONTINUE = TOK_FIRST_KEYWORD + JS_ATOM_continue, + TOK_SWITCH = TOK_FIRST_KEYWORD + JS_ATOM_switch, + TOK_CASE = TOK_FIRST_KEYWORD + JS_ATOM_case, + TOK_DEFAULT = TOK_FIRST_KEYWORD + JS_ATOM_default, + TOK_THROW = TOK_FIRST_KEYWORD + JS_ATOM_throw, + TOK_TRY = TOK_FIRST_KEYWORD + JS_ATOM_try, + TOK_CATCH = TOK_FIRST_KEYWORD + JS_ATOM_catch, + TOK_FINALLY = TOK_FIRST_KEYWORD + JS_ATOM_finally, + TOK_FUNCTION = TOK_FIRST_KEYWORD + JS_ATOM_function, + TOK_DEBUGGER = TOK_FIRST_KEYWORD + JS_ATOM_debugger, + TOK_WITH = TOK_FIRST_KEYWORD + JS_ATOM_with, + TOK_CLASS = TOK_FIRST_KEYWORD + JS_ATOM_class, + TOK_CONST = TOK_FIRST_KEYWORD + JS_ATOM_const, + TOK_ENUM = TOK_FIRST_KEYWORD + JS_ATOM_enum, + TOK_EXPORT = TOK_FIRST_KEYWORD + JS_ATOM_export, + TOK_EXTENDS = TOK_FIRST_KEYWORD + JS_ATOM_extends, + TOK_IMPORT = TOK_FIRST_KEYWORD + JS_ATOM_import, + TOK_SUPER = TOK_FIRST_KEYWORD + JS_ATOM_super, + TOK_IMPLEMENTS = TOK_FIRST_KEYWORD + JS_ATOM_implements, + TOK_INTERFACE = TOK_FIRST_KEYWORD + JS_ATOM_interface, + TOK_LET = TOK_FIRST_KEYWORD + JS_ATOM_let, + TOK_PACKAGE = TOK_FIRST_KEYWORD + JS_ATOM_package, + TOK_PRIVATE = TOK_FIRST_KEYWORD + JS_ATOM_private, + TOK_PROTECTED = TOK_FIRST_KEYWORD + JS_ATOM_protected, + TOK_PUBLIC = TOK_FIRST_KEYWORD + JS_ATOM_public, + TOK_STATIC = TOK_FIRST_KEYWORD + JS_ATOM_static, + TOK_YIELD = TOK_FIRST_KEYWORD + JS_ATOM_yield, +}; + +/* this structure is pushed on the JS stack, so all members must be JSValue */ +typedef struct BlockEnv { + JSValue prev; /* JS_NULL or stack index */ + JSValue label_name; /* JS_NULL if none */ + JSValue label_break; + JSValue label_cont; + JSValue label_finally; + JSValue drop_count; /* (int) number of stack elements to drop */ +} BlockEnv; + +typedef uint32_t JSSourcePos; + +typedef struct JSToken { + int val; + JSSourcePos source_pos; /* position in source */ + union { + double d; /* TOK_NUMBER */ + struct { + uint32_t re_flags; /* regular expression flags */ + uint32_t re_end_pos; /* at the final '/' */ + } regexp; + } u; + JSValue value; /* associated value: string for TOK_STRING, TOK_REGEXP; + identifier for TOK_IDENT or keyword */ +} JSToken; + +typedef struct JSParseState { + JSContext *ctx; + JSToken token; + + BOOL got_lf : 8; /* true if got line feed before the current token */ + /* global eval: variables are defined as global */ + BOOL is_eval : 8; + /* if true, return the last value. */ + BOOL has_retval : 8; + /* if true, implicitly define global variables in an + assignment. */ + BOOL is_repl : 8; + BOOL has_column : 8; /* column debug info is present */ + /* TRUE if the expression result has been dropped (see PF_DROP) */ + BOOL dropped_result : 8; + JSValue source_str; /* source string or JS_NULL */ + JSValue filename_str; /* 'filename' converted to string */ + /* zero terminated source buffer. Automatically updated by the GC + if source_str is a string */ + const uint8_t *source_buf; + uint32_t buf_pos; + uint32_t buf_len; + + /* current function */ + JSValue cur_func; + JSValue byte_code; + uint32_t byte_code_len; + int last_opcode_pos; /* -1 if no last opcode */ + int last_pc2line_pos; /* pc2line pos for the last opcode */ + JSSourcePos last_pc2line_source_pos; + + uint32_t pc2line_bit_len; + JSSourcePos pc2line_source_pos; /* last generated source pos */ + + uint16_t cpool_len; + /* size of the byte code necessary to define the hoisted functions */ + uint32_t hoisted_code_len; + + /* argument + defined local variable count */ + uint16_t local_vars_len; + + int eval_ret_idx; /* variable index for the eval return value, -1 + if no return value */ + JSValue top_break; /* JS_NULL or SP_TO_VALUE(BlockEnv *) */ + + /* regexp parsing only */ + uint8_t capture_count; + uint8_t re_in_js: 1; + uint8_t multi_line : 1; + uint8_t dotall : 1; + uint8_t ignore_case : 1; + uint8_t is_unicode : 1; + + /* error handling */ + jmp_buf jmp_env; + char error_msg[64]; +} JSParseState; + +static int js_parse_json_value(JSParseState *s, int state, int dummy_param); +static JSValue js_parse_regexp(JSParseState *s, int eval_flags); +static size_t js_parse_regexp_flags(int *pre_flags, const uint8_t *buf); +static int re_parse_alternative(JSParseState *s, int state, int dummy_param); +static int re_parse_disjunction(JSParseState *s, int state, int dummy_param); + +#ifdef DUMP_BYTECODE +static __maybe_unused void dump_byte_code(JSContext *ctx, JSFunctionBytecode *b) +{ + JSByteArray *arr, *pc2line; + JSValueArray *cpool, *vars, *ext_vars; + const JSOpCode *oi; + int pos, op, size, addr, idx, arg_count, len, i, line_num, col_num; + int line_num1, col_num1, hoisted_code_len; + uint8_t *tab; + uint32_t pc2line_pos; + + arr = JS_VALUE_TO_PTR(b->byte_code); + if (b->cpool != JS_NULL) + cpool = JS_VALUE_TO_PTR(b->cpool); + else + cpool = NULL; + if (b->vars != JS_NULL) + vars = JS_VALUE_TO_PTR(b->vars); + else + vars = NULL; + if (b->ext_vars != JS_NULL) + ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + else + ext_vars = NULL; + if (b->pc2line != JS_NULL) + pc2line = JS_VALUE_TO_PTR(b->pc2line); + else + pc2line = NULL; + + arg_count = b->arg_count; + + JS_PrintValueF(ctx, b->filename, JS_DUMP_NOQUOTE); + js_printf(ctx, ": function "); + JS_PrintValueF(ctx, b->func_name, JS_DUMP_NOQUOTE); + js_printf(ctx, ":\n"); + + if (b->arg_count && vars) { + js_printf(ctx, " args:"); + for(i = 0; i < b->arg_count; i++) { + js_printf(ctx, " "); + JS_PrintValue(ctx, vars->arr[i]); + } + js_printf(ctx, "\n"); + } + if (vars) { + js_printf(ctx, " locals:"); + for(i = 0; i < vars->size - b->arg_count; i++) { + js_printf(ctx, " "); + JS_PrintValue(ctx, vars->arr[i + b->arg_count]); + } + js_printf(ctx, "\n"); + } + if (ext_vars) { + js_printf(ctx, " refs:"); + for(i = 0; i < b->ext_vars_len; i++) { + int var_kind, var_idx, decl; + static const char *var_kind_str[] = { "arg", "var", "ref", "global" }; + js_printf(ctx, " "); + JS_PrintValue(ctx, ext_vars->arr[2 * i]); + decl = JS_VALUE_GET_INT(ext_vars->arr[2 * i + 1]); + var_kind = decl >> 16; + var_idx = decl & 0xffff; + js_printf(ctx, " (%s:%d)", var_kind_str[var_kind], var_idx); + } + js_printf(ctx, "\n"); + } + + js_printf(ctx, " cpool_size: %d\n", cpool ? (int)cpool->size : 0); + js_printf(ctx, " stack_size: %d\n", b->stack_size); + js_printf(ctx, " opcodes:\n"); + tab = arr->buf; + len = arr->size; + pos = 0; + pc2line_pos = 0; + hoisted_code_len = 0; + if (pc2line) + hoisted_code_len = get_pc2line_hoisted_code_len(pc2line->buf, pc2line->size); + line_num = 1; + col_num = 1; + line_num1 = 0; + col_num1 = 0; + while (pos < len) { + /* extract the debug info */ + if (pc2line && pos >= hoisted_code_len) { + get_pc2line(&line_num, &col_num, pc2line->buf, pc2line->size, + &pc2line_pos, b->has_column); + if (line_num != line_num1 || col_num != col_num1) { + js_printf(ctx, " # %d", line_num); + if (b->has_column) + js_printf(ctx, ", %d", col_num); + js_printf(ctx, "\n"); + line_num1 = line_num; + col_num1 = col_num; + } + } + op = tab[pos]; + js_printf(ctx, "%5d: ", pos); + if (op >= OP_COUNT) { + js_printf(ctx, "invalid opcode (0x%02x)\n", op); + pos++; + continue; + } + oi = &opcode_info[op]; + size = oi->size; + if ((pos + size) > len) { + js_printf(ctx, "truncated opcode (0x%02x)\n", op); + break; + } + js_printf(ctx, "%s", oi->name); + pos++; + switch(oi->fmt) { + case OP_FMT_u8: + js_printf(ctx, " %u", (int)get_u8(tab + pos)); + break; + case OP_FMT_i8: + js_printf(ctx, " %d", (int)get_i8(tab + pos)); + break; + case OP_FMT_u16: + case OP_FMT_npop: + js_printf(ctx, " %u", (int)get_u16(tab + pos)); + break; + case OP_FMT_i16: + js_printf(ctx, " %d", (int)get_i16(tab + pos)); + break; + case OP_FMT_i32: + js_printf(ctx, " %d", (int)get_i32(tab + pos)); + break; + case OP_FMT_u32: + js_printf(ctx, " %u", (int)get_u32(tab + pos)); + break; + case OP_FMT_none_int: + js_printf(ctx, " %d", op - OP_push_0); + break; +#if 0 + case OP_FMT_npopx: + js_printf(ctx, " %d", op - OP_call0); + break; +#endif + case OP_FMT_label8: + addr = get_i8(tab + pos); + goto has_addr1; + case OP_FMT_label16: + addr = get_i16(tab + pos); + goto has_addr1; + case OP_FMT_label: + addr = get_u32(tab + pos); + goto has_addr1; + has_addr1: + js_printf(ctx, " %u", addr + pos); + break; + case OP_FMT_const8: + idx = get_u8(tab + pos); + goto has_pool_idx; + case OP_FMT_const16: + idx = get_u16(tab + pos); + goto has_pool_idx; + has_pool_idx: + js_printf(ctx, " %u: ", idx); + if (idx < cpool->size) { + JS_PrintValue(ctx, cpool->arr[idx]); + } + break; + case OP_FMT_none_loc: + idx = (op - OP_get_loc0) % 4; + goto has_loc; + case OP_FMT_loc8: + idx = get_u8(tab + pos); + goto has_loc; + case OP_FMT_loc: + idx = get_u16(tab + pos); + has_loc: + js_printf(ctx, " %d: ", idx); + idx += arg_count; + if (idx < vars->size) { + JS_PrintValue(ctx, vars->arr[idx]); + } + break; + case OP_FMT_none_arg: + idx = (op - OP_get_arg0) % 4; + goto has_arg; + case OP_FMT_arg: + idx = get_u16(tab + pos); + has_arg: + js_printf(ctx, " %d: ", idx); + if (idx < vars->size) { + JS_PrintValue(ctx, vars->arr[idx]); + } + break; +#if 0 + case OP_FMT_none_var_ref: + idx = (op - OP_get_var_ref0) % 4; + goto has_var_ref; +#endif + case OP_FMT_var_ref: + idx = get_u16(tab + pos); + // has_var_ref: + js_printf(ctx, " %d: ", idx); + if (2 * idx < ext_vars->size) { + JS_PrintValue(ctx, ext_vars->arr[2 * idx]); + } + break; + case OP_FMT_value: + js_printf(ctx, " "); + idx = get_u32(tab + pos); + JS_PrintValue(ctx, idx); + break; + default: + break; + } + js_printf(ctx, "\n"); + pos += oi->size - 1; + } +} +#endif /* DUMP_BYTECODE */ + +static void next_token(JSParseState *s); + +static void __attribute((unused)) dump_token(JSParseState *s, + const JSToken *token) +{ + JSContext *ctx = s->ctx; + switch(token->val) { + case TOK_NUMBER: + /* XXX: TODO */ + js_printf(ctx, "number: %d\n", (int)token->u.d); + break; + case TOK_IDENT: + { + js_printf(ctx, "ident: "); + JS_PrintValue(s->ctx, token->value); + js_printf(ctx, "\n"); + } + break; + case TOK_STRING: + { + js_printf(ctx, "string: "); + JS_PrintValue(s->ctx, token->value); + js_printf(ctx, "\n"); + } + break; + case TOK_REGEXP: + { + js_printf(ctx, "regexp: "); + JS_PrintValue(s->ctx, token->value); + js_printf(ctx, "\n"); + } + break; + case TOK_EOF: + js_printf(ctx, "eof\n"); + break; + default: + if (s->token.val >= TOK_FIRST_KEYWORD) { + js_printf(ctx, "token: "); + JS_PrintValue(s->ctx, token->value); + js_printf(ctx, "\n"); + } else if (s->token.val >= 128) { + js_printf(ctx, "token: %d\n", token->val); + } else { + js_printf(ctx, "token: '%c'\n", token->val); + } + break; + } +} + +/* return the zero based line and column number in the source. */ +static int get_line_col(int *pcol_num, const uint8_t *buf, size_t len) +{ + int line_num, col_num, c; + size_t i; + + line_num = 0; + col_num = 0; + for(i = 0; i < len; i++) { + c = buf[i]; + if (c == '\n') { + line_num++; + col_num = 0; + } else if (c < 0x80 || c >= 0xc0) { + col_num++; + } + } + *pcol_num = col_num; + return line_num; +} + +static void __attribute__((format(printf, 2, 3), noreturn)) js_parse_error(JSParseState *s, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + js_vsnprintf(s->error_msg, sizeof(s->error_msg), fmt, ap); + va_end(ap); + longjmp(s->jmp_env, 1); +} + +static void js_parse_error_mem(JSParseState *s) +{ + return js_parse_error(s, "not enough memory"); +} + +static void js_parse_error_stack_overflow(JSParseState *s) +{ + return js_parse_error(s, "stack overflow"); +} + +static void js_parse_expect1(JSParseState *s, int ch) +{ + if (s->token.val != ch) + js_parse_error(s, "expecting '%c'", ch); +} + +static void js_parse_expect(JSParseState *s, int ch) +{ + js_parse_expect1(s, ch); + next_token(s); +} + +static void 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; + } + js_parse_error(s, "expecting '%c'", ';'); + } + next_token(s); +} + +#define SKIP_HAS_ARGUMENTS (1 << 0) +#define SKIP_HAS_FUNC_NAME (1 << 1) +#define SKIP_HAS_SEMI (1 << 2) /* semicolon found inside the first level */ + +/* Skip parenthesis or blocks. The current token should be '(', '[' or + '{'. 'func_name' can be JS_NULL. */ +static int js_skip_parens(JSParseState *s, JSValue *pfunc_name) +{ + uint8_t state[128]; + int level, c, bits = 0; + + /* protect from underflow */ + level = 0; + state[level++] = 0; + for (;;) { + switch(s->token.val) { + case '(': + c = ')'; + goto add_level; + case '[': + c = ']'; + goto add_level; + case '{': + c = '}'; + add_level: + if (level >= sizeof(state)) { + js_parse_error(s, "too many nested blocks"); + } + state[level++] = c; + break; + case ')': + case ']': + case '}': + c = state[--level]; + if (s->token.val != c) + js_parse_error(s, "expecting '%c'", c); + break; + case TOK_EOF: + js_parse_error(s, "expecting '%c'", state[level - 1]); + case TOK_IDENT: + if (s->token.value == js_get_atom(s->ctx, JS_ATOM_arguments)) + bits |= SKIP_HAS_ARGUMENTS; + if (pfunc_name && s->token.value == *pfunc_name) + bits |= SKIP_HAS_FUNC_NAME; + break; + case ';': + if (level == 2) + bits |= SKIP_HAS_SEMI; + break; + } + next_token(s); + if (level <= 1) + break; + } + return bits; +} + +/* skip an expression until ')' */ +static void js_skip_expr(JSParseState *s) +{ + for(;;) { + switch(s->token.val) { + case ')': + return; + case ';': + case TOK_EOF: + js_parse_error(s, "expecting '%c'", ')'); + case '(': + case '[': + case '{': + js_skip_parens(s, NULL); + break; + default: + next_token(s); + break; + } + } +} + +typedef struct JSParsePos { + BOOL got_lf : 8; + BOOL regexp_allowed : 8; + uint32_t source_pos; +} JSParsePos; + +/* return TRUE if a regexp literal is allowed after this token */ +static BOOL is_regexp_allowed(int tok) +{ + switch (tok) { + case TOK_NUMBER: + case TOK_STRING: + case TOK_REGEXP: + case TOK_DEC: + case TOK_INC: + case TOK_NULL: + case TOK_FALSE: + case TOK_TRUE: + case TOK_THIS: + case TOK_IF: + case TOK_WHILE: + case TOK_FOR: + case TOK_DO: + case TOK_CASE: + case TOK_CATCH: + case ')': + case ']': + case TOK_IDENT: + return FALSE; + default: + return TRUE; + } +} + +static void js_parse_get_pos(JSParseState *s, JSParsePos *sp) +{ + sp->source_pos = s->token.source_pos; + sp->got_lf = s->got_lf; + sp->regexp_allowed = is_regexp_allowed(s->token.val); +} + +static void js_parse_seek_token(JSParseState *s, const JSParsePos *sp) +{ + s->buf_pos = sp->source_pos; + s->got_lf = sp->got_lf; + /* the previous token value is only needed so that + is_regexp_allowed() returns the correct value */ + s->token.val = sp->regexp_allowed ? ' ' : ')'; + next_token(s); +} + +/* same as js_skip_parens but go back to the current token */ +static int js_parse_skip_parens_token(JSParseState *s) +{ + JSParsePos pos; + int bits; + + js_parse_get_pos(s, &pos); + bits = js_skip_parens(s, NULL); + js_parse_seek_token(s, &pos); + return bits; +} + +/* return the escape value or -1 */ +static int js_parse_escape(const uint8_t *buf, size_t *plen) +{ + int c; + const uint8_t *p = buf; + c = *p++; + switch(c) { + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case '\'': + case '\"': + case '\\': + break; + case 'x': + { + int h0, h1; + + h0 = from_hex(*p++); + if (h0 < 0) + return -1; + h1 = from_hex(*p++); + if (h1 < 0) + return -1; + c = (h0 << 4) | h1; + } + break; + case 'u': + { + int h, i; + + if (*p == '{') { + p++; + c = 0; + for(;;) { + h = from_hex(*p++); + if (h < 0) + return -1; + c = (c << 4) | h; + if (c > 0x10FFFF) + return -1; + if (*p == '}') + break; + } + p++; + } else { + c = 0; + for(i = 0; i < 4; i++) { + h = from_hex(*p++); + if (h < 0) { + return -1; + } + c = (c << 4) | h; + } + } + } + break; + case '0': + c -= '0'; + if (c != 0 || is_num(*p)) + return -1; + break; + default: + return -2; + } + *plen = p - buf; + return c; +} + +static JSValue js_parse_string(JSParseState *s, uint32_t *ppos, int sep) +{ + JSContext *ctx = s->ctx; + JSValue res; + const uint8_t *buf; + uint32_t pos; + uint32_t c; + size_t escape_len = 0; /* avoid warning */ + StringBuffer b_s, *b = &b_s; + + if (string_buffer_push(ctx, b, 16)) + js_parse_error_mem(s); + buf = s->source_buf; + /* string */ + pos = *ppos; + for(;;) { + c = buf[pos]; + if (c == '\0' || c == '\n' || c == '\r') { + js_parse_error(s, "unexpected end of string"); + } + pos++; + if (c == sep) + break; + if (c == '\\') { + if (buf[pos] == '\n') { + /* ignore escaped newline sequence */ + pos++; + continue; + } + c = js_parse_escape(buf + pos, &escape_len); + if (c == -1) { + js_parse_error(s, "invalid escape sequence"); + } else if (c == -2) { + /* ignore invalid escapes */ + continue; + } + pos += escape_len; + } else if (c >= 0x80) { + size_t clen; + pos--; + c = unicode_from_utf8(buf + pos, UTF8_CHAR_LEN_MAX, &clen); + pos += clen; + if (c == -1) { + js_parse_error(s, "invalid UTF-8 sequence"); + } + } + if (string_buffer_putc(ctx, b, c)) + break; + buf = s->source_buf; /* may be reallocated */ + } + *ppos = pos; + res = string_buffer_pop(ctx, b); + if (JS_IsException(res)) + js_parse_error_mem(s); + return res; +} + +static void js_parse_ident(JSParseState *s, JSToken *token, + uint32_t *ppos, int c) +{ + JSContext *ctx = s->ctx; + uint32_t pos; + JSValue val, val2; + JSGCRef val2_ref; + const uint8_t *buf; + StringBuffer b_s, *b = &b_s; + + if (string_buffer_push(ctx, b, 16)) + js_parse_error_mem(s); + string_buffer_putc(ctx, b, c); /* no allocation */ + buf = s->source_buf; + pos = *ppos; + while (pos < s->buf_len) { + c = buf[pos]; + if (!is_ident_next(c)) + break; + pos++; + if (string_buffer_putc(ctx, b, c)) + break; + buf = s->source_buf; /* may be reallocated */ + } + /* convert to token if necessary */ + token->val = TOK_IDENT; + val2 = string_buffer_pop(ctx, b); + JS_PUSH_VALUE(ctx, val2); + val = JS_MakeUniqueString(ctx, val2); + JS_POP_VALUE(ctx, val2); + if (JS_IsException(val)) + js_parse_error_mem(s); + if (val != val2) + js_free(ctx, JS_VALUE_TO_PTR(val2)); + token->value = val; + if (JS_IsPtr(val)) { + const JSWord *atom_start, *atom_last, *ptr; + atom_start = ctx->atom_table; + atom_last = atom_start + JS_ATOM_yield; + ptr = JS_VALUE_TO_PTR(val); + if (ptr >= atom_start && ptr <= atom_last) { + token->val = TOK_NULL + (ptr - atom_start); + } + } + *ppos = pos; +} + +static void js_parse_regexp_token(JSParseState *s, uint32_t *ppos) +{ + JSContext *ctx = s->ctx; + uint32_t pos; + uint32_t c; + BOOL in_class; + size_t clen; + int re_flags, end_pos, start_pos; + JSString *p; + + in_class = FALSE; + pos = *ppos; + start_pos = pos; + for(;;) { + c = unicode_from_utf8(s->source_buf + pos, UTF8_CHAR_LEN_MAX, &clen); + if (c == -1) + js_parse_error(s, "invalid UTF-8 sequence"); + pos += clen; + if (c == '\0' || c == '\n' || c == '\r') { + goto invalid_char; + } else if (c == '/') { + if (!in_class) + break; + } else if (c == '[') { + in_class = TRUE; + } else if (c == ']') { + in_class = FALSE; + } else if (c == '\\') { + c = unicode_from_utf8(s->source_buf + pos, UTF8_CHAR_LEN_MAX, &clen); + if (c == -1) + js_parse_error(s, "invalid UTF-8 sequence"); + if (c == '\0' || c == '\n' || c == '\r') { + invalid_char: + js_parse_error(s, "unexpected line terminator in regexp"); + } + pos += clen; + } + } + end_pos = pos - 1; + + clen = js_parse_regexp_flags(&re_flags, s->source_buf + pos); + pos += clen; + if (is_ident_next(s->source_buf[pos])) + js_parse_error(s, "invalid regular expression flags"); + + /* XXX: single char string is not optimized */ + p = js_alloc_string(ctx, end_pos - start_pos); + if (!p) + js_parse_error_mem(s); + p->is_ascii = is_ascii_string((char *)(s->source_buf + start_pos), end_pos - start_pos); + memcpy(p->buf, s->source_buf + start_pos, end_pos - start_pos); + + *ppos = pos; + s->token.val = TOK_REGEXP; + s->token.value = JS_VALUE_FROM_PTR(p); + s->token.u.regexp.re_flags = re_flags; + s->token.u.regexp.re_end_pos = end_pos; +} + +static void next_token(JSParseState *s) +{ + uint32_t pos; + const uint8_t *p; + int c; + + pos = s->buf_pos; + s->got_lf = FALSE; + s->token.value = JS_NULL; + p = s->source_buf + s->buf_pos; + redo: + s->token.source_pos = p - s->source_buf; + c = *p; + switch(c) { + case 0: + s->token.val = TOK_EOF; + break; + case '\"': + case '\'': + p++; + pos = p - s->source_buf; + s->token.value = js_parse_string(s, &pos, c); + s->token.val = TOK_STRING; + p = s->source_buf + pos; + break; + case '\n': + s->got_lf = TRUE; + p++; + goto redo; + case ' ': + case '\t': + case '\f': + case '\v': + case '\r': + p++; + goto redo; + case '/': + if (p[1] == '*') { + /* comment */ + p += 2; + for(;;) { + if (*p == '\0') + js_parse_error(s, "unexpected end of comment"); + if (p[0] == '*' && p[1] == '/') { + p += 2; + break; + } + p++; + } + goto redo; + } else if (p[1] == '/') { + /* line comment */ + p += 2; + for(;;) { + if (*p == '\0' || *p == '\n') + break; + p++; + } + goto redo; + } else if (is_regexp_allowed(s->token.val)) { + /* Note: we recognize regexps in the lexer. It does not + handle all the cases e.g. "({x:1} / 2)" or "a.void / 2" but + is consistent when we tokenize the input without + parsing it. */ + p++; + pos = p - s->source_buf; + js_parse_regexp_token(s, &pos); + p = s->source_buf + pos; + } else if (p[1] == '=') { + p += 2; + s->token.val = TOK_DIV_ASSIGN; + } else { + p++; + s->token.val = c; + } + break; + case 'a' ... 'z': + case 'A' ... 'Z': + case '_': + case '$': + p++; + pos = p - s->source_buf; + js_parse_ident(s, &s->token, &pos, c); + p = s->source_buf + pos; + break; + case '.': + if (is_digit(p[1])) + goto parse_number; + else + goto def_token; + case '0': + /* in strict mode, octal literals are not accepted */ + if (is_digit(p[1])) + goto invalid_number; + goto parse_number; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + /* number */ + parse_number: + { + double d; + JSByteArray *tmp_arr; + pos = p - s->source_buf; + tmp_arr = js_alloc_byte_array(s->ctx, sizeof(JSATODTempMem)); + if (!tmp_arr) + js_parse_error_mem(s); + p = s->source_buf + pos; + d = js_atod((const char *)p, (const char **)&p, 0, + JS_ATOD_ACCEPT_BIN_OCT | JS_ATOD_ACCEPT_UNDERSCORES, + (JSATODTempMem *)tmp_arr->buf); + js_free(s->ctx, tmp_arr); + if (isnan(d)) { + invalid_number: + js_parse_error(s, "invalid number literal"); + } + s->token.val = TOK_NUMBER; + s->token.u.d = d; + } + 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] == '-') { + 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 { + goto def_token; + } + break; + case '>': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_GTE; + } else if (p[1] == '>') { + if (p[2] == '>') { + if (p[3] == '=') { + p += 4; + s->token.val = TOK_SHR_ASSIGN; + } else { + p += 3; + s->token.val = TOK_SHR; + } + } else if (p[2] == '=') { + p += 3; + s->token.val = TOK_SAR_ASSIGN; + } else { + p += 2; + s->token.val = TOK_SAR; + } + } else { + goto def_token; + } + break; + case '=': + if (p[1] == '=') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_STRICT_EQ; + } else { + p += 2; + s->token.val = TOK_EQ; + } + } else { + goto def_token; + } + break; + case '!': + if (p[1] == '=') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_STRICT_NEQ; + } else { + p += 2; + s->token.val = TOK_NEQ; + } + } else { + goto def_token; + } + break; + case '&': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_AND_ASSIGN; + } else if (p[1] == '&') { + p += 2; + s->token.val = TOK_LAND; + } else { + goto def_token; + } + break; + case '^': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_XOR_ASSIGN; + } else { + goto def_token; + } + break; + case '|': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_OR_ASSIGN; + } else if (p[1] == '|') { + p += 2; + s->token.val = TOK_LOR; + } else { + goto def_token; + } + break; + default: + if (c >= 128) { + js_parse_error(s, "unexpected character"); + } + def_token: + s->token.val = c; + p++; + break; + } + s->buf_pos = p - s->source_buf; +#if defined(DUMP_TOKEN) + dump_token(s, &s->token); +#endif +} + +/* test if the current token is a label. XXX: we assume there is no + space between the identifier and the ':' to avoid having to push + back a token */ +static BOOL is_label(JSParseState *s) +{ + return (s->token.val == TOK_IDENT && s->source_buf[s->buf_pos] == ':'); +} + +static inline uint8_t *get_byte_code(JSParseState *s) +{ + JSByteArray *arr; + arr = JS_VALUE_TO_PTR(s->byte_code); + return arr->buf; +} + +static void emit_claim_size(JSParseState *s, int n) +{ + JSValue val; + val = js_resize_byte_array(s->ctx, s->byte_code, s->byte_code_len + n); + if (JS_IsException(val)) + js_parse_error_mem(s); + s->byte_code = val; +} + +static void emit_u8(JSParseState *s, uint8_t val) +{ + JSByteArray *arr; + emit_claim_size(s, 1); + arr = JS_VALUE_TO_PTR(s->byte_code); + arr->buf[s->byte_code_len++] = val; +} + +static void emit_u16(JSParseState *s, uint16_t val) +{ + JSByteArray *arr; + emit_claim_size(s, 2); + arr = JS_VALUE_TO_PTR(s->byte_code); + put_u16(arr->buf + s->byte_code_len, val); + s->byte_code_len += 2; +} + +static void emit_u32(JSParseState *s, uint32_t val) +{ + JSByteArray *arr; + emit_claim_size(s, 4); + arr = JS_VALUE_TO_PTR(s->byte_code); + put_u32(arr->buf + s->byte_code_len, val); + s->byte_code_len += 4; +} + +/* precondition: 1 <= n <= 25. */ +static void pc2line_put_bits_short(JSParseState *s, int n, uint32_t bits) +{ + JSFunctionBytecode *b; + JSValue val1; + JSByteArray *arr; + uint32_t index, pos; + unsigned int val; + int shift; + uint8_t *p; + + index = s->pc2line_bit_len; + pos = index >> 3; + + /* resize the array if needed */ + b = JS_VALUE_TO_PTR(s->cur_func); + val1 = js_resize_byte_array(s->ctx, b->pc2line, pos + 4); + if (JS_IsException(val1)) + js_parse_error_mem(s); + b = JS_VALUE_TO_PTR(s->cur_func); + b->pc2line = val1; + + arr = JS_VALUE_TO_PTR(val1); + p = arr->buf + pos; + val = get_be32(p); + shift = (32 - (index & 7) - n); + val &= ~(((1U << n) - 1) << shift); /* reset the bits */ + val |= bits << shift; + put_be32(p, val); + s->pc2line_bit_len = index + n; +} + +/* precondition: 1 <= n <= 32 */ +static void pc2line_put_bits(JSParseState *s, int n, uint32_t bits) +{ + int n_max = 25; + if (unlikely(n > n_max)) { + pc2line_put_bits_short(s, n - n_max, bits >> n_max); + bits &= (1 << n_max) - 1; + n = n_max; + } + pc2line_put_bits_short(s, n, bits); +} + +/* 0 <= v < 2^32-1 */ +static void put_ugolomb(JSParseState *s, uint32_t v) +{ + int n; + // printf("put_ugolomb: %u\n", v); + v++; + n = 32 - clz32(v); + if (n > 1) + pc2line_put_bits(s, n - 1, 0); + pc2line_put_bits(s, n, v); +} + +/* v != -2^31 */ +static void put_sgolomb(JSParseState *s, int32_t v1) +{ + uint32_t v = v1; + put_ugolomb(s, (2 * v) ^ -(v >> 31)); +} + +//#define DUMP_PC2LINE_STATS + +#ifdef DUMP_PC2LINE_STATS +static int pc2line_freq[256]; +static int pc2line_freq_tot; +#endif + +/* return the difference between the line numbers from 'pos1' to + 'pos2'. If the difference is zero, '*pcol_num' contains the + difference between the column numbers. Otherwise it contains the + zero based absolute column number. +*/ +static int get_line_col_delta(int *pcol_num, const uint8_t *buf, + int pos1, int pos2) +{ + int line_num, col_num, c, i; + line_num = 0; + col_num = 0; + if (pos2 >= pos1) { + line_num = get_line_col(&col_num, buf + pos1, pos2 - pos1); + } else { + line_num = get_line_col(&col_num, buf + pos2, pos1 - pos2); + line_num = -line_num; + col_num = -col_num; + if (line_num != 0) { + /* find the absolute column position */ + col_num = 0; + for(i = pos2 - 1; i >= 0; i--) { + c = buf[i]; + if (c == '\n') { + break; + } else if (c < 0x80 || c >= 0xc0) { + col_num++; + } + } + } + } + *pcol_num = col_num; + return line_num; +} + +static void emit_pc2line(JSParseState *s, JSSourcePos pos) +{ + int line_delta, col_delta; + + line_delta = get_line_col_delta(&col_delta, s->source_buf, + s->pc2line_source_pos, pos); + put_sgolomb(s, line_delta); + if (s->has_column) { + if (line_delta == 0) { +#ifdef DUMP_PC2LINE_STATS + pc2line_freq[min_int(max_int(col_delta + 128, 0), 255)]++; + pc2line_freq_tot++; +#endif + put_sgolomb(s, col_delta); + } else { + put_ugolomb(s, col_delta); + } + } + s->pc2line_source_pos = pos; +} + +#ifdef DUMP_PC2LINE_STATS +void dump_pc2line(void) +{ + int i; + for(i = 0; i < 256; i++) { + if (pc2line_freq[i] != 0) { + printf("%d: %d %0.2f\n", + i - 128, pc2line_freq[i], + -log2((double)pc2line_freq[i] / pc2line_freq_tot)); + } + } +} +#endif + +/* warning: pc2line info must be associated to each generated opcode */ +static void emit_op_pos(JSParseState *s, uint8_t op, JSSourcePos source_pos) +{ + s->last_opcode_pos = s->byte_code_len; + s->last_pc2line_pos = s->pc2line_bit_len; + s->last_pc2line_source_pos = s->pc2line_source_pos; + + emit_pc2line(s, source_pos); + emit_u8(s, op); +} + +static void emit_op(JSParseState *s, uint8_t op) +{ + emit_op_pos(s, op, s->pc2line_source_pos); +} + +static void emit_op_param(JSParseState *s, uint8_t op, uint32_t param, + JSSourcePos source_pos) +{ + const JSOpCode *oi; + + emit_op_pos(s, op, source_pos); + oi = &opcode_info[op]; + switch(oi->fmt) { + case OP_FMT_none: + break; + case OP_FMT_npop: + emit_u16(s, param); + break; + default: + assert(0); + } +} + +/* insert 'n' bytes at position pos */ +static void emit_insert(JSParseState *s, int pos, int n) +{ + JSByteArray *arr; + emit_claim_size(s, n); + arr = JS_VALUE_TO_PTR(s->byte_code); + memmove(arr->buf + pos + n, arr->buf + pos, s->byte_code_len - pos); + s->byte_code_len += n; +} + +static inline int get_prev_opcode(JSParseState *s) +{ + if (s->last_opcode_pos < 0) { + return OP_invalid; + } else { + uint8_t *byte_code = get_byte_code(s); + return byte_code[s->last_opcode_pos]; + } +} + +static BOOL js_is_live_code(JSParseState *s) { + switch (get_prev_opcode(s)) { + case OP_return: + case OP_return_undef: + case OP_throw: + case OP_goto: + case OP_ret: + return FALSE; + default: + return TRUE; + } +} + +static void remove_last_op(JSParseState *s) +{ + s->byte_code_len = s->last_opcode_pos; + s->pc2line_bit_len = s->last_pc2line_pos; + s->pc2line_source_pos = s->last_pc2line_source_pos; + s->last_opcode_pos = -1; +} + +static void emit_push_short_int(JSParseState *s, int val) +{ + if (val >= -1 && val <= 7) { + emit_op(s, OP_push_0 + val); + } else if (val == (int8_t)val) { + emit_op(s, OP_push_i8); + emit_u8(s, val); + } else if (val == (int16_t)val) { + emit_op(s, OP_push_i16); + emit_u16(s, val); + } else { + emit_op(s, OP_push_value); + emit_u32(s, JS_NewShortInt(val)); + } +} + +static void emit_var(JSParseState *s, int opcode, int var_idx, + JSSourcePos source_pos) +{ + switch(opcode) { + case OP_get_loc: + if (var_idx < 4) { + emit_op_pos(s, OP_get_loc0 + var_idx, source_pos); + return; + } else if (var_idx < 256) { + emit_op_pos(s, OP_get_loc8, source_pos); + emit_u8(s, var_idx); + return; + } + break; + case OP_put_loc: + if (var_idx < 4) { + emit_op_pos(s, OP_put_loc0 + var_idx, source_pos); + return; + } else if (var_idx < 256) { + emit_op_pos(s, OP_put_loc8, source_pos); + emit_u8(s, var_idx); + return; + } + break; + case OP_get_arg: + if (var_idx < 4) { + emit_op_pos(s, OP_get_arg0 + var_idx, source_pos); + return; + } + break; + case OP_put_arg: + if (var_idx < 4) { + emit_op_pos(s, OP_put_arg0 + var_idx, source_pos); + return; + } + break; + } + emit_op_pos(s, opcode, source_pos); + emit_u16(s, var_idx); +} + + +typedef enum { + JS_PARSE_FUNC_STATEMENT, + JS_PARSE_FUNC_EXPR, + JS_PARSE_FUNC_METHOD, +} JSParseFunctionEnum; + +static void js_parse_function_decl(JSParseState *s, + JSParseFunctionEnum func_type, JSValue func_name); + +/* labels are short integers so they can be used as JSValue. -1 is not + a valid label. */ +#define LABEL_RESOLVED_FLAG (1 << 29) +#define LABEL_OFFSET_MASK ((1 << 29) - 1) + +#define LABEL_NONE JS_NewShortInt(-1) + +static BOOL label_is_none(JSValue label) +{ + return JS_VALUE_GET_INT(label) < 0; +} + +static JSValue new_label(JSParseState *s) +{ + return JS_NewShortInt(LABEL_OFFSET_MASK); +} + +static void emit_label_pos(JSParseState *s, JSValue *plabel, int pos) +{ + int label; + JSByteArray *arr; + int next; + + label = JS_VALUE_GET_INT(*plabel); + assert(!(label & LABEL_RESOLVED_FLAG)); + arr = JS_VALUE_TO_PTR(s->byte_code); + while (label != LABEL_OFFSET_MASK) { + next = get_u32(arr->buf + label); + put_u32(arr->buf + label, pos - label); + label = next; + } + *plabel = JS_NewShortInt(pos | LABEL_RESOLVED_FLAG); +} + +static void emit_label(JSParseState *s, JSValue *plabel) +{ + emit_label_pos(s, plabel, s->byte_code_len); + /* prevent get_lvalue from using the last expression as an + lvalue. */ + s->last_opcode_pos = -1; +} + +static void emit_goto(JSParseState *s, int opcode, JSValue *plabel) +{ + int label; + /* XXX: generate smaller gotos when possible */ + emit_op(s, opcode); + label = JS_VALUE_GET_INT(*plabel); + if (label & LABEL_RESOLVED_FLAG) { + emit_u32(s, (label & LABEL_OFFSET_MASK) - s->byte_code_len); + } else { + emit_u32(s, label); + *plabel = JS_NewShortInt(s->byte_code_len - 4); + } +} + +/* return the constant pool index. 'val' is not duplicated. */ +static int cpool_add(JSParseState *s, JSValue val) +{ + JSFunctionBytecode *b; + JSValueArray *arr; + int i; + JSValue new_cpool; + JSGCRef val_ref; + + b = JS_VALUE_TO_PTR(s->cur_func); + arr = JS_VALUE_TO_PTR(b->cpool); + /* check if the value is already present */ + for(i = 0; i < s->cpool_len; i++) { + if (arr->arr[i] == val) + return i; + } + + if (s->cpool_len > 65535) + js_parse_error(s, "too many constants"); + JS_PUSH_VALUE(s->ctx, val); + new_cpool = js_resize_value_array(s->ctx, b->cpool, max_int(s->cpool_len + 1, 4)); + JS_POP_VALUE(s->ctx, val); + if (JS_IsException(new_cpool)) + js_parse_error_mem(s); + b = JS_VALUE_TO_PTR(s->cur_func); + b->cpool = new_cpool; + arr = JS_VALUE_TO_PTR(b->cpool); + arr->arr[s->cpool_len++] = val; + return s->cpool_len - 1; +} + +static void js_emit_push_const(JSParseState *s, JSValue val) +{ + int idx; + + if (JS_IsPtr(val) +#ifdef JS_USE_SHORT_FLOAT + || JS_IsShortFloat(val) +#endif + ) { + /* We use a constant pool to avoid scanning the bytecode + during the GC. XXX: is it a good choice ? */ + idx = cpool_add(s, val); + emit_op(s, OP_push_const); + emit_u16(s, idx); + } else { + /* no GC mark */ + emit_op(s, OP_push_value); + emit_u32(s, val); + } +} + +/* return the local variable index or -1 if not found */ +static int find_func_var(JSContext *ctx, JSValue func, JSValue name) +{ + JSFunctionBytecode *b; + JSValueArray *arr; + int i; + + b = JS_VALUE_TO_PTR(func); + if (b->vars == JS_NULL) + return -1; + arr = JS_VALUE_TO_PTR(b->vars); + for(i = 0; i < arr->size; i++) { + if (arr->arr[i] == name) + return i; + } + return -1; +} + +static int find_var(JSParseState *s, JSValue name) +{ + JSFunctionBytecode *b; + JSValueArray *arr; + int i; + + b = JS_VALUE_TO_PTR(s->cur_func); + arr = JS_VALUE_TO_PTR(b->vars); + for(i = 0; i < s->local_vars_len; i++) { + if (arr->arr[i] == name) + return i; + } + return -1; +} + +static JSValue get_ext_var_name(JSParseState *s, int var_idx) +{ + JSFunctionBytecode *b; + JSValueArray *arr; + + b = JS_VALUE_TO_PTR(s->cur_func); + arr = JS_VALUE_TO_PTR(b->ext_vars); + return arr->arr[2 * var_idx]; +} + +static int find_func_ext_var(JSParseState *s, JSValue func, JSValue name) +{ + JSFunctionBytecode *b; + JSValueArray *arr; + int i; + + b = JS_VALUE_TO_PTR(func); + arr = JS_VALUE_TO_PTR(b->ext_vars); + for(i = 0; i < b->ext_vars_len; i++) { + if (arr->arr[2 * i] == name) + return i; + } + return -1; +} + +/* return the external variable index or -1 if not found */ +static int find_ext_var(JSParseState *s, JSValue name) +{ + return find_func_ext_var(s, s->cur_func, name); +} + +/* return the external variable index */ +static int add_func_ext_var(JSParseState *s, JSValue func, JSValue name, int decl) +{ + JSFunctionBytecode *b; + JSValueArray *arr; + JSValue new_ext_vars; + JSGCRef name_ref, func_ref; + + b = JS_VALUE_TO_PTR(func); + if (b->ext_vars_len >= JS_MAX_LOCAL_VARS) + js_parse_error(s, "too many variable references"); + JS_PUSH_VALUE(s->ctx, func); + JS_PUSH_VALUE(s->ctx, name); + new_ext_vars = js_resize_value_array(s->ctx, b->ext_vars, max_int(b->ext_vars_len + 1, 2) * 2); + JS_POP_VALUE(s->ctx, name); + JS_POP_VALUE(s->ctx, func); + if (JS_IsException(new_ext_vars)) + js_parse_error_mem(s); + b = JS_VALUE_TO_PTR(func); + b->ext_vars = new_ext_vars; + arr = JS_VALUE_TO_PTR(b->ext_vars); + arr->arr[2 * b->ext_vars_len] = name; + arr->arr[2 * b->ext_vars_len + 1] = JS_NewShortInt(decl); + b->ext_vars_len++; + return b->ext_vars_len - 1; +} + +/* return the external variable index */ +static int add_ext_var(JSParseState *s, JSValue name, int decl) +{ + return add_func_ext_var(s, s->cur_func, name, decl); +} + +/* return the local variable index */ +static int add_var(JSParseState *s, JSValue name) +{ + JSFunctionBytecode *b; + JSValueArray *arr; + JSValue new_vars; + JSGCRef name_ref; + + b = JS_VALUE_TO_PTR(s->cur_func); + if (s->local_vars_len >= JS_MAX_LOCAL_VARS) + js_parse_error(s, "too many local variables"); + JS_PUSH_VALUE(s->ctx, name); + new_vars = js_resize_value_array(s->ctx, b->vars, max_int(s->local_vars_len + 1, 4)); + JS_POP_VALUE(s->ctx, name); + if (JS_IsException(new_vars)) + js_parse_error_mem(s); + b = JS_VALUE_TO_PTR(s->cur_func); + b->vars = new_vars; + arr = JS_VALUE_TO_PTR(b->vars); + arr->arr[s->local_vars_len++] = name; + return s->local_vars_len - 1; +} + +static void get_lvalue(JSParseState *s, int *popcode, + int *pvar_idx, JSSourcePos *psource_pos, BOOL keep) +{ + int opcode, var_idx; + JSSourcePos source_pos; + + /* we check the last opcode to get the lvalue type */ + opcode = get_prev_opcode(s); + switch(opcode) { + case OP_get_loc0: + case OP_get_loc1: + case OP_get_loc2: + case OP_get_loc3: + var_idx = opcode - OP_get_loc0; + opcode = OP_get_loc; + break; + case OP_get_arg0: + case OP_get_arg1: + case OP_get_arg2: + case OP_get_arg3: + var_idx = opcode - OP_get_arg0; + opcode = OP_get_arg; + break; + case OP_get_loc8: + var_idx = get_u8(get_byte_code(s) + s->last_opcode_pos + 1); + opcode = OP_get_loc; + break; + case OP_get_loc: + case OP_get_arg: + case OP_get_var_ref: + case OP_get_field: + var_idx = get_u16(get_byte_code(s) + s->last_opcode_pos + 1); + break; + case OP_get_array_el: + case OP_get_length: + var_idx = -1; + break; + default: + js_parse_error(s, "invalid lvalue"); + } + source_pos = s->pc2line_source_pos; + + /* remove the last opcode */ + remove_last_op(s); + + if (keep) { + /* get the value but keep the object/fields on the stack */ + switch(opcode) { + case OP_get_loc: + case OP_get_arg: + case OP_get_var_ref: + emit_var(s, opcode, var_idx, source_pos); + break; + case OP_get_field: + emit_op_pos(s, OP_get_field2, source_pos); + emit_u16(s, var_idx); + break; + case OP_get_length: + emit_op_pos(s, OP_get_length2, source_pos); + break; + case OP_get_array_el: + emit_op(s, OP_dup2); + emit_op_pos(s, OP_get_array_el, source_pos); /* XXX: add OP_get_array_el3 but need to modify tail call */ + break; + default: + abort(); + } + } + + *popcode = opcode; + *pvar_idx = var_idx; + *psource_pos = source_pos; +} + +typedef enum { + PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ + PUT_LVALUE_NOKEEP_TOP, /* [depth] v -> */ + PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ + PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ +} PutLValueEnum; + +static void put_lvalue(JSParseState *s, int opcode, + int var_idx, JSSourcePos source_pos, + PutLValueEnum special) +{ + switch(opcode) { + case OP_get_loc: + case OP_get_arg: + case OP_get_var_ref: + if (special == PUT_LVALUE_KEEP_TOP) + emit_op(s, OP_dup); + if (opcode == OP_get_var_ref && s->is_repl) + opcode = OP_put_var_ref_nocheck; /* an assignment defines the variable in the REPL */ + else + opcode++; + emit_var(s, opcode, var_idx, source_pos); + break; + case OP_get_field: + case OP_get_length: + switch(special) { + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert2); /* obj a -> a obj a */ + break; + case PUT_LVALUE_NOKEEP_TOP: + break; + case PUT_LVALUE_NOKEEP_BOTTOM: + emit_op(s, OP_swap); /* a obj -> obj a */ + break; + default: + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm3); /* obj a b -> a obj b */ + break; + } + emit_op_pos(s, OP_put_field, source_pos); + if (opcode == OP_get_length) { + emit_u16(s, cpool_add(s, js_get_atom(s->ctx, JS_ATOM_length))); + } else { + emit_u16(s, var_idx); + } + break; + case OP_get_array_el: + switch(special) { + case PUT_LVALUE_KEEP_TOP: + emit_op(s, OP_insert3); /* obj prop a -> a obj prop a */ + break; + case PUT_LVALUE_NOKEEP_TOP: + break; + case PUT_LVALUE_NOKEEP_BOTTOM: /* a obj prop -> obj prop a */ + emit_op(s, OP_rot3l); /* obj prop a b -> a obj prop b */ + break; + default: + case PUT_LVALUE_KEEP_SECOND: + emit_op(s, OP_perm4); /* obj prop a b -> a obj prop b */ + break; + } + emit_op_pos(s, OP_put_array_el, source_pos); + break; + default: + abort(); + } +} + +enum { + PARSE_PROP_FIELD, + PARSE_PROP_GET, + PARSE_PROP_SET, + PARSE_PROP_METHOD, +}; + +static int js_parse_property_name(JSParseState *s, JSValue *pname) +{ + JSContext *ctx = s->ctx; + JSValue name; + JSGCRef name_ref; + int prop_type; + + prop_type = PARSE_PROP_FIELD; + + if (s->token.val == TOK_IDENT) { + int is_set; + if (s->token.value == js_get_atom(ctx, JS_ATOM_get)) + is_set = 0; + else if (s->token.value == js_get_atom(ctx, JS_ATOM_set)) + is_set = 1; + else + is_set = -1; + if (is_set >= 0) { + next_token(s); + if (s->token.val == ':' || s->token.val == ',' || + s->token.val == '}' || s->token.val == '(') { + /* not a get set */ + name = js_get_atom(ctx, is_set ? JS_ATOM_set : JS_ATOM_get); + goto done; + } + prop_type = PARSE_PROP_GET + is_set; + } + } + + if (s->token.val == TOK_IDENT || s->token.val >= TOK_FIRST_KEYWORD) { + name = s->token.value; + } else if (s->token.val == TOK_STRING) { + name = s->token.value; + } else if (s->token.val == TOK_NUMBER) { + name = JS_NewFloat64(s->ctx, s->token.u.d); + if (JS_IsException(name)) + js_parse_error_mem(s); + } else { + js_parse_error(s, "invalid property name"); + } + name = JS_ToPropertyKey(s->ctx, name); + if (JS_IsException(name)) + js_parse_error_mem(s); + JS_PUSH_VALUE(ctx, name); + next_token(s); + JS_POP_VALUE(ctx, name); + done: + if (prop_type == PARSE_PROP_FIELD && s->token.val == '(') + prop_type = PARSE_PROP_METHOD; + *pname = name; + return prop_type; +} + +/* recursion free parser definitions */ + +#define PF_NO_IN (1 << 0) /* the 'in' operator is not accepted*/ +#define PF_DROP (1 << 1) /* drop result */ +#define PF_ACCEPT_LPAREN (1 << 2) /* js_parse_postfix_expr only */ +#define PF_LEVEL_SHIFT 4 /* optional level parameter */ +#define PF_LEVEL_MASK (0xf << PF_LEVEL_SHIFT) + +typedef enum { + PARSE_FUNC_js_parse_expr_comma, + PARSE_FUNC_js_parse_assign_expr, + PARSE_FUNC_js_parse_cond_expr, + PARSE_FUNC_js_parse_logical_and_or, + PARSE_FUNC_js_parse_expr_binary, + PARSE_FUNC_js_parse_unary, + PARSE_FUNC_js_parse_postfix_expr, + PARSE_FUNC_js_parse_statement, + PARSE_FUNC_js_parse_block, + PARSE_FUNC_js_parse_json_value, + PARSE_FUNC_re_parse_alternative, + PARSE_FUNC_re_parse_disjunction, +} ParseExprFuncEnum; + +typedef int JSParseFunc(JSParseState *s, int state, int param); + +#define PARSE_STATE_INIT 0xfe +#define PARSE_STATE_RET 0xff + +/* may trigger a gc */ +static JSValue parse_stack_alloc(JSParseState *s, JSValue val) +{ + JSGCRef val_ref; + + JS_PUSH_VALUE(s->ctx, val); + if (JS_StackCheck(s->ctx, 1)) + js_parse_error_stack_overflow(s); + JS_POP_VALUE(s->ctx, val); + return val; +} + +/* WARNING: 'val' may be modified after this val if it is a pointer */ +static void js_parse_push_val(JSParseState *s, JSValue val) +{ + JSContext *ctx = s->ctx; + if (unlikely(ctx->sp <= ctx->stack_bottom)) { + val = parse_stack_alloc(s, val); + } + *--(ctx->sp) = val; +} + +/* update the stack bottom when there is a large stack space */ +static JSValue js_parse_pop_val(JSParseState *s) +{ + JSContext *ctx = s->ctx; + JSValue val; + val = *(ctx->sp)++; + if (unlikely(ctx->sp - JS_STACK_SLACK > ctx->stack_bottom)) + ctx->stack_bottom = ctx->sp - JS_STACK_SLACK; + return val; +} + +#define PARSE_PUSH_VAL(s, v) js_parse_push_val(s, v) +#define PARSE_POP_VAL(s, v) v = js_parse_pop_val(s) + +#define PARSE_PUSH_INT(s, v) js_parse_push_val(s, JS_NewShortInt(v)) +#define PARSE_POP_INT(s, v) v = JS_VALUE_GET_INT(js_parse_pop_val(s)) + +#define PARSE_START1() \ + switch(state) {\ + case PARSE_STATE_INIT: break;\ + default: abort();\ + case 0: goto parse_state0;\ + } + +#define PARSE_START2() \ + switch(state) {\ + case PARSE_STATE_INIT: break;\ + default: abort();\ + case 0: goto parse_state0;\ + case 1: goto parse_state1;\ + } + +#define PARSE_START3() \ + switch(state) {\ + case PARSE_STATE_INIT: break;\ + default: abort();\ + case 0: goto parse_state0;\ + case 1: goto parse_state1;\ + case 2: goto parse_state2;\ + } + +#define PARSE_START7() \ + switch(state) {\ + case PARSE_STATE_INIT: break;\ + default: abort();\ + case 0: goto parse_state0;\ + case 1: goto parse_state1;\ + case 2: goto parse_state2;\ + case 3: goto parse_state3; \ + case 4: goto parse_state4;\ + case 5: goto parse_state5;\ + case 6: goto parse_state6;\ + } + +#define PARSE_START12() \ + switch(state) {\ + case PARSE_STATE_INIT: break;\ + default: abort();\ + case 0: goto parse_state0;\ + case 1: goto parse_state1;\ + case 2: goto parse_state2;\ + case 3: goto parse_state3; \ + case 4: goto parse_state4;\ + case 5: goto parse_state5;\ + case 6: goto parse_state6;\ + case 7: goto parse_state7; \ + case 8: goto parse_state8;\ + case 9: goto parse_state9;\ + case 10: goto parse_state10;\ + case 11: goto parse_state11;\ + } + +/* WARNING: local variables are not preserved across PARSE_CALL(). So + they must be explicitly saved and restored */ +#define PARSE_CALL(s, cur_state, func, param) return (cur_state | (PARSE_FUNC_ ## func << 8) | ((param) << 16)); parse_state ## cur_state : ; + +/* preserve var1, ... across the call */ +#define PARSE_CALL_SAVE1(s, cur_state, func, param, var1) \ + PARSE_PUSH_INT(s, var1); \ + PARSE_CALL(s, cur_state, func, param); \ + PARSE_POP_INT(s, var1); + +#define PARSE_CALL_SAVE2(s, cur_state, func, param, var1, var2) \ + PARSE_PUSH_INT(s, var1); \ + PARSE_PUSH_INT(s, var2); \ + PARSE_CALL(s, cur_state, func, param); \ + PARSE_POP_INT(s, var2); \ + PARSE_POP_INT(s, var1); + +#define PARSE_CALL_SAVE3(s, cur_state, func, param, var1, var2, var3) \ + PARSE_PUSH_INT(s, var1); \ + PARSE_PUSH_INT(s, var2); \ + PARSE_PUSH_INT(s, var3); \ + PARSE_CALL(s, cur_state, func, param); \ + PARSE_POP_INT(s, var3); \ + PARSE_POP_INT(s, var2); \ + PARSE_POP_INT(s, var1); + +#define PARSE_CALL_SAVE4(s, cur_state, func, param, var1, var2, var3, var4) \ + PARSE_PUSH_INT(s, var1); \ + PARSE_PUSH_INT(s, var2); \ + PARSE_PUSH_INT(s, var3); \ + PARSE_PUSH_INT(s, var4); \ + PARSE_CALL(s, cur_state, func, param); \ + PARSE_POP_INT(s, var4); \ + PARSE_POP_INT(s, var3); \ + PARSE_POP_INT(s, var2); \ + PARSE_POP_INT(s, var1); + +#define PARSE_CALL_SAVE5(s, cur_state, func, param, var1, var2, var3, var4, var5) \ + PARSE_PUSH_INT(s, var1); \ + PARSE_PUSH_INT(s, var2); \ + PARSE_PUSH_INT(s, var3); \ + PARSE_PUSH_INT(s, var4); \ + PARSE_PUSH_INT(s, var5); \ + PARSE_CALL(s, cur_state, func, param); \ + PARSE_POP_INT(s, var5); \ + PARSE_POP_INT(s, var4); \ + PARSE_POP_INT(s, var3); \ + PARSE_POP_INT(s, var2); \ + PARSE_POP_INT(s, var1); + +#define PARSE_CALL_SAVE6(s, cur_state, func, param, var1, var2, var3, var4, var5, var6) \ + PARSE_PUSH_INT(s, var1); \ + PARSE_PUSH_INT(s, var2); \ + PARSE_PUSH_INT(s, var3); \ + PARSE_PUSH_INT(s, var4); \ + PARSE_PUSH_INT(s, var5); \ + PARSE_PUSH_INT(s, var6); \ + PARSE_CALL(s, cur_state, func, param); \ + PARSE_POP_INT(s, var6); \ + PARSE_POP_INT(s, var5); \ + PARSE_POP_INT(s, var4); \ + PARSE_POP_INT(s, var3); \ + PARSE_POP_INT(s, var2); \ + PARSE_POP_INT(s, var1); + +static JSParseFunc *parse_func_table[]; + +static void js_parse_call(JSParseState *s, ParseExprFuncEnum func_idx, + int param) +{ + JSContext *ctx = s->ctx; + int ret, state; + JSValue *stack_top; + + stack_top = ctx->sp; + state = PARSE_STATE_INIT; + for(;;) { + ret = parse_func_table[func_idx](s, state, param); + state = ret & 0xff; + if (state == PARSE_STATE_RET) { + /* the function terminated: go back to the calling + function if any */ + if (ctx->sp == stack_top) + break; + PARSE_POP_INT(s, ret); + state = ret & 0xff; + func_idx = (ret >> 8) & 0xff; + param = -1; /* the parameter is not saved */ + } else { + /* push the call position and call another function */ + PARSE_PUSH_INT(s, state | (func_idx << 8)); + state = PARSE_STATE_INIT; + func_idx = (ret >> 8) & 0xff; + param = (ret >> 16); + } + } +} + +static BOOL may_drop_result(JSParseState *s, int parse_flags) +{ + return ((parse_flags & PF_DROP) && + (s->token.val == ';' || s->token.val == ')' || + s->token.val == ',')); +} + +static void js_emit_push_number(JSParseState *s, double d) +{ + JSValue val; + + val = JS_NewFloat64(s->ctx, d); + if (JS_IsException(val)) + js_parse_error_mem(s); + if (JS_IsInt(val)) { + emit_push_short_int(s, JS_VALUE_GET_INT(val)); + } else { + js_emit_push_const(s, val); + } +} + +static int js_parse_postfix_expr(JSParseState *s, int state, int parse_flags) +{ + BOOL is_new = FALSE; + + PARSE_START7(); + switch(s->token.val) { + case TOK_NUMBER: + js_emit_push_number(s, s->token.u.d); + next_token(s); + break; + case TOK_STRING: + { + js_emit_push_const(s, s->token.value); + next_token(s); + } + break; + case TOK_REGEXP: + { + uint32_t saved_buf_pos, saved_buf_len; + uint32_t saved_byte_code_len; + JSValue byte_code; + JSFunctionBytecode *b; + + js_emit_push_const(s, s->token.value); /* regexp source */ + + saved_buf_pos = s->buf_pos; + saved_buf_len = s->buf_len; + /* save the current bytecode back to the function */ + b = JS_VALUE_TO_PTR(s->cur_func); + b->byte_code = s->byte_code; + saved_byte_code_len = s->byte_code_len; + + /* modify the parser to parse the regexp. This way we + avoid instantiating a new JSParseState */ + /* XXX: find a better way as it relies on the regexp + parser to correctly handle the end of regexp */ + s->buf_pos = s->token.source_pos + 1; + s->buf_len = s->token.u.regexp.re_end_pos; + byte_code = js_parse_regexp(s, s->token.u.regexp.re_flags); + + s->buf_pos = saved_buf_pos; + s->buf_len = saved_buf_len; + b = JS_VALUE_TO_PTR(s->cur_func); + s->byte_code = b->byte_code; + s->byte_code_len = saved_byte_code_len; + + js_emit_push_const(s, byte_code); + emit_op(s, OP_regexp); + next_token(s); + } + break; + case '(': + next_token(s); + PARSE_CALL_SAVE1(s, 0, js_parse_expr_comma, 0, parse_flags); + js_parse_expect(s, ')'); + break; + case TOK_FUNCTION: + js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, JS_NULL); + break; + case TOK_NULL: + emit_op(s, OP_null); + next_token(s); + break; + case TOK_THIS: + emit_op(s, OP_push_this); + next_token(s); + break; + case TOK_FALSE: + case TOK_TRUE: + emit_op(s, OP_push_false + (s->token.val == TOK_TRUE)); + next_token(s); + break; + case TOK_IDENT: + { + JSFunctionBytecode *b; + JSValue name; + int var_idx, arg_count, opcode; + + b = JS_VALUE_TO_PTR(s->cur_func); + arg_count = b->arg_count; + + name = s->token.value; + + var_idx = find_var(s, name); + if (var_idx >= 0) { + if (var_idx < arg_count) { + opcode = OP_get_arg; + } else { + opcode = OP_get_loc; + var_idx -= arg_count; + } + } else { + var_idx = find_ext_var(s, name); + if (var_idx < 0) { + var_idx = add_ext_var(s, name, (JS_VARREF_KIND_GLOBAL << 16) | 0); + } + opcode = OP_get_var_ref; + } + emit_var(s, opcode, var_idx, s->token.source_pos); + next_token(s); + } + break; + case '{': + { + JSValue name; + int prop_idx, prop_type, count_pos; + BOOL has_proto; + + next_token(s); + emit_op(s, OP_object); + count_pos = s->byte_code_len; + emit_u16(s, 0); + + has_proto = FALSE; + while (s->token.val != '}') { + prop_type = js_parse_property_name(s, &name); + if (prop_type == PARSE_PROP_FIELD && + name == js_get_atom(s->ctx, JS_ATOM___proto__)) { + if (has_proto) + js_parse_error(s, "duplicate __proto__ property name"); + has_proto = TRUE; + prop_idx = -1; + } else { + uint8_t *byte_code; + int count; + prop_idx = cpool_add(s, name); + /* increment the count */ + byte_code = get_byte_code(s); + count = get_u16(byte_code + count_pos); + put_u16(byte_code + count_pos, min_int(count + 1, 0xffff)); + } + if (prop_type == PARSE_PROP_FIELD) { + js_parse_expect(s, ':'); + PARSE_CALL_SAVE4(s, 1, js_parse_assign_expr, 0, prop_idx, parse_flags, has_proto, count_pos); + if (prop_idx >= 0) { + emit_op(s, OP_define_field); + emit_u16(s, prop_idx); + } else { + emit_op(s, OP_set_proto); + } + } else { + /* getter/setter/method */ + js_parse_function_decl(s, JS_PARSE_FUNC_METHOD, name); + if (prop_type == PARSE_PROP_METHOD) + emit_op(s, OP_define_field); + else if (prop_type == PARSE_PROP_GET) + emit_op(s, OP_define_getter); + else + emit_op(s, OP_define_setter); + emit_u16(s, prop_idx); + } + if (s->token.val != ',') + break; + next_token(s); + } + js_parse_expect(s, '}'); + } + break; + case '[': + { + uint32_t idx; + + next_token(s); + /* small regular arrays are created on the stack */ + idx = 0; + while (s->token.val != ']' && idx < 32) { + /* SPEC: we don't accept empty elements */ + PARSE_CALL_SAVE2(s, 2, js_parse_assign_expr, 0, idx, parse_flags); + idx++; + /* accept trailing comma */ + if (s->token.val == ',') { + next_token(s); + } else if (s->token.val != ']') { + goto done; + } + } + + emit_op_param(s, OP_array_from, idx, s->pc2line_source_pos); + + while (s->token.val != ']') { + if (idx >= JS_SHORTINT_MAX) + js_parse_error(s, "too many elements"); + emit_op(s, OP_dup); + emit_push_short_int(s, idx); + PARSE_CALL_SAVE2(s, 3, js_parse_assign_expr, 0, idx, parse_flags); + emit_op(s, OP_put_array_el); + idx++; + /* accept trailing comma */ + if (s->token.val == ',') { + next_token(s); + } + } + done: + js_parse_expect(s, ']'); + } + break; + case TOK_NEW: + next_token(s); + if (s->token.val == '.') { + next_token(s); + if (s->token.val != TOK_IDENT || + s->token.value != js_get_atom(s->ctx, JS_ATOM_target)) { + js_parse_error(s, "expecting target"); + } + next_token(s); + emit_op(s, OP_new_target); + } else { + PARSE_CALL_SAVE1(s, 4, js_parse_postfix_expr, 0, parse_flags); + if (s->token.val != '(') { + /* new operator on an object */ + emit_op_param(s, OP_call_constructor, 0, s->token.source_pos); + } else { + is_new = TRUE; + break; + } + } + break; + default: + js_parse_error(s, "unexpected character in expression"); + } + + for(;;) { + if (s->token.val == '(' && (parse_flags & PF_ACCEPT_LPAREN)) { + int opcode, arg_count; + uint8_t *byte_code; + JSSourcePos op_source_pos; + + /* function call */ + op_source_pos = s->token.source_pos; + next_token(s); + + if (!is_new) { + opcode = get_prev_opcode(s); + byte_code = get_byte_code(s); + switch(opcode) { + case OP_get_field: + byte_code[s->last_opcode_pos] = OP_get_field2; + break; + case OP_get_length: + byte_code[s->last_opcode_pos] = OP_get_length2; + break; + case OP_get_array_el: + byte_code[s->last_opcode_pos] = OP_get_array_el2; + break; + case OP_get_var_ref: + { + int var_idx = get_u16(byte_code + s->last_opcode_pos + 1); + if (get_ext_var_name(s, var_idx) == js_get_atom(s->ctx, JS_ATOM_eval)) { + js_parse_error(s, "direct eval is not supported. Use (1,eval) instead for indirect eval"); + } + } + /* fall thru */ + default: + opcode = OP_invalid; + break; + } + } else { + opcode = OP_invalid; + } + + arg_count = 0; + if (s->token.val != ')') { + for(;;) { + if (arg_count >= JS_MAX_ARGC) + js_parse_error(s, "too many call arguments"); + arg_count++; + PARSE_CALL_SAVE5(s, 5, js_parse_assign_expr, 0, + parse_flags, arg_count, opcode, is_new, op_source_pos); + if (s->token.val == ')') + break; + js_parse_expect(s, ','); + } + } + next_token(s); + if (opcode == OP_get_field || + opcode == OP_get_length || + opcode == OP_get_array_el) { + emit_op_param(s, OP_call_method, arg_count, op_source_pos); + } else { + if (is_new) { + emit_op_param(s, OP_call_constructor, arg_count, op_source_pos); + } else { + emit_op_param(s, OP_call, arg_count, op_source_pos); + } + } + is_new = FALSE; + } else if (s->token.val == '.') { + JSSourcePos op_source_pos; + int prop_idx; + + op_source_pos = s->token.source_pos; + next_token(s); + if (!(s->token.val == TOK_IDENT || s->token.val >= TOK_FIRST_KEYWORD)) { + js_parse_error(s, "expecting field name"); + } + /* we ensure that no numeric property is used with + OP_get_field to enable some optimizations. The only + possible identifiers are NaN and Infinity */ + if (s->token.value == js_get_atom(s->ctx, JS_ATOM_NaN) || + s->token.value == js_get_atom(s->ctx, JS_ATOM_Infinity)) { + js_emit_push_const(s, s->token.value); + emit_op_pos(s, OP_get_array_el, op_source_pos); + } else if (s->token.value == js_get_atom(s->ctx, JS_ATOM_length)) { + emit_op_pos(s, OP_get_length, op_source_pos); + } else { + prop_idx = cpool_add(s, s->token.value); + emit_op_pos(s, OP_get_field, op_source_pos); + emit_u16(s, prop_idx); + } + next_token(s); + } else if (s->token.val == '[') { + JSSourcePos op_source_pos; + op_source_pos = s->token.source_pos; + next_token(s); + PARSE_CALL_SAVE3(s, 6, js_parse_expr_comma, 0, + parse_flags, is_new, op_source_pos); + js_parse_expect(s, ']'); + emit_op_pos(s, OP_get_array_el, op_source_pos); + } else if (!s->got_lf && (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { + int opcode, op, var_idx; + JSSourcePos op_source_pos, source_pos; + + op = s->token.val; + op_source_pos = s->token.source_pos; + next_token(s); + get_lvalue(s, &opcode, &var_idx, &source_pos, TRUE); + if (may_drop_result(s, parse_flags)) { + s->dropped_result = TRUE; + emit_op_pos(s, OP_dec + op - TOK_DEC, op_source_pos); + put_lvalue(s, opcode, var_idx, source_pos, PUT_LVALUE_NOKEEP_TOP); + } else { + emit_op_pos(s, OP_post_dec + op - TOK_DEC, op_source_pos); + put_lvalue(s, opcode, var_idx, source_pos, PUT_LVALUE_KEEP_SECOND); + } + } else { + break; + } + } + return PARSE_STATE_RET; +} + +static void js_emit_delete(JSParseState *s) +{ + int opcode; + + opcode = get_prev_opcode(s); + switch(opcode) { + case OP_get_field: + { + JSByteArray *byte_code; + int prop_idx; + byte_code = JS_VALUE_TO_PTR(s->byte_code); + prop_idx = get_u16(byte_code->buf + s->last_opcode_pos + 1); + remove_last_op(s); + emit_op(s, OP_push_const); + emit_u16(s, prop_idx); + } + break; + case OP_get_length: + remove_last_op(s); + js_emit_push_const(s, js_get_atom(s->ctx, JS_ATOM_length)); + break; + case OP_get_array_el: + remove_last_op(s); + break; + default: + js_parse_error(s, "invalid lvalue for delete"); + } + emit_op(s, OP_delete); +} + +static int js_parse_unary(JSParseState *s, int state, int parse_flags) +{ + PARSE_START7(); + + switch(s->token.val) { + case '+': + case '-': + case '!': + case '~': + { + int op; + JSSourcePos op_source_pos; + + op = s->token.val; + op_source_pos = s->token.source_pos; + next_token(s); + + /* XXX: could handle more cases */ + if (s->token.val == TOK_NUMBER && (op == '-' || op == '+')) { + double d = s->token.u.d; + if (op == '-') + d = -d; + js_emit_push_number(s, d); + next_token(s); + } else { + PARSE_CALL_SAVE2(s, 0, js_parse_unary, 0, op, op_source_pos); + switch(op) { + case '-': + emit_op_pos(s, OP_neg, op_source_pos); + break; + case '+': + emit_op_pos(s, OP_plus, op_source_pos); + break; + case '!': + emit_op_pos(s, OP_lnot, op_source_pos); + break; + case '~': + emit_op_pos(s, OP_not, op_source_pos); + break; + default: + abort(); + } + } + } + break; + case TOK_VOID: + next_token(s); + PARSE_CALL(s, 1, js_parse_unary, 0); + emit_op(s, OP_drop); + emit_op(s, OP_undefined); + break; + case TOK_DEC: + case TOK_INC: + { + int opcode, op, var_idx; + PutLValueEnum special; + JSSourcePos op_source_pos, source_pos; + + op = s->token.val; + op_source_pos = s->token.source_pos; + next_token(s); + PARSE_CALL_SAVE3(s, 2, js_parse_unary, 0, op, parse_flags, op_source_pos); + get_lvalue(s, &opcode, &var_idx, &source_pos, TRUE); + emit_op_pos(s, OP_dec + op - TOK_DEC, op_source_pos); + + if (may_drop_result(s, parse_flags)) { + special = PUT_LVALUE_NOKEEP_TOP; + s->dropped_result = TRUE; + } else { + special = PUT_LVALUE_KEEP_TOP; + } + put_lvalue(s, opcode, var_idx, source_pos, special); + } + break; + case TOK_TYPEOF: + { + next_token(s); + PARSE_CALL(s, 3, js_parse_unary, 0); + /* access to undefined variable should not return an + exception, so we patch the get_var */ + if (get_prev_opcode(s) == OP_get_var_ref) { + uint8_t *byte_code = get_byte_code(s); + byte_code[s->last_opcode_pos] = OP_get_var_ref_nocheck; + } + emit_op(s, OP_typeof); + } + break; + case TOK_DELETE: + next_token(s); + PARSE_CALL(s, 4, js_parse_unary, 0); + js_emit_delete(s); + break; + default: + PARSE_CALL(s, 5, js_parse_postfix_expr, parse_flags | PF_ACCEPT_LPAREN); + /* XXX: we do not follow the ES7 grammar in order to have a + * more natural expression */ + if (s->token.val == TOK_POW) { + JSSourcePos op_source_pos; + op_source_pos = s->token.source_pos; + next_token(s); + PARSE_CALL_SAVE1(s, 6, js_parse_unary, 0, op_source_pos); + emit_op_pos(s, OP_pow, op_source_pos); + } + break; + } + return PARSE_STATE_RET; +} + +static int js_parse_expr_binary(JSParseState *s, int state, int parse_flags) +{ + int op, opcode, level; + JSSourcePos op_source_pos; + + PARSE_START3(); + level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT; + if (level == 0) { + PARSE_CALL(s, 0, js_parse_unary, parse_flags); + return PARSE_STATE_RET; + } + PARSE_CALL_SAVE1(s, 1, js_parse_expr_binary, parse_flags - (1 << PF_LEVEL_SHIFT), parse_flags); + parse_flags &= ~PF_DROP; + for(;;) { + op = s->token.val; + op_source_pos = s->token.source_pos; + level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT; + switch(level) { + case 1: + switch(op) { + case '*': + opcode = OP_mul; + break; + case '/': + opcode = OP_div; + break; + case '%': + opcode = OP_mod; + break; + default: + return PARSE_STATE_RET; + } + break; + case 2: + switch(op) { + case '+': + opcode = OP_add; + break; + case '-': + opcode = OP_sub; + break; + default: + return PARSE_STATE_RET; + } + break; + case 3: + switch(op) { + case TOK_SHL: + opcode = OP_shl; + break; + case TOK_SAR: + opcode = OP_sar; + break; + case TOK_SHR: + opcode = OP_shr; + break; + default: + return PARSE_STATE_RET; + } + break; + case 4: + switch(op) { + case '<': + opcode = OP_lt; + break; + case '>': + opcode = OP_gt; + break; + case TOK_LTE: + opcode = OP_lte; + break; + case TOK_GTE: + opcode = OP_gte; + break; + case TOK_INSTANCEOF: + opcode = OP_instanceof; + break; + case TOK_IN: + if (!(parse_flags & PF_NO_IN)) { + opcode = OP_in; + } else { + return PARSE_STATE_RET; + } + break; + default: + return PARSE_STATE_RET; + } + break; + case 5: + switch(op) { + case TOK_EQ: + opcode = OP_eq; + break; + case TOK_NEQ: + opcode = OP_neq; + break; + case TOK_STRICT_EQ: + opcode = OP_strict_eq; + break; + case TOK_STRICT_NEQ: + opcode = OP_strict_neq; + break; + default: + return PARSE_STATE_RET; + } + break; + case 6: + switch(op) { + case '&': + opcode = OP_and; + break; + default: + return PARSE_STATE_RET; + } + break; + case 7: + switch(op) { + case '^': + opcode = OP_xor; + break; + default: + return PARSE_STATE_RET; + } + break; + case 8: + switch(op) { + case '|': + opcode = OP_or; + break; + default: + return PARSE_STATE_RET; + } + break; + default: + abort(); + } + next_token(s); + PARSE_CALL_SAVE3(s, 2, js_parse_expr_binary, parse_flags - (1 << PF_LEVEL_SHIFT), parse_flags, opcode, op_source_pos); + emit_op_pos(s, opcode, op_source_pos); + } + return PARSE_STATE_RET; +} + +static int js_parse_logical_and_or(JSParseState *s, int state, int parse_flags) +{ + JSValue label1; + int level, op; + + PARSE_START3(); + level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT; + if (level == 0) { + PARSE_CALL(s, 0, js_parse_expr_binary, (parse_flags & ~PF_LEVEL_MASK) | (8 << PF_LEVEL_SHIFT)); + return PARSE_STATE_RET; + } + + PARSE_CALL_SAVE1(s, 1, js_parse_logical_and_or, parse_flags - (1 << PF_LEVEL_SHIFT), parse_flags); + + level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT; + if (level == 1) + op = TOK_LAND; + else + op = TOK_LOR; + parse_flags &= ~PF_DROP; + if (s->token.val == op) { + label1 = new_label(s); + + for(;;) { + next_token(s); + emit_op(s, OP_dup); + emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, &label1); + emit_op(s, OP_drop); + + PARSE_PUSH_VAL(s, label1); + PARSE_CALL_SAVE1(s, 2, js_parse_logical_and_or, parse_flags - (1 << PF_LEVEL_SHIFT), parse_flags); + PARSE_POP_VAL(s, label1); + + level = (parse_flags & PF_LEVEL_MASK) >> PF_LEVEL_SHIFT; + if (level == 1) + op = TOK_LAND; + else + op = TOK_LOR; + + if (s->token.val != op) + break; + } + + emit_label(s, &label1); + } + return PARSE_STATE_RET; +} + +static int js_parse_cond_expr(JSParseState *s, int state, int parse_flags) +{ + JSValue label1, label2; + + PARSE_START3(); + + PARSE_CALL_SAVE1(s, 2, js_parse_logical_and_or, parse_flags | (2 << PF_LEVEL_SHIFT), parse_flags); + + parse_flags &= ~PF_DROP; + if (s->token.val == '?') { + next_token(s); + label1 = new_label(s); + emit_goto(s, OP_if_false, &label1); + + PARSE_PUSH_VAL(s, label1); + PARSE_CALL_SAVE1(s, 0, js_parse_assign_expr, parse_flags, + parse_flags); + PARSE_POP_VAL(s, label1); + + label2 = new_label(s); + emit_goto(s, OP_goto, &label2); + + js_parse_expect(s, ':'); + + emit_label(s, &label1); + + PARSE_PUSH_VAL(s, label2); + PARSE_CALL_SAVE1(s, 1, js_parse_assign_expr, parse_flags, + parse_flags); + PARSE_POP_VAL(s, label2); + + emit_label(s, &label2); + } + return PARSE_STATE_RET; +} + +static int js_parse_assign_expr(JSParseState *s, int state, int parse_flags) +{ + int opcode, op, var_idx; + PutLValueEnum special; + JSSourcePos op_source_pos, source_pos; + + PARSE_START2(); + + PARSE_CALL_SAVE1(s, 1, js_parse_cond_expr, parse_flags, parse_flags); + + op = s->token.val; + if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_OR_ASSIGN)) { + op_source_pos = s->token.source_pos; + next_token(s); + get_lvalue(s, &opcode, &var_idx, &source_pos, (op != '=')); + + PARSE_CALL_SAVE6(s, 0, js_parse_assign_expr, parse_flags & ~PF_DROP, + op, opcode, var_idx, parse_flags, + op_source_pos, source_pos); + + if (op != '=') { + static const uint8_t assign_opcodes[] = { + OP_mul, OP_div, OP_mod, OP_add, OP_sub, + OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or, + OP_pow, + }; + emit_op_pos(s, assign_opcodes[op - TOK_MUL_ASSIGN], op_source_pos); + } + + if (may_drop_result(s, parse_flags)) { + special = PUT_LVALUE_NOKEEP_TOP; + s->dropped_result = TRUE; + } else { + special = PUT_LVALUE_KEEP_TOP; + } + put_lvalue(s, opcode, var_idx, source_pos, special); + } + return PARSE_STATE_RET; +} + +static int js_parse_expr_comma(JSParseState *s, int state, int parse_flags) +{ + BOOL comma = FALSE; + + PARSE_START1(); + + for(;;) { + s->dropped_result = FALSE; + PARSE_CALL_SAVE2(s, 0, js_parse_assign_expr, parse_flags, + comma, parse_flags); + if (comma) { + /* prevent get_lvalue from using the last expression as an + lvalue. */ + s->last_opcode_pos = -1; + } + if (s->token.val != ',') + break; + comma = TRUE; + if (!s->dropped_result) + emit_op(s, OP_drop); + next_token(s); + } + if ((parse_flags & PF_DROP) && !s->dropped_result) { + emit_op(s, OP_drop); + } + return PARSE_STATE_RET; +} + +static void js_parse_assign_expr2(JSParseState *s, int parse_flags) +{ + js_parse_call(s, PARSE_FUNC_js_parse_assign_expr, parse_flags); +} + +static void js_parse_expr2(JSParseState *s, int parse_flags) +{ + js_parse_call(s, PARSE_FUNC_js_parse_expr_comma, parse_flags); +} + +static void js_parse_expr(JSParseState *s) +{ + js_parse_expr2(s, 0); +} + +static void js_parse_expr_paren(JSParseState *s) +{ + js_parse_expect(s, '('); + js_parse_expr(s); + js_parse_expect(s, ')'); +} + +static BlockEnv *push_break_entry(JSParseState *s, JSValue label_name, + JSValue label_break, JSValue label_cont, + int drop_count) +{ + JSContext *ctx = s->ctx; + JSGCRef label_name_ref; + int ret, block_env_len; + BlockEnv *be; + + block_env_len = sizeof(BlockEnv) / sizeof(JSValue); + JS_PUSH_VALUE(ctx, label_name); + ret = JS_StackCheck(ctx, block_env_len); + JS_POP_VALUE(ctx, label_name); + if (ret) + js_parse_error_stack_overflow(s); + ctx->sp -= block_env_len; + be = (BlockEnv *)ctx->sp; + be->prev = s->top_break; + s->top_break = SP_TO_VALUE(ctx, be); + be->label_name = label_name; + be->label_break = label_break; + be->label_cont = label_cont; + be->label_finally = LABEL_NONE; + be->drop_count = JS_NewShortInt(drop_count); + return be; +} + +static void pop_break_entry(JSParseState *s) +{ + JSContext *ctx = s->ctx; + BlockEnv *be; + + be = VALUE_TO_SP(ctx, s->top_break); + s->top_break = be->prev; + ctx->sp += sizeof(BlockEnv) / sizeof(JSValue); + ctx->stack_bottom = ctx->sp; +} + +static void emit_return(JSParseState *s, BOOL hasval, JSSourcePos source_pos) +{ + JSValue top_val; + BlockEnv *top; + int i, drop_count; + + drop_count = 0; + top_val = s->top_break; + while (!JS_IsNull(top_val)) { + top = VALUE_TO_SP(s->ctx, top_val); + /* no need to drop if no "finally" */ + drop_count += JS_VALUE_GET_INT(top->drop_count); + + if (!label_is_none(top->label_finally)) { + if (!hasval) { + emit_op(s, OP_undefined); + hasval = TRUE; + } + for(i = 0; i < drop_count; i++) + emit_op(s, OP_nip); /* must keep the stack stop */ + drop_count = 0; + /* execute the "finally" block */ + emit_goto(s, OP_gosub, &top->label_finally); + } + top_val = top->prev; + } + emit_op_pos(s, hasval ? OP_return : OP_return_undef, source_pos); +} + +static void emit_break(JSParseState *s, JSValue label_name, int is_cont) +{ + JSValue top_val; + BlockEnv *top; + int i; + JSValue *plabel; + JSGCRef label_name_ref; + BOOL is_labelled_stmt; + + top_val = s->top_break; + while (!JS_IsNull(top_val)) { + top = VALUE_TO_SP(s->ctx, top_val); + is_labelled_stmt = (top->label_cont == LABEL_NONE && + JS_VALUE_GET_INT(top->drop_count) == 0); + if ((label_name == JS_NULL && !is_labelled_stmt) || + top->label_name == label_name) { + if (is_cont) + plabel = &top->label_cont; + else + plabel = &top->label_break; + if (!label_is_none(*plabel)) { + emit_goto(s, OP_goto, plabel); + return; + } + } + JS_PUSH_VALUE(s->ctx, label_name); + for(i = 0; i < JS_VALUE_GET_INT(top->drop_count); i++) + emit_op(s, OP_drop); + if (!label_is_none(top->label_finally)) { + /* must push dummy value to keep same stack depth */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, &top->label_finally); + emit_op(s, OP_drop); + } + JS_POP_VALUE(s->ctx, label_name); + top_val = top->prev; + } + if (label_name == JS_NULL) { + if (is_cont) + js_parse_error(s, "continue must be inside loop"); + else + js_parse_error(s, "break must be inside loop or switch"); + } else { + js_parse_error(s, "break/continue label not found"); + } +} + +static int define_var(JSParseState *s, JSVarRefKindEnum *pvar_kind, JSValue name) +{ + JSVarRefKindEnum var_kind; + int var_idx; + + if (s->is_eval) { + var_idx = find_ext_var(s, name); + if (var_idx < 0) { + var_idx = add_ext_var(s, name, (JS_VARREF_KIND_GLOBAL << 16) | 1); + } else { + JSFunctionBytecode *b = JS_VALUE_TO_PTR(s->cur_func); + JSValueArray *arr = JS_VALUE_TO_PTR(b->ext_vars); + arr->arr[2 * var_idx + 1] = JS_NewShortInt((JS_VARREF_KIND_GLOBAL << 16) | 1); + } + var_kind = JS_VARREF_KIND_VAR_REF; + } else { + JSFunctionBytecode *b; + int arg_count; + + b = JS_VALUE_TO_PTR(s->cur_func); + arg_count = b->arg_count; + + var_idx = find_var(s, name); + if (var_idx >= 0) { + if (var_idx < arg_count) { + var_kind = JS_VARREF_KIND_ARG; + } else { + var_kind = JS_VARREF_KIND_VAR; + var_idx -= arg_count; + } + } else { + var_idx = add_var(s, name); + var_kind = JS_VARREF_KIND_VAR; + var_idx -= arg_count; + } + } + *pvar_kind = var_kind; + return var_idx; +} + +static void put_var(JSParseState *s, JSVarRefKindEnum var_kind, int var_idx, JSSourcePos source_pos) +{ + int opcode; + if (var_kind == JS_VARREF_KIND_ARG) + opcode = OP_put_arg; + else if (var_kind == JS_VARREF_KIND_VAR) + opcode = OP_put_loc; + else + opcode = OP_put_var_ref_nocheck; + emit_var(s, opcode, var_idx, source_pos); +} + +static void js_parse_var(JSParseState *s, BOOL in_accepted) +{ + JSVarRefKindEnum var_kind; + int var_idx; + JSSourcePos ident_source_pos; + + for(;;) { + ident_source_pos = s->token.source_pos; + if (s->token.val != TOK_IDENT) + js_parse_error(s, "variable name expected"); + if (s->token.value == js_get_atom(s->ctx, JS_ATOM_arguments)) + js_parse_error(s, "invalid variable name"); + var_idx = define_var(s, &var_kind, s->token.value); + next_token(s); + if (s->token.val == '=') { + next_token(s); + js_parse_assign_expr2(s, in_accepted ? 0 : PF_NO_IN); + put_var(s, var_kind, var_idx, ident_source_pos); + } + if (s->token.val != ',') + break; + next_token(s); + } +} + +static void set_eval_ret_undefined(JSParseState *s) +{ + if (s->eval_ret_idx >= 0) { + emit_op(s, OP_undefined); + emit_var(s, OP_put_loc, s->eval_ret_idx, s->pc2line_source_pos); + } +} + +static int js_parse_block(JSParseState *s, int state, int dummy_param) +{ + PARSE_START1(); + js_parse_expect(s, '{'); + if (s->token.val != '}') { + for(;;) { + PARSE_CALL(s, 0, js_parse_statement, 0); + if (s->token.val == '}') + break; + } + } + next_token(s); + return PARSE_STATE_RET; +} + +/* The statement parser assumes that the stack contains the result of + the last statement. Note: if not in eval code, the return value of + a statement does not matter */ +static int js_parse_statement(JSParseState *s, int state, int dummy_param) +{ + JSValue label_name; + JSGCRef label_name_ref; + + PARSE_START12(); + + /* specific label handling */ + if (is_label(s)) { + JSValue top_val; + BlockEnv *top; + + label_name = s->token.value; + JS_PUSH_VALUE(s->ctx, label_name); + next_token(s); + js_parse_expect(s, ':'); + JS_POP_VALUE(s->ctx, label_name); + + for(top_val = s->top_break; !JS_IsNull(top_val); top_val = top->prev) { + top = VALUE_TO_SP(s->ctx, top_val); + if (top->label_name == label_name) + js_parse_error(s, "duplicate label name"); + } + + if (s->token.val != TOK_FOR && + s->token.val != TOK_DO && + s->token.val != TOK_WHILE) { + /* labelled regular statement */ + BlockEnv *be; + push_break_entry(s, label_name, new_label(s), LABEL_NONE, 0); + + PARSE_CALL(s, 11, js_parse_statement, 0); + + be = VALUE_TO_SP(s->ctx, s->top_break); + emit_label(s, &be->label_break); + pop_break_entry(s); + goto done; + } + } else { + label_name = JS_NULL; + } + + switch(s->token.val) { + case '{': + PARSE_CALL(s, 0, js_parse_block, 0); + break; + case TOK_RETURN: + { + BOOL has_val; + JSSourcePos op_source_pos; + if (s->is_eval) + js_parse_error(s, "return not in a function"); + op_source_pos = s->token.source_pos; + next_token(s); + if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { + js_parse_expr(s); + has_val = TRUE; + } else { + has_val = FALSE; + } + emit_return(s, has_val, op_source_pos); + js_parse_expect_semi(s); + } + break; + case TOK_THROW: + { + JSSourcePos op_source_pos; + op_source_pos = s->token.source_pos; + next_token(s); + if (s->got_lf) + js_parse_error(s, "line terminator not allowed after throw"); + js_parse_expr(s); + emit_op_pos(s, OP_throw, op_source_pos); + js_parse_expect_semi(s); + } + break; + case TOK_VAR: + next_token(s); + js_parse_var(s, TRUE); + js_parse_expect_semi(s); + break; + case TOK_IF: + { + JSValue label1, label2; + next_token(s); + set_eval_ret_undefined(s); + js_parse_expr_paren(s); + label1 = new_label(s); + emit_goto(s, OP_if_false, &label1); + + PARSE_PUSH_VAL(s, label1); + PARSE_CALL(s, 1, js_parse_statement, 0); + PARSE_POP_VAL(s, label1); + + if (s->token.val == TOK_ELSE) { + next_token(s); + + label2 = new_label(s); + emit_goto(s, OP_goto, &label2); + + emit_label(s, &label1); + + PARSE_PUSH_VAL(s, label2); + PARSE_CALL(s, 2, js_parse_statement, 0); + PARSE_POP_VAL(s, label2); + + label1 = label2; + } + emit_label(s, &label1); + } + break; + case TOK_WHILE: + { + BlockEnv *be; + + be = push_break_entry(s, label_name, new_label(s), new_label(s), 0); + next_token(s); + + set_eval_ret_undefined(s); + + emit_label(s, &be->label_cont); + js_parse_expr_paren(s); + emit_goto(s, OP_if_false, &be->label_break); + + PARSE_CALL(s, 3, js_parse_statement, 0); + + be = VALUE_TO_SP(s->ctx, s->top_break); + emit_goto(s, OP_goto, &be->label_cont); + + emit_label(s, &be->label_break); + + pop_break_entry(s); + } + break; + case TOK_DO: + { + JSValue label1; + BlockEnv *be; + + be = push_break_entry(s, label_name, new_label(s), new_label(s), 0); + + label1 = new_label(s); + + next_token(s); + set_eval_ret_undefined(s); + + emit_label(s, &label1); + + PARSE_PUSH_VAL(s, label1); + PARSE_CALL(s, 4, js_parse_statement, 0); + PARSE_POP_VAL(s, label1); + + be = VALUE_TO_SP(s->ctx, s->top_break); + emit_label(s, &be->label_cont); + js_parse_expect(s, TOK_WHILE); + js_parse_expr_paren(s); + /* Insert semicolon if missing */ + if (s->token.val == ';') { + next_token(s); + } + emit_goto(s, OP_if_true, &label1); + + emit_label(s, &be->label_break); + + pop_break_entry(s); + } + break; + case TOK_FOR: + { + int bits; + BlockEnv *be; + + be = push_break_entry(s, label_name, new_label(s), new_label(s), 0); + + next_token(s); + set_eval_ret_undefined(s); + + js_parse_expect1(s, '('); + bits = js_parse_skip_parens_token(s); + next_token(s); + + if (!(bits & SKIP_HAS_SEMI)) { + JSValue label_expr, label_body, label_next; + int opcode, var_idx; + + be->drop_count = JS_NewShortInt(1); + + label_expr = new_label(s); + label_body = new_label(s); + label_next = new_label(s); + + emit_goto(s, OP_goto, &label_expr); + + emit_label(s, &label_next); + + if (s->token.val == TOK_VAR) { + JSVarRefKindEnum var_kind; + next_token(s); + var_idx = define_var(s, &var_kind, s->token.value); + put_var(s, var_kind, var_idx, s->pc2line_source_pos); + + next_token(s); + } else { + JSSourcePos source_pos; + + /* XXX: js_parse_left_hand_side_expr */ + js_parse_assign_expr2(s, PF_NO_IN); + + get_lvalue(s, &opcode, &var_idx, &source_pos, FALSE); + put_lvalue(s, opcode, var_idx, source_pos, + PUT_LVALUE_NOKEEP_BOTTOM); + } + + emit_goto(s, OP_goto, &label_body); + + if (s->token.val == TOK_IN) { + opcode = OP_for_in_start; + } else if (s->token.val == TOK_IDENT && + s->token.value == js_get_atom(s->ctx, JS_ATOM_of)) { + opcode = OP_for_of_start; + } else { + js_parse_error(s, "expected 'of' or 'in' in for control expression"); + } + + next_token(s); + + emit_label(s, &label_expr); + js_parse_expr(s); + emit_op(s, opcode); + + emit_goto(s, OP_goto, &be->label_cont); + + js_parse_expect(s, ')'); + + emit_label(s, &label_body); + + PARSE_PUSH_VAL(s, label_next); + PARSE_CALL(s, 5, js_parse_statement, 0); + PARSE_POP_VAL(s, label_next); + + be = VALUE_TO_SP(s->ctx, s->top_break); + emit_label(s, &be->label_cont); + emit_op(s, OP_for_of_next); + + /* on stack: enum_rec / enum_obj value bool */ + emit_goto(s, OP_if_false, &label_next); + /* drop the undefined value from for_xx_next */ + emit_op(s, OP_drop); + + emit_label(s, &be->label_break); + emit_op(s, OP_drop); + } else { + JSValue label_test; + JSParsePos expr3_pos; + int tmp_val; + + /* initial expression */ + if (s->token.val != ';') { + if (s->token.val == TOK_VAR) { + next_token(s); + js_parse_var(s, FALSE); + } else { + js_parse_expr2(s, PF_NO_IN | PF_DROP); + } + } + js_parse_expect(s, ';'); + + label_test = new_label(s); + + /* test expression */ + emit_label(s, &label_test); + if (s->token.val != ';') { + js_parse_expr(s); + emit_goto(s, OP_if_false, &be->label_break); + } + js_parse_expect(s, ';'); + + if (s->token.val != ')') { + /* skip the third expression if present */ + js_parse_get_pos(s, &expr3_pos); + js_skip_expr(s); + } else { + expr3_pos.source_pos = -1; + expr3_pos.got_lf = 0; /* avoid warning */ + expr3_pos.regexp_allowed = 0; /* avoid warning */ + } + js_parse_expect(s, ')'); + + PARSE_PUSH_VAL(s, label_test); + PARSE_PUSH_INT(s, expr3_pos.got_lf | (expr3_pos.regexp_allowed << 1)); + PARSE_PUSH_INT(s, expr3_pos.source_pos); + PARSE_CALL(s, 6, js_parse_statement, 0); + PARSE_POP_INT(s, expr3_pos.source_pos); + PARSE_POP_INT(s, tmp_val); + expr3_pos.got_lf = tmp_val & 1; + expr3_pos.regexp_allowed = tmp_val >> 1; + PARSE_POP_VAL(s, label_test); + + be = VALUE_TO_SP(s->ctx, s->top_break); + emit_label(s, &be->label_cont); + + /* parse the third expression, if present, after the + statement */ + if (expr3_pos.source_pos != -1) { + JSParsePos end_pos; + js_parse_get_pos(s, &end_pos); + js_parse_seek_token(s, &expr3_pos); + js_parse_expr2(s, PF_DROP); + js_parse_seek_token(s, &end_pos); + } + + emit_goto(s, OP_goto, &label_test); + + be = VALUE_TO_SP(s->ctx, s->top_break); + emit_label(s, &be->label_break); + } + pop_break_entry(s); + } + break; + case TOK_BREAK: + case TOK_CONTINUE: + { + int is_cont = (s->token.val == TOK_CONTINUE); + JSValue label_name; + + next_token(s); + if (!s->got_lf && s->token.val == TOK_IDENT) + label_name = s->token.value; + else + label_name = JS_NULL; + emit_break(s, label_name, is_cont); + if (label_name != JS_NULL) { + next_token(s); + } + js_parse_expect_semi(s); + } + break; + case TOK_SWITCH: + { + JSValue label_case; + int default_label_pos; + BlockEnv *be; + + be = push_break_entry(s, label_name, new_label(s), LABEL_NONE, 1); + + next_token(s); + set_eval_ret_undefined(s); + + js_parse_expr_paren(s); + + js_parse_expect(s, '{'); + default_label_pos = -1; + label_case = LABEL_NONE; /* label to the next case */ + while (s->token.val != '}') { + if (s->token.val == TOK_CASE) { + JSValue label1 = LABEL_NONE; + if (!label_is_none(label_case)) { + /* skip the case if needed */ + label1 = new_label(s); + emit_goto(s, OP_goto, &label1); + emit_label(s, &label_case); + label_case = LABEL_NONE; + } + for (;;) { + /* parse a sequence of case clauses */ + next_token(s); + emit_op(s, OP_dup); + js_parse_expr(s); + js_parse_expect(s, ':'); + emit_op(s, OP_strict_eq); + if (s->token.val == TOK_CASE) { + if (label_is_none(label1)) + label1 = new_label(s); + emit_goto(s, OP_if_true, &label1); + } else { + label_case = new_label(s); + emit_goto(s, OP_if_false, &label_case); + if (!label_is_none(label1)) + emit_label(s, &label1); + break; + } + } + } else if (s->token.val == TOK_DEFAULT) { + next_token(s); + js_parse_expect(s, ':'); + if (default_label_pos >= 0) + js_parse_error(s, "duplicate default"); + if (label_is_none(label_case)) { + /* falling thru direct from switch expression */ + label_case = new_label(s); + emit_goto(s, OP_goto, &label_case); + } + default_label_pos = s->byte_code_len; + } else { + if (label_is_none(label_case)) + js_parse_error(s, "invalid switch statement"); + PARSE_PUSH_VAL(s, label_case); + PARSE_CALL_SAVE1(s, 7, js_parse_statement, 0, + default_label_pos); + PARSE_POP_VAL(s, label_case); + } + } + js_parse_expect(s, '}'); + if (default_label_pos >= 0) { + /* patch the default label */ + emit_label_pos(s, &label_case, default_label_pos); + } else if (!label_is_none(label_case)) { + emit_label(s, &label_case); + } + be = VALUE_TO_SP(s->ctx, s->top_break); + emit_label(s, &be->label_break); + emit_op(s, OP_drop); /* drop the switch expression */ + + pop_break_entry(s); + } + break; + case TOK_TRY: + { + JSValue label_catch, label_finally, label_end; + BlockEnv *be; + + set_eval_ret_undefined(s); + next_token(s); + label_catch = new_label(s); + label_finally = new_label(s); + + emit_goto(s, OP_catch, &label_catch); + + be = push_break_entry(s, JS_NULL, LABEL_NONE, LABEL_NONE, 1); + be->label_finally = label_finally; + + PARSE_PUSH_VAL(s, label_catch); + PARSE_CALL(s, 8, js_parse_block, 0); + PARSE_POP_VAL(s, label_catch); + + be = VALUE_TO_SP(s->ctx, s->top_break); + label_finally = be->label_finally; + pop_break_entry(s); + + /* drop the catch offset */ + emit_op(s, OP_drop); + + /* must push dummy value to keep same stack size */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, &label_finally); + emit_op(s, OP_drop); + + label_end = new_label(s); + emit_goto(s, OP_goto, &label_end); + + if (s->token.val == TOK_CATCH) { + JSValue label_catch2; + int var_idx; + JSValue name; + + label_catch2 = new_label(s); + + next_token(s); + js_parse_expect(s, '('); + if (s->token.val != TOK_IDENT) + js_parse_error(s, "identifier expected"); + name = s->token.value; + /* XXX: the local scope is not implemented, so we add + a normal variable */ + if (find_var(s, name) >= 0 || find_ext_var(s, name) >= 0) { + js_parse_error(s, "catch variable already exists"); + } + var_idx = add_var(s, name); + next_token(s); + js_parse_expect(s, ')'); + + /* store the exception value in the variable */ + emit_label(s, &label_catch); + { + JSFunctionBytecode *b = JS_VALUE_TO_PTR(s->cur_func); + emit_var(s, OP_put_loc, var_idx - b->arg_count, s->pc2line_source_pos); + } + + emit_goto(s, OP_catch, &label_catch2); + + be = push_break_entry(s, JS_NULL, LABEL_NONE, LABEL_NONE, 1); + be->label_finally = label_finally; + + PARSE_PUSH_VAL(s, label_end); + PARSE_PUSH_VAL(s, label_catch2); + PARSE_CALL(s, 9, js_parse_block, 0); + PARSE_POP_VAL(s, label_catch2); + PARSE_POP_VAL(s, label_end); + + be = VALUE_TO_SP(s->ctx, s->top_break); + label_finally = be->label_finally; + pop_break_entry(s); + + /* drop the catch2 offset */ + emit_op(s, OP_drop); + /* must push dummy value to keep same stack size */ + emit_op(s, OP_undefined); + emit_goto(s, OP_gosub, &label_finally); + emit_op(s, OP_drop); + emit_goto(s, OP_goto, &label_end); + + /* catch exceptions thrown in the catch block to execute the + * finally clause and rethrow the exception */ + emit_label(s, &label_catch2); + /* catch value is at TOS, no need to push undefined */ + emit_goto(s, OP_gosub, &label_finally); + emit_op(s, OP_throw); + + } else if (s->token.val == TOK_FINALLY) { + /* finally without catch : execute the finally clause + * and rethrow the exception */ + emit_label(s, &label_catch); + /* catch value is at TOS, no need to push undefined */ + emit_goto(s, OP_gosub, &label_finally); + emit_op(s, OP_throw); + } else { + js_parse_error(s, "expecting catch or finally"); + } + + emit_label(s, &label_finally); + if (s->token.val == TOK_FINALLY) { + next_token(s); + /* XXX: we don't return the correct value in eval() */ + /* on the stack: ret_value gosub_ret_value */ + push_break_entry(s, JS_NULL, LABEL_NONE, LABEL_NONE, 2); + + PARSE_PUSH_VAL(s, label_end); + PARSE_CALL(s, 10, js_parse_block, 0); + PARSE_POP_VAL(s, label_end); + + pop_break_entry(s); + } + emit_op(s, OP_ret); + emit_label(s, &label_end); + } + break; + case ';': + /* empty statement */ + next_token(s); + break; + default: + if (s->eval_ret_idx >= 0) { + /* store the expression value so that it can be returned + by eval() */ + js_parse_expr(s); + emit_var(s, OP_put_loc, s->eval_ret_idx, s->pc2line_source_pos); + } else { + js_parse_expr2(s, PF_DROP); + } + js_parse_expect_semi(s); + break; + } + done: + return PARSE_STATE_RET; +} + +static JSParseFunc *parse_func_table[] = { + js_parse_expr_comma, + js_parse_assign_expr, + js_parse_cond_expr, + js_parse_logical_and_or, + js_parse_expr_binary, + js_parse_unary, + js_parse_postfix_expr, + js_parse_statement, + js_parse_block, + js_parse_json_value, + re_parse_alternative, + re_parse_disjunction, +}; + +static void js_parse_source_element(JSParseState *s) +{ + if (s->token.val == TOK_FUNCTION) { + js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT, JS_NULL); + } else { + js_parse_call(s, PARSE_FUNC_js_parse_statement, 0); + } +} + +static JSFunctionBytecode *js_alloc_function_bytecode(JSContext *ctx) +{ + JSFunctionBytecode *b; + b = js_mallocz(ctx, sizeof(JSFunctionBytecode), JS_MTAG_FUNCTION_BYTECODE); + if (!b) + return NULL; + b->func_name = JS_NULL; + b->byte_code = JS_NULL; + b->cpool = JS_NULL; + b->vars = JS_NULL; + b->ext_vars = JS_NULL; + b->filename = JS_NULL; + b->pc2line = JS_NULL; + return b; +} + +/* the current token must be TOK_FUNCTION for JS_PARSE_FUNC_STATEMENT + or JS_PARSE_FUNC_EXPR. Otherwise it is '('. */ +static void js_parse_function_decl(JSParseState *s, + JSParseFunctionEnum func_type, JSValue func_name) +{ + JSContext *ctx = s->ctx; + BOOL is_expr; + JSFunctionBytecode *b; + int idx, skip_bits; + JSVarRefKindEnum var_kind; + JSValue bfunc; + JSGCRef func_name_ref, bfunc_ref; + + is_expr = (func_type != JS_PARSE_FUNC_STATEMENT); + + if (func_type == JS_PARSE_FUNC_STATEMENT || + func_type == JS_PARSE_FUNC_EXPR) { + next_token(s); + if (s->token.val != TOK_IDENT && !is_expr) + js_parse_error(s, "function name expected"); + if (s->token.val == TOK_IDENT) { + func_name = s->token.value; + JS_PUSH_VALUE(ctx, func_name); + next_token(s); + JS_POP_VALUE(ctx, func_name); + } + } + + JS_PUSH_VALUE(ctx, func_name); + b = js_alloc_function_bytecode(s->ctx); + if (!b) + js_parse_error_mem(s); + bfunc = JS_VALUE_FROM_PTR(b); + JS_PUSH_VALUE(ctx, bfunc); + + b->filename = s->filename_str; + b->func_name = func_name_ref.val; + b->source_pos = s->token.source_pos; + b->has_column = s->has_column; + + js_parse_expect1(s, '('); + /* skip the arguments */ + js_skip_parens(s, NULL); + + js_parse_expect1(s, '{'); + + /* skip the code */ + skip_bits = js_skip_parens(s, is_expr ? &func_name_ref.val : NULL); + + b = JS_VALUE_TO_PTR(bfunc_ref.val); + b->has_arguments = ((skip_bits & SKIP_HAS_ARGUMENTS) != 0); + b->has_local_func_name = ((skip_bits & SKIP_HAS_FUNC_NAME) != 0); + + idx = cpool_add(s, bfunc_ref.val); + if (is_expr) { + /* create the function object */ + emit_op(s, OP_fclosure); + emit_u16(s, idx); + } else { + idx = define_var(s, &var_kind, func_name_ref.val); + /* size of hoisted for OP_fclosure + OP_put_loc/OP_put_arg/OP_put_ref */ + s->hoisted_code_len += 3 + 3; + if (var_kind == JS_VARREF_KIND_VAR) { + b = JS_VALUE_TO_PTR(s->cur_func); + idx += b->arg_count; + } + b = JS_VALUE_TO_PTR(bfunc_ref.val); + /* hoisted function definition: save the variable index to + define it at the start of the function */ + b->arg_count = idx + 1; + } + JS_POP_VALUE(ctx, bfunc); + JS_POP_VALUE(ctx, func_name); +} + +static void define_hoisted_functions(JSParseState *s, BOOL is_eval) +{ + JSValueArray *cpool; + JSValue val; + JSFunctionBytecode *b; + int idx, saved_byte_code_len, arg_count, i, op; + + /* add pc2line info */ + b = JS_VALUE_TO_PTR(s->cur_func); + if (b->pc2line != JS_NULL) { + int h, n; + + /* byte align */ + n = (-s->pc2line_bit_len) & 7; + if (n != 0) + pc2line_put_bits(s, n, 0); + + n = s->hoisted_code_len; + h = 0; + for(;;) { + pc2line_put_bits(s, 8, (n & 0x7f) | h); + n >>= 7; + if (n == 0) + break; + h |= 0x80; + } + } + + if (s->hoisted_code_len == 0) + return; + emit_insert(s, 0, s->hoisted_code_len); + + b = JS_VALUE_TO_PTR(s->cur_func); + arg_count = b->arg_count; + + saved_byte_code_len = s->byte_code_len; + s->byte_code_len = 0; + cpool = JS_VALUE_TO_PTR(b->cpool); + for(i = 0; i < s->cpool_len; i++) { + val = cpool->arr[i]; + if (JS_IsPtr(val)) { + b = JS_VALUE_TO_PTR(val); + if (b->mtag == JS_MTAG_FUNCTION_BYTECODE && + b->arg_count != 0) { + idx = b->arg_count - 1; + /* XXX: could use smaller opcodes */ + if (is_eval) { + op = OP_put_var_ref_nocheck; + } else if (idx < arg_count) { + op = OP_put_arg; + } else { + idx -= arg_count; + op = OP_put_loc; + } + /* no realloc possible here */ + emit_u8(s, OP_fclosure); + emit_u16(s, i); + + emit_u8(s, op); + emit_u16(s, idx); + } + } + } + s->byte_code_len = saved_byte_code_len; +} + +static void js_parse_function(JSParseState *s) +{ + JSFunctionBytecode *b; + int arg_count; + + next_token(s); + + js_parse_expect(s, '('); + + while (s->token.val != ')') { + JSValue name; + /* XXX: gc */ + if (s->token.val != TOK_IDENT) + js_parse_error(s, "missing formal parameter"); + name = s->token.value; + if (name == js_get_atom(s->ctx, JS_ATOM_eval) || + name == js_get_atom(s->ctx, JS_ATOM_arguments)) { + js_parse_error(s, "invalid argument name"); + } + if (find_var(s, name) >= 0) + js_parse_error(s, "duplicate argument name"); + add_var(s, name); + next_token(s); + if (s->token.val == ')') + break; + js_parse_expect(s, ','); + } + b = JS_VALUE_TO_PTR(s->cur_func); + arg_count = b->arg_count = s->local_vars_len; + + next_token(s); + + js_parse_expect(s, '{'); + + /* initialize the arguments */ + b = JS_VALUE_TO_PTR(s->cur_func); + if (b->has_arguments) { + int var_idx; + var_idx = add_var(s, js_get_atom(s->ctx, JS_ATOM_arguments)); + emit_op(s, OP_arguments); + put_var(s, JS_VARREF_KIND_VAR, var_idx - arg_count, s->pc2line_source_pos); + } + + /* XXX: initialize the function name */ + b = JS_VALUE_TO_PTR(s->cur_func); + if (b->has_local_func_name) { + int var_idx; + /* XXX: */ + var_idx = add_var(s, b->func_name); + emit_op(s, OP_this_func); + put_var(s, JS_VARREF_KIND_VAR, var_idx - arg_count, s->pc2line_source_pos); + } + + while (s->token.val != '}') { + js_parse_source_element(s); + } + + if (js_is_live_code(s)) + emit_op(s, OP_return_undef); + + next_token(s); + + define_hoisted_functions(s, FALSE); + + /* save the bytecode to the function */ + b = JS_VALUE_TO_PTR(s->cur_func); + b->byte_code = s->byte_code; +} + +static void js_parse_program(JSParseState *s) +{ + JSFunctionBytecode *b; + + next_token(s); + + /* hidden variable for the return value */ + if (s->has_retval) { + s->eval_ret_idx = add_var(s, js_get_atom(s->ctx, JS_ATOM__ret_)); + } + + while (s->token.val != TOK_EOF) { + js_parse_source_element(s); + } + + if (s->eval_ret_idx >= 0) { + emit_var(s, OP_get_loc, s->eval_ret_idx, s->pc2line_source_pos); + emit_op(s, OP_return); + } else { + emit_op(s, OP_return_undef); + } + + define_hoisted_functions(s, TRUE); + + /* save the bytecode to the function */ + b = JS_VALUE_TO_PTR(s->cur_func); + b->byte_code = s->byte_code; +} + +#define CVT_VAR_SIZE_MAX 16 + +typedef struct { + uint16_t new_var_idx; /* new local var index */ + uint8_t is_local; +} ConvertVarEntry; + +static void convert_ext_vars_to_local_vars_bytecode(JSParseState *s, + uint8_t *byte_code, int byte_code_len, + int var_start, const ConvertVarEntry *cvt_tab, + int tab_len) +{ + int pos, var_end, j, op, var_idx; + const JSOpCode *oi; + + var_end = var_start + tab_len; + pos = 0; + while (pos < byte_code_len) { + op = byte_code[pos]; + oi = &opcode_info[op]; + switch(op) { + case OP_get_var_ref: + case OP_put_var_ref: + case OP_get_var_ref_nocheck: + case OP_put_var_ref_nocheck: + var_idx = get_u16(byte_code + pos + 1); + if (var_idx >= var_start && var_idx < var_end) { + j = var_idx - var_start; + put_u16(byte_code + pos + 1, cvt_tab[j].new_var_idx); + if (cvt_tab[j].is_local) { + if (op == OP_get_var_ref || op == OP_get_var_ref_nocheck) { + byte_code[pos] = OP_get_loc; + } else { + byte_code[pos] = OP_put_loc; + } + } + } + break; + default: + break; + } + pos += oi->size; + } +} + +/* no allocation */ +static void convert_ext_vars_to_local_vars(JSParseState *s) +{ + JSValueArray *ext_vars; + JSFunctionBytecode *b; + JSByteArray *bc_arr; + JSValue var_name, decl; + int i0, i, j, var_idx, l; + ConvertVarEntry cvt_tab[CVT_VAR_SIZE_MAX]; + + b = JS_VALUE_TO_PTR(s->cur_func); + if (s->local_vars_len == 0 || b->ext_vars_len == 0) + return; + bc_arr = JS_VALUE_TO_PTR(b->byte_code); + ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + + /* do it by parts to save memory */ + j = 0; + for(i0 = 0; i0 < b->ext_vars_len; i0 += CVT_VAR_SIZE_MAX) { + l = min_int(b->ext_vars_len - i0, CVT_VAR_SIZE_MAX); + for(i = 0; i < l; i++) { + var_name = ext_vars->arr[2 * (i0 + i)]; + decl = ext_vars->arr[2 * (i0 + i) + 1]; + var_idx = find_var(s, var_name); + /* fail safe: we avoid arguments even if they cannot appear */ + if (var_idx >= b->arg_count) { + cvt_tab[i].new_var_idx = var_idx - b->arg_count; + cvt_tab[i].is_local = TRUE; + } else { + cvt_tab[i].new_var_idx = j; + cvt_tab[i].is_local = FALSE; + ext_vars->arr[2 * j] = var_name; + ext_vars->arr[2 * j + 1] = decl; + j++; + } + } + if (j != (i0 + l)) { + convert_ext_vars_to_local_vars_bytecode(s, bc_arr->buf, s->byte_code_len, + i0, cvt_tab, l); + } + } + b->ext_vars_len = j; +} + +/* prepare the analysis of the code starting at position 'pos' */ +static void compute_stack_size_push(JSParseState *s, + JSByteArray *arr, + uint8_t *explore_tab, + uint32_t pos, int stack_len) +{ + int short_stack_len; + +#if 0 + js_printf(s->ctx, "%5d: %d\n", pos, stack_len); +#endif + if (pos >= (uint32_t)arr->size) + js_parse_error(s, "bytecode buffer overflow (pc=%d)", pos); + /* XXX: could avoid the division */ + short_stack_len = 1 + ((unsigned)stack_len % 255); + if (explore_tab[pos] != 0) { + /* already explored: check that the stack size is consistent */ + if (explore_tab[pos] != short_stack_len) { + js_parse_error(s, "inconsistent stack size: %d %d (pc=%d)", explore_tab[pos] - 1, short_stack_len - 1, (int)pos); + } + } else { + explore_tab[pos] = short_stack_len; + /* may initiate a GC */ + PARSE_PUSH_INT(s, pos); + PARSE_PUSH_INT(s, stack_len); + } +} + +static void compute_stack_size(JSParseState *s, JSValue *pfunc) +{ + JSContext *ctx = s->ctx; + JSByteArray *explore_arr, *arr; + JSFunctionBytecode *b; + uint8_t *explore_tab; + JSValue *stack_top, explore_arr_val; + uint32_t pos; + int op, op_len, pos1, n_pop, stack_len; + const JSOpCode *oi; + JSGCRef explore_arr_val_ref; + + b = JS_VALUE_TO_PTR(*pfunc); + arr = JS_VALUE_TO_PTR(b->byte_code); + + explore_arr = js_alloc_byte_array(s->ctx, arr->size); + if (!explore_arr) + js_parse_error_mem(s); + + b = JS_VALUE_TO_PTR(*pfunc); + arr = JS_VALUE_TO_PTR(b->byte_code); + + explore_arr_val = JS_VALUE_FROM_PTR(explore_arr); + explore_tab = explore_arr->buf; + memset(explore_tab, 0, arr->size); + + JS_PUSH_VALUE(ctx, explore_arr_val); + + stack_top = ctx->sp; + + compute_stack_size_push(s, arr, explore_tab, 0, 0); + + while (ctx->sp < stack_top) { + PARSE_POP_INT(s, stack_len); + PARSE_POP_INT(s, pos); + + /* compute_stack_size_push may have initiated a GC */ + b = JS_VALUE_TO_PTR(*pfunc); + arr = JS_VALUE_TO_PTR(b->byte_code); + explore_arr = JS_VALUE_TO_PTR(explore_arr_val_ref.val); + explore_tab = explore_arr->buf; + + op = arr->buf[pos++]; + if (op == OP_invalid || op >= OP_COUNT) + js_parse_error(s, "invalid opcode (pc=%d)", (int)(pos - 1)); + oi = &opcode_info[op]; + op_len = oi->size; + if ((pos + op_len - 1) > arr->size) { + js_parse_error(s, "bytecode buffer overflow (pc=%d)", (int)(pos - 1)); + } + n_pop = oi->n_pop; + if (oi->fmt == OP_FMT_npop) + n_pop += get_u16(arr->buf + pos); + + if (stack_len < n_pop) { + js_parse_error(s, "stack underflow (pc=%d)", (int)(pos - 1)); + } + stack_len += oi->n_push - n_pop; + if (stack_len > b->stack_size) { + if (stack_len > JS_MAX_FUNC_STACK_SIZE) + js_parse_error(s, "stack overflow (pc=%d)", (int)(pos - 1)); + b->stack_size = stack_len; + } + switch(op) { + case OP_return: + case OP_return_undef: + case OP_throw: + case OP_ret: + goto done; /* no code after */ + case OP_goto: + pos += get_u32(arr->buf + pos); + break; + case OP_if_true: + case OP_if_false: + pos1 = pos + get_u32(arr->buf + pos); + compute_stack_size_push(s, arr, explore_tab, pos1, stack_len); + pos += op_len - 1; + break; + case OP_gosub: + pos1 = pos + get_u32(arr->buf + pos); + compute_stack_size_push(s, arr, explore_tab, pos1, stack_len + 1); + pos += op_len - 1; + break; + default: + pos += op_len - 1; + break; + } + compute_stack_size_push(s, arr, explore_tab, pos, stack_len); + done: ; + } + + JS_POP_VALUE(ctx, explore_arr_val); + explore_arr = JS_VALUE_TO_PTR(explore_arr_val); + js_free(s->ctx, explore_arr); +} + +static void resolve_var_refs(JSParseState *s, JSValue *pfunc, JSValue *pparent_func) +{ + JSContext *ctx = s->ctx; + int i, decl, var_idx, arg_count, ext_vars_len; + JSValueArray *ext_vars; + JSValue var_name; + JSFunctionBytecode *b1, *b; + + b = JS_VALUE_TO_PTR(*pfunc); + if (b->ext_vars_len == 0) + return; + b1 = JS_VALUE_TO_PTR(*pparent_func); + arg_count = b1->arg_count; + + ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + ext_vars_len = b->ext_vars_len; + + for(i = 0; i < ext_vars_len; i++) { + b = JS_VALUE_TO_PTR(*pfunc); + ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + var_name = ext_vars->arr[2 * i]; + var_idx = find_func_var(ctx, *pparent_func, var_name); + if (var_idx >= 0) { + if (var_idx < arg_count) { + decl = (JS_VARREF_KIND_ARG << 16) | var_idx; + } else { + decl = (JS_VARREF_KIND_VAR << 16) | (var_idx - arg_count); + } + } else { + var_idx = find_func_ext_var(s, *pparent_func, var_name); + if (var_idx < 0) { + /* the global type may be patched later */ + var_idx = add_func_ext_var(s, *pparent_func, var_name, + (JS_VARREF_KIND_GLOBAL << 16)); + } + decl = (JS_VARREF_KIND_VAR_REF << 16) | var_idx; + } + b = JS_VALUE_TO_PTR(*pfunc); + ext_vars = JS_VALUE_TO_PTR(b->ext_vars); + ext_vars->arr[2 * i + 1] = JS_NewShortInt(decl); + } +} + +static void reset_parse_state(JSParseState *s, uint32_t input_pos, + JSValue cur_func) +{ + s->buf_pos = input_pos; + s->token.val = ' '; + + s->cur_func = cur_func; + s->byte_code = JS_NULL; + s->byte_code_len = 0; + s->last_opcode_pos = -1; + + s->pc2line_bit_len = 0; + s->pc2line_source_pos = 0; + + s->cpool_len = 0; + s->hoisted_code_len = 0; + + s->local_vars_len = 0; + + s->eval_ret_idx = -1; +} + +static void js_parse_local_functions(JSParseState *s, JSValue *pfunc) +{ + JSContext *ctx = s->ctx; + JSValue *pparent_func; + JSValueArray *cpool; + int err, cpool_pos; + JSValue func; + JSFunctionBytecode *b, *b1; + JSGCRef func_ref; + JSValue *stack_top; + + err = JS_StackCheck(ctx, 3); + if (err) + js_parse_error_stack_overflow(s); + stack_top = ctx->sp; + + *--ctx->sp = JS_NULL; /* parent_func */ + *--ctx->sp = *pfunc; /* func */ + *--ctx->sp = JS_NewShortInt(0); /* cpool_pos */ + + while (ctx->sp < stack_top) { + pparent_func = &ctx->sp[2]; + pfunc = &ctx->sp[1]; + cpool_pos = JS_VALUE_GET_INT(ctx->sp[0]); +#if 0 + JS_DumpValue(ctx, "func", *pfunc); + JS_DumpValue(ctx, "parent", *pparent_func); + JS_DumpValue(ctx, "cpool_pos", ctx->sp[0]); +#endif + if (cpool_pos == 0) { + b = JS_VALUE_TO_PTR(*pfunc); + + convert_ext_vars_to_local_vars(s); + + js_shrink_byte_array(ctx, &b->byte_code, s->byte_code_len); + js_shrink_value_array(ctx, &b->cpool, s->cpool_len); + js_shrink_value_array(ctx, &b->vars, s->local_vars_len); + js_shrink_byte_array(ctx, &b->pc2line, (s->pc2line_bit_len + 7) / 8); + + compute_stack_size(s, pfunc); + } + + b = JS_VALUE_TO_PTR(*pfunc); + if (b->cpool != JS_NULL) { + int cpool_size; + cpool = JS_VALUE_TO_PTR(b->cpool); + cpool_size = cpool->size; + for(; cpool_pos < cpool_size; cpool_pos++) { + b = JS_VALUE_TO_PTR(*pfunc); + cpool = JS_VALUE_TO_PTR(b->cpool); + func = cpool->arr[cpool_pos]; + if (!JS_IsPtr(func)) + continue; + b1 = JS_VALUE_TO_PTR(func); + if (b1->mtag != JS_MTAG_FUNCTION_BYTECODE) + continue; + + reset_parse_state(s, b1->source_pos, func); + + s->is_eval = FALSE; + s->is_repl = FALSE; + s->has_retval = FALSE; + + JS_PUSH_VALUE(ctx, func); + js_parse_function(s); + + /* parse a local function */ + err = JS_StackCheck(ctx, 3); + JS_POP_VALUE(ctx, func); + if (err) + js_parse_error_stack_overflow(s); + /* set the next cpool position */ + *ctx->sp = JS_NewShortInt(cpool_pos + 1); + + *--ctx->sp = *pfunc; /* parent_func */ + *--ctx->sp = func; /* func */ + *--ctx->sp = JS_NewShortInt(0); /* cpool_pos */ + goto next; + } + } + + if (*pparent_func != JS_NULL) { + resolve_var_refs(s, pfunc, pparent_func); + } + /* now we can shrink the external vars */ + b = JS_VALUE_TO_PTR(*pfunc); + js_shrink_value_array(ctx, &b->ext_vars, 2 * b->ext_vars_len); +#ifdef DUMP_FUNC_BYTECODE + dump_byte_code(ctx, b); +#endif + /* remove the stack entry */ + ctx->sp += 3; + ctx->stack_bottom = ctx->sp; + next: ; + } +} + +/* return the parsed value in s->token.value */ +/* XXX: use exact JSON white space definition */ +static int js_parse_json_value(JSParseState *s, int state, int dummy_param) +{ + JSContext *ctx = s->ctx; + const uint8_t *p; + JSValue val; + + PARSE_START2(); + + p = s->source_buf + s->buf_pos; + p += skip_spaces((const char *)p); + s->buf_pos = p - s->source_buf; + if ((*p >= '0' && *p <= '9') || *p == '-') { + double d; + JSByteArray *tmp_arr; + tmp_arr = js_alloc_byte_array(s->ctx, sizeof(JSATODTempMem)); + if (!tmp_arr) + js_parse_error_mem(s); + p = s->source_buf + s->buf_pos; + d = js_atod((const char *)p, (const char **)&p, 10, 0, + (JSATODTempMem *)tmp_arr->buf); + js_free(s->ctx, tmp_arr); + if (isnan(d)) + js_parse_error(s, "invalid number literal"); + val = JS_NewFloat64(s->ctx, d); + } else if (*p == 't' && + p[1] == 'r' && p[2] == 'u' && p[3] == 'e') { + p += 4; + val = JS_TRUE; + } else if (*p == 'f' && + p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[4] == 'e') { + p += 5; + val = JS_FALSE; + } else if (*p == 'n' && + p[1] == 'u' && p[2] == 'l' && p[3] == 'l') { + p += 4; + val = JS_NULL; + } else if (*p == '\"') { + uint32_t pos; + pos = p + 1 - s->source_buf; + val = js_parse_string(s, &pos, '\"'); + p = s->source_buf + pos; + } else if (*p == '[') { + JSValue val2; + uint32_t idx; + + val = JS_NewArray(ctx, 0); + if (JS_IsException(val)) + js_parse_error_mem(s); + PARSE_PUSH_VAL(s, val); /* 'val' is not usable after this call */ + p = s->source_buf + s->buf_pos + 1; + p += skip_spaces((const char *)p); + if (*p != ']') { + idx = 0; + for(;;) { + s->buf_pos = p - s->source_buf; + PARSE_PUSH_INT(s, idx); + PARSE_CALL(s, 0, js_parse_json_value, 0); + PARSE_POP_INT(s, idx); + val2 = s->token.value; + val2 = JS_SetPropertyUint32(ctx, *ctx->sp, idx, val2); + if (JS_IsException(val2)) + js_parse_error_mem(s); + idx++; + p = s->source_buf + s->buf_pos; + p += skip_spaces((const char *)p); + if (*p != ',') + break; + p++; + } + } + if (*p != ']') + js_parse_error(s, "expecting ']'"); + p++; + PARSE_POP_VAL(s, val); + } else if (*p == '{') { + JSValue val2, prop; + uint32_t pos; + + val = JS_NewObject(ctx); + if (JS_IsException(val)) + js_parse_error_mem(s); + PARSE_PUSH_VAL(s, val); /* 'val' is not usable after this call */ + p = s->source_buf + s->buf_pos + 1; + p += skip_spaces((const char *)p); + if (*p != '}') { + for(;;) { + p += skip_spaces((const char *)p); + s->buf_pos = p - s->source_buf; + if (*p != '\"') + js_parse_error(s, "expecting '\"'"); + pos = p + 1 - s->source_buf; + prop = js_parse_string(s, &pos, '\"'); + prop = JS_ToPropertyKey(ctx, prop); + if (JS_IsException(prop)) + js_parse_error_mem(s); + p = s->source_buf + pos; + p += skip_spaces((const char *)p); + if (*p != ':') + js_parse_error(s, "expecting ':'"); + p++; + s->buf_pos = p - s->source_buf; + PARSE_PUSH_VAL(s, prop); + PARSE_CALL(s, 1, js_parse_json_value, 0); + val2 = s->token.value; + PARSE_POP_VAL(s, prop); + val2 = JS_DefinePropertyValue(ctx, *ctx->sp, prop, val2); + if (JS_IsException(val2)) + js_parse_error_mem(s); + p = s->source_buf + s->buf_pos; + p += skip_spaces((const char *)p); + if (*p != ',') + break; + p++; + } + } + if (*p != '}') + js_parse_error(s, "expecting '}'"); + p++; + PARSE_POP_VAL(s, val); + } else { + js_parse_error(s, "unexpected character"); + } + s->buf_pos = p - s->source_buf; + s->token.value = val; + return PARSE_STATE_RET; +} + +static JSValue js_parse_json(JSParseState *s) +{ + s->buf_pos = 0; + js_parse_call(s, PARSE_FUNC_js_parse_json_value, 0); + s->buf_pos += skip_spaces((const char *)(s->source_buf + s->buf_pos)); + if (s->buf_pos != s->buf_len) { + js_parse_error(s, "unexpected character"); + } + return s->token.value; +} + +/* source_str must be a string or JS_NULL. (input, input_len) is + meaningful only if source_str is JS_NULL. */ +static JSValue JS_Parse2(JSContext *ctx, JSValue source_str, + const char *input, size_t input_len, + const char *filename, int eval_flags) +{ + JSParseState parse_state, *s; + JSFunctionBytecode *b; + JSValue top_func, *saved_sp; + JSGCRef top_func_ref, *saved_top_gc_ref; + uint8_t str_buf[5]; + + /* XXX: start gc at the start of parsing ? */ + /* XXX: if the parse state is too large, move it to JSContext */ + s = &parse_state; + memset(s, 0, sizeof(*s)); + + s->ctx = ctx; + ctx->parse_state = s; + s->source_str = JS_NULL; + s->filename_str = JS_NULL; + s->has_column = ((eval_flags & JS_EVAL_STRIP_COL) == 0); + + if (JS_IsPtr(source_str)) { + JSString *p = JS_VALUE_TO_PTR(source_str); + s->source_str = source_str; + s->buf_len = p->len; + s->source_buf = p->buf; + } else if (JS_VALUE_GET_SPECIAL_TAG(source_str) == JS_TAG_STRING_CHAR) { + s->buf_len = get_short_string(str_buf, source_str); + s->source_buf = str_buf; + } else { + s->buf_len = input_len; + s->source_buf = (const uint8_t *)input; + } + s->top_break = JS_NULL; + saved_top_gc_ref = ctx->top_gc_ref; + saved_sp = ctx->sp; + + if (setjmp(s->jmp_env)) { + int line_num, col_num; + JSValue val; + + ctx->parse_state = NULL; + ctx->top_gc_ref = saved_top_gc_ref; + ctx->sp = saved_sp; + ctx->stack_bottom = ctx->sp; + + line_num = get_line_col(&col_num, s->source_buf, + (eval_flags & (JS_EVAL_JSON | JS_EVAL_REGEXP)) ? + s->buf_pos : s->token.source_pos); + val = JS_ThrowError(ctx, JS_CLASS_SYNTAX_ERROR, "%s", s->error_msg); + build_backtrace(ctx, ctx->current_exception, filename, line_num + 1, col_num + 1, 0); + return val; + } + + if (eval_flags & JS_EVAL_JSON) { + top_func = js_parse_json(s); + } else if (eval_flags & JS_EVAL_REGEXP) { + top_func = js_parse_regexp(s, eval_flags >> JS_EVAL_REGEXP_FLAGS_SHIFT); + } else { + s->filename_str = JS_NewString(ctx, filename); + if (JS_IsException(s->filename_str)) + js_parse_error_mem(s); + + b = js_alloc_function_bytecode(ctx); + if (!b) + js_parse_error_mem(s); + b->filename = s->filename_str; + b->func_name = js_get_atom(ctx, JS_ATOM__eval_); + b->has_column = s->has_column; + top_func = JS_VALUE_FROM_PTR(b); + + reset_parse_state(s, 0, top_func); + + s->is_eval = TRUE; + s->has_retval = ((eval_flags & JS_EVAL_RETVAL) != 0); + s->is_repl = ((eval_flags & JS_EVAL_REPL) != 0); + + JS_PUSH_VALUE(ctx, top_func); + + js_parse_program(s); + + js_parse_local_functions(s, &top_func_ref.val); + + JS_POP_VALUE(ctx, top_func); + } + ctx->parse_state = NULL; + return top_func; +} + +/* warning: it is assumed that input[input_len] = '\0' */ +JSValue JS_Parse(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags) +{ + return JS_Parse2(ctx, JS_NULL, input, input_len, filename, eval_flags); +} + +JSValue JS_Run(JSContext *ctx, JSValue val) +{ + JSFunctionBytecode *b; + JSGCRef val_ref; + int err; + + if (!JS_IsPtr(val)) + goto fail; + b = JS_VALUE_TO_PTR(val); + if (b->mtag != JS_MTAG_FUNCTION_BYTECODE) { + fail: + return JS_ThrowTypeError(ctx, "bytecode function expected"); + } + + val = js_closure(ctx, val, NULL); + if (JS_IsException(val)) + return val; + JS_PUSH_VALUE(ctx, val); + err = JS_StackCheck(ctx, 2); + JS_POP_VALUE(ctx, val); + if (err) + return JS_EXCEPTION; + JS_PushArg(ctx, val); + JS_PushArg(ctx, JS_NULL); + val = JS_Call(ctx, 0); + return val; +} + +/* warning: it is assumed that input[input_len] = '\0' */ +JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags) +{ + JSValue val; + val = JS_Parse(ctx, input, input_len, filename, eval_flags); + if (JS_IsException(val)) + return val; + return JS_Run(ctx, val); +} + +/**********************************************************************/ +/* garbage collector */ + +/* return the size in bytes */ +static int get_mblock_size(const void *ptr) +{ + int mtag = ((JSMemBlockHeader *)ptr)->mtag; + int size; + switch(mtag) { + case JS_MTAG_OBJECT: + { + const JSObject *p = ptr; + size = offsetof(JSObject, u) + p->extra_size * JSW; + } + break; + case JS_MTAG_FLOAT64: + size = sizeof(JSFloat64); + break; + case JS_MTAG_STRING: + { + const JSString *p = ptr; + size = sizeof(JSString) + ((p->len + JSW) & ~(JSW - 1)); + } + break; + case JS_MTAG_BYTE_ARRAY: + { + const JSByteArray *p = ptr; + size = sizeof(JSByteArray) + ((p->size + JSW - 1) & ~(JSW - 1)); + } + break; + case JS_MTAG_VALUE_ARRAY: + { + const JSValueArray *p = ptr; + size = sizeof(JSValueArray) + p->size * sizeof(p->arr[0]); + } + break; + case JS_MTAG_FREE: + { + const JSFreeBlock *p = ptr; + size = sizeof(JSFreeBlock) + p->size * sizeof(JSWord); + } + break; + case JS_MTAG_VARREF: + { + const JSVarRef *p = ptr; + size = sizeof(JSVarRef); + if (p->is_detached) + size -= sizeof(JSValue); + } + break; + case JS_MTAG_FUNCTION_BYTECODE: + size = sizeof(JSFunctionBytecode); + break; + default: + size = 0; + assert(0); + } + return size; +} + +/* gc mark pass */ + +typedef struct { + JSContext *ctx; + JSValue *gsp; + JSValue *gs_bottom; + JSValue *gs_top; + BOOL overflow; +} GCMarkState; + +static BOOL mtag_has_references(int mtag) +{ + return (mtag == JS_MTAG_OBJECT || + mtag == JS_MTAG_VALUE_ARRAY || + mtag == JS_MTAG_VARREF || + mtag == JS_MTAG_FUNCTION_BYTECODE); +} + +static void gc_mark(GCMarkState *s, JSValue val) +{ + JSContext *ctx = s->ctx; + void *ptr; + JSMemBlockHeader *mb; + + if (!JS_IsPtr(val)) + return; + ptr = JS_VALUE_TO_PTR(val); + if (JS_IS_ROM_PTR(ctx, ptr)) + return; + mb = ptr; + if (mb->gc_mark) + return; + mb->gc_mark = 1; + if (mtag_has_references(mb->mtag)) { + if (mb->mtag == JS_MTAG_VALUE_ARRAY) { + /* value array are handled specifically to save stack space */ + if ((s->gsp - s->gs_bottom) < 2) { + s->overflow = TRUE; + } else { + *--s->gsp = 0; + *--s->gsp = val; + } + } else { + if ((s->gsp - s->gs_bottom) < 1) { + s->overflow = TRUE; + } else { + *--s->gsp = val; + } + } + } +} + +/* flush the GC mark stack */ +static void gc_mark_flush(GCMarkState *s) +{ + void *ptr; + JSMemBlockHeader *mb; + JSValue val; + + while (s->gsp < s->gs_top) { + val = *s->gsp++; + ptr = JS_VALUE_TO_PTR(val); + mb = ptr; + + switch(mb->mtag) { + case JS_MTAG_OBJECT: + { + const JSObject *p = ptr; + gc_mark(s, p->proto); + gc_mark(s, p->props); + switch(p->class_id) { + case JS_CLASS_CLOSURE: + { + int i; + gc_mark(s, p->u.closure.func_bytecode); + for(i = 0; i < p->extra_size - 1; i++) + gc_mark(s, p->u.closure.var_refs[i]); + } + break; + case JS_CLASS_C_FUNCTION: + if (p->extra_size > 1) + gc_mark(s, p->u.cfunc.params); + break; + case JS_CLASS_ARRAY: + gc_mark(s, p->u.array.tab); + break; + case JS_CLASS_ERROR: + gc_mark(s, p->u.error.message); + gc_mark(s, p->u.error.stack); + break; + case JS_CLASS_ARRAY_BUFFER: + gc_mark(s, p->u.array_buffer.byte_buffer); + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + gc_mark(s, p->u.typed_array.buffer); + break; + case JS_CLASS_REGEXP: + gc_mark(s, p->u.regexp.source); + gc_mark(s, p->u.regexp.byte_code); + break; + } + } + break; + case JS_MTAG_VALUE_ARRAY: + { + const JSValueArray *p = ptr; + int pos; + + pos = *s->gsp++; + + /* fast path to skip non objects */ + while (pos < p->size && !JS_IsPtr(p->arr[pos])) + pos++; + + if (pos < p->size) { + if ((pos + 1) < p->size) { + /* the next element needs to be scanned */ + *--s->gsp = pos + 1; + *--s->gsp = val; + } + /* mark the current element */ + gc_mark(s, p->arr[pos]); + } + } + break; + case JS_MTAG_VARREF: + { + const JSVarRef *p = ptr; + gc_mark(s, p->u.value); + } + break; + case JS_MTAG_FUNCTION_BYTECODE: + { + const JSFunctionBytecode *b = ptr; + gc_mark(s, b->func_name); + gc_mark(s, b->byte_code); + gc_mark(s, b->cpool); + gc_mark(s, b->vars); + gc_mark(s, b->ext_vars); + gc_mark(s, b->filename); + gc_mark(s, b->pc2line); + } + break; + default: + break; + } + } +} + +static void gc_mark_root(GCMarkState *s, JSValue val) +{ + gc_mark(s, val); + gc_mark_flush(s); +} + +/* return true if the memory block is marked i.e. it won't be freed by the GC */ +static BOOL gc_mb_is_marked(JSValue val) +{ + JSFreeBlock *b; + if (!JS_IsPtr(val)) + return FALSE; + b = (JSFreeBlock *)JS_VALUE_TO_PTR(val); + return b->gc_mark; +} + +static void gc_mark_all(JSContext *ctx, BOOL keep_atoms) +{ + GCMarkState s_s, *s = &s_s; + JSValue *sp, *sp_end; + + s->ctx = ctx; + /* initialize the GC stack */ + s->overflow = FALSE; + s->gs_top = ctx->sp; + s->gsp = s->gs_top; +#if 1 + s->gs_bottom = (JSValue *)ctx->heap_free; +#else + s->gs_bottom = s->gs_top - 3; /* TEST small stack space */ +#endif + + /* keep the atoms if they are in RAM (only used when compiling to file) */ + if ((uint8_t *)ctx->atom_table == ctx->heap_base && + keep_atoms) { + uint8_t *ptr; + for(ptr = (uint8_t *)ctx->atom_table; + ptr < (uint8_t *)(ctx->atom_table + JS_ATOM_END); + ptr += get_mblock_size(ptr)) { + gc_mark_root(s, JS_VALUE_FROM_PTR(ptr)); + } + } + + /* mark all the memory blocks */ + sp_end = ctx->class_proto + 2 * ctx->class_count; + for(sp = &ctx->current_exception; sp < sp_end; sp++) { + gc_mark_root(s, *sp); + } + + for(sp = ctx->sp; sp < (JSValue *)ctx->stack_top; sp++) { + gc_mark_root(s, *sp); + } + + { + JSGCRef *ref; + for(ref = ctx->top_gc_ref; ref != NULL; ref = ref->prev) { + gc_mark_root(s, ref->val); + } + for(ref = ctx->last_gc_ref; ref != NULL; ref = ref->prev) { + gc_mark_root(s, ref->val); + } + } + if (ctx->parse_state) { + JSParseState *ps = ctx->parse_state; + + gc_mark_root(s, ps->source_str); + gc_mark_root(s, ps->filename_str); + gc_mark_root(s, ps->token.value); + gc_mark_root(s, ps->cur_func); + gc_mark_root(s, ps->byte_code); + } + + /* if the mark stack overflowed, need to scan the heap */ + while (s->overflow) { + uint8_t *ptr; + int size; + JSMemBlockHeader *mb; + + s->overflow = FALSE; + + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + size = get_mblock_size(ptr); + mb = (JSMemBlockHeader *)ptr; + if (mb->gc_mark && mtag_has_references(mb->mtag)) { + if (mb->mtag == JS_MTAG_VALUE_ARRAY) + *--s->gsp = 0; + *--s->gsp = JS_VALUE_FROM_PTR(ptr); + gc_mark_flush(s); + } + ptr += size; + } + } + + /* update the unique string table (its elements are considered as + weak string references) */ + if (!JS_IsNull(ctx->unique_strings)) { + JSValueArray *arr = JS_VALUE_TO_PTR(ctx->unique_strings); + int i, j; + + j = 0; + for(i = 0; i < arr->size; i++) { + if (gc_mb_is_marked(arr->arr[i])) { + arr->arr[j++] = arr->arr[i]; + } + } + ctx->unique_strings_len = j; + if (j > 0) { + arr->gc_mark = 1; + if (j < arr->size) { + /* shrink the array */ + set_free_block(&arr->arr[j], (arr->size - j) * sizeof(JSValue)); + arr->size = j; + } + } else { + arr->gc_mark = 0; + ctx->unique_strings = JS_NULL; + } + } + + /* update the weak references in the string position cache */ + { + int i; + JSStringPosCacheEntry *ce; + for(i = 0; i < JS_STRING_POS_CACHE_SIZE; i++) { + ce = &ctx->string_pos_cache[i]; + if (!gc_mb_is_marked(ce->str)) + ce->str = JS_NULL; + } + } + + /* reset the gc marks and mark the free blocks as free */ + { + uint8_t *ptr, *ptr1; + int size; + JSFreeBlock *b; + + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + size = get_mblock_size(ptr); + b = (JSFreeBlock *)ptr; + if (b->gc_mark) { + b->gc_mark = 0; + } else { + JSObject *p = (void *)ptr; + /* call the user finalizer if needed */ + if (p->mtag == JS_MTAG_OBJECT && p->class_id >= JS_CLASS_USER && + ctx->c_finalizer_table[p->class_id - JS_CLASS_USER] != NULL) { + ctx->c_finalizer_table[p->class_id - JS_CLASS_USER](ctx, p->u.user.opaque); + } + /* merge all the consecutive free blocks */ + ptr1 = ptr + size; + while (ptr1 < ctx->heap_free && ((JSFreeBlock *)ptr1)->gc_mark == 0) { + ptr1 += get_mblock_size(ptr1); + } + size = ptr1 - ptr; + set_free_block(b, size); + } + ptr += size; + } + } +} + +static JSValue js_value_from_pval(JSContext *ctx, JSValue *pval) +{ + return JS_VALUE_FROM_PTR(pval); +} + +static JSValue *js_value_to_pval(JSContext *ctx, JSValue val) +{ + return JS_VALUE_TO_PTR(val); +} + +static void gc_thread_pointer(JSContext *ctx, JSValue *pval) +{ + JSValue val; + JSValue *ptr; + + val = *pval; + if (!JS_IsPtr(val)) + return; + ptr = JS_VALUE_TO_PTR(val); + if (JS_IS_ROM_PTR(ctx, ptr)) + return; + /* gc_mark = 0 indicates a normal memory block header, gc_mark = 1 + indicates a pointer to another element */ + *pval = *ptr; + *ptr = js_value_from_pval(ctx, pval); +} + +static void gc_update_threaded_pointers(JSContext *ctx, + void *ptr, void *new_ptr) +{ + JSValue val, *pv; + + val = *(JSValue *)ptr; + if (JS_IsPtr(val)) { + /* update the threaded pointers to the node 'ptr' and + unthread it. */ + for(;;) { + pv = js_value_to_pval(ctx, val); + val = *pv; + *pv = JS_VALUE_FROM_PTR(new_ptr); + if (!JS_IsPtr(val)) + break; + } + *(JSValue *)ptr = val; + } +} + +static void gc_thread_block(JSContext *ctx, void *ptr) +{ + int mtag; + + mtag = ((JSMemBlockHeader *)ptr)->mtag; + switch(mtag) { + case JS_MTAG_OBJECT: + { + JSObject *p = ptr; + gc_thread_pointer(ctx, &p->proto); + gc_thread_pointer(ctx, &p->props); + switch(p->class_id) { + case JS_CLASS_CLOSURE: + { + int i; + gc_thread_pointer(ctx, &p->u.closure.func_bytecode); + for(i = 0; i < p->extra_size - 1; i++) + gc_thread_pointer(ctx, &p->u.closure.var_refs[i]); + } + break; + case JS_CLASS_C_FUNCTION: + if (p->extra_size > 1) + gc_thread_pointer(ctx, &p->u.cfunc.params); + break; + case JS_CLASS_ARRAY: + gc_thread_pointer(ctx, &p->u.array.tab); + break; + case JS_CLASS_ERROR: + gc_thread_pointer(ctx, &p->u.error.message); + gc_thread_pointer(ctx, &p->u.error.stack); + break; + case JS_CLASS_ARRAY_BUFFER: + gc_thread_pointer(ctx, &p->u.array_buffer.byte_buffer); + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + gc_thread_pointer(ctx, &p->u.typed_array.buffer); + break; + case JS_CLASS_REGEXP: + gc_thread_pointer(ctx, &p->u.regexp.source); + gc_thread_pointer(ctx, &p->u.regexp.byte_code); + break; + } + } + break; + case JS_MTAG_VALUE_ARRAY: + { + JSValueArray *p = ptr; + int i; + for(i = 0; i < p->size; i++) { + gc_thread_pointer(ctx, &p->arr[i]); + } + } + break; + case JS_MTAG_VARREF: + { + JSVarRef *p = ptr; + gc_thread_pointer(ctx, &p->u.value); + } + break; + case JS_MTAG_FUNCTION_BYTECODE: + { + JSFunctionBytecode *b = ptr; + gc_thread_pointer(ctx, &b->func_name); + gc_thread_pointer(ctx, &b->byte_code); + gc_thread_pointer(ctx, &b->cpool); + gc_thread_pointer(ctx, &b->vars); + gc_thread_pointer(ctx, &b->ext_vars); + gc_thread_pointer(ctx, &b->filename); + gc_thread_pointer(ctx, &b->pc2line); + } + break; + default: + break; + } +} + +/* Heap compaction using Jonkers algorithm */ +static void gc_compact_heap(JSContext *ctx) +{ + uint8_t *ptr, *new_ptr; + int size; + JSValue *sp, *sp_end; + + /* thread all the external pointers */ + sp_end = ctx->class_proto + 2 * ctx->class_count; + for(sp = &ctx->unique_strings; sp < sp_end; sp++) { + gc_thread_pointer(ctx, sp); + } + { + int i; + JSStringPosCacheEntry *ce; + for(i = 0; i < JS_STRING_POS_CACHE_SIZE; i++) { + ce = &ctx->string_pos_cache[i]; + gc_thread_pointer(ctx, &ce->str); + } + } + + for(sp = ctx->sp; sp < (JSValue *)ctx->stack_top; sp++) { + gc_thread_pointer(ctx, sp); + } + + { + JSGCRef *ref; + for(ref = ctx->top_gc_ref; ref != NULL; ref = ref->prev) { + gc_thread_pointer(ctx, &ref->val); + } + for(ref = ctx->last_gc_ref; ref != NULL; ref = ref->prev) { + gc_thread_pointer(ctx, &ref->val); + } + } + + if (ctx->parse_state) { + JSParseState *ps = ctx->parse_state; + + gc_thread_pointer(ctx, &ps->source_str); + gc_thread_pointer(ctx, &ps->filename_str); + gc_thread_pointer(ctx, &ps->token.value); + gc_thread_pointer(ctx, &ps->cur_func); + gc_thread_pointer(ctx, &ps->byte_code); + } + + /* pass 1: thread the pointers and update the previous ones */ + new_ptr = ctx->heap_base; + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + gc_update_threaded_pointers(ctx, ptr, new_ptr); + size = get_mblock_size(ptr); + if (js_get_mtag(ptr) != JS_MTAG_FREE) { + gc_thread_block(ctx, ptr); + new_ptr += size; + } + ptr += size; + } + + /* pass 2: update the threaded pointers and move the block to its + final position */ + new_ptr = ctx->heap_base; + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + gc_update_threaded_pointers(ctx, ptr, new_ptr); + size = get_mblock_size(ptr); + if (js_get_mtag(ptr) != JS_MTAG_FREE) { + if (new_ptr != ptr) { + memmove(new_ptr, ptr, size); + } + new_ptr += size; + } + ptr += size; + } + ctx->heap_free = new_ptr; + + /* update the source pointer in the parser */ + if (ctx->parse_state) { + JSParseState *ps = ctx->parse_state; + if (JS_IsPtr(ps->source_str)) { + JSString *p = JS_VALUE_TO_PTR(ps->source_str); + ps->source_buf = p->buf; + } + } + + /* rehash the object properties */ + /* XXX: try to do it in the previous pass (add a specific tag ?) */ + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + size = get_mblock_size(ptr); + if (js_get_mtag(ptr) == JS_MTAG_OBJECT) { + js_rehash_props(ctx, (JSObject *)ptr, TRUE); + } + ptr += size; + } +} + +static void JS_GC2(JSContext *ctx, BOOL keep_atoms) +{ +#ifdef DUMP_GC + js_printf(ctx, "GC : heap size=%u/%u stack_size=%u\n", + (uint32_t)(ctx->heap_free - ctx->heap_base), + (uint32_t)(ctx->stack_top - ctx->heap_base), + (uint32_t)(ctx->stack_top - (uint8_t *)ctx->sp)); +#endif +#if defined(DEBUG_GC) + /* reduce the dummy block size at each GC to change the addresses + after compaction */ + /* XXX: only works a finite number of times */ + { + JSByteArray *arr; + if (JS_IsPtr(ctx->dummy_block)) { + arr = JS_VALUE_TO_PTR(ctx->dummy_block); + if (arr->size >= 8) { + js_shrink_byte_array(ctx, &ctx->dummy_block, arr->size - 4); + if (arr->size == 4) { + js_printf(ctx, "WARNING: debug GC: no longer modifying the addresses\n"); + } + } + } + } +#endif + gc_mark_all(ctx, keep_atoms); + gc_compact_heap(ctx); +#ifdef DUMP_GC + js_printf(ctx, "AFTER: heap size=%u/%u stack_size=%u\n", + (uint32_t)(ctx->heap_free - ctx->heap_base), + (uint32_t)(ctx->stack_top - ctx->heap_base), + (uint32_t)(ctx->stack_top - (uint8_t *)ctx->sp)); +#endif +} + +void JS_GC(JSContext *ctx) +{ + JS_GC2(ctx, TRUE); +} + +/* bytecode saving and loading */ + +#define JS_BYTECODE_VERSION_32 0x0001 +/* bit 15 of bytecode version is a 64-bit indicator */ +#define JS_BYTECODE_VERSION (JS_BYTECODE_VERSION_32 | ((JSW & 8) << 12)) + +void JS_PrepareBytecode(JSContext *ctx, + JSBytecodeHeader *hdr, + const uint8_t **pdata_buf, uint32_t *pdata_len, + JSValue eval_code) +{ + JSGCRef eval_code_ref; + int i; + + /* remove all the objects except the compiled code */ + ctx->empty_props = JS_NULL; + for(i = 0; i < ctx->class_count; i++) { + ctx->class_proto[i] = JS_NULL; + ctx->class_obj[i] = JS_NULL; + } + ctx->global_obj = JS_NULL; +#ifdef DEBUG_GC + ctx->dummy_block = JS_NULL; +#endif + + JS_PUSH_VALUE(ctx, eval_code); + JS_GC2(ctx, FALSE); + JS_POP_VALUE(ctx, eval_code); + + hdr->magic = JS_BYTECODE_MAGIC; + hdr->version = JS_BYTECODE_VERSION; + hdr->base_addr = (uintptr_t)ctx->heap_base; + hdr->unique_strings = ctx->unique_strings; + hdr->main_func = eval_code; + + *pdata_buf = ctx->heap_base; + *pdata_len = ctx->heap_free - ctx->heap_base; +} + +#if JSW == 8 + +typedef uint32_t JSValue_32; +typedef uint32_t JSWord_32; + +#define JS_MB_HEADER_32 \ + JSWord_32 gc_mark: 1; \ + JSWord_32 mtag: (JS_MTAG_BITS - 1) + +#define JS_MB_PAD_32(n) (32 - (n)) + +typedef struct { + JS_MB_HEADER_32; + JSWord_32 dummy: JS_MB_PAD_32(JS_MTAG_BITS); +} JSMemBlockHeader_32; + +typedef struct { + JS_MB_HEADER_32; + JSWord_32 size: JS_MB_PAD_32(JS_MTAG_BITS); + JSValue_32 arr[]; +} JSValueArray_32; + +typedef struct { + JS_MB_HEADER_32; + JSWord_32 size: JS_MB_PAD_32(JS_MTAG_BITS); + uint8_t buf[]; +} JSByteArray_32; + +typedef struct { + JS_MB_HEADER_32; + JSWord_32 dummy: JS_MB_PAD_32(JS_MTAG_BITS); + /* unaligned 64 bit access in 32-bit mode */ + struct __attribute__((packed)) { + double dval; + } u; +} JSFloat64_32; + +#define JS_STRING_LEN_MAX_32 ((1 << (32 - JS_MTAG_BITS - 3)) - 1) + +typedef struct { + JS_MB_HEADER_32; + JSWord_32 is_unique: 1; + JSWord_32 is_ascii: 1; + /* true if the string content represents a number, only meaningful + is is_unique = true */ + JSWord_32 is_numeric: 1; + JSWord_32 len: JS_MB_PAD_32(JS_MTAG_BITS + 3); + uint8_t buf[]; +} JSString_32; + +typedef struct { + JS_MB_HEADER_32; + JSWord_32 has_arguments : 1; /* only used during parsing */ + JSWord_32 has_local_func_name : 1; /* only used during parsing */ + JSWord_32 has_column : 1; /* column debug info is present */ + JSWord_32 arg_count : 16; + JSWord_32 dummy: JS_MB_PAD_32(JS_MTAG_BITS + 3 + 16); + + JSValue_32 func_name; /* JS_NULL if anonymous function */ + JSValue_32 byte_code; /* JS_NULL if the function is not parsed yet */ + JSValue_32 cpool; /* constant pool */ + JSValue_32 vars; /* only for debug */ + JSValue_32 ext_vars; /* records of (var_name, var_kind (2 bits) var_idx (16 bits)) */ + uint16_t stack_size; /* maximum stack size */ + uint16_t ext_vars_len; /* XXX: only used during parsing */ + JSValue_32 filename; /* filename in which the function is defined */ + JSValue_32 pc2line; /* JSByteArray or JS_NULL if not initialized */ + uint32_t source_pos; /* only used during parsing (XXX: shrink) */ +} JSFunctionBytecode_32; + +/* warning: ptr1 and ptr may overlap. However there is always: ptr1 <= ptr. Return 0 if OK. */ +static int convert_mblock_64to32(void *ptr1, const void *ptr) +{ + int mtag, i; + + mtag = ((JSMemBlockHeader*)ptr)->mtag; + switch(mtag) { + case JS_MTAG_FUNCTION_BYTECODE: + { + const JSFunctionBytecode *b = ptr; + JSFunctionBytecode_32 *b1 = ptr1; + b1->gc_mark = b->gc_mark; + b1->mtag = b->mtag; + b1->has_arguments = b->has_arguments; + b1->has_local_func_name = b->has_local_func_name; + b1->has_column = b->has_column; + b1->arg_count = b->arg_count; + b1->dummy = 0; + b1->func_name = b->func_name; + b1->byte_code = b->byte_code; + b1->cpool = b->cpool; + b1->vars = b->vars; + b1->ext_vars = b->ext_vars; + b1->stack_size = b->stack_size; + b1->ext_vars_len = b->ext_vars_len; + b1->filename = b->filename; + b1->pc2line = b->pc2line; + b1->source_pos = b->source_pos; + } + break; + case JS_MTAG_FLOAT64: + { + const JSFloat64 *b = ptr; + JSFloat64_32 *b1 = ptr1; + + b1->gc_mark = b->gc_mark; + b1->mtag = b->mtag; + b1->dummy = 0; + b1->u.dval = b->u.dval; + } + break; + case JS_MTAG_VALUE_ARRAY: + { + const JSValueArray *b = ptr; + JSValueArray_32 *b1 = ptr1; + + b1->gc_mark = b->gc_mark; + b1->mtag = b->mtag; + b1->size = b->size; /* no test needed as long as JS_VALUE_ARRAY_SIZE_MAX is identical */ + for(i = 0; i < b1->size; i++) + b1->arr[i] = b->arr[i]; + } + break; + case JS_MTAG_BYTE_ARRAY: + { + const JSByteArray *b = ptr; + JSByteArray_32 *b1 = ptr1; + + b1->gc_mark = b->gc_mark; + b1->mtag = b->mtag; + b1->size = b->size; /* no test needed as long as JS_BYTE_ARRAY_SIZE_MAX is identical */ + memmove(b1->buf, b->buf, b1->size); + } + break; + case JS_MTAG_STRING: + { + const JSString *b = ptr; + JSString_32 *b1 = ptr1; + + if (b->len > JS_STRING_LEN_MAX_32) + return -1; + b1->gc_mark = b->gc_mark; + b1->mtag = b->mtag; + b1->is_unique = b->is_unique; + b1->is_ascii = b->is_ascii; + b1->is_numeric = b->is_numeric; + b1->len = b->len; + memmove(b1->buf, b->buf, b1->len + 1); + } + break; + default: + abort(); + } + return 0; +} + +/* return the size in bytes */ +static int get_mblock_size_32(const void *ptr) +{ + int mtag = ((JSMemBlockHeader_32 *)ptr)->mtag; + int size; + switch(mtag) { + case JS_MTAG_FLOAT64: + size = sizeof(JSFloat64_32); + break; + case JS_MTAG_STRING: + { + const JSString_32 *p = ptr; + size = sizeof(JSString_32) + ((p->len + 4) & ~(4 - 1)); + } + break; + case JS_MTAG_BYTE_ARRAY: + { + const JSByteArray_32 *p = ptr; + size = sizeof(JSByteArray_32) + ((p->size + 4 - 1) & ~(4 - 1)); + } + break; + case JS_MTAG_VALUE_ARRAY: + { + const JSValueArray_32 *p = ptr; + size = sizeof(JSValueArray_32) + p->size * sizeof(p->arr[0]); + } + break; + case JS_MTAG_FUNCTION_BYTECODE: + size = sizeof(JSFunctionBytecode_32); + break; + default: + size = 0; + assert(0); + } + return size; +} + +/* Compact and convert a 64 bit heap to a 32 bit heap at offset + 0. Only used for code compilation. Return 0 if OK. */ +static int gc_compact_heap_64to32(JSContext *ctx) +{ + uint8_t *ptr; + int size, size_32; + uintptr_t new_offset; + + gc_thread_pointer(ctx, &ctx->unique_strings); + + /* thread all the external pointers */ + { + JSGCRef *ref; + /* necessary because JS_PUSH_VAL() is called before + gc_compact_heap_64to32() */ + for(ref = ctx->top_gc_ref; ref != NULL; ref = ref->prev) { + gc_thread_pointer(ctx, &ref->val); + } + } + + /* pass 1: thread the pointers and update the previous ones */ + new_offset = 0; + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + gc_update_threaded_pointers(ctx, ptr, (uint8_t *)new_offset); + size = get_mblock_size(ptr); + if (js_get_mtag(ptr) != JS_MTAG_FREE) { + gc_thread_block(ctx, ptr); + size_32 = get_mblock_size_32(ptr); + new_offset += size_32; + } + ptr += size; + } + + /* pass 2: update the threaded pointers and move the block to its + final position */ + new_offset = 0; + ptr = ctx->heap_base; + while (ptr < ctx->heap_free) { + gc_update_threaded_pointers(ctx, ptr, (uint8_t *)new_offset); + size = get_mblock_size(ptr); + if (js_get_mtag(ptr) != JS_MTAG_FREE) { + size_32 = get_mblock_size_32(ptr); + if (convert_mblock_64to32(ctx->heap_base + new_offset, ptr)) + return -1; + new_offset += size_32; + } + ptr += size; + } + ctx->heap_free = ctx->heap_base + new_offset; + return 0; +} + +#ifdef JS_USE_SHORT_FLOAT + +static int expand_short_float(JSContext *ctx, JSValue *pval) +{ + JSFloat64 *f; + if (JS_IsShortFloat(*pval)) { + f = js_malloc(ctx, sizeof(JSFloat64), JS_MTAG_FLOAT64); + if (!f) + return -1; + f->u.dval = js_get_short_float(*pval); + *pval = JS_VALUE_FROM_PTR(f); + } + return 0; +} + +/* Expand all the short floats to JSFloat64 structures. Return < 0 if + not enough memory. */ +static int expand_short_floats(JSContext *ctx) +{ + uint8_t *ptr, *p_end; + int mtag, size; + + ptr = ctx->heap_base; + p_end = ctx->heap_free; + while (ptr < p_end) { + size = get_mblock_size(ptr); + mtag = ((JSMemBlockHeader *)ptr)->mtag; + switch(mtag) { + case JS_MTAG_FUNCTION_BYTECODE: + /* we assume no short floats here */ + break; + case JS_MTAG_VALUE_ARRAY: + { + JSValueArray *p = (JSValueArray *)ptr; + int i; + for(i = 0; i < p->size; i++) { + if (expand_short_float(ctx, &p->arr[i])) + return -1; + } + } + break; + case JS_MTAG_STRING: + case JS_MTAG_FLOAT64: + case JS_MTAG_BYTE_ARRAY: + break; + default: + abort(); + } + ptr += size; + } + return 0; +} + +#endif /* JS_USE_SHORT_FLOAT */ + +int JS_PrepareBytecode64to32(JSContext *ctx, + JSBytecodeHeader32 *hdr, + const uint8_t **pdata_buf, uint32_t *pdata_len, + JSValue eval_code) +{ + JSGCRef eval_code_ref; + int i; + + /* remove all the objects except the compiled code */ + ctx->empty_props = JS_NULL; + for(i = 0; i < ctx->class_count; i++) { + ctx->class_proto[i] = JS_NULL; + ctx->class_obj[i] = JS_NULL; + } + ctx->global_obj = JS_NULL; +#ifdef DEBUG_GC + ctx->dummy_block = JS_NULL; +#endif + + JS_PUSH_VALUE(ctx, eval_code); +#ifdef JS_USE_SHORT_FLOAT + JS_GC2(ctx, FALSE); + if (expand_short_floats(ctx)) + return -1; +#else + gc_mark_all(ctx, FALSE); +#endif + if (gc_compact_heap_64to32(ctx)) + return -1; + JS_POP_VALUE(ctx, eval_code); + + hdr->magic = JS_BYTECODE_MAGIC; + hdr->version = JS_BYTECODE_VERSION_32; + hdr->base_addr = 0; + hdr->unique_strings = ctx->unique_strings; + hdr->main_func = eval_code; + + *pdata_buf = ctx->heap_base; + *pdata_len = ctx->heap_free - ctx->heap_base; + /* ensure that JS_FreeContext() will do nothing */ + ctx->heap_free = ctx->heap_base; + return 0; +} +#endif /* JSW == 8 */ + +BOOL JS_IsBytecode(const uint8_t *buf, size_t buf_len) +{ + const JSBytecodeHeader *hdr = (const JSBytecodeHeader *)buf; + return (buf_len >= sizeof(*hdr) && hdr->magic == JS_BYTECODE_MAGIC); +} + +typedef struct { + JSContext *ctx; + uintptr_t offset; + BOOL update_atoms; +} BCRelocState; + +static void bc_reloc_value(BCRelocState *s, JSValue *pval) +{ + JSContext *ctx = s->ctx; + JSString *p; + JSValue val, str; + + val = *pval; + if (JS_IsPtr(val)) { + val += s->offset; + + /* unique strings must be unique, so modify the unique string + value if it already exists in the context */ + if (s->update_atoms) { + p = JS_VALUE_TO_PTR(val); + if (p->mtag == JS_MTAG_STRING && p->is_unique) { + const JSValueArray *arr1; + int a, i; + for(i = 0; i < ctx->n_rom_atom_tables; i++) { + arr1 = ctx->rom_atom_tables[i]; + str = find_atom(ctx, &a, arr1, arr1->size, val); + if (!JS_IsNull(str)) { + val = str; + break; + } + } + } + } + *pval = val; + } +} + +int JS_RelocateBytecode2(JSContext *ctx, JSBytecodeHeader *hdr, + uint8_t *buf, uint32_t buf_len, + uintptr_t new_base_addr, BOOL update_atoms) +{ + uint8_t *ptr, *p_end; + int size, mtag; + BCRelocState ss, *s = &ss; + + if (hdr->magic != JS_BYTECODE_MAGIC) + return -1; + if (hdr->version != JS_BYTECODE_VERSION) + return -1; + + /* XXX: add atom checksum to avoid problems if the stdlib is + modified */ + s->ctx = ctx; + s->offset = new_base_addr - hdr->base_addr; + s->update_atoms = update_atoms; + + bc_reloc_value(s, &hdr->unique_strings); + bc_reloc_value(s, &hdr->main_func); + + ptr = buf; + p_end = buf + buf_len; + while (ptr < p_end) { + size = get_mblock_size(ptr); + mtag = ((JSMemBlockHeader *)ptr)->mtag; + switch(mtag) { + case JS_MTAG_FUNCTION_BYTECODE: + { + JSFunctionBytecode *b = (JSFunctionBytecode *)ptr; + bc_reloc_value(s, &b->func_name); + bc_reloc_value(s, &b->byte_code); + bc_reloc_value(s, &b->cpool); + bc_reloc_value(s, &b->vars); + bc_reloc_value(s, &b->ext_vars); + bc_reloc_value(s, &b->filename); + bc_reloc_value(s, &b->pc2line); + } + break; + case JS_MTAG_VALUE_ARRAY: + { + JSValueArray *p = (JSValueArray *)ptr; + int i; + for(i = 0; i < p->size; i++) { + bc_reloc_value(s, &p->arr[i]); + } + } + break; + case JS_MTAG_STRING: + case JS_MTAG_FLOAT64: + case JS_MTAG_BYTE_ARRAY: + break; + default: + abort(); + } + ptr += size; + } + hdr->base_addr = new_base_addr; + return 0; +} + +/* Relocate the bytecode in 'buf' so that it can be executed + later. Return 0 if OK, != 0 if error */ +int JS_RelocateBytecode(JSContext *ctx, + uint8_t *buf, uint32_t buf_len) +{ + uint8_t *data_ptr; + + if (buf_len < sizeof(JSBytecodeHeader)) + return -1; + data_ptr = buf + sizeof(JSBytecodeHeader); + return JS_RelocateBytecode2(ctx, (JSBytecodeHeader *)buf, + data_ptr, + buf_len - sizeof(JSBytecodeHeader), + (uintptr_t)data_ptr, TRUE); +} + +/* Load the precompiled bytecode from 'buf'. 'buf' must be allocated + as long as the JSContext exists. Use JS_Run() to execute + it. warning: the bytecode is not checked so it should come from a + trusted source. */ +JSValue JS_LoadBytecode(JSContext *ctx, const uint8_t *buf) +{ + const JSBytecodeHeader *hdr = (const JSBytecodeHeader *)buf; + + if (ctx->unique_strings_len != 0) + return JS_ThrowInternalError(ctx, "no atom must be defined in RAM"); + /* XXX: could stack atom_tables */ + if (ctx->n_rom_atom_tables >= N_ROM_ATOM_TABLES_MAX) + return JS_ThrowInternalError(ctx, "too many rom atom tables"); + if (hdr->magic != JS_BYTECODE_MAGIC) + return JS_ThrowInternalError(ctx, "invalid bytecode magic"); + if ((hdr->version & 0x8000) != (JS_BYTECODE_VERSION & 0x8000)) + return JS_ThrowInternalError(ctx, "bytecode not saved for %d-bit", JSW * 8); + if (hdr->version != JS_BYTECODE_VERSION) + return JS_ThrowInternalError(ctx, "invalid bytecode version"); + if (hdr->base_addr != (uintptr_t)(hdr + 1)) + return JS_ThrowInternalError(ctx, "bytecode not relocated"); + ctx->rom_atom_tables[ctx->n_rom_atom_tables++] = (JSValueArray *)JS_VALUE_TO_PTR(hdr->unique_strings); + return hdr->main_func; +} + +/**********************************************************************/ +/* runtime */ + +JSValue js_function_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + StringBuffer b_s, *b = &b_s; + JSValue val; + int i, n; + + argc &= ~FRAME_CF_CTOR; + string_buffer_push(ctx, b, 0); + string_buffer_puts(ctx, b, "(function anonymous("); + n = argc - 1; + for(i = 0; i < n; i++) { + if (i != 0) { + string_buffer_putc(ctx, b, ','); + } + if (string_buffer_concat(ctx, b, argv[i])) + goto done; + } + string_buffer_puts(ctx, b, "\n) {\n"); + if (n >= 0) { + if (string_buffer_concat(ctx, b, argv[n])) + goto done; + } + string_buffer_puts(ctx, b, "\n})"); + done: + val = string_buffer_pop(ctx, b); + if (JS_IsException(val)) + return val; + val = JS_Parse2(ctx, val, NULL, 0, "", JS_EVAL_RETVAL); + if (JS_IsException(val)) + return val; + return JS_Run(ctx, val); +} + +JSValue js_function_get_prototype(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue obj; + JSGCRef obj_ref; + + if (!JS_IsPtr(*this_val)) { + if (JS_VALUE_GET_SPECIAL_TAG(*this_val) != JS_TAG_SHORT_FUNC) + goto fail; + return JS_UNDEFINED; + } else { + JSObject *p = JS_VALUE_TO_PTR(*this_val); + if (p->mtag != JS_MTAG_OBJECT) + goto fail; + if (p->class_id == JS_CLASS_CLOSURE) { + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return obj; + } else if (p->class_id == JS_CLASS_C_FUNCTION) { + /* for C constructors, the prototype property is already present */ + return JS_UNDEFINED; + } else { + fail: + return JS_ThrowTypeError(ctx, "not a function"); + } + JS_PUSH_VALUE(ctx, obj); + JS_DefinePropertyValue(ctx, obj, js_get_atom(ctx, JS_ATOM_constructor), + *this_val); + JS_POP_VALUE(ctx, obj); + JS_PUSH_VALUE(ctx, obj); + JS_DefinePropertyValue(ctx, *this_val, js_get_atom(ctx, JS_ATOM_prototype), + obj); + JS_POP_VALUE(ctx, obj); + } + return obj; +} + +JSValue js_function_set_prototype(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + if (!JS_IsFunctionObject(ctx, *this_val)) + return JS_ThrowTypeError(ctx, "not a function"); + + JS_DefinePropertyValue(ctx, *this_val, js_get_atom(ctx, JS_ATOM_prototype), + argv[0]); + return JS_UNDEFINED; +} + +JSValue js_function_get_length_name(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_name) +{ + JSFunctionBytecode *b; + JSValue ret = js_function_get_length_name1(ctx, this_val, is_name, &b); + if (JS_IsNull(ret)) + return JS_ThrowTypeError(ctx, "not a function"); + return ret; +} + +JSValue js_function_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue str, val; + JSGCRef str_ref; + + str = js_function_get_length_name(ctx, this_val, 0, NULL, 1); + if (JS_IsException(str)) + return str; + JS_PUSH_VALUE(ctx, str); + val = JS_NewString(ctx, "function "); + JS_POP_VALUE(ctx, str); + str = JS_ConcatString(ctx, val, str); + JS_PUSH_VALUE(ctx, str); + val = JS_NewString(ctx, "() {\n [native code]\n}"); + JS_POP_VALUE(ctx, str); + return JS_ConcatString(ctx, str, val); +} + +JSValue js_function_call(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int i; + argc = max_int(argc, 1); + if (JS_StackCheck(ctx, argc + 1)) + return JS_EXCEPTION; + for(i = 0; i < argc - 1; i++) + JS_PushArg(ctx, argv[argc - 1 - i]); + JS_PushArg(ctx, *this_val); + JS_PushArg(ctx, argv[0]); + /* we avoid recursing on the C stack */ + return JS_NewTailCall(argc - 1); +} + +JSValue js_function_apply(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValueArray *arr; + JSObject *p; + int len, i; + p = js_get_object_class(ctx, argv[1], JS_CLASS_ARRAY); + if (!p) + return JS_ThrowTypeError(ctx, "not an array"); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + len = p->u.array.len; + if (len > JS_MAX_ARGC) + return JS_ThrowTypeError(ctx, "too many call arguments"); + if (JS_StackCheck(ctx, len + 2)) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(argv[1]); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + for(i = 0; i < len; i++) + JS_PushArg(ctx, arr->arr[len - 1 - i]); + JS_PushArg(ctx, *this_val); + JS_PushArg(ctx, argv[0]); + /* we avoid recursing on the C stack */ + return JS_NewTailCall(len); +} + +JSValue js_function_bind(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int arg_count; + JSValueArray *arr; + int i; + + arg_count = max_int(argc - 1, 0); + arr = js_alloc_value_array(ctx, 0, 2 + arg_count); + if (!arr) + return JS_EXCEPTION; + /* arr[0] = func, arr[1] = this */ + arr->arr[0] = *this_val; + for(i = 0; i < arg_count + 1; i++) + arr->arr[1 + i] = argv[i]; + return JS_NewCFunctionParams(ctx, JS_CFUNCTION_bound, JS_VALUE_FROM_PTR(arr)); +} + +/* XXX: handle constructor case */ +JSValue js_function_bound(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, JSValue params) +{ + JSValueArray *arr; + JSGCRef params_ref; + int i, err, size, argc2; + + arr = JS_VALUE_TO_PTR(params); + size = arr->size; + JS_PUSH_VALUE(ctx, params); + err = JS_StackCheck(ctx, size + argc); + JS_POP_VALUE(ctx, params); + if (err) + return JS_EXCEPTION; + argc2 = size - 2 + argc; + if (argc2 > JS_MAX_ARGC) + return JS_ThrowTypeError(ctx, "too many call arguments"); + arr = JS_VALUE_TO_PTR(params); + for(i = argc - 1; i >= 0; i--) + JS_PushArg(ctx, argv[i]); + for(i = size - 1; i >= 2; i--) { + JS_PushArg(ctx, arr->arr[i]); + } + JS_PushArg(ctx, arr->arr[0]); /* func */ + JS_PushArg(ctx, arr->arr[1]); /* this_val */ + /* we avoid recursing on the C stack */ + return JS_NewTailCall(argc2); +} + +/**********************************************************************/ + +JSValue js_number_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + double d; + if (argc & FRAME_CF_CTOR) + return JS_ThrowTypeError(ctx, "number constructor not supported"); + if (argc == 0) { + return JS_NewShortInt(0); + } else { + if (JS_ToNumber(ctx, &d, argv[0])) + return JS_EXCEPTION; + return JS_NewFloat64(ctx, d); + } +} + +static int js_thisNumberValue(JSContext *ctx, double *pres, JSValue val) +{ + if (!JS_IsNumber(ctx, val)) { + JS_ThrowTypeError(ctx, "not a number"); + return -1; + } + return JS_ToNumber(ctx, pres, val); +} + +JSValue js_number_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int radix, flags; + double d; + + if (js_thisNumberValue(ctx, &d, *this_val)) + return JS_EXCEPTION; + if (JS_IsUndefined(argv[0])) { + radix = 10; + } else { + if (JS_ToInt32Sat(ctx, &radix, argv[0])) + return JS_EXCEPTION; + if (radix < 2 || radix > 36) + return JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); + } + /* cannot fail */ + flags = JS_DTOA_FORMAT_FREE; + if (radix != 10) + flags |= JS_DTOA_EXP_DISABLED; + return js_dtoa2(ctx, d, radix, 0, flags); +} + +JSValue js_number_toFixed(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int f, flags; + double d; + + if (js_thisNumberValue(ctx, &d, *this_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) { + flags = JS_DTOA_FORMAT_FREE; + } else { + flags = JS_DTOA_FORMAT_FRAC; + } + return js_dtoa2(ctx, d, 10, f, flags); +} + +JSValue js_number_toExponential(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int f, flags; + double d; + + if (js_thisNumberValue(ctx, &d, *this_val)) + return JS_EXCEPTION; + if (JS_ToInt32Sat(ctx, &f, argv[0])) + return JS_EXCEPTION; + if (JS_IsUndefined(argv[0]) || !isfinite(d)) { + f = 0; + flags = JS_DTOA_FORMAT_FREE; + } else { + if (f < 0 || f > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + f++; + flags = JS_DTOA_FORMAT_FIXED; + } + return js_dtoa2(ctx, d, 10, f, flags | JS_DTOA_EXP_ENABLED); +} + +JSValue js_number_toPrecision(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int p, flags; + double d; + + if (js_thisNumberValue(ctx, &d, *this_val)) + return JS_EXCEPTION; + if (JS_IsUndefined(argv[0])) { + flags = JS_DTOA_FORMAT_FREE; + p = 0; + } else { + if (JS_ToInt32Sat(ctx, &p, argv[0])) + return JS_EXCEPTION; + if (!isfinite(d)) { + flags = JS_DTOA_FORMAT_FREE; + } else { + if (p < 1 || p > 100) + return JS_ThrowRangeError(ctx, "invalid number of digits"); + flags = JS_DTOA_FORMAT_FIXED; + } + } + return js_dtoa2(ctx, d, 10, p, flags); +} + +JSValue js_number_parseInt(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int radix; + double d; + + argv[0] = JS_ToString(ctx, argv[0]); + if (JS_IsException(argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &radix, argv[1])) + return JS_EXCEPTION; + if (radix != 0 && (radix < 2 || radix > 36)) { + d = NAN; + } else { + if (js_atod1(ctx, &d, argv[0], radix, JS_ATOD_INT_ONLY)) + return JS_EXCEPTION; + } + return JS_NewFloat64(ctx, d); +} + +JSValue js_number_parseFloat(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + double d; + + argv[0] = JS_ToString(ctx, argv[0]); + if (JS_IsException(argv[0])) + return JS_EXCEPTION; + if (js_atod1(ctx, &d, argv[0], 10, 0)) + return JS_EXCEPTION; + return JS_NewFloat64(ctx, d); +} + +/**********************************************************************/ + +JSValue js_boolean_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + if (argc & FRAME_CF_CTOR) + return JS_ThrowTypeError(ctx, "Boolean constructor not supported"); + return JS_NewBool(JS_ToBool(ctx, argv[0])); +} + +/**********************************************************************/ + +JSValue js_string_get_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int len; + + if (!JS_IsString(ctx, *this_val)) + return JS_ThrowTypeError(ctx, "not a string"); + len = js_string_len(ctx, *this_val); + return JS_NewShortInt(len); +} + +JSValue js_string_set_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + return JS_UNDEFINED; /* ignored */ +} + +JSValue js_string_slice(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int len, start, end; + + *this_val = JS_ToStringCheckObject(ctx, *this_val); + if (JS_IsException(*this_val)) + return JS_EXCEPTION; + len = js_string_len(ctx, *this_val); + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + return JS_EXCEPTION; + end = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + return js_sub_string(ctx, *this_val, start, max_int(end, start)); +} + +JSValue js_string_substring(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int a, b, start, end, len; + + *this_val = JS_ToStringCheckObject(ctx, *this_val); + if (JS_IsException(*this_val)) + return JS_EXCEPTION; + len = js_string_len(ctx, *this_val); + if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, 0)) + return JS_EXCEPTION; + b = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, len, 0)) + return JS_EXCEPTION; + } + if (a < b) { + start = a; + end = b; + } else { + start = b; + end = a; + } + return js_sub_string(ctx, *this_val, start, end); +} + +JSValue js_string_charAt(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + JSValue ret; + int idx, c; + + *this_val = JS_ToStringCheckObject(ctx, *this_val); + if (JS_IsException(*this_val)) + return JS_EXCEPTION; + if (JS_ToInt32Sat(ctx, &idx, argv[0])) + return JS_EXCEPTION; + if (idx < 0) + goto ret_undef; + c = string_getcp(ctx, *this_val, idx, (magic == magic_codePointAt)); + if (c == -1) { + ret_undef: + if (magic == magic_charCodeAt) + ret = JS_NewFloat64(ctx, NAN); + else if (magic == magic_charAt) + ret = js_get_atom(ctx, JS_ATOM_empty); + else + ret = JS_UNDEFINED; + } else { + if (magic == magic_charCodeAt || magic == magic_codePointAt) + ret = JS_NewShortInt(c); + else + ret = JS_NewStringChar(c); + } + // dump_string_pos_cache(ctx); + return ret; +} + +JSValue js_string_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + if (argc & FRAME_CF_CTOR) + return JS_ThrowTypeError(ctx, "string constructor not supported"); + if (argc <= 0) { + return js_get_atom(ctx, JS_ATOM_empty); + } else { + return JS_ToString(ctx, argv[0]); + } +} + +JSValue js_string_fromCharCode(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_fromCodePoint) +{ + int i; + StringBuffer b_s, *b = &b_s; + + string_buffer_push(ctx, b, 0); + for(i = 0; i < argc; i++) { + int c; + if (JS_ToInt32(ctx, &c, argv[i])) + goto fail; + if (is_fromCodePoint) { + if (c < 0 || c > 0x10ffff) { + JS_ThrowRangeError(ctx, "invalid code point"); + goto fail; + } + } else { + c &= 0xffff; + } + if (string_buffer_putc(ctx, b, c)) + break; + } + return string_buffer_pop(ctx, b); + fail: + string_buffer_pop(ctx, b); + return JS_EXCEPTION; +} + +JSValue js_string_concat(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int i; + StringBuffer b_s, *b = &b_s; + JSValue r; + + r = JS_ToStringCheckObject(ctx, *this_val); + if (JS_IsException(r)) + return JS_EXCEPTION; + string_buffer_push(ctx, b, 0); + if (string_buffer_concat(ctx, b, r)) + goto done; + + for (i = 0; i < argc; i++) { + if (string_buffer_concat(ctx, b, argv[i])) + goto done; + } + done: + return string_buffer_pop(ctx, b); +} + +JSValue js_string_indexOf(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int lastIndexOf) +{ + int i, len, v_len, pos, start, stop, ret, inc, j; + + *this_val = JS_ToStringCheckObject(ctx, *this_val); + if (JS_IsException(*this_val)) + return JS_EXCEPTION; + argv[0] = JS_ToString(ctx, argv[0]); + if (JS_IsException(argv[0])) + return JS_EXCEPTION; + len = js_string_len(ctx, *this_val); + v_len = js_string_len(ctx, argv[0]); + if (lastIndexOf) { + pos = len - v_len; + if (argc > 1) { + double d; + if (JS_ToNumber(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) { + for(j = 0; j < v_len; j++) { + if (string_getc(ctx, *this_val, i + j) != string_getc(ctx, argv[0], j)) { + goto next; + } + } + ret = i; + break; + next: + if (i == stop) + break; + } + } + return JS_NewShortInt(ret); + +fail: + return JS_EXCEPTION; +} + +static int js_string_indexof(JSContext *ctx, JSValue str, JSValue needle, + int start, int str_len, int needle_len) +{ + int i, j; + for(i = start; i <= str_len - needle_len; i++) { + for(j = 0; j < needle_len; j++) { + if (string_getc(ctx, str, i + j) != + string_getc(ctx, needle, j)) { + goto next; + } + + } + return i; + next: ; + } + return -1; +} + +/* Note: ascii only */ +JSValue js_string_toLowerCase(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int to_lower) +{ + StringBuffer b_s, *b = &b_s; + int i, c, len; + + *this_val = JS_ToStringCheckObject(ctx, *this_val); + if (JS_IsException(*this_val)) + return *this_val; + len = js_string_len(ctx, *this_val); + if (string_buffer_push(ctx, b, len)) + return JS_EXCEPTION; + for(i = 0; i < len; i++) { + c = string_getc(ctx, *this_val, i); + if (to_lower) { + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + } else { + if (c >= 'a' && c <= 'z') + c += 'A' - 'a'; + } + string_buffer_putc(ctx, b, c); + } + return string_buffer_pop(ctx, b); +} + +/* c < 128 */ +static force_inline BOOL unicode_is_space_ascii(uint32_t c) +{ + return (c >= 0x0009 && c <= 0x000D) || (c == 0x0020); +} + +static BOOL unicode_is_space_non_ascii(uint32_t c) +{ + return (c == 0x00A0 || + c == 0x1680 || + (c >= 0x2000 && c <= 0x200A) || + (c >= 0x2028 && c <= 0x2029) || + c == 0x202F || + c == 0x205F || + c == 0x3000 || + c == 0xFEFF); +} + +static force_inline BOOL unicode_is_space(uint32_t c) +{ + if (likely(c < 128)) { + return unicode_is_space_ascii(c); + } else { + return unicode_is_space_non_ascii(c); + } +} + +JSValue js_string_trim(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + int a, b, len; + + *this_val = JS_ToStringCheckObject(ctx, *this_val); + if (JS_IsException(*this_val)) + return *this_val; + len = js_string_len(ctx, *this_val); + a = 0; + b = len; + if (magic & 1) { + while (a < len && unicode_is_space(string_getc(ctx, *this_val, a))) + a++; + } + if (magic & 2) { + while (b > a && unicode_is_space(string_getc(ctx, *this_val, b - 1))) + b--; + } + return js_sub_string(ctx, *this_val, a, b); +} + +JSValue js_string_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + if (!JS_IsString(ctx, *this_val)) + return JS_ThrowTypeError(ctx, "not a string"); + return *this_val; +} + +JSValue js_string_repeat(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + StringBuffer b_s, *b = &b_s; + JSStringCharBuf buf; + JSString *p; + int n; + int64_t len; + + if (!JS_IsString(ctx, *this_val)) + return JS_ThrowTypeError(ctx, "not a string"); + if (JS_ToInt32Sat(ctx, &n, argv[0])) + return -1; + p = get_string_ptr(ctx, &buf, *this_val); + if (n < 0 || (len = (int64_t)n * p->len) > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid repeat count"); + if (p->len == 0 || n == 1) + return *this_val; + if (string_buffer_push(ctx, b, len)) + return JS_EXCEPTION; + while (n-- > 0) { + string_buffer_concat_str(ctx, b, *this_val); + } + return string_buffer_pop(ctx, b); +} + +/**********************************************************************/ + +JSValue js_object_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + /* XXX: incomplete */ + argc &= ~FRAME_CF_CTOR; + if (argc <= 0) { + return JS_NewObject(ctx); + } else { + return argv[0]; + } +} + +JSValue js_object_defineProperty(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue *pobj, *pprop, *pdesc; + JSValue val, getter, setter; + JSGCRef val_ref, getter_ref; + int flags; + + pobj = &argv[0]; + pprop = &argv[1]; + pdesc = &argv[2]; + + if (!JS_IsObject(ctx, *pobj)) + return JS_ThrowTypeErrorNotAnObject(ctx); + *pprop = JS_ToPropertyKey(ctx, *pprop); + if (JS_IsException(*pprop)) + return JS_EXCEPTION; + val = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + flags = 0; + if (JS_HasProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_value))) { + flags |= JS_DEF_PROP_HAS_VALUE; + val = JS_GetProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_value)); + if (JS_IsException(val)) + return JS_EXCEPTION; + } + if (JS_HasProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_get))) { + flags |= JS_DEF_PROP_HAS_GET; + JS_PUSH_VALUE(ctx, val); + getter = JS_GetProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_get)); + JS_POP_VALUE(ctx, val); + if (JS_IsException(getter)) + return JS_EXCEPTION; + if (!JS_IsUndefined(getter) && !JS_IsFunction(ctx, getter)) + goto bad_getset; + } + if (JS_HasProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_set))) { + flags |= JS_DEF_PROP_HAS_SET; + JS_PUSH_VALUE(ctx, val); + JS_PUSH_VALUE(ctx, getter); + setter = JS_GetProperty(ctx, *pdesc, js_get_atom(ctx, JS_ATOM_set)); + JS_POP_VALUE(ctx, getter); + JS_POP_VALUE(ctx, val); + if (JS_IsException(setter)) + return JS_EXCEPTION; + if (!JS_IsUndefined(setter) && !JS_IsFunction(ctx, setter)) { + bad_getset: + return JS_ThrowTypeError(ctx, "invalid getter or setter"); + } + } + if (flags & (JS_DEF_PROP_HAS_GET | JS_DEF_PROP_HAS_SET)) { + if (flags & JS_DEF_PROP_HAS_VALUE) + return JS_ThrowTypeError(ctx, "cannot have both value and get/set"); + val = getter; + } + val = JS_DefinePropertyInternal(ctx, *pobj, *pprop, val, setter, + flags | JS_DEF_PROP_LOOKUP); + if (JS_IsException(val)) + return val; + return *pobj; +} + +JSValue js_object_getPrototypeOf(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p; + if (!JS_IsObject(ctx, argv[0])) + return JS_ThrowTypeErrorNotAnObject(ctx); + p = JS_VALUE_TO_PTR(argv[0]); + return p->proto; +} + +/* 'obj' must be an object. 'proto' must be JS_NULL or an object */ +static JSValue js_set_prototype_internal(JSContext *ctx, JSValue obj, JSValue proto) +{ + JSObject *p, *p1; + + p = JS_VALUE_TO_PTR(obj); + if (p->proto != proto) { + if (proto != JS_NULL) { + /* check if there is a cycle */ + p1 = JS_VALUE_TO_PTR(proto); + for(;;) { + if (p1 == p) + return JS_ThrowTypeError(ctx, "circular prototype chain"); + if (p1->proto == JS_NULL) + break; + p1 = JS_VALUE_TO_PTR(p1->proto); + } + } + + p->proto = proto; + } + return JS_UNDEFINED; +} + +JSValue js_object_setPrototypeOf(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue proto; + + if (!JS_IsObject(ctx, argv[0])) + return JS_ThrowTypeErrorNotAnObject(ctx); + proto = argv[1]; + if (proto != JS_NULL && !JS_IsObject(ctx, proto)) + return JS_ThrowTypeError(ctx, "not a prototype"); + if (JS_IsException(js_set_prototype_internal(ctx, argv[0], proto))) + return JS_EXCEPTION; + return argv[0]; +} + +JSValue js_object_create(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue proto; + proto = argv[0]; + if (proto != JS_NULL && !JS_IsObject(ctx, proto)) + return JS_ThrowTypeError(ctx, "not a prototype"); + if (argc >= 2) + return JS_ThrowTypeError(ctx, "unsupported additional properties"); + return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT, 0); +} + +JSValue js_object_keys(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p, *pret; + JSValue ret, str; + JSValueArray *arr, *ret_arr; + int array_len, prop_count, hash_mask, alloc_size, i, j, pos; + JSGCRef ret_ref; + + if (!JS_IsObject(ctx, argv[0])) + return JS_ThrowTypeErrorNotAnObject(ctx); + p = JS_VALUE_TO_PTR(argv[0]); + + if (p->class_id == JS_CLASS_ARRAY) { + array_len = p->u.array.len; + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + array_len = p->u.typed_array.len; + } else { + array_len = 0; + } + + arr = JS_VALUE_TO_PTR(p->props); + prop_count = JS_VALUE_GET_INT(arr->arr[0]); + hash_mask = JS_VALUE_GET_INT(arr->arr[1]); + + alloc_size = array_len + prop_count; + + ret = JS_NewArray(ctx, alloc_size); + if (JS_IsException(ret)) + return ret; + + pos = 0; + for(i = 0; i < array_len; i++) { + JS_PUSH_VALUE(ctx, ret); + str = JS_ToString(ctx, JS_NewShortInt(i)); + JS_POP_VALUE(ctx, ret); + if (JS_IsException(str)) + return str; + pret = JS_VALUE_TO_PTR(ret); + ret_arr = JS_VALUE_TO_PTR(pret->u.array.tab); + ret_arr->arr[pos++] = str; + } + + for(i = 0, j = 0; j < prop_count; i++) { + JSProperty *pr; + p = JS_VALUE_TO_PTR(argv[0]); + arr = JS_VALUE_TO_PTR(p->props); + pr = (JSProperty *)&arr->arr[2 + hash_mask + 1 + 3 * i]; + /* exclude deleted properties */ + if (pr->key != JS_UNINITIALIZED) { + JS_PUSH_VALUE(ctx, ret); + str = JS_ToString(ctx, pr->key); + JS_POP_VALUE(ctx, ret); + if (JS_IsException(str)) + return str; + pret = JS_VALUE_TO_PTR(ret); + ret_arr = JS_VALUE_TO_PTR(pret->u.array.tab); + ret_arr->arr[pos++] = str; + j++; + } + } + pret = JS_VALUE_TO_PTR(ret); + pret->u.array.len = pos; + return ret; +} + +JSValue js_object_hasOwnProperty(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p; + JSValue prop; + int array_len, idx; + + if (JS_IsNull(*this_val) || JS_IsUndefined(*this_val)) + return JS_ThrowTypeError(ctx, "cannot convert to object"); + if (!JS_IsObject(ctx, *this_val)) + return JS_FALSE; /* XXX: could improve for strings */ + prop = JS_ToPropertyKey(ctx, argv[0]); + p = JS_VALUE_TO_PTR(*this_val); + if (p->class_id == JS_CLASS_ARRAY) { + array_len = p->u.array.len; + goto check_array; + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + array_len = p->u.typed_array.len; + check_array: + if (JS_IsInt(prop)) { + idx = JS_VALUE_GET_INT(prop); + return JS_NewBool((idx >= 0 && idx < array_len)); + } + } + return JS_NewBool((find_own_property(ctx, p, prop) != NULL)); +} + +JSValue js_object_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + const char *str; + char buf[64]; + /* XXX: not fully compliant */ + if (JS_IsIntOrShortFloat(*this_val)) { + goto number; + } else if (!JS_IsPtr(*this_val)) { + switch(JS_VALUE_GET_SPECIAL_TAG(*this_val)) { + case JS_TAG_NULL: + str = "Null"; + break; + case JS_TAG_UNDEFINED: + str = "Undefined"; + break; + case JS_TAG_SHORT_FUNC: + str = "Function"; + break; + case JS_TAG_BOOL: + str = "Boolean"; + break; + case JS_TAG_STRING_CHAR: + goto string; + default: + goto object; + } + } else { + JSObject *p = JS_VALUE_TO_PTR(*this_val); + switch(p->mtag) { + case JS_MTAG_OBJECT: + switch(p->class_id) { + case JS_CLASS_ARRAY: + str = "Array"; + break; + case JS_CLASS_ERROR: + str = "Error"; + break; + case JS_CLASS_CLOSURE: + case JS_CLASS_C_FUNCTION: + str = "Function"; + break; + default: + object: + str = "Object"; + break; + } + break; + case JS_MTAG_STRING: + string: + str = "String"; + break; + case JS_MTAG_FLOAT64: + number: + str = "Number"; + break; + default: + goto object; + } + } + js_snprintf(buf, sizeof(buf), "[object %s]", str); + return JS_NewString(ctx, buf); +} + +/**********************************************************************/ + +JSValue js_error_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + JSValue obj, msg; + JSObject *p; + JSGCRef obj_ref; + + argc &= ~FRAME_CF_CTOR; + + obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[magic], JS_CLASS_ERROR, + sizeof(JSErrorData)); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_TO_PTR(obj); + p->u.error.message = JS_NULL; + p->u.error.stack = JS_NULL; + + if (!JS_IsUndefined(argv[0])) { + JS_PUSH_VALUE(ctx, obj); + msg = JS_ToString(ctx, argv[0]); + JS_POP_VALUE(ctx, obj); + if (JS_IsException(msg)) + return msg; + p = JS_VALUE_TO_PTR(obj); + p->u.error.message = msg; + } else { + p = JS_VALUE_TO_PTR(obj); + p->u.error.message = js_get_atom(ctx, JS_ATOM_empty); + } + JS_PUSH_VALUE(ctx, obj); + build_backtrace(ctx, obj, NULL, 0, 0, 1); + JS_POP_VALUE(ctx, obj); + return obj; +} + +JSValue js_error_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p; + JSValue name; + StringBuffer b_s, *b = &b_s; + + if (!JS_IsError(ctx, *this_val)) + return JS_ThrowTypeError(ctx, "not an Error object"); + name = JS_GetProperty(ctx, *this_val, js_get_atom(ctx, JS_ATOM_name)); + if (JS_IsException(name)) + return name; + if (JS_IsUndefined(name)) + name = js_get_atom(ctx, JS_ATOM_Error); + else + name = JS_ToString(ctx, name); + if (JS_IsException(name)) + return name; + string_buffer_push(ctx, b, 0); + string_buffer_concat(ctx, b, name); + p = JS_VALUE_TO_PTR(*this_val); + if (p->u.error.message != JS_NULL) { + string_buffer_puts(ctx, b, ": "); + p = JS_VALUE_TO_PTR(*this_val); + string_buffer_concat(ctx, b, p->u.error.message); + } + return string_buffer_pop(ctx, b); +} + +JSValue js_error_get_message(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + JSObject *p; + if (!JS_IsError(ctx, *this_val)) + return JS_ThrowTypeError(ctx, "not an Error object"); + p = JS_VALUE_TO_PTR(*this_val); + if (magic == 0) + return p->u.error.message; + else + return p->u.error.stack; +} + +/**********************************************************************/ + +static JSObject *js_get_array(JSContext *ctx, JSValue obj) +{ + JSObject *p; + p = js_get_object_class(ctx, obj, JS_CLASS_ARRAY); + if (!p) { + JS_ThrowTypeError(ctx, "not an array"); + return NULL; + } + return p; +} + +JSValue js_array_get_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p; + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + return JS_NewShortInt(p->u.array.len); +} + +static int js_array_resize(JSContext *ctx, JSValue *this_val, int new_len) +{ + JSObject *p; + int i; + + if (new_len < 0 || new_len > JS_SHORTINT_MAX) { + JS_ThrowTypeError(ctx, "invalid array length"); + return -1; + } + p = JS_VALUE_TO_PTR(*this_val); + if (new_len < p->u.array.len) { + JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab); + /* shrink the array if the new size is small enough */ + if (new_len < (arr->size / 2) && arr->size >= 4) { + js_shrink_value_array(ctx, &p->u.array.tab, new_len); + p = JS_VALUE_TO_PTR(*this_val); + } else { + for(i = new_len; i < p->u.array.len; i++) + arr->arr[i] = JS_UNDEFINED; + } + } else if (new_len > p->u.array.len) { + JSValueArray *arr; + JSValue new_tab; + new_tab = js_resize_value_array(ctx, p->u.array.tab, new_len); + if (JS_IsException(new_tab)) + return -1; + p = JS_VALUE_TO_PTR(*this_val); + p->u.array.tab = new_tab; + arr = JS_VALUE_TO_PTR(p->u.array.tab); + for(i = p->u.array.len; i < new_len; i++) + arr->arr[i] = JS_UNDEFINED; + } + p->u.array.len = new_len; + return 0; +} + +JSValue js_array_set_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int new_len; + + if (!js_get_array(ctx, *this_val)) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &new_len, argv[0])) + return JS_EXCEPTION; + if (js_array_resize(ctx, this_val, new_len)) + return JS_EXCEPTION; + return JS_UNDEFINED; +} + +JSValue js_array_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue obj; + JSObject *p; + int len, i; + BOOL has_init; + + argc &= ~FRAME_CF_CTOR; + + if (argc == 1 && JS_IsNumber(ctx, argv[0])) { + /* XXX: we create undefined properties instead of just setting the length */ + if (JS_ToInt32(ctx, &len, argv[0])) + return JS_EXCEPTION; + has_init = FALSE; + } else { + len = argc; + has_init = TRUE; + } + + if (len < 0 || len > JS_SHORTINT_MAX) + return JS_ThrowRangeError(ctx, "invalid array length"); + obj = JS_NewArray(ctx, len); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_TO_PTR(obj); + p->u.array.len = len; + + if (has_init) { + JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab); + for(i = 0; i < argc; i++) { + arr->arr[i] = argv[i]; + } + } + return obj; +} + +JSValue js_array_push(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_unshift) +{ + JSObject *p; + int new_len, i, from; + JSValueArray *arr; + JSValue new_tab; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + from = p->u.array.len; + new_len = from + argc; + if (new_len > JS_SHORTINT_MAX) + return JS_ThrowRangeError(ctx, "invalid array length"); + new_tab = js_resize_value_array(ctx, p->u.array.tab, new_len); + if (JS_IsException(new_tab)) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(*this_val); + p->u.array.tab = new_tab; + p->u.array.len = new_len; + arr = JS_VALUE_TO_PTR(p->u.array.tab); + if (is_unshift && argc > 0) { + memmove(arr->arr + argc, arr->arr, from * sizeof(JSValue)); + from = 0; + } + for(i = 0; i < argc; i++) { + arr->arr[from + i] = argv[i]; + } + return JS_NewShortInt(new_len); +} + +JSValue js_array_pop(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p; + JSValue ret; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + if (p->u.array.len > 0) { + JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab); + ret = arr->arr[--p->u.array.len]; + } else { + ret = JS_UNDEFINED; + } + return ret; +} + +JSValue js_array_shift(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p; + JSValue ret; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + if (p->u.array.len > 0) { + JSValueArray *arr = JS_VALUE_TO_PTR(p->u.array.tab); + ret = arr->arr[0]; + p->u.array.len--; + memmove(arr->arr, arr->arr + 1, p->u.array.len * sizeof(JSValue)); + } else { + ret = JS_UNDEFINED; + } + return ret; +} + +JSValue js_array_join(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + uint32_t i, len; + BOOL is_array; + JSValue sep, val; + JSGCRef sep_ref; + JSObject *p; + JSValueArray *arr; + StringBuffer b_s, *b = &b_s; + + if (!JS_IsObject(ctx, *this_val)) + return JS_ThrowTypeErrorNotAnObject(ctx); + p = JS_VALUE_TO_PTR(*this_val); + is_array = (p->class_id == JS_CLASS_ARRAY); + if (is_array) { + len = p->u.array.len; + } else { + if (js_get_length32(ctx, &len, *this_val)) + return JS_EXCEPTION; + } + + if (argc > 0 && !JS_IsUndefined(argv[0])) { + sep = JS_ToString(ctx, argv[0]); + if (JS_IsException(sep)) + return sep; + } else { + sep = JS_NewStringChar(','); + } + JS_PUSH_VALUE(ctx, sep); + + string_buffer_push(ctx, b, 0); + for(i = 0; i < len; i++) { + if (i > 0) { + if (string_buffer_concat(ctx, b, sep_ref.val)) + goto exception; + } + if (is_array) { + p = JS_VALUE_TO_PTR(*this_val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + if (i < p->u.array.len) + val = arr->arr[i]; + else + val = JS_UNDEFINED; + } else { + val = JS_GetPropertyUint32(ctx, *this_val, i); + if (JS_IsException(val)) + goto exception; + } + if (!JS_IsUndefined(val) && !JS_IsNull(val)) { + if (string_buffer_concat(ctx, b, val)) + goto exception; + } + } + val = string_buffer_pop(ctx, b); + JS_POP_VALUE(ctx, sep); + return val; + + exception: + string_buffer_pop(ctx, b); + JS_POP_VALUE(ctx, sep); + return JS_EXCEPTION; +} + +JSValue js_array_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + return js_array_join(ctx, this_val, 0, NULL); +} + +JSValue js_array_isArray(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p; + p = js_get_object_class(ctx, argv[0], JS_CLASS_ARRAY); + return JS_NewBool(p != NULL); +} + +JSValue js_array_reverse(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + int len; + JSObject *p; + JSValueArray *arr; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + len = p->u.array.len; + arr = JS_VALUE_TO_PTR(p->u.array.tab); + js_reverse_val(arr->arr, len); + return *this_val; +} + +JSValue js_array_concat(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p; + int len, i, j, pos; + int64_t len64; + JSValue obj, val; + JSValueArray *arr, *arr1; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + /* do a first pass to estimate the length */ + len64 = p->u.array.len; + for(i = 0; i < argc; i++) { + p = js_get_object_class(ctx, argv[i], JS_CLASS_ARRAY); + if (p) { + len64 += p->u.array.len; + } else { + len64++; + } + } + if (len64 > JS_SHORTINT_MAX) + return JS_ThrowTypeError(ctx, "Array loo long"); + len = len64; + + obj = JS_NewArray(ctx, len); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_TO_PTR(obj); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + + pos = 0; + for(i = -1; i < argc; i++) { + val = i == -1 ? *this_val : argv[i]; + p = js_get_object_class(ctx, val, JS_CLASS_ARRAY); + if (p) { + arr1 = JS_VALUE_TO_PTR(p->u.array.tab); + for(j = 0; j < p->u.array.len; j++) + arr->arr[pos + j] = arr1->arr[j]; + pos += p->u.array.len; + } else { + arr->arr[pos++] = val; + } + } + return obj; +} + +JSValue js_array_indexOf(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_lastIndexOf) +{ + JSObject *p; + int len, n, res; + JSValueArray *arr; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + len = p->u.array.len; + if (is_lastIndexOf) { + n = len - 1; + } else { + n = 0; + } + if (argc > 1) { + if (JS_ToInt32Clamp(ctx, &n, argv[1], + -is_lastIndexOf, len - is_lastIndexOf, len)) + return JS_EXCEPTION; + } + /* the array may be modified */ + p = JS_VALUE_TO_PTR(*this_val); + len = p->u.array.len; /* the length may be modified */ + arr = JS_VALUE_TO_PTR(p->u.array.tab); + res = -1; + if (is_lastIndexOf) { + n = min_int(n, len - 1); + for(;n >= 0; n--) { + if (js_strict_eq(ctx, argv[0], arr->arr[n])) { + res = n; + break; + } + } + } else { + for(;n < len; n++) { + if (js_strict_eq(ctx, argv[0], arr->arr[n])) { + res = n; + break; + } + } + } + return JS_NewShortInt(res); +} + +JSValue js_array_slice(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p, *p1; + int len, start, final, k; + JSValueArray *arr, *arr1; + JSValue obj; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + len = p->u.array.len; + + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + return JS_EXCEPTION; + final = len; + if (!JS_IsUndefined(argv[1])) { + if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + /* the array may have been modified */ + p = JS_VALUE_TO_PTR(*this_val); + len = p->u.array.len; /* the length may be modified */ + final = min_int(final, len); + + obj = JS_NewArray(ctx, max_int(final - start, 0)); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_TO_PTR(*this_val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + p1 = JS_VALUE_TO_PTR(obj); + arr1 = JS_VALUE_TO_PTR(p1->u.array.tab); + for(k = start; k < final; k++) { + arr1->arr[k - start] = arr->arr[k]; + } + return obj; +} + +JSValue js_array_splice(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p, *p1; + int start, len, item_count, del_count, new_len, i, ret; + JSValueArray *arr, *arr1; + JSValue obj; + JSGCRef obj_ref; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + len = p->u.array.len; + + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + return JS_EXCEPTION; + + 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_ToInt32Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) + return JS_EXCEPTION; + } + new_len = len + item_count - del_count; + + obj = JS_NewArray(ctx, del_count); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_TO_PTR(*this_val); + /* handling this case has no practical use */ + if (p->u.array.len != len) + return JS_ThrowTypeError(ctx, "array length was modified"); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + p1 = JS_VALUE_TO_PTR(obj); + arr1 = JS_VALUE_TO_PTR(p1->u.array.tab); + + for(i = 0; i < del_count; i++) { + arr1->arr[i] = arr->arr[start + i]; + } + + if (item_count != del_count) { + /* resize */ + if (del_count > item_count) { + memmove(arr->arr + start + item_count, + arr->arr + start + del_count, + (len - (start + del_count)) * sizeof(JSValue)); + } + JS_PUSH_VALUE(ctx, obj); + ret = js_array_resize(ctx, this_val, new_len); + JS_POP_VALUE(ctx, obj); + if (ret) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(*this_val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + if (del_count < item_count) { + memmove(arr->arr + start + item_count, + arr->arr + start + del_count, + (len - (start + del_count)) * sizeof(JSValue)); + } + } + + for(i = 0; i < item_count; i++) + arr->arr[start + i] = argv[2 + i]; + + return obj; +} + +JSValue js_array_every(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int special) +{ + JSObject *p; + JSValueArray *arr; + JSValue res, ret, val; + JSValue *pfunc, *pthis_arg; + JSGCRef val_ref, ret_ref; + int len, k, n; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + len = p->u.array.len; + + pfunc = &argv[0]; + pthis_arg = NULL; + if (argc > 1) + pthis_arg = &argv[1]; + + if (!JS_IsFunction(ctx, *pfunc)) + return JS_ThrowTypeError(ctx, "not a function"); + + switch (special) { + case js_special_every: + ret = JS_TRUE; + break; + case js_special_some: + ret = JS_FALSE; + break; + case js_special_map: + ret = JS_NewArray(ctx, len); + if (JS_IsException(ret)) + return JS_EXCEPTION; + break; + case js_special_filter: + ret = JS_NewArray(ctx, 0); + if (JS_IsException(ret)) + return JS_EXCEPTION; + break; + case js_special_forEach: + default: + ret = JS_UNDEFINED; + break; + } + n = 0; + + JS_PUSH_VALUE(ctx, ret); + for(k = 0; k < len; k++) { + if (JS_StackCheck(ctx, 5)) + goto exception; + + p = JS_VALUE_TO_PTR(*this_val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + /* the array length may have been modified by the function call*/ + if (k >= p->u.array.len) + break; + val = arr->arr[k]; + + JS_PushArg(ctx, *this_val); + JS_PushArg(ctx, JS_NewShortInt(k)); + JS_PushArg(ctx, val); /* arg0 */ + JS_PushArg(ctx, *pfunc); /* func */ + JS_PushArg(ctx, pthis_arg ? *pthis_arg : JS_UNDEFINED); /* this */ + JS_PUSH_VALUE(ctx, val); + res = JS_Call(ctx, 3); + JS_POP_VALUE(ctx, val); + if (JS_IsException(res)) + goto exception; + + switch (special) { + case js_special_every: + if (!JS_ToBool(ctx, res)) { + ret_ref.val = JS_FALSE; + goto done; + } + break; + case js_special_some: + if (JS_ToBool(ctx, res)) { + ret_ref.val = JS_TRUE; + goto done; + } + break; + case js_special_map: + /* Note: same as defineProperty for arrays */ + res = JS_SetPropertyUint32(ctx, ret_ref.val, k, res); + if (JS_IsException(res)) + goto exception; + break; + case js_special_filter: + if (JS_ToBool(ctx, res)) { + res = JS_SetPropertyUint32(ctx, ret_ref.val, n++, val); + if (JS_IsException(res)) + goto exception; + } + break; + case js_special_forEach: + default: + break; + } + } +done: + JS_POP_VALUE(ctx, ret); + return ret; + exception: + ret_ref.val = JS_EXCEPTION; + goto done; +} + +JSValue js_array_reduce(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int special) +{ + JSObject *p; + JSValueArray *arr; + JSValue acc, *pfunc; + JSGCRef acc_ref; + int len, k, k1, ret; + + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + len = p->u.array.len; + pfunc = &argv[0]; + + if (!JS_IsFunction(ctx, *pfunc)) + return JS_ThrowTypeError(ctx, "not a function"); + + k = 0; + if (argc > 1) { + acc = argv[1]; + } else { + if (len == 0) + return JS_ThrowTypeError(ctx, "empty array"); + k1 = (special == js_special_reduceRight) ? len - k - 1 : k; + arr = JS_VALUE_TO_PTR(p->u.array.tab); + acc = arr->arr[k1]; + k++; + } + for (; k < len; k++) { + JS_PUSH_VALUE(ctx, acc); + ret = JS_StackCheck(ctx, 6); + JS_POP_VALUE(ctx, acc); + if (ret) + return JS_EXCEPTION; + + k1 = (special == js_special_reduceRight) ? len - k - 1 : k; + p = JS_VALUE_TO_PTR(*this_val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + /* Note: the array length may have been modified, hence the check */ + if (k1 >= p->u.array.len) + break; + + JS_PushArg(ctx, *this_val); + JS_PushArg(ctx, JS_NewShortInt(k1)); + JS_PushArg(ctx, arr->arr[k1]); + JS_PushArg(ctx, acc); /* arg0 */ + JS_PushArg(ctx, *pfunc); /* func */ + JS_PushArg(ctx, JS_UNDEFINED); /* this */ + acc = JS_Call(ctx, 4); + if (JS_IsException(acc)) + return JS_EXCEPTION; + } + return acc; +} + +/* heapsort algorithm */ +static void rqsort_idx(size_t nmemb, + int (*cmp)(size_t, size_t, void *), + void (*swap)(size_t, size_t, void *), + void *opaque) +{ + size_t i, n, c, r, size; + + size = 1; + if (nmemb > 1) { + i = (nmemb / 2) * size; + n = nmemb * size; + + while (i > 0) { + i -= size; + for (r = i; (c = r * 2 + size) < n; r = c) { + if (c < n - size && cmp(c, c + size, opaque) <= 0) + c += size; + if (cmp(r, c, opaque) > 0) + break; + swap(r, c, opaque); + } + } + for (i = n - size; i > 0; i -= size) { + swap(0, i, opaque); + + for (r = 0; (c = r * 2 + size) < i; r = c) { + if (c < i - size && cmp(c, c + size, opaque) <= 0) + c += size; + if (cmp(r, c, opaque) > 0) + break; + swap(r, c, opaque); + } + } + } +} + +typedef struct { + JSContext *ctx; + BOOL exception; + JSValue *parr; + JSValue *pfunc; +} JSArraySortContext; + +/* return -1, 0, 1 */ +static int js_array_sort_cmp(size_t i1, size_t i2, void *opaque) +{ + JSArraySortContext *s = opaque; + JSContext *ctx = s->ctx; + JSValueArray *arr; + int cmp, j1, j2; + + if (s->exception) + return 0; + + arr = JS_VALUE_TO_PTR(*s->parr); + if (s->pfunc) { + JSValue res; + /* custom sort function is specified as returning 0 for identical + * objects: avoid method call overhead. + */ + if (arr->arr[2 * i1] == arr->arr[2 * i2]) + goto cmp_same; + if (JS_StackCheck(ctx, 4)) + goto exception; + arr = JS_VALUE_TO_PTR(*s->parr); + + JS_PushArg(ctx, arr->arr[2 * i2]); + JS_PushArg(ctx, arr->arr[2 * i1]); /* arg0 */ + JS_PushArg(ctx, *s->pfunc); /* func */ + JS_PushArg(ctx, JS_UNDEFINED); /* this */ + res = JS_Call(ctx, 2); + if (JS_IsException(res)) + return JS_EXCEPTION; + if (JS_IsInt(res)) { + int val = JS_VALUE_GET_INT(res); + cmp = (val > 0) - (val < 0); + } else { + double val; + if (JS_ToNumber(ctx, &val, res)) + goto exception; + cmp = (val > 0) - (val < 0); + } + } else { + JSValue str1, str2; + JSGCRef str1_ref; + + str1 = arr->arr[2 * i1]; + if (!JS_IsString(ctx, str1)) { + str1 = JS_ToString(ctx, str1); + if (JS_IsException(str1)) + goto exception; + arr = JS_VALUE_TO_PTR(*s->parr); + } + str2 = arr->arr[2 * i2]; + if (!JS_IsString(ctx, str2)) { + JS_PUSH_VALUE(ctx, str1); + str2 = JS_ToString(ctx, str2); + JS_POP_VALUE(ctx, str1); + if (JS_IsException(str2)) + goto exception; + } + cmp = js_string_compare(ctx, str1, str2); + } + if (cmp != 0) + return cmp; + cmp_same: + /* make sort stable: compare array offsets */ + arr = JS_VALUE_TO_PTR(*s->parr); + j1 = JS_VALUE_GET_INT(arr->arr[2 * i1 + 1]); + j2 = JS_VALUE_GET_INT(arr->arr[2 * i2 + 1]); + return (j1 > j2) - (j1 < j2); + +exception: + s->exception = TRUE; + return 0; +} + +static void js_array_sort_swap(size_t i1, size_t i2, void *opaque) +{ + JSArraySortContext *s = opaque; + JSValueArray *arr; + JSValue tmp, *tab; + + arr = JS_VALUE_TO_PTR(*s->parr); + tab = arr->arr; + tmp = tab[2 * i1]; + tab[2 * i1] = tab[2 * i2]; + tab[2 * i2] = tmp; + + tmp = tab[2 * i1 + 1]; + tab[2 * i1 + 1] = tab[2 * i2 + 1]; + tab[2 * i2 + 1] = tmp; +} + +JSValue js_array_sort(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue *pfunc = &argv[0]; + JSObject *p; + JSValue tab_val; + JSGCRef tab_val_ref; + JSValueArray *tab, *arr; + int i, len, n; + JSArraySortContext ss, *s = &ss; + + if (!JS_IsUndefined(*pfunc)) { + if (!JS_IsFunction(ctx, *pfunc)) + return JS_ThrowTypeError(ctx, "not a function"); + } else { + pfunc = NULL; + } + p = js_get_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + + /* create a temporary array for sorting */ + len = p->u.array.len; + tab = js_alloc_value_array(ctx, 0, len * 2); + if (!tab) + return JS_EXCEPTION; + + p = JS_VALUE_TO_PTR(*this_val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + n = 0; + for(i = 0; i < len; i++) { + if (!JS_IsUndefined(arr->arr[i])) { + tab->arr[2 * n] = arr->arr[i]; + tab->arr[2 * n + 1] = JS_NewShortInt(i); + n++; + } + } + /* the end of 'tab' is already filled with JS_UNDEFINED */ + tab_val = JS_VALUE_FROM_PTR(tab); + + JS_PUSH_VALUE(ctx, tab_val); + s->ctx = ctx; + s->exception = FALSE; + s->parr = &tab_val_ref.val; + s->pfunc = pfunc; + rqsort_idx(n, js_array_sort_cmp, js_array_sort_swap, s); + JS_POP_VALUE(ctx, tab_val); + tab = JS_VALUE_TO_PTR(tab_val); + if (s->exception) { + js_free(ctx, tab); + return JS_EXCEPTION; + } + + p = JS_VALUE_TO_PTR(*this_val); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + /* XXX: could resize the array in case it was shrank by the compare function */ + len = min_int(len, p->u.array.len); + for(i = 0; i < len; i++) { + arr->arr[i] = tab->arr[2 * i]; + } + js_free(ctx, tab); + return *this_val; +} + +/**********************************************************************/ + +/* precondition: a and b are not NaN */ +static double js_fmin(double a, double b) +{ + if (a == 0 && b == 0) { + return uint64_as_float64(float64_as_uint64(a) | float64_as_uint64(b)); + } else if (a <= b) { + return a; + } else { + return b; + } +} + +/* precondition: a and b are not NaN */ +static double js_fmax(double a, double b) +{ + if (a == 0 && b == 0) { + return uint64_as_float64(float64_as_uint64(a) & float64_as_uint64(b)); + } else if (a >= b) { + return a; + } else { + return b; + } +} + +JSValue js_math_min_max(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + BOOL is_max = magic; + double r, a; + int i; + + if (unlikely(argc == 0)) { + return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); + } + + if (JS_IsInt(argv[0])) { + int a1, r1 = JS_VALUE_GET_INT(argv[0]); + for(i = 1; i < argc; i++) { + if (!JS_IsInt(argv[i])) { + 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_NewShortInt(r1); + } else { + if (JS_ToNumber(ctx, &r, argv[0])) + return JS_EXCEPTION; + i = 1; + generic_case: + while (i < argc) { + if (JS_ToNumber(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_fround(double a) +{ + return (float)a; +} + +JSValue js_math_imul(JSContext *ctx, JSValue *this_val, + int argc, JSValue *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, (uint32_t)a * (uint32_t)b); +} + +JSValue js_math_clz32(JSContext *ctx, JSValue *this_val, + int argc, JSValue *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); +} + +JSValue js_math_atan2(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + double y, x; + + if (JS_ToNumber(ctx, &y, argv[0])) + return JS_EXCEPTION; + if (JS_ToNumber(ctx, &x, argv[1])) + return JS_EXCEPTION; + return JS_NewFloat64(ctx, js_atan2(y, x)); +} + +JSValue js_math_pow(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + double y, x; + + if (JS_ToNumber(ctx, &x, argv[0])) + return JS_EXCEPTION; + if (JS_ToNumber(ctx, &y, argv[1])) + return JS_EXCEPTION; + return JS_NewFloat64(ctx, js_pow(x, y)); +} + +/* xorshift* random number generator by Marsaglia */ +static uint64_t xorshift64star(uint64_t *pstate) +{ + uint64_t x; + x = *pstate; + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + *pstate = x; + return x * 0x2545F4914F6CDD1D; +} + +JSValue js_math_random(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + double d; + uint64_t v; + + v = xorshift64star(&ctx->random_state); + /* 1.0 <= u.d < 2 */ + d = uint64_as_float64(((uint64_t)0x3ff << 52) | (v >> 12)); + return __JS_NewFloat64(ctx, d - 1.0); +} + +/* typed array */ + +#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) + +static uint8_t typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { + 0, 0, 0, 1, 1, 2, 2, 2, 3 +}; + +static int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValue val) +{ + int v; + /* XXX: should support 53 bit inteers */ + if (JS_ToInt32Sat(ctx, &v, val)) + return -1; + if (v < 0 || v > JS_SHORTINT_MAX) { + JS_ThrowRangeError(ctx, "invalid array index"); + return -1; + } + *plen = v; + return 0; +} + +JSValue js_array_buffer_alloc(JSContext *ctx, uint64_t len) +{ + JSByteArray *arr; + JSValue buffer, obj; + JSGCRef buffer_ref; + JSObject *p; + + if (len > JS_SHORTINT_MAX) + return JS_ThrowRangeError(ctx, "invalid array buffer length"); + arr = js_alloc_byte_array(ctx, len); + if (!arr) + return JS_EXCEPTION; + memset(arr->buf, 0, len); + buffer = JS_VALUE_FROM_PTR(arr); + JS_PUSH_VALUE(ctx, buffer); + obj = JS_NewObjectClass(ctx, JS_CLASS_ARRAY_BUFFER, sizeof(JSArrayBuffer)); + JS_POP_VALUE(ctx, buffer); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_TO_PTR(obj); + p->u.array_buffer.byte_buffer = buffer; + return obj; +} + +JSValue js_array_buffer_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + uint64_t len; + if (!(argc & FRAME_CF_CTOR)) + return JS_ThrowTypeError(ctx, "must be called with new"); + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + return js_array_buffer_alloc(ctx, len); +} + +JSValue js_array_buffer_get_byteLength(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p = js_get_object_class(ctx, *this_val, JS_CLASS_ARRAY_BUFFER); + JSByteArray *arr; + if (!p) + return JS_ThrowTypeError(ctx, "expected an ArrayBuffer"); + arr = JS_VALUE_TO_PTR(p->u.array_buffer.byte_buffer); + return JS_NewShortInt(arr->size); +} + +JSValue js_typed_array_base_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + return JS_ThrowTypeError(ctx, "cannot be called"); +} + +static JSValue js_typed_array_constructor_obj(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + int i, len; + JSValue val, obj; + JSGCRef obj_ref; + JSObject *p; + + p = JS_VALUE_TO_PTR(argv[0]); + if (p->class_id == JS_CLASS_ARRAY) { + len = p->u.array.len; + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + len = p->u.typed_array.len; + } else { + return JS_ThrowTypeError(ctx, "unsupported object class"); + } + val = JS_NewShortInt(len); + obj = js_typed_array_constructor(ctx, NULL, 1 | FRAME_CF_CTOR, &val, magic); + if (JS_IsException(obj)) + return obj; + + for(i = 0; i < len; i++) { + JS_PUSH_VALUE(ctx, obj); + val = JS_GetProperty(ctx, argv[0], JS_NewShortInt(i)); + JS_POP_VALUE(ctx, obj); + if (JS_IsException(val)) + return val; + JS_PUSH_VALUE(ctx, obj); + val = JS_SetPropertyInternal(ctx, obj, JS_NewShortInt(i), val, FALSE); + JS_POP_VALUE(ctx, obj); + if (JS_IsException(val)) + return val; + } + return obj; +} + +JSValue js_typed_array_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + int size_log2; + uint64_t len, offset, byte_length; + JSObject *p; + JSByteArray *arr; + JSValue buffer, obj; + JSGCRef buffer_ref; + + if (!(argc & FRAME_CF_CTOR)) + return JS_ThrowTypeError(ctx, "must be called with new"); + size_log2 = typed_array_size_log2[magic - JS_CLASS_UINT8C_ARRAY]; + if (!JS_IsObject(ctx, argv[0])) { + if (JS_ToIndex(ctx, &len, argv[0])) + return JS_EXCEPTION; + buffer = js_array_buffer_alloc(ctx, len << size_log2); + if (JS_IsException(buffer)) + return JS_EXCEPTION; + offset = 0; + } else { + p = JS_VALUE_TO_PTR(argv[0]); + if (p->class_id == JS_CLASS_ARRAY_BUFFER) { + arr = JS_VALUE_TO_PTR(p->u.array_buffer.byte_buffer); + byte_length = arr->size; + if (JS_ToIndex(ctx, &offset, argv[1])) + return JS_EXCEPTION; + if ((offset & ((1 << size_log2) - 1)) != 0 || + offset > byte_length) + return JS_ThrowRangeError(ctx, "invalid offset"); + if (JS_IsUndefined(argv[2])) { + if ((byte_length & ((1 << size_log2) - 1)) != 0) + goto invalid_length; + len = (byte_length - offset) >> size_log2; + } else { + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + if ((offset + (len << size_log2)) > byte_length) { + invalid_length: + return JS_ThrowRangeError(ctx, "invalid length"); + } + } + buffer = argv[0]; + offset >>= size_log2; + } else { + return js_typed_array_constructor_obj(ctx, this_val, + argc, argv, magic); + } + } + + JS_PUSH_VALUE(ctx, buffer); + obj = JS_NewObjectClass(ctx, magic, sizeof(JSTypedArray)); + JS_POP_VALUE(ctx, buffer); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_TO_PTR(obj); + p->u.typed_array.buffer = buffer; + p->u.typed_array.offset = offset; + p->u.typed_array.len = len; + return obj; +} + +static JSObject *get_typed_array(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (!JS_IsObject(ctx, val)) + goto fail; + p = JS_VALUE_TO_PTR(val); + if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { + fail: + JS_ThrowTypeError(ctx, "not a TypedArray"); + return NULL; + } + return p; +} + +JSValue js_typed_array_get_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + JSObject *p; + int size_log2; + + p = get_typed_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + size_log2 = typed_array_size_log2[p->class_id - JS_CLASS_UINT8C_ARRAY]; + switch(magic) { + default: + case 0: + return JS_NewShortInt(p->u.typed_array.len); + case 1: + return JS_NewShortInt(p->u.typed_array.len << size_log2); + case 2: + return JS_NewShortInt(p->u.typed_array.offset << size_log2); + case 3: + return p->u.typed_array.buffer; + } +} + +JSValue js_typed_array_subarray(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p, *p1; + JSByteArray *arr; + int start, final, len; + uint32_t offset, count; + JSValue obj; + + p = get_typed_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + len = p->u.typed_array.len; + if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) + return JS_EXCEPTION; + if (JS_IsUndefined(argv[1])) { + final = len; + } else { + if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) + return JS_EXCEPTION; + } + p = JS_VALUE_TO_PTR(*this_val); + offset = p->u.typed_array.offset + start; + count = max_int(final - start, 0); + + /* check offset and count */ + p1 = JS_VALUE_TO_PTR(p->u.typed_array.buffer); + arr = JS_VALUE_TO_PTR(p1->u.array_buffer.byte_buffer); + if (offset + count > arr->size) + return JS_ThrowRangeError(ctx, "invalid length"); + + obj = JS_NewObjectClass(ctx, p->class_id, sizeof(JSTypedArray)); + if (JS_IsException(obj)) + return JS_EXCEPTION; + p = JS_VALUE_TO_PTR(*this_val); + p1 = JS_VALUE_TO_PTR(obj); + p1->u.typed_array.buffer = p->u.typed_array.buffer; + p1->u.typed_array.offset = offset; + p1->u.typed_array.len = count; + return obj; +} + +JSValue js_typed_array_set(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSObject *p, *p1; + uint32_t dst_len, src_len, i; + int offset; + + p = get_typed_array(ctx, *this_val); + if (!p) + return JS_EXCEPTION; + if (argc > 1) { + if (JS_ToInt32Sat(ctx, &offset, argv[1])) + return JS_EXCEPTION; + } else { + offset = 0; + } + if (offset < 0) + goto range_error; + if (!JS_IsObject(ctx, argv[0])) + return JS_ThrowTypeErrorNotAnObject(ctx); + p = JS_VALUE_TO_PTR(*this_val); + dst_len = p->u.typed_array.len; + p1 = JS_VALUE_TO_PTR(argv[0]); + if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && + p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + src_len = p1->u.typed_array.len; + if (src_len > dst_len || offset > dst_len - src_len) + goto range_error; + if (p1->class_id == p->class_id) { + JSObject *src_buffer, *dst_buffer; + JSByteArray *src_arr, *dst_arr; + int shift = typed_array_size_log2[p->class_id - JS_CLASS_UINT8C_ARRAY]; + dst_buffer = JS_VALUE_TO_PTR(p->u.typed_array.buffer); + dst_arr = JS_VALUE_TO_PTR(dst_buffer->u.array_buffer.byte_buffer); + src_buffer = JS_VALUE_TO_PTR(p1->u.typed_array.buffer); + src_arr = JS_VALUE_TO_PTR(src_buffer->u.array_buffer.byte_buffer); + /* same type: must copy to preserve float bits */ + memmove(dst_arr->buf + ((p->u.typed_array.offset + offset) << shift), + src_arr->buf + (p1->u.typed_array.offset << shift), + src_len << shift); + goto done; + } + } else { + if (js_get_length32(ctx, (uint32_t *)&src_len, argv[0])) + return JS_EXCEPTION; + if (src_len > dst_len || offset > dst_len - src_len) { + range_error: + return JS_ThrowRangeError(ctx, "invalid array length"); + } + } + for(i = 0; i < src_len; i++) { + JSValue val; + val = JS_GetPropertyUint32(ctx, argv[0], i); + if (JS_IsException(val)) + return JS_EXCEPTION; + val = JS_SetPropertyUint32(ctx, *this_val, offset + i, val); + if (JS_IsException(val)) + return JS_EXCEPTION; + } + done: + return JS_UNDEFINED; +} + +/* Date */ + +JSValue js_date_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + return JS_ThrowTypeError(ctx, "only Date.now() is supported"); +} + +/* global */ + +JSValue js_global_eval(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue val; + + if (!JS_IsString(ctx, argv[0])) + return argv[0]; + val = JS_Parse2(ctx, argv[0], NULL, 0, "", JS_EVAL_RETVAL); + if (JS_IsException(val)) + return val; + return JS_Run(ctx, val); +} + +JSValue js_global_isNaN(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + double d; + if (unlikely(JS_ToNumber(ctx, &d, argv[0]))) + return JS_EXCEPTION; + return JS_NewBool(isnan(d)); +} + +JSValue js_global_isFinite(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + double d; + if (unlikely(JS_ToNumber(ctx, &d, argv[0]))) + return JS_EXCEPTION; + return JS_NewBool(isfinite(d)); +} + +/* JSON */ + +JSValue js_json_parse(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue val; + + val = JS_ToString(ctx, argv[0]); + if (JS_IsException(val)) + return val; + return JS_Parse2(ctx, val, NULL, 0, "", JS_EVAL_JSON); +} + +static int js_to_quoted_string(JSContext *ctx, StringBuffer *b, JSValue str) +{ + int i, c; + JSStringCharBuf buf; + JSString *p; + JSGCRef str_ref; + size_t clen; + + JS_PUSH_VALUE(ctx, str); + string_buffer_putc(ctx, b, '\"'); + + i = 0; + for(;;) { + /* XXX: inefficient */ + p = get_string_ptr(ctx, &buf, str_ref.val); + if (i >= p->len) + break; + c = utf8_get(p->buf + i, &clen); + i += clen; + + 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: + string_buffer_putc(ctx, b, '\\'); + string_buffer_putc(ctx, b, c); + break; + default: + if (c < 32 || (c >= 0xd800 && c < 0xe000)) { + char buf[7]; + js_snprintf(buf, sizeof(buf), "\\u%04x", c); + string_buffer_puts(ctx, b, buf); + } else { + string_buffer_putc(ctx, b, c); + } + break; + } + } + string_buffer_putc(ctx, b, '\"'); + JS_POP_VALUE(ctx, str); + return 0; +} + +#define JSON_REC_SIZE 3 + +static int check_circular_ref(JSContext *ctx, JSValue *stack_top, JSValue val) +{ + JSValue *sp; + for(sp = ctx->sp; sp < stack_top; sp += JSON_REC_SIZE) { + if (sp[0] == val) { + JS_ThrowTypeError(ctx, "circular reference"); + return -1; + } + } + return 0; +} + +/* XXX: no space nor replacer */ +JSValue js_json_stringify(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue obj, *stack_top; + StringBuffer b_s, *b = &b_s; + int idx, ret; + +#if 0 + if (JS_IsNumber(ctx, *pspace)) { + int n; + if (JS_ToInt32Clamp(ctx, &n, *pspace, 0, 10, 0)) + return JS_EXCEPTION; + *pspace = JS_NewStringLen(ctx, " ", n); + } else if (JS_IsString(ctx, *pspace)) { + *pspace = js_sub_string(ctx, *pspace, 0, 10); + } else { + *pspace = js_get_atom(ctx, JS_ATOM_empty); + } +#endif + string_buffer_push(ctx, b, 0); + stack_top = ctx->sp; + + ret = JS_StackCheck(ctx, JSON_REC_SIZE); + if (ret) + goto fail; + *--ctx->sp = JS_NULL; /* keys */ + *--ctx->sp = JS_NewShortInt(0); /* prop index */ + *--ctx->sp = argv[0]; /* object */ + + while (ctx->sp < stack_top) { + obj = ctx->sp[0]; + if (JS_IsFunction(ctx, obj)) { + goto output_null; + } else if (JS_IsObject(ctx, obj)) { + JSObject *p = JS_VALUE_TO_PTR(obj); + idx = JS_VALUE_GET_INT(ctx->sp[1]); + if (p->class_id == JS_CLASS_ARRAY) { + JSValueArray *arr; + JSValue val; + + /* array */ + if (idx == 0) + string_buffer_putc(ctx, b, '['); + p = JS_VALUE_TO_PTR(ctx->sp[0]); + if (idx >= p->u.array.len) { + /* end of array */ + string_buffer_putc(ctx, b, ']'); + ctx->sp += JSON_REC_SIZE; + } else { + if (idx != 0) + string_buffer_putc(ctx, b, ','); + ctx->sp[1] = JS_NewShortInt(idx + 1); + ret = JS_StackCheck(ctx, JSON_REC_SIZE); + if (ret) + goto fail; + p = JS_VALUE_TO_PTR(ctx->sp[0]); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + val = arr->arr[idx]; + if (check_circular_ref(ctx, stack_top, val)) + goto fail; + *--ctx->sp = JS_NULL; + *--ctx->sp = JS_NewShortInt(0); + *--ctx->sp = val; + } + } else { + JSValueArray *arr; + JSValue val, prop; + JSGCRef val_ref; + int saved_idx; + + /* object */ + if (idx == 0) { + string_buffer_putc(ctx, b, '{'); + ctx->sp[2] = js_object_keys(ctx, NULL, 1, &ctx->sp[0]); + if (JS_IsException(ctx->sp[2])) + goto fail; + } + saved_idx = idx; + for(;;) { + p = JS_VALUE_TO_PTR(ctx->sp[2]); /* keys */ + if (idx >= p->u.array.len) { + /* end of object */ + string_buffer_putc(ctx, b, '}'); + ctx->sp += JSON_REC_SIZE; + goto end_obj; + } else { + arr = JS_VALUE_TO_PTR(p->u.array.tab); + prop = JS_ToPropertyKey(ctx, arr->arr[idx]); + val = JS_GetProperty(ctx, ctx->sp[0], prop); + if (JS_IsException(val)) + goto fail; + /* skip undefined properties */ + if (!JS_IsUndefined(val)) + break; + idx++; + } + } + JS_PUSH_VALUE(ctx, val); + if (saved_idx != 0) + string_buffer_putc(ctx, b, ','); + ctx->sp[1] = JS_NewShortInt(idx + 1); + p = JS_VALUE_TO_PTR(ctx->sp[2]); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + ret = js_to_quoted_string(ctx, b, arr->arr[idx]); + string_buffer_putc(ctx, b, ':'); + ret |= JS_StackCheck(ctx, JSON_REC_SIZE); + JS_POP_VALUE(ctx, val); + if (ret) + goto fail; + if (check_circular_ref(ctx, stack_top, val)) + goto fail; + *--ctx->sp = JS_NULL; + *--ctx->sp = JS_NewShortInt(0); + *--ctx->sp = val; + end_obj: ; + } + } else if (JS_IsNumber(ctx, obj)) { + double d; + ret = JS_ToNumber(ctx, &d, obj); + if (ret) + goto fail; + if (!isfinite(d)) + goto output_null; + goto to_string; + } else if (JS_IsBool(obj)) { + to_string: + if (string_buffer_concat(ctx, b, obj)) + goto fail; + ctx->sp += JSON_REC_SIZE; + } else if (JS_IsString(ctx, obj)) { + if (js_to_quoted_string(ctx, b, obj)) + goto fail; + ctx->sp += JSON_REC_SIZE; + } else { + output_null: + string_buffer_concat(ctx, b, js_get_atom(ctx, JS_ATOM_null)); + ctx->sp += JSON_REC_SIZE; + } + } + return string_buffer_pop(ctx, b); + + fail: + ctx->sp = stack_top; + string_buffer_pop(ctx, b); + return JS_EXCEPTION; +} + +/**********************************************************************/ +/* regexp */ + +typedef enum { +#define REDEF(id, size) REOP_ ## id, +#include "mquickjs_opcode.h" +#undef REDEF + REOP_COUNT, +} REOPCodeEnum; + +#define CAPTURE_COUNT_MAX 255 +#define REGISTER_COUNT_MAX 255 + +typedef struct { +#ifdef DUMP_REOP + const char *name; +#endif + uint8_t size; +} REOpCode; + +static const REOpCode reopcode_info[REOP_COUNT] = { +#ifdef DUMP_REOP +#define REDEF(id, size) { #id, size }, +#else +#define REDEF(id, size) { size }, +#endif +#include "mquickjs_opcode.h" +#undef REDEF +}; + +#define LRE_FLAG_GLOBAL (1 << 0) +#define LRE_FLAG_IGNORECASE (1 << 1) +#define LRE_FLAG_MULTILINE (1 << 2) +#define LRE_FLAG_DOTALL (1 << 3) +#define LRE_FLAG_UNICODE (1 << 4) +#define LRE_FLAG_STICKY (1 << 5) + +#define RE_HEADER_FLAGS 0 +#define RE_HEADER_CAPTURE_COUNT 2 +#define RE_HEADER_REGISTER_COUNT 3 + +#define RE_HEADER_LEN 4 + +#define CLASS_RANGE_BASE 0x40000000 + +typedef enum { + CHAR_RANGE_d, + CHAR_RANGE_D, + CHAR_RANGE_s, + CHAR_RANGE_S, + CHAR_RANGE_w, + CHAR_RANGE_W, +} CharRangeEnum; + +static int lre_get_capture_count(const uint8_t *bc_buf) +{ + return bc_buf[RE_HEADER_CAPTURE_COUNT]; +} + +static int lre_get_alloc_count(const uint8_t *bc_buf) +{ + return bc_buf[RE_HEADER_CAPTURE_COUNT] * 2 + bc_buf[RE_HEADER_REGISTER_COUNT]; +} + +static int lre_get_flags(const uint8_t *bc_buf) +{ + return get_u16(bc_buf + RE_HEADER_FLAGS); +} + +#ifdef DUMP_REOP +static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, + int buf_len) +{ + int pos, len, opcode, bc_len, re_flags; + uint32_t val, val2; + + assert(buf_len >= RE_HEADER_LEN); + re_flags = lre_get_flags(buf); + bc_len = buf_len - RE_HEADER_LEN; + + printf("flags: 0x%x capture_count=%d reg_count=%d bytecode_len=%d\n", + re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_REGISTER_COUNT], + bc_len); + + buf += RE_HEADER_LEN; + + pos = 0; + while (pos < bc_len) { + printf("%5u: ", pos); + opcode = buf[pos]; + len = reopcode_info[opcode].size; + if (opcode >= REOP_COUNT) { + printf(" invalid opcode=0x%02x\n", opcode); + break; + } + if ((pos + len) > bc_len) { + printf(" buffer overflow (opcode=0x%02x)\n", opcode); + break; + } + printf("%s", reopcode_info[opcode].name); + switch(opcode) { + case REOP_char1: + case REOP_char2: + case REOP_char3: + case REOP_char4: + { + int i, n; + n = opcode - REOP_char1 + 1; + for(i = 0; i < n; i++) { + val = buf[pos + 1 + i]; + if (val >= ' ' && val <= 126) + printf(" '%c'", val); + else + printf(" 0x%2x", val); + } + } + break; + case REOP_goto: + case REOP_split_goto_first: + case REOP_split_next_first: + case REOP_lookahead: + case REOP_negative_lookahead: + val = get_u32(buf + pos + 1); + val += (pos + 5); + printf(" %u", val); + break; + case REOP_loop: + val2 = buf[pos + 1]; + val = get_u32(buf + pos + 2); + val += (pos + 6); + printf(" r%u, %u", val2, val); + break; + case REOP_loop_split_goto_first: + case REOP_loop_split_next_first: + case REOP_loop_check_adv_split_goto_first: + case REOP_loop_check_adv_split_next_first: + { + uint32_t limit; + val2 = buf[pos + 1]; + limit = get_u32(buf + pos + 2); + val = get_u32(buf + pos + 6); + val += (pos + 10); + printf(" r%u, %u, %u", val2, limit, val); + } + break; + case REOP_save_start: + case REOP_save_end: + case REOP_back_reference: + case REOP_back_reference_i: + printf(" %u", buf[pos + 1]); + break; + case REOP_save_reset: + printf(" %u %u", buf[pos + 1], buf[pos + 2]); + break; + case REOP_set_i32: + val = buf[pos + 1]; + val2 = get_u32(buf + pos + 2); + printf(" r%u, %d", val, val2); + break; + case REOP_set_char_pos: + case REOP_check_advance: + val = buf[pos + 1]; + printf(" r%u", val); + break; + case REOP_range8: + { + int n, i; + n = buf[pos + 1]; + len += n * 2; + for(i = 0; i < n * 2; i++) { + val = buf[pos + 2 + i]; + printf(" 0x%02x", val); + } + } + break; + case REOP_range: + { + int n, i; + n = get_u16(buf + pos + 1); + len += n * 8; + for(i = 0; i < n * 2; i++) { + val = get_u32(buf + pos + 3 + i * 4); + printf(" 0x%05x", val); + } + } + break; + default: + break; + } + printf("\n"); + pos += len; + } +} +#endif + +static void re_emit_op(JSParseState *s, int op) +{ + emit_u8(s, op); +} + +static void re_emit_op_u8(JSParseState *s, int op, uint32_t val) +{ + emit_u8(s, op); + emit_u8(s, val); +} + +static void re_emit_op_u16(JSParseState *s, int op, uint32_t val) +{ + emit_u8(s, op); + emit_u16(s, val); +} + +/* return the offset of the u32 value */ +static int re_emit_op_u32(JSParseState *s, int op, uint32_t val) +{ + int pos; + emit_u8(s, op); + pos = s->byte_code_len; + emit_u32(s, val); + return pos; +} + +static int re_emit_goto(JSParseState *s, int op, uint32_t val) +{ + int pos; + emit_u8(s, op); + pos = s->byte_code_len; + emit_u32(s, val - (pos + 4)); + return pos; +} + +static int re_emit_goto_u8(JSParseState *s, int op, uint32_t arg, uint32_t val) +{ + int pos; + emit_u8(s, op); + emit_u8(s, arg); + pos = s->byte_code_len; + emit_u32(s, val - (pos + 4)); + return pos; +} + +static int re_emit_goto_u8_u32(JSParseState *s, int op, uint32_t arg0, uint32_t arg1, uint32_t val) +{ + int pos; + emit_u8(s, op); + emit_u8(s, arg0); + emit_u32(s, arg1); + pos = s->byte_code_len; + emit_u32(s, val - (pos + 4)); + return pos; +} + +static void re_emit_char(JSParseState *s, int c) +{ + uint8_t buf[4]; + size_t n, i; + n = unicode_to_utf8(buf, c); + re_emit_op(s, REOP_char1 + n - 1); + for(i = 0; i < n; i++) + emit_u8(s, buf[i]); +} + +static void re_parse_expect(JSParseState *s, int c) +{ + if (s->source_buf[s->buf_pos] != c) + return js_parse_error(s, "expecting '%c'", c); + s->buf_pos++; +} + +/* return JS_SHORTINT_MAX in case of overflow */ +static int parse_digits(const uint8_t **pp) +{ + const uint8_t *p; + uint64_t v; + int c; + + p = *pp; + v = 0; + for(;;) { + c = *p; + if (c < '0' || c > '9') + break; + v = v * 10 + c - '0'; + if (v >= JS_SHORTINT_MAX) + v = JS_SHORTINT_MAX; + p++; + } + *pp = p; + return v; +} + +/* need_check_adv: false if the opcodes always advance the char pointer + need_capture_init: true if all the captures in the atom are not set +*/ +static BOOL re_need_check_adv_and_capture_init(BOOL *pneed_capture_init, + const uint8_t *bc_buf, int bc_buf_len) +{ + int pos, opcode, len; + uint32_t val; + BOOL need_check_adv, need_capture_init; + + need_check_adv = TRUE; + need_capture_init = FALSE; + pos = 0; + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + switch(opcode) { + case REOP_range8: + val = bc_buf[pos + 1]; + len += val * 2; + need_check_adv = FALSE; + break; + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + need_check_adv = FALSE; + break; + case REOP_char1: + case REOP_char2: + case REOP_char3: + case REOP_char4: + case REOP_dot: + case REOP_any: + case REOP_space: + case REOP_not_space: + need_check_adv = FALSE; + break; + case REOP_line_start: + case REOP_line_start_m: + case REOP_line_end: + case REOP_line_end_m: + case REOP_set_i32: + case REOP_set_char_pos: + case REOP_word_boundary: + case REOP_not_word_boundary: + /* no effect */ + break; + case REOP_save_start: + case REOP_save_end: + case REOP_save_reset: + break; + default: + /* safe behavior: we cannot predict the outcome */ + need_capture_init = TRUE; + goto done; + } + pos += len; + } + done: + *pneed_capture_init = need_capture_init; + return need_check_adv; +} + +/* return the character or a class range (>= CLASS_RANGE_BASE) if inclass + = TRUE */ +static int get_class_atom(JSParseState *s, BOOL inclass) +{ + const uint8_t *p; + uint32_t c; + int ret; + size_t len; + + p = s->source_buf + s->buf_pos; + c = *p; + switch(c) { + case '\\': + p++; + c = *p++; + switch(c) { + case 'd': + c = CHAR_RANGE_d; + goto class_range; + case 'D': + c = CHAR_RANGE_D; + goto class_range; + case 's': + c = CHAR_RANGE_s; + goto class_range; + case 'S': + c = CHAR_RANGE_S; + goto class_range; + case 'w': + c = CHAR_RANGE_w; + goto class_range; + case 'W': + c = CHAR_RANGE_W; + class_range: + c += CLASS_RANGE_BASE; + break; + case 'c': + c = *p; + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (((c >= '0' && c <= '9') || c == '_') && + inclass && !s->is_unicode)) { /* Annex B.1.4 */ + c &= 0x1f; + p++; + } else if (s->is_unicode) { + goto invalid_escape; + } else { + /* otherwise return '\' and 'c' */ + p--; + c = '\\'; + } + break; + case '-': + if (!inclass && s->is_unicode) + goto invalid_escape; + break; + case '^': + case '$': + case '\\': + case '.': + case '*': + case '+': + case '?': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case '|': + case '/': + /* always valid to escape these characters */ + break; + default: + p--; + ret = js_parse_escape(p, &len); + if (ret < 0) { + if (s->is_unicode) { + invalid_escape: + s->buf_pos = p - s->source_buf; + js_parse_error(s, "invalid escape sequence in regular expression"); + } else { + goto normal_char; + } + } + p += len; + c = ret; + break; + } + break; + case '\0': + case '/': /* safety for end of regexp in JS parser */ + if ((p - s->source_buf) >= s->buf_len) + js_parse_error(s, "unexpected end"); + goto normal_char; + default: + normal_char: + /* normal char */ + ret = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &len); + /* Note: should not fail with normal JS strings */ + if (ret < 0) + js_parse_error(s, "malformed unicode char"); + p += len; + c = ret; + break; + } + s->buf_pos = p - s->source_buf; + return c; +} + +/* code point ranges for Zs,Zl or Zp property */ +static const uint16_t char_range_s[] = { + 0x0009, 0x000D + 1, + 0x0020, 0x0020 + 1, + 0x00A0, 0x00A0 + 1, + 0x1680, 0x1680 + 1, + 0x2000, 0x200A + 1, + /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ + /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ + 0x2028, 0x2029 + 1, + 0x202F, 0x202F + 1, + 0x205F, 0x205F + 1, + 0x3000, 0x3000 + 1, + /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ + 0xFEFF, 0xFEFF + 1, +}; + +static const uint16_t char_range_w[] = { + 0x0030, 0x0039 + 1, + 0x0041, 0x005A + 1, + 0x005F, 0x005F + 1, + 0x0061, 0x007A + 1, +}; + +static void re_emit_range_base1(JSParseState *s, const uint16_t *tab, int n) +{ + int i; + for(i = 0; i < n; i++) + emit_u32(s, tab[i]); +} + +static void re_emit_range_base(JSParseState *s, int c) +{ + BOOL invert; + invert = c & 1; + if (invert) + emit_u32(s, 0); + switch(c & ~1) { + case CHAR_RANGE_d: + emit_u32(s, 0x30); + emit_u32(s, 0x39 + 1); + break; + case CHAR_RANGE_s: + re_emit_range_base1(s, char_range_s, countof(char_range_s)); + break; + case CHAR_RANGE_w: + re_emit_range_base1(s, char_range_w, countof(char_range_w)); + break; + default: + abort(); + } + if (invert) + emit_u32(s, 0x110000); +} + +static int range_sort_cmp(size_t i1, size_t i2, void *opaque) +{ + uint8_t *tab = opaque; + return get_u32(&tab[8 * i1]) - get_u32(&tab[8 * i2]); +} + +static void range_sort_swap(size_t i1, size_t i2, void *opaque) +{ + uint8_t *tab = opaque; + uint64_t tmp; + tmp = get_u64(&tab[8 * i1]); + put_u64(&tab[8 * i1], get_u64(&tab[8 * i2])); + put_u64(&tab[8 * i2], tmp); +} + +/* merge consecutive intervals, remove empty intervals and handle overlapping intervals */ +static int range_compress(uint8_t *tab, int len) +{ + int i, j; + uint32_t start, end, start2, end2; + + i = 0; + j = 0; + while (i < len) { + start = get_u32(&tab[8 * i]); + end = get_u32(&tab[8 * i + 4]); + if (start == end) { + /* empty interval : remove */ + } else if ((i + 1) < len) { + start2 = get_u32(&tab[8 * i + 8]); + end2 = get_u32(&tab[8 * i + 12]); + if (end < start2) { + goto copy; + } else { + /* union of the intervals */ + put_u32(&tab[8 * i + 8], start); + put_u32(&tab[8 * i + 12], max_uint32(end, end2)); + } + } else { + copy: + put_u32(&tab[8 * j], start); + put_u32(&tab[8 * j + 4], end); + j++; + } + i++; + } + return j; +} + +static void re_range_optimize(JSParseState *s, int range_start, BOOL invert) +{ + int n, n1; + JSByteArray *arr; + + n = (unsigned)(s->byte_code_len - range_start) / 8; + + arr = JS_VALUE_TO_PTR(s->byte_code); + rqsort_idx(n, range_sort_cmp, range_sort_swap, arr->buf + range_start); + + /* must compress before inverting */ + n1 = range_compress(arr->buf + range_start, n); + s->byte_code_len -= (n - n1) * 8; + + if (invert) { + emit_insert(s, range_start, 4); + arr = JS_VALUE_TO_PTR(s->byte_code); + put_u32(arr->buf + range_start, 0); + emit_u32(s, 0x110000); + arr = JS_VALUE_TO_PTR(s->byte_code); + n = n1 + 1; + n1 = range_compress(arr->buf + range_start, n); + s->byte_code_len -= (n - n1) * 8; + } + n = n1; + + if (n > 65534) + js_parse_error(s, "range too big"); + + /* compress to 8 bit if possible */ + /* XXX: adjust threshold */ + if (n > 0 && n < 16) { + uint8_t *tab = arr->buf + range_start; + int c, i; + c = get_u32(&tab[8 * (n - 1) + 4]); + if (c < 254 || (c == 0x110000 && + get_u32(&tab[8 * (n - 1)]) < 254)) { + s->byte_code_len = range_start - 3; + re_emit_op_u8(s, REOP_range8, n); + for(i = 0; i < 2 * n; i++) { + c = get_u32(&tab[4 * i]); + if (c == 0x110000) + c = 0xff; + emit_u8(s, c); + } + goto done; + } + } + + put_u16(arr->buf + range_start - 2, n); + done: ; +} + +/* add the intersection of the two intervals and if offset != 0 the + translated interval */ +static void add_interval_intersect(JSParseState *s, + uint32_t start, uint32_t end, + uint32_t start1, uint32_t end1, + int offset) +{ + start = max_uint32(start, start1); + end = min_uint32(end, end1); + if (start < end) { + emit_u32(s, start); + emit_u32(s, end); + if (offset != 0) { + emit_u32(s, start + offset); + emit_u32(s, end + offset); + } + } +} + +static void re_parse_char_class(JSParseState *s) +{ + uint32_t c1, c2; + BOOL invert; + int range_start; + + s->buf_pos++; /* skip '[' */ + + invert = FALSE; + if (s->source_buf[s->buf_pos] == '^') { + s->buf_pos++; + invert = TRUE; + } + + re_emit_op_u16(s, REOP_range, 0); + range_start = s->byte_code_len; + + for(;;) { + if (s->source_buf[s->buf_pos] == ']') + break; + + c1 = get_class_atom(s, TRUE); + if (s->source_buf[s->buf_pos] == '-' && s->source_buf[s->buf_pos + 1] != ']') { + s->buf_pos++; + if (c1 >= CLASS_RANGE_BASE) + goto invalid_class_range; + c2 = get_class_atom(s, TRUE); + if (c2 >= CLASS_RANGE_BASE) + goto invalid_class_range; + if (c2 < c1) { + invalid_class_range: + js_parse_error(s, "invalid class range"); + } + goto add_range; + } else { + if (c1 >= CLASS_RANGE_BASE) { + re_emit_range_base(s, c1 - CLASS_RANGE_BASE); + } else { + c2 = c1; + add_range: + c2++; + if (s->ignore_case) { + /* add the intervals exclude the cased characters */ + add_interval_intersect(s, c1, c2, 0, 'A', 0); + add_interval_intersect(s, c1, c2, 'Z' + 1, 'a', 0); + add_interval_intersect(s, c1, c2, 'z' + 1, INT32_MAX, 0); + /* include all the possible cases */ + add_interval_intersect(s, c1, c2, 'A', 'Z' + 1, 32); + add_interval_intersect(s, c1, c2, 'a', 'z' + 1, -32); + } else { + emit_u32(s, c1); + emit_u32(s, c2); + } + } + } + } + s->buf_pos++; /* skip ']' */ + re_range_optimize(s, range_start, invert); +} + +static void re_parse_quantifier(JSParseState *s, int last_atom_start, int last_capture_count) +{ + int c, quant_min, quant_max; + JSByteArray *arr; + BOOL greedy; + const uint8_t *p; + + p = s->source_buf + s->buf_pos; + c = *p; + switch(c) { + case '*': + p++; + quant_min = 0; + quant_max = JS_SHORTINT_MAX; + goto quantifier; + case '+': + p++; + quant_min = 1; + quant_max = JS_SHORTINT_MAX; + goto quantifier; + case '?': + p++; + quant_min = 0; + quant_max = 1; + goto quantifier; + case '{': + { + if (!is_digit(p[1])) + goto invalid_quant_count; + p++; + quant_min = parse_digits(&p); + quant_max = quant_min; + if (*p == ',') { + p++; + if (is_digit(*p)) { + quant_max = parse_digits(&p); + if (quant_max < quant_min) { + invalid_quant_count: + js_parse_error(s, "invalid repetition count"); + } + } else { + quant_max = JS_SHORTINT_MAX; /* infinity */ + } + } + s->buf_pos = p - s->source_buf; + re_parse_expect(s, '}'); + p = s->source_buf + s->buf_pos; + } + quantifier: + greedy = TRUE; + + if (*p == '?') { + p++; + greedy = FALSE; + } + s->buf_pos = p - s->source_buf; + + if (last_atom_start < 0) + js_parse_error(s, "nothing to repeat"); + { + BOOL need_capture_init, add_zero_advance_check; + int len, pos; + + /* the spec tells that if there is no advance when + running the atom after the first quant_min times, + then there is no match. We remove this test when we + are sure the atom always advances the position. */ + arr = JS_VALUE_TO_PTR(s->byte_code); + add_zero_advance_check = + re_need_check_adv_and_capture_init(&need_capture_init, + arr->buf + last_atom_start, + s->byte_code_len - last_atom_start); + + /* general case: need to reset the capture at each + iteration. We don't do it if there are no captures + in the atom or if we are sure all captures are + initialized in the atom. If quant_min = 0, we still + need to reset once the captures in case the atom + does not match. */ + if (need_capture_init && last_capture_count != s->capture_count) { + emit_insert(s, last_atom_start, 3); + int pos = last_atom_start; + arr = JS_VALUE_TO_PTR(s->byte_code); + arr->buf[pos++] = REOP_save_reset; + arr->buf[pos++] = last_capture_count; + arr->buf[pos++] = s->capture_count - 1; + } + + len = s->byte_code_len - last_atom_start; + if (quant_min == 0) { + /* need to reset the capture in case the atom is + not executed */ + if (!need_capture_init && last_capture_count != s->capture_count) { + emit_insert(s, last_atom_start, 3); + arr = JS_VALUE_TO_PTR(s->byte_code); + arr->buf[last_atom_start++] = REOP_save_reset; + arr->buf[last_atom_start++] = last_capture_count; + arr->buf[last_atom_start++] = s->capture_count - 1; + } + if (quant_max == 0) { + s->byte_code_len = last_atom_start; + } else if (quant_max == 1 || quant_max == JS_SHORTINT_MAX) { + BOOL has_goto = (quant_max == JS_SHORTINT_MAX); + emit_insert(s, last_atom_start, 5 + add_zero_advance_check * 2); + arr = JS_VALUE_TO_PTR(s->byte_code); + arr->buf[last_atom_start] = REOP_split_goto_first + + greedy; + put_u32(arr->buf + last_atom_start + 1, + len + 5 * has_goto + add_zero_advance_check * 2 * 2); + if (add_zero_advance_check) { + arr->buf[last_atom_start + 1 + 4] = REOP_set_char_pos; + arr->buf[last_atom_start + 1 + 4 + 1] = 0; + re_emit_op_u8(s, REOP_check_advance, 0); + } + if (has_goto) + re_emit_goto(s, REOP_goto, last_atom_start); + } else { + emit_insert(s, last_atom_start, 11 + add_zero_advance_check * 2); + pos = last_atom_start; + arr = JS_VALUE_TO_PTR(s->byte_code); + arr->buf[pos++] = REOP_split_goto_first + greedy; + put_u32(arr->buf + pos, 6 + add_zero_advance_check * 2 + len + 10); + pos += 4; + + arr->buf[pos++] = REOP_set_i32; + arr->buf[pos++] = 0; + put_u32(arr->buf + pos, quant_max); + pos += 4; + last_atom_start = pos; + if (add_zero_advance_check) { + arr->buf[pos++] = REOP_set_char_pos; + arr->buf[pos++] = 0; + } + re_emit_goto_u8_u32(s, (add_zero_advance_check ? REOP_loop_check_adv_split_next_first : REOP_loop_split_next_first) - greedy, 0, quant_max, last_atom_start); + } + } else if (quant_min == 1 && quant_max == JS_SHORTINT_MAX && + !add_zero_advance_check) { + re_emit_goto(s, REOP_split_next_first - greedy, + last_atom_start); + } else { + if (quant_min == quant_max) + add_zero_advance_check = FALSE; + emit_insert(s, last_atom_start, 6 + add_zero_advance_check * 2); + /* Note: we assume the string length is < JS_SHORTINT_MAX */ + pos = last_atom_start; + arr = JS_VALUE_TO_PTR(s->byte_code); + arr->buf[pos++] = REOP_set_i32; + arr->buf[pos++] = 0; + put_u32(arr->buf + pos, quant_max); + pos += 4; + last_atom_start = pos; + if (add_zero_advance_check) { + arr->buf[pos++] = REOP_set_char_pos; + arr->buf[pos++] = 0; + } + if (quant_min == quant_max) { + /* a simple loop is enough */ + re_emit_goto_u8(s, REOP_loop, 0, last_atom_start); + } else { + re_emit_goto_u8_u32(s, (add_zero_advance_check ? REOP_loop_check_adv_split_next_first : REOP_loop_split_next_first) - greedy, 0, quant_max - quant_min, last_atom_start); + } + } + last_atom_start = -1; + } + break; + default: + break; + } +} + +/* return the number of bytes if char otherwise 0 */ +static int re_is_char(const uint8_t *buf, int start, int end) +{ + int n; + if (!(buf[start] >= REOP_char1 && buf[start] <= REOP_char4)) + return 0; + n = buf[start] - REOP_char1 + 1; + if ((end - start) != (n + 1)) + return 0; + return n; +} + +static int re_parse_alternative(JSParseState *s, int state, int dummy_param) +{ + int term_start, last_term_start, last_atom_start, last_capture_count, c, n1, n2, i; + JSByteArray *arr; + + PARSE_START3(); + + last_term_start = -1; + for(;;) { + if (s->buf_pos >= s->buf_len) + break; + term_start = s->byte_code_len; + + last_atom_start = -1; + last_capture_count = 0; + c = s->source_buf[s->buf_pos]; + switch(c) { + case '|': + case ')': + goto done; + case '^': + s->buf_pos++; + re_emit_op(s, s->multi_line ? REOP_line_start_m : REOP_line_start); + break; + case '$': + s->buf_pos++; + re_emit_op(s, s->multi_line ? REOP_line_end_m : REOP_line_end); + break; + case '.': + s->buf_pos++; + last_atom_start = s->byte_code_len; + last_capture_count = s->capture_count; + re_emit_op(s, s->dotall ? REOP_any : REOP_dot); + break; + case '{': + /* As an extension (see ES6 annex B), we accept '{' not + followed by digits as a normal atom */ + if (!s->is_unicode && !is_digit(s->source_buf[s->buf_pos + 1])) + goto parse_class_atom; + /* fall thru */ + case '*': + case '+': + case '?': + js_parse_error(s, "nothing to repeat"); + case '(': + if (s->source_buf[s->buf_pos + 1] == '?') { + c = s->source_buf[s->buf_pos + 2]; + if (c == ':') { + s->buf_pos += 3; + last_atom_start = s->byte_code_len; + last_capture_count = s->capture_count; + PARSE_CALL_SAVE4(s, 0, re_parse_disjunction, 0, + last_term_start, term_start, last_atom_start, last_capture_count); + re_parse_expect(s, ')'); + } else if ((c == '=' || c == '!')) { + int is_neg, pos; + is_neg = (c == '!'); + s->buf_pos += 3; + /* lookahead */ + pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0); + PARSE_CALL_SAVE6(s, 1, re_parse_disjunction, 0, + last_term_start, term_start, last_atom_start, last_capture_count, + is_neg, pos); + re_parse_expect(s, ')'); + re_emit_op(s, REOP_lookahead_match + is_neg); + /* jump after the 'match' after the lookahead is successful */ + arr = JS_VALUE_TO_PTR(s->byte_code); + put_u32(arr->buf + pos, s->byte_code_len - (pos + 4)); + } else { + js_parse_error(s, "invalid group"); + } + } else { + int capture_index; + s->buf_pos++; + /* capture without group name */ + if (s->capture_count >= CAPTURE_COUNT_MAX) + js_parse_error(s, "too many captures"); + last_atom_start = s->byte_code_len; + last_capture_count = s->capture_count; + capture_index = s->capture_count++; + re_emit_op_u8(s, REOP_save_start, capture_index); + + PARSE_CALL_SAVE5(s, 2, re_parse_disjunction, 0, + last_term_start, term_start, last_atom_start, last_capture_count, + capture_index); + + re_emit_op_u8(s, REOP_save_end, capture_index); + + re_parse_expect(s, ')'); + } + break; + case '\\': + switch(s->source_buf[s->buf_pos + 1]) { + case 'b': + case 'B': + if (s->source_buf[s->buf_pos + 1] != 'b') { + re_emit_op(s, REOP_not_word_boundary); + } else { + re_emit_op(s, REOP_word_boundary); + } + s->buf_pos += 2; + break; + case '0': + s->buf_pos += 2; + c = 0; + if (is_digit(s->source_buf[s->buf_pos])) + js_parse_error(s, "invalid decimal escape in regular expression"); + goto normal_char; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + { + const uint8_t *p; + p = s->source_buf + s->buf_pos + 1; + c = parse_digits(&p); + s->buf_pos = p - s->source_buf; + if (c > CAPTURE_COUNT_MAX) + js_parse_error(s, "back reference is out of range"); + /* the range is checked afterwards as we don't know the number of captures */ + last_atom_start = s->byte_code_len; + last_capture_count = s->capture_count; + re_emit_op_u8(s, REOP_back_reference + s->ignore_case, c); + } + break; + default: + goto parse_class_atom; + } + break; + case '[': + last_atom_start = s->byte_code_len; + last_capture_count = s->capture_count; + re_parse_char_class(s); + break; + case ']': + case '}': + if (s->is_unicode) + js_parse_error(s, "syntax error"); + goto parse_class_atom; + default: + parse_class_atom: + c = get_class_atom(s, FALSE); + normal_char: + last_atom_start = s->byte_code_len; + last_capture_count = s->capture_count; + if (c >= CLASS_RANGE_BASE) { + int range_start; + c -= CLASS_RANGE_BASE; + if (c == CHAR_RANGE_s || c == CHAR_RANGE_S) { + re_emit_op(s, REOP_space + c - CHAR_RANGE_s); + } else { + re_emit_op_u16(s, REOP_range, 0); + range_start = s->byte_code_len; + + re_emit_range_base(s, c); + re_range_optimize(s, range_start, FALSE); + } + } else { + if (s->ignore_case && + ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) { + /* XXX: could add specific operation */ + if (c >= 'a') + c -= 32; + re_emit_op_u8(s, REOP_range8, 2); + emit_u8(s, c); + emit_u8(s, c + 1); + emit_u8(s, c + 32); + emit_u8(s, c + 32 + 1); + } else { + re_emit_char(s, c); + } + } + break; + } + + /* quantifier */ + if (last_atom_start >= 0) { + re_parse_quantifier(s, last_atom_start, last_capture_count); + } + + /* combine several characters when possible */ + arr = JS_VALUE_TO_PTR(s->byte_code); + if (last_term_start >= 0 && + (n1 = re_is_char(arr->buf, last_term_start, term_start)) > 0 && + (n2 = re_is_char(arr->buf, term_start, s->byte_code_len)) > 0 && + (n1 + n2) <= 4) { + n1 += n2; + arr->buf[last_term_start] = REOP_char1 + n1 - 1; + for(i = 0; i < n2; i++) + arr->buf[last_term_start + n1 + i] = arr->buf[last_term_start + n1 + i + 1]; + s->byte_code_len--; + } else { + last_term_start = term_start; + } + } + done: + return PARSE_STATE_RET; +} + +static int re_parse_disjunction(JSParseState *s, int state, int dummy_param) +{ + int start, len, pos; + JSByteArray *arr; + + PARSE_START2(); + + start = s->byte_code_len; + + PARSE_CALL_SAVE1(s, 0, re_parse_alternative, 0, start); + while (s->source_buf[s->buf_pos] == '|') { + s->buf_pos++; + + len = s->byte_code_len - start; + + /* insert a split before the first alternative */ + emit_insert(s, start, 5); + arr = JS_VALUE_TO_PTR(s->byte_code); + arr->buf[start] = REOP_split_next_first; + put_u32(arr->buf + start + 1, len + 5); + + pos = re_emit_op_u32(s, REOP_goto, 0); + + PARSE_CALL_SAVE2(s, 1, re_parse_alternative, 0, start, pos); + + /* patch the goto */ + len = s->byte_code_len - (pos + 4); + arr = JS_VALUE_TO_PTR(s->byte_code); + put_u32(arr->buf + pos, len); + } + return PARSE_STATE_RET; +} + +/* Allocate the registers as a stack. The control flow is recursive so + the analysis can be linear. */ +static int re_compute_register_count(JSParseState *s, uint8_t *bc_buf, int bc_buf_len) +{ + int stack_size, stack_size_max, pos, opcode, len; + uint32_t val; + + stack_size = 0; + stack_size_max = 0; + pos = 0; + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + assert(opcode < REOP_COUNT); + assert((pos + len) <= bc_buf_len); + switch(opcode) { + case REOP_set_i32: + case REOP_set_char_pos: + bc_buf[pos + 1] = stack_size; + stack_size++; + if (stack_size > stack_size_max) { + if (stack_size > REGISTER_COUNT_MAX) + js_parse_error(s, "too many regexp registers"); + stack_size_max = stack_size; + } + break; + case REOP_check_advance: + case REOP_loop: + case REOP_loop_split_goto_first: + case REOP_loop_split_next_first: + assert(stack_size > 0); + stack_size--; + bc_buf[pos + 1] = stack_size; + break; + case REOP_loop_check_adv_split_goto_first: + case REOP_loop_check_adv_split_next_first: + assert(stack_size >= 2); + stack_size -= 2; + bc_buf[pos + 1] = stack_size; + break; + case REOP_range8: + val = bc_buf[pos + 1]; + len += val * 2; + break; + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + break; + case REOP_back_reference: + case REOP_back_reference_i: + /* validate back references */ + if (bc_buf[pos + 1] >= s->capture_count) + js_parse_error(s, "back reference is out of range"); + break; + } + pos += len; + } + return stack_size_max; +} + +/* return a JSByteArray. 'source' must be a string */ +static JSValue js_parse_regexp(JSParseState *s, int re_flags) +{ + JSByteArray *arr; + int register_count; + + s->multi_line = ((re_flags & LRE_FLAG_MULTILINE) != 0); + s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); + s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); + s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0); + s->byte_code = JS_NULL; + s->byte_code_len = 0; + s->capture_count = 1; + + emit_u16(s, re_flags); + emit_u8(s, 0); /* number of captures */ + emit_u8(s, 0); /* number of registers */ + + if (!(re_flags & LRE_FLAG_STICKY)) { + re_emit_op_u32(s, REOP_split_goto_first, 1 + 5); + re_emit_op(s, REOP_any); + re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5)); + } + re_emit_op_u8(s, REOP_save_start, 0); + + js_parse_call(s, PARSE_FUNC_re_parse_disjunction, 0); + + re_emit_op_u8(s, REOP_save_end, 0); + re_emit_op(s, REOP_match); + + if (s->buf_pos != s->buf_len) + js_parse_error(s, "extraneous characters at the end"); + + arr = JS_VALUE_TO_PTR(s->byte_code); + arr->buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; + register_count = + re_compute_register_count(s, arr->buf + RE_HEADER_LEN, + s->byte_code_len - RE_HEADER_LEN); + arr->buf[RE_HEADER_REGISTER_COUNT] = register_count; + + js_shrink_byte_array(s->ctx, &s->byte_code, s->byte_code_len); + +#ifdef DUMP_REOP + arr = JS_VALUE_TO_PTR(s->byte_code); + lre_dump_bytecode(arr->buf, arr->size); +#endif + + return s->byte_code; +} + +/* regexp interpreter */ + +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +static BOOL is_line_terminator(uint32_t c) +{ + return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS); +} + +static BOOL is_word_char(uint32_t c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c == '_')); +} + +/* Note: we canonicalize as in the unicode case, but only handle ASCII characters */ +static int lre_canonicalize(uint32_t c) +{ + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + return c; +} + +#define GET_CHAR(c, cptr, cbuf_end) \ + do { \ + size_t clen; \ + c = utf8_get(cptr, &clen); \ + cptr += clen; \ + } while (0) + +#define PEEK_CHAR(c, cptr, cbuf_end) \ + do { \ + size_t clen; \ + c = utf8_get(cptr, &clen); \ + } while (0) + +#define PEEK_PREV_CHAR(c, cptr, cbuf_start) \ + do { \ + const uint8_t *cptr1 = cptr - 1; \ + size_t clen; \ + while ((*cptr1 & 0xc0) == 0x80) \ + cptr1--; \ + c = utf8_get(cptr1, &clen); \ + } while (0) + +typedef enum { + RE_EXEC_STATE_SPLIT, + RE_EXEC_STATE_LOOKAHEAD, + RE_EXEC_STATE_NEGATIVE_LOOKAHEAD, +} REExecStateEnum; + +//#define DUMP_REEXEC + +/* return 1 if match, 0 if not match or < 0 if error. str must be a + JSString. capture_buf and byte_code are JSByteArray */ +static int lre_exec(JSContext *ctx, JSValue capture_buf, + JSValue byte_code, JSValue str, int cindex) +{ + const uint8_t *pc, *cptr, *cbuf; + uint32_t *capture; + int opcode, capture_count; + uint32_t val, c, idx; + const uint8_t *cbuf_end; + JSValue *sp, *bp, *initial_sp, *saved_stack_bottom; + JSByteArray *arr; /* temporary use */ + JSString *ps; /* temporary use */ + JSGCRef capture_buf_ref, byte_code_ref, str_ref; + + arr = JS_VALUE_TO_PTR(byte_code); + pc = arr->buf; + arr = JS_VALUE_TO_PTR(capture_buf); + capture = (uint32_t *)arr->buf; + capture_count = lre_get_capture_count(pc); + pc += RE_HEADER_LEN; + ps = JS_VALUE_TO_PTR(str); + cbuf = ps->buf; + cbuf_end = cbuf + ps->len; + cptr = cbuf + cindex; + + saved_stack_bottom = ctx->stack_bottom; + initial_sp = ctx->sp; + sp = initial_sp; + bp = initial_sp; + +#define LRE_POLL_INTERRUPT() do { \ + if (unlikely(--ctx->interrupt_counter <= 0)) { \ + JSValue ret; \ + int saved_pc, saved_cptr; \ + arr = JS_VALUE_TO_PTR(byte_code); \ + saved_pc = pc - arr->buf; \ + saved_cptr = cptr - cbuf; \ + JS_PUSH_VALUE(ctx, capture_buf); \ + JS_PUSH_VALUE(ctx, byte_code); \ + JS_PUSH_VALUE(ctx, str); \ + ctx->sp = sp; \ + ret = __js_poll_interrupt(ctx); \ + JS_POP_VALUE(ctx, str); \ + JS_POP_VALUE(ctx, byte_code); \ + JS_POP_VALUE(ctx, capture_buf); \ + if (JS_IsException(ret)) { \ + ctx->sp = initial_sp; \ + ctx->stack_bottom = saved_stack_bottom; \ + return -1; \ + } \ + arr = JS_VALUE_TO_PTR(byte_code); \ + pc = arr->buf + saved_pc; \ + ps = JS_VALUE_TO_PTR(str); \ + cbuf = ps->buf; \ + cbuf_end = cbuf + ps->len; \ + cptr = cbuf + saved_cptr; \ + arr = JS_VALUE_TO_PTR(capture_buf); \ + capture = (uint32_t *)arr->buf; \ + } \ + } while(0) + +#define CHECK_STACK_SPACE(n) \ + { \ + if (unlikely((sp - ctx->stack_bottom) < (n))) { \ + int ret, saved_pc, saved_cptr; \ + arr = JS_VALUE_TO_PTR(byte_code); \ + saved_pc = pc - arr->buf; \ + saved_cptr = cptr - cbuf; \ + JS_PUSH_VALUE(ctx, capture_buf); \ + JS_PUSH_VALUE(ctx, byte_code); \ + JS_PUSH_VALUE(ctx, str); \ + ctx->sp = sp; \ + ret = JS_StackCheck(ctx, n); \ + JS_POP_VALUE(ctx, str); \ + JS_POP_VALUE(ctx, byte_code); \ + JS_POP_VALUE(ctx, capture_buf); \ + if (ret < 0) { \ + ctx->sp = initial_sp; \ + ctx->stack_bottom = saved_stack_bottom; \ + return -1; \ + } \ + arr = JS_VALUE_TO_PTR(byte_code); \ + pc = arr->buf + saved_pc; \ + ps = JS_VALUE_TO_PTR(str); \ + cbuf = ps->buf; \ + cbuf_end = cbuf + ps->len; \ + cptr = cbuf + saved_cptr; \ + arr = JS_VALUE_TO_PTR(capture_buf); \ + capture = (uint32_t *)arr->buf; \ + } \ + } + +#define SAVE_CAPTURE(idx, value) \ + { \ + int __v = (value); \ + CHECK_STACK_SPACE(2); \ + sp[-2] = JS_NewShortInt(idx); \ + sp[-1] = JS_NewShortInt(capture[idx]); \ + sp -= 2; \ + capture[idx] = __v; \ + } + + /* avoid saving the previous value if already saved */ +#define SAVE_CAPTURE_CHECK(idx, value) \ + { \ + int __v = (value); \ + JSValue *sp1; \ + sp1 = sp; \ + for(;;) { \ + if (sp1 < bp) { \ + if (JS_VALUE_GET_INT(sp1[0]) == (idx)) \ + break; \ + sp1 += 2; \ + } else { \ + CHECK_STACK_SPACE(2); \ + sp[-2] = JS_NewShortInt(idx); \ + sp[-1] = JS_NewShortInt(capture[idx]); \ + sp -= 2; \ + break; \ + } \ + } \ + capture[idx] = __v; \ + } + +#define RE_PC_TYPE_TO_VALUE(pc, type) (((type) << 1) | (((pc) - ((JSByteArray *)JS_VALUE_TO_PTR(byte_code))->buf) << 3)) +#define RE_VALUE_TO_PC(val) (((val) >> 3) + ((JSByteArray *)JS_VALUE_TO_PTR(byte_code))->buf) +#define RE_VALUE_TO_TYPE(val) (((val) >> 1) & 3) + +#ifdef DUMP_REEXEC + printf("%5s %5s %5s %5s %s\n", "PC", "CP", "BP", "SP", "OPCODE"); +#endif + for(;;) { + opcode = *pc++; +#ifdef DUMP_REEXEC + printf("%5ld %5ld %5ld %5ld %s\n", + pc - 1 - ((JSByteArray *)JS_VALUE_TO_PTR(byte_code))->buf - RE_HEADER_LEN, + cptr - cbuf, + bp - initial_sp, + sp - initial_sp, + reopcode_info[opcode].name); +#endif + switch(opcode) { + case REOP_match: + ctx->sp = initial_sp; + ctx->stack_bottom = saved_stack_bottom; + return 1; + no_match: + for(;;) { + REExecStateEnum type; + if (bp == initial_sp) { + ctx->sp = initial_sp; + ctx->stack_bottom = saved_stack_bottom; + return 0; + } + /* undo the modifications to capture[] and regs[] */ + while (sp < bp) { + int idx2 = JS_VALUE_GET_INT(sp[0]); + capture[idx2] = JS_VALUE_GET_INT(sp[1]); + sp += 2; + } + + pc = RE_VALUE_TO_PC(sp[0]); + type = RE_VALUE_TO_TYPE(sp[0]); + cptr = JS_VALUE_GET_INT(sp[1]) + cbuf; + bp = VALUE_TO_SP(ctx, sp[2]); + sp += 3; + if (type != RE_EXEC_STATE_LOOKAHEAD) + break; + } + LRE_POLL_INTERRUPT(); + break; + case REOP_lookahead_match: + /* pop all the saved states until reaching the start of + the lookahead and keep the updated captures and + variables and the corresponding undo info. */ + { + JSValue *sp1, *sp_start, *next_sp; + REExecStateEnum type; + + sp_start = sp; + for(;;) { + sp1 = sp; + sp = bp; + pc = RE_VALUE_TO_PC(sp[0]); + type = RE_VALUE_TO_TYPE(sp[0]); + cptr = JS_VALUE_GET_INT(sp[1]) + cbuf; + bp = VALUE_TO_SP(ctx, sp[2]); + sp[2] = SP_TO_VALUE(ctx, sp1); /* save the next value for the copy step */ + sp += 3; + if (type == RE_EXEC_STATE_LOOKAHEAD) + break; + } + if (sp != initial_sp) { + /* keep the undo info if there is a saved state */ + sp1 = sp; + while (sp1 != sp_start) { + sp1 -= 3; + next_sp = VALUE_TO_SP(ctx, sp1[2]); + while (sp1 != next_sp) { + *--sp = *--sp1; + } + } + } + } + break; + case REOP_negative_lookahead_match: + /* pop all the saved states until reaching start of the negative lookahead */ + for(;;) { + REExecStateEnum type; + type = RE_VALUE_TO_TYPE(bp[0]); + /* undo the modifications to capture[] and regs[] */ + while (sp < bp) { + int idx2 = JS_VALUE_GET_INT(sp[0]); + capture[idx2] = JS_VALUE_GET_INT(sp[1]); + sp += 2; + } + pc = RE_VALUE_TO_PC(sp[0]); + type = RE_VALUE_TO_TYPE(sp[0]); + cptr = JS_VALUE_GET_INT(sp[1]) + cbuf; + bp = VALUE_TO_SP(ctx, sp[2]); + sp += 3; + if (type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD) + break; + } + goto no_match; + + case REOP_char1: + if ((cbuf_end - cptr) < 1) + goto no_match; + if (pc[0] != cptr[0]) + goto no_match; + pc++; + cptr++; + break; + case REOP_char2: + if ((cbuf_end - cptr) < 2) + goto no_match; + if (get_u16(pc) != get_u16(cptr)) + goto no_match; + pc += 2; + cptr += 2; + break; + case REOP_char3: + if ((cbuf_end - cptr) < 3) + goto no_match; + if (get_u16(pc) != get_u16(cptr) || pc[2] != cptr[2]) + goto no_match; + pc += 3; + cptr += 3; + break; + case REOP_char4: + if ((cbuf_end - cptr) < 4) + goto no_match; + if (get_u32(pc) != get_u32(cptr)) + goto no_match; + pc += 4; + cptr += 4; + break; + case REOP_split_goto_first: + case REOP_split_next_first: + { + const uint8_t *pc1; + + val = get_u32(pc); + pc += 4; + CHECK_STACK_SPACE(3); + if (opcode == REOP_split_next_first) { + pc1 = pc + (int)val; + } else { + pc1 = pc; + pc = pc + (int)val; + } + sp -= 3; + sp[0] = RE_PC_TYPE_TO_VALUE(pc1, RE_EXEC_STATE_SPLIT); + sp[1] = JS_NewShortInt(cptr - cbuf); + sp[2] = SP_TO_VALUE(ctx, bp); + bp = sp; + } + break; + case REOP_lookahead: + case REOP_negative_lookahead: + val = get_u32(pc); + pc += 4; + CHECK_STACK_SPACE(3); + sp -= 3; + sp[0] = RE_PC_TYPE_TO_VALUE(pc + (int)val, + RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead); + sp[1] = JS_NewShortInt(cptr - cbuf); + sp[2] = SP_TO_VALUE(ctx, bp); + bp = sp; + break; + case REOP_goto: + val = get_u32(pc); + pc += 4 + (int)val; + LRE_POLL_INTERRUPT(); + break; + case REOP_line_start: + case REOP_line_start_m: + if (cptr == cbuf) + break; + if (opcode == REOP_line_start) + goto no_match; + PEEK_PREV_CHAR(c, cptr, cbuf); + if (!is_line_terminator(c)) + goto no_match; + break; + case REOP_line_end: + case REOP_line_end_m: + if (cptr == cbuf_end) + break; + if (opcode == REOP_line_end) + goto no_match; + PEEK_CHAR(c, cptr, cbuf_end); + if (!is_line_terminator(c)) + goto no_match; + break; + case REOP_dot: + if (cptr == cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + if (is_line_terminator(c)) + goto no_match; + break; + case REOP_any: + if (cptr == cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + break; + case REOP_space: + case REOP_not_space: + { + BOOL v1; + if (cptr == cbuf_end) + goto no_match; + c = cptr[0]; + if (c < 128) { + cptr++; + v1 = unicode_is_space_ascii(c); + } else { + size_t clen; + c = __utf8_get(cptr, &clen); + cptr += clen; + v1 = unicode_is_space_non_ascii(c); + } + v1 ^= (opcode - REOP_space); + if (!v1) + goto no_match; + } + break; + case REOP_save_start: + case REOP_save_end: + val = *pc++; + assert(val < capture_count); + idx = 2 * val + opcode - REOP_save_start; + SAVE_CAPTURE(idx, cptr - cbuf); + break; + case REOP_save_reset: + { + uint32_t val2; + val = pc[0]; + val2 = pc[1]; + pc += 2; + assert(val2 < capture_count); + CHECK_STACK_SPACE(2 * (val2 - val + 1)); + while (val <= val2) { + idx = 2 * val; + SAVE_CAPTURE(idx, 0); + idx = 2 * val + 1; + SAVE_CAPTURE(idx, 0); + val++; + } + } + break; + case REOP_set_i32: + idx = pc[0]; + val = get_u32(pc + 1); + pc += 5; + SAVE_CAPTURE_CHECK(2 * capture_count + idx, val); + break; + case REOP_loop: + { + uint32_t val2; + idx = pc[0]; + val = get_u32(pc + 1); + pc += 5; + + val2 = capture[2 * capture_count + idx] - 1; + SAVE_CAPTURE_CHECK(2 * capture_count + idx, val2); + if (val2 != 0) { + pc += (int)val; + LRE_POLL_INTERRUPT(); + } + } + break; + case REOP_loop_split_goto_first: + case REOP_loop_split_next_first: + case REOP_loop_check_adv_split_goto_first: + case REOP_loop_check_adv_split_next_first: + { + const uint8_t *pc1; + uint32_t val2, limit; + idx = pc[0]; + limit = get_u32(pc + 1); + val = get_u32(pc + 5); + pc += 9; + + /* decrement the counter */ + val2 = capture[2 * capture_count + idx] - 1; + SAVE_CAPTURE_CHECK(2 * capture_count + idx, val2); + + if (val2 > limit) { + /* normal loop if counter > limit */ + pc += (int)val; + LRE_POLL_INTERRUPT(); + } else { + /* check advance */ + if ((opcode == REOP_loop_check_adv_split_goto_first || + opcode == REOP_loop_check_adv_split_next_first) && + capture[2 * capture_count + idx + 1] == (cptr - cbuf) && + val2 != limit) { + goto no_match; + } + + /* otherwise conditional split */ + if (val2 != 0) { + CHECK_STACK_SPACE(3); + if (opcode == REOP_loop_split_next_first || + opcode == REOP_loop_check_adv_split_next_first) { + pc1 = pc + (int)val; + } else { + pc1 = pc; + pc = pc + (int)val; + } + sp -= 3; + sp[0] = RE_PC_TYPE_TO_VALUE(pc1, RE_EXEC_STATE_SPLIT); + sp[1] = JS_NewShortInt(cptr - cbuf); + sp[2] = SP_TO_VALUE(ctx, bp); + bp = sp; + } + } + } + break; + case REOP_set_char_pos: + idx = pc[0]; + pc++; + SAVE_CAPTURE_CHECK(2 * capture_count + idx, cptr - cbuf); + break; + case REOP_check_advance: + idx = pc[0]; + pc++; + if (capture[2 * capture_count + idx] == cptr - cbuf) + goto no_match; + break; + case REOP_word_boundary: + case REOP_not_word_boundary: + { + BOOL v1, v2; + BOOL is_boundary = (opcode == REOP_word_boundary); + /* char before */ + if (cptr == cbuf) { + v1 = FALSE; + } else { + PEEK_PREV_CHAR(c, cptr, cbuf); + v1 = is_word_char(c); + } + /* current char */ + if (cptr >= cbuf_end) { + v2 = FALSE; + } else { + PEEK_CHAR(c, cptr, cbuf_end); + v2 = is_word_char(c); + } + if (v1 ^ v2 ^ is_boundary) + goto no_match; + } + break; + /* assumption: 8 bit and small number of ranges */ + case REOP_range8: + { + int n, i; + n = pc[0]; + pc++; + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + for(i = 0; i < n - 1; i++) { + if (c >= pc[2 * i] && c < pc[2 * i + 1]) + goto range8_match; + } + /* 0xff = max code point value */ + if (c >= pc[2 * i] && + (c < pc[2 * i + 1] || pc[2 * i + 1] == 0xff)) + goto range8_match; + goto no_match; + range8_match: + pc += 2 * n; + } + break; + case REOP_range: + { + int n; + uint32_t low, high, idx_min, idx_max, idx; + + n = get_u16(pc); /* n must be >= 1 */ + pc += 2; + if (cptr >= cbuf_end || n == 0) + goto no_match; + GET_CHAR(c, cptr, cbuf_end); + idx_min = 0; + low = get_u32(pc + 0 * 8); + if (c < low) + goto no_match; + idx_max = n - 1; + high = get_u32(pc + idx_max * 8 + 4); + if (c >= high) + goto no_match; + while (idx_min <= idx_max) { + idx = (idx_min + idx_max) / 2; + low = get_u32(pc + idx * 8); + high = get_u32(pc + idx * 8 + 4); + if (c < low) + idx_max = idx - 1; + else if (c >= high) + idx_min = idx + 1; + else + goto range_match; + } + goto no_match; + range_match: + pc += 8 * n; + } + break; + case REOP_back_reference: + case REOP_back_reference_i: + val = pc[0]; + pc++; + if (capture[2 * val] != -1 && capture[2 * val + 1] != -1) { + const uint8_t *cptr1, *cptr1_end; + int c1, c2; + + cptr1 = cbuf + capture[2 * val]; + cptr1_end = cbuf + capture[2 * val + 1]; + while (cptr1 < cptr1_end) { + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c1, cptr1, cptr1_end); + GET_CHAR(c2, cptr, cbuf_end); + if (opcode == REOP_back_reference_i) { + c1 = lre_canonicalize(c1); + c2 = lre_canonicalize(c2); + } + if (c1 != c2) + goto no_match; + } + } + break; + default: +#ifdef DUMP_REEXEC + printf("unknown opcode pc=%ld\n", pc - 1 - ((JSByteArray *)JS_VALUE_TO_PTR(byte_code))->buf - RE_HEADER_LEN); +#endif + abort(); + } + } +} + +/* regexp js interface */ + +/* return the length */ +static size_t js_parse_regexp_flags(int *pre_flags, const uint8_t *buf) +{ + const uint8_t *p = buf; + int mask, re_flags; + re_flags = 0; + while (*p != '\0') { + switch(*p) { +#if 0 + case 'd': + mask = LRE_FLAG_INDICES; + break; +#endif + case 'g': + mask = LRE_FLAG_GLOBAL; + break; + case 'i': + mask = LRE_FLAG_IGNORECASE; + break; + case 'm': + mask = LRE_FLAG_MULTILINE; + break; + case 's': + mask = LRE_FLAG_DOTALL; + break; + case 'u': + mask = LRE_FLAG_UNICODE; + break; +#if 0 + case 'v': + mask = LRE_FLAG_UNICODE_SETS; + break; +#endif + case 'y': + mask = LRE_FLAG_STICKY; + break; + default: + goto done; + } + if ((re_flags & mask) != 0) + break; + re_flags |= mask; + p++; + } + done: + *pre_flags = re_flags; + return p - buf; +} + +/* pattern and flags must be strings */ +static JSValue js_compile_regexp(JSContext *ctx, JSValue pattern, JSValue flags) +{ + int re_flags; + + re_flags = 0; + if (!JS_IsUndefined(flags)) { + JSString *ps; + JSStringCharBuf buf; + size_t len; + ps = get_string_ptr(ctx, &buf, flags); + len = js_parse_regexp_flags(&re_flags, ps->buf); + if (len != ps->len) + return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); + } + + return JS_Parse2(ctx, pattern, NULL, 0, "", + JS_EVAL_REGEXP | (re_flags << JS_EVAL_REGEXP_FLAGS_SHIFT)); +} + +static JSRegExp *js_get_regexp(JSContext *ctx, JSValue obj) +{ + JSObject *p; + p = js_get_object_class(ctx, obj, JS_CLASS_REGEXP); + if (!p) { + JS_ThrowTypeError(ctx, "not a regular expression"); + return NULL; + } + return &p->u.regexp; +} + +JSValue js_regexp_get_lastIndex(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSRegExp *re = js_get_regexp(ctx, *this_val); + if (!re) + return JS_EXCEPTION; + return JS_NewInt32(ctx, re->last_index); +} + +JSValue js_regexp_get_source(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSRegExp *re = js_get_regexp(ctx, *this_val); + if (!re) + return JS_EXCEPTION; + /* XXX: not complete */ + return re->source; +} + +JSValue js_regexp_set_lastIndex(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSRegExp *re; + int last_index; + if (JS_ToInt32(ctx, &last_index, argv[0])) + return JS_EXCEPTION; + re = js_get_regexp(ctx, *this_val); + if (!re) + return JS_EXCEPTION; + re->last_index = last_index; + return JS_UNDEFINED; +} + +#define RE_FLAG_COUNT 6 + +/* return the string length */ +static size_t js_regexp_flags_str(char *buf, int re_flags) +{ + static const char flag_char[RE_FLAG_COUNT] = { 'g', 'i', 'm', 's', 'u', 'y' }; + char *p = buf; + int i; + + for(i = 0; i < RE_FLAG_COUNT; i++) { + if ((re_flags >> i) & 1) + *p++ = flag_char[i]; + } + *p = '\0'; + return p - buf; +} + +static void dump_regexp(JSContext *ctx, JSObject *p) +{ + JSStringCharBuf buf; + JSString *ps; + char buf2[RE_FLAG_COUNT + 1]; + JSByteArray *arr; + + js_putchar(ctx, '/'); + ps = get_string_ptr(ctx, &buf, p->u.regexp.source); + if (ps->len == 0) { + js_printf(ctx, "(?:)"); + } else { + js_printf(ctx, "%" JSValue_PRI, p->u.regexp.source); + } + arr = JS_VALUE_TO_PTR(p->u.regexp.byte_code); + js_regexp_flags_str(buf2, lre_get_flags(arr->buf)); + js_printf(ctx, "/%s", buf2); +} + +JSValue js_regexp_get_flags(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSRegExp *re; + JSByteArray *arr; + size_t len; + char buf[RE_FLAG_COUNT + 1]; + + re = js_get_regexp(ctx, *this_val); + if (!re) + return JS_EXCEPTION; + arr = JS_VALUE_TO_PTR(re->byte_code); + len = js_regexp_flags_str(buf, lre_get_flags(arr->buf)); + return JS_NewStringLen(ctx, buf, len); +} + +JSValue js_regexp_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue obj, byte_code; + JSObject *p; + JSGCRef byte_code_ref; + + argc &= ~FRAME_CF_CTOR; + + argv[0] = JS_ToString(ctx, argv[0]); + if (JS_IsException(argv[0])) + return JS_EXCEPTION; + if (!JS_IsUndefined(argv[1])) { + argv[1] = JS_ToString(ctx, argv[1]); + if (JS_IsException(argv[1])) + return JS_EXCEPTION; + } + byte_code = js_compile_regexp(ctx, argv[0], argv[1]); + if (JS_IsException(byte_code)) + return JS_EXCEPTION; + JS_PUSH_VALUE(ctx, byte_code); + obj = JS_NewObjectClass(ctx, JS_CLASS_REGEXP, sizeof(JSRegExp)); + JS_POP_VALUE(ctx, byte_code); + if (JS_IsException(obj)) + return obj; + p = JS_VALUE_TO_PTR(obj); + p->u.regexp.source = argv[0]; + p->u.regexp.byte_code = byte_code; + p->u.regexp.last_index = 0; + return obj; +} + +enum { + MAGIC_REGEXP_EXEC, + MAGIC_REGEXP_TEST, + MAGIC_REGEXP_SEARCH, + MAGIC_REGEXP_FORCE_GLOBAL, /* same as exec but force the global flag */ +}; + +JSValue js_regexp_exec(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic) +{ + JSObject *p; + JSRegExp *re; + JSValue obj, *capture_buf, res; + uint32_t *capture, last_index_utf8; + int rc, capture_count, i, re_flags, last_index; + JSByteArray *bc_arr, *carr; + JSGCRef capture_buf_ref, obj_ref; + JSString *str; + JSStringCharBuf str_buf; + + re = js_get_regexp(ctx, *this_val); + if (!re) + return JS_EXCEPTION; + + argv[0] = JS_ToString(ctx, argv[0]); + if (JS_IsException(argv[0])) + return JS_EXCEPTION; + + p = JS_VALUE_TO_PTR(*this_val); + re = &p->u.regexp; + last_index = max_int(re->last_index, 0); + + bc_arr = JS_VALUE_TO_PTR(re->byte_code); + re_flags = lre_get_flags(bc_arr->buf); + if (magic == MAGIC_REGEXP_FORCE_GLOBAL) + re_flags |= MAGIC_REGEXP_FORCE_GLOBAL; + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0 || + magic == MAGIC_REGEXP_SEARCH) { + last_index = 0; + } + capture_count = lre_get_capture_count(bc_arr->buf); + + carr = js_alloc_byte_array(ctx, sizeof(uint32_t) * lre_get_alloc_count(bc_arr->buf)); + if (!carr) + goto fail; + capture_buf = JS_PushGCRef(ctx, &capture_buf_ref); + *capture_buf = JS_VALUE_FROM_PTR(carr); + capture = (uint32_t *)carr->buf; + for(i = 0; i < 2 * capture_count; i++) + capture[i] = -1; + + if (last_index <= 0) + last_index_utf8 = 0; + else + last_index_utf8 = js_string_utf16_to_utf8_pos(ctx, argv[0], last_index) / 2; + if (last_index_utf8 > js_string_byte_len(ctx, argv[0])) { + rc = 2; + } else { + p = JS_VALUE_TO_PTR(*this_val); + re = &p->u.regexp; + str = get_string_ptr(ctx, &str_buf, argv[0]); + /* JS_VALUE_FROM_PTR(str) is acceptable here because the + GC ignores pointers outside the heap */ + rc = lre_exec(ctx, *capture_buf, re->byte_code, JS_VALUE_FROM_PTR(str), + last_index_utf8); + } + if (rc != 1) { + if (rc >= 0) { + if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { + p = JS_VALUE_TO_PTR(*this_val); + re = &p->u.regexp; + re->last_index = 0; + } + if (magic == MAGIC_REGEXP_SEARCH) + obj = JS_NewShortInt(-1); + else if (magic == MAGIC_REGEXP_TEST) + obj = JS_FALSE; + else + obj = JS_NULL; + } else { + goto fail; + } + } else { + capture = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf; + if (magic == MAGIC_REGEXP_SEARCH) { + obj = JS_NewShortInt(js_string_utf8_to_utf16_pos(ctx, argv[0], capture[0] * 2)); + goto done; + } + if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { + p = JS_VALUE_TO_PTR(*this_val); + re = &p->u.regexp; + re->last_index = js_string_utf8_to_utf16_pos(ctx, argv[0], capture[1] * 2); + } + if (magic == MAGIC_REGEXP_TEST) { + obj = JS_TRUE; + } else { + obj = JS_NewArray(ctx, capture_count); + if (JS_IsException(obj)) + goto fail; + + JS_PUSH_VALUE(ctx, obj); + capture = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf; + res = JS_DefinePropertyValue(ctx, obj, js_get_atom(ctx, JS_ATOM_index), + JS_NewShortInt(js_string_utf8_to_utf16_pos(ctx, argv[0], capture[0] * 2))); + JS_POP_VALUE(ctx, obj); + if (JS_IsException(res)) + goto fail; + + JS_PUSH_VALUE(ctx, obj); + res = JS_DefinePropertyValue(ctx, obj, js_get_atom(ctx, JS_ATOM_input), + argv[0]); + JS_POP_VALUE(ctx, obj); + if (JS_IsException(res)) + goto fail; + + for(i = 0; i < capture_count; i++) { + int start, end; + JSValue val; + + capture = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf; + start = capture[2 * i]; + end = capture[2 * i + 1]; + if (start != -1 && end != -1) { + JSValueArray *arr; + JS_PUSH_VALUE(ctx, obj); + val = js_sub_string_utf8(ctx, argv[0], 2 * start, 2 * end); + JS_POP_VALUE(ctx, obj); + if (JS_IsException(val)) + goto fail; + p = JS_VALUE_TO_PTR(obj); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + arr->arr[i] = val; + } + } + } + } + done: + JS_PopGCRef(ctx, &capture_buf_ref); + return obj; + fail: + obj = JS_EXCEPTION; + goto done; +} + +/* if regexp replace: capture_buf != NULL, needle = NULL + if string replace: capture_buf = NULL, captures_len = 1, needle != NULL +*/ +static int js_string_concat_subst(JSContext *ctx, StringBuffer *b, + JSValue *str, JSValue *rep, + uint32_t pos, uint32_t end_of_match, + JSValue *capture_buf, uint32_t captures_len, + JSValue *needle) +{ + JSStringCharBuf buf_rep; + JSString *p; + int rep_len, i, j, j0, c, k; + + if (JS_IsFunction(ctx, *rep)) { + JSValue res, val; + JSGCRef val_ref; + int ret; + + if (JS_StackCheck(ctx, 4 + captures_len)) + return -1; + JS_PushArg(ctx, *str); + JS_PushArg(ctx, JS_NewShortInt(pos)); + if (capture_buf) { + for(k = captures_len - 1; k >= 0; k--) { + uint32_t *captures = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf; + if (captures[2 * k] != -1 && captures[2 * k + 1] != -1) { + val = js_sub_string_utf8(ctx, *str, captures[2 * k] * 2, captures[2 * k + 1] * 2); + if (JS_IsException(val)) + return -1; + JS_PUSH_VALUE(ctx, val); + ret = JS_StackCheck(ctx, 3 + k); + JS_POP_VALUE(ctx, val); + if (ret) + return -1; + } else { + val = JS_UNDEFINED; + } + JS_PushArg(ctx, val); + } + } else { + JS_PushArg(ctx, *needle); + } + JS_PushArg(ctx, *rep); /* function */ + JS_PushArg(ctx, JS_UNDEFINED); /* this_val */ + res = JS_Call(ctx, 2 + captures_len); + if (JS_IsException(res)) + return -1; + return string_buffer_concat(ctx, b, res); + } + + p = get_string_ptr(ctx, &buf_rep, *rep); + rep_len = p->len; + i = 0; + for(;;) { + p = get_string_ptr(ctx, &buf_rep, *rep); + j = i; + while (j < rep_len && p->buf[j] != '$') + j++; + if (j + 1 >= rep_len) + break; + j0 = j++; /* j0 = position of '$' */ + c = p->buf[j++]; + string_buffer_concat_utf8(ctx, b, *rep, 2 * i, 2 * j0); + if (c == '$') { + string_buffer_putc(ctx, b, '$'); + } else if (c == '&') { + if (capture_buf) { + string_buffer_concat_utf16(ctx, b, *str, pos, end_of_match); + } else { + string_buffer_concat_str(ctx, b, *needle); + } + } else if (c == '`') { + string_buffer_concat_utf16(ctx, b, *str, 0, pos); + } else if (c == '\'') { + string_buffer_concat_utf16(ctx, b, *str, end_of_match, js_string_len(ctx, *str)); + } else if (c >= '0' && c <= '9') { + k = c - '0'; + if (j < rep_len) { + c = p->buf[j]; + if (c >= '0' && c <= '9') { + k = k * 10 + c - '0'; + j++; + } + } + if (k >= 1 && k < captures_len) { + uint32_t *captures = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf; + if (captures[2 * k] != -1 && captures[2 * k + 1] != -1) { + string_buffer_concat_utf8(ctx, b, *str, + captures[2 * k] * 2, captures[2 * k + 1] * 2); + } + } else { + goto no_rep; + } + } else { + no_rep: + string_buffer_concat_utf8(ctx, b, *rep, 2 * j0, 2 * j); + } + i = j; + } + return string_buffer_concat_utf8(ctx, b, *rep, 2 * i, 2 * rep_len); +} + +JSValue js_string_replace(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_replaceAll) +{ + StringBuffer b_s, *b = &b_s; + int pos, endOfLastMatch, needle_len, input_len; + BOOL is_first, is_regexp; + + *this_val = JS_ToString(ctx, *this_val); + if (JS_IsException(*this_val)) + return JS_EXCEPTION; + is_regexp = (JS_GetClassID(ctx, argv[0]) == JS_CLASS_REGEXP); + if (!is_regexp) { + argv[0] = JS_ToString(ctx, argv[0]); + if (JS_IsException(argv[0])) + return JS_EXCEPTION; + } + if (!JS_IsFunction(ctx, argv[1])) { + argv[1] = JS_ToString(ctx, argv[1]); + if (JS_IsException(argv[1])) + return JS_EXCEPTION; + } + input_len = js_string_len(ctx, *this_val); + endOfLastMatch = 0; + + string_buffer_push(ctx, b, 0); + + if (is_regexp) { + int start, end, last_index, ret, re_flags, i, capture_count; + JSObject *p; + JSByteArray *bc_arr, *carr; + JSValue *capture_buf; + uint32_t *capture; + JSGCRef capture_buf_ref; + + p = JS_VALUE_TO_PTR(argv[0]); + bc_arr = JS_VALUE_TO_PTR(p->u.regexp.byte_code); + re_flags = lre_get_flags(bc_arr->buf); + capture_count = lre_get_capture_count(bc_arr->buf); + + if (re_flags & LRE_FLAG_GLOBAL) + p->u.regexp.last_index = 0; + + if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { + last_index = 0; + } else { + last_index = max_int(p->u.regexp.last_index, 0); + } + + carr = js_alloc_byte_array(ctx, sizeof(uint32_t) * lre_get_alloc_count(bc_arr->buf)); + if (!carr) { + string_buffer_pop(ctx, b); + return JS_EXCEPTION; + } + capture_buf = JS_PushGCRef(ctx, &capture_buf_ref); + *capture_buf = JS_VALUE_FROM_PTR(carr); + capture = (uint32_t *)carr->buf; + for(i = 0; i < 2 * capture_count; i++) + capture[i] = -1; + + for(;;) { + if (last_index > input_len) { + ret = 0; + } else { + JSString *str; + JSStringCharBuf str_buf; + p = JS_VALUE_TO_PTR(argv[0]); + str = get_string_ptr(ctx, &str_buf, *this_val); + /* JS_VALUE_FROM_PTR(str) is acceptable here because the + GC ignores pointers outside the heap */ + ret = lre_exec(ctx, *capture_buf, p->u.regexp.byte_code, + JS_VALUE_FROM_PTR(str), + js_string_utf16_to_utf8_pos(ctx, *this_val, last_index) / 2); + } + if (ret < 0) { + JS_PopGCRef(ctx, &capture_buf_ref); + string_buffer_pop(ctx, b); + return JS_EXCEPTION; + } + if (ret == 0) { + if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { + p = JS_VALUE_TO_PTR(argv[0]); + p->u.regexp.last_index = 0; + } + break; + } + capture = (uint32_t *)((JSByteArray *)JS_VALUE_TO_PTR(*capture_buf))->buf; + start = js_string_utf8_to_utf16_pos(ctx, *this_val, capture[0] * 2); + end = js_string_utf8_to_utf16_pos(ctx, *this_val, capture[1] * 2); + string_buffer_concat_utf16(ctx, b, *this_val, endOfLastMatch, start); + js_string_concat_subst(ctx, b, this_val, &argv[1], + start, end, capture_buf, capture_count, NULL); + endOfLastMatch = end; + if (!(re_flags & LRE_FLAG_GLOBAL)) { + if (re_flags & LRE_FLAG_STICKY) { + p = JS_VALUE_TO_PTR(argv[0]); + p->u.regexp.last_index = end; + } + break; + } + if (end == start) { + int c = string_getcp(ctx, *this_val, end, TRUE); + /* since regexp are unicode by default, replace is also unicode by default */ + end += 1 + (c >= 0x10000); + } + last_index = end; + } + JS_PopGCRef(ctx, &capture_buf_ref); + } else { + needle_len = js_string_len(ctx, argv[0]); + + is_first = TRUE; + for(;;) { + if (unlikely(needle_len == 0)) { + if (is_first) + pos = 0; + else if (endOfLastMatch >= input_len) + pos = -1; + else + pos = endOfLastMatch + 1; + } else { + pos = js_string_indexof(ctx, *this_val, argv[0], endOfLastMatch, + input_len, needle_len); + } + if (pos < 0) { + if (is_first) { + string_buffer_pop(ctx, b); + return *this_val; + } else { + break; + } + } + + string_buffer_concat_utf16(ctx, b, *this_val, endOfLastMatch, pos); + + js_string_concat_subst(ctx, b, this_val, &argv[1], + pos, pos + needle_len, NULL, 1, &argv[0]); + + endOfLastMatch = pos + needle_len; + is_first = FALSE; + if (!is_replaceAll) + break; + } + } + string_buffer_concat_utf16(ctx, b, *this_val, endOfLastMatch, input_len); + return string_buffer_pop(ctx, b); +} + +// split(sep, limit) +JSValue js_string_split(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSValue *A, T, ret, *z; + uint32_t lim, lengthA; + int p, q, s, e; + BOOL undef_sep; + JSGCRef A_ref, z_ref; + BOOL is_regexp; + + *this_val = JS_ToString(ctx, *this_val); + if (JS_IsException(*this_val)) + return JS_EXCEPTION; + if (JS_IsUndefined(argv[1])) { + lim = 0xffffffff; + } else { + if (JS_ToUint32(ctx, &lim, argv[1]) < 0) + return JS_EXCEPTION; + } + is_regexp = (JS_GetClassID(ctx, argv[0]) == JS_CLASS_REGEXP); + if (!is_regexp) { + undef_sep = JS_IsUndefined(argv[0]); + argv[0] = JS_ToString(ctx, argv[0]); + if (JS_IsException(argv[0])) + return JS_EXCEPTION; + } else { + undef_sep = FALSE; + } + + A = JS_PushGCRef(ctx, &A_ref); + z = JS_PushGCRef(ctx, &z_ref); + *A = JS_NewArray(ctx, 0); + if (JS_IsException(*A)) + goto exception; + lengthA = 0; + + s = js_string_len(ctx, *this_val); + p = 0; + if (lim == 0) + goto done; + if (undef_sep) + goto add_tail; + + if (is_regexp) { + int numberOfCaptures, i, re_flags; + JSObject *p1; + JSValueArray *arr; + JSByteArray *bc_arr; + + p1 = JS_VALUE_TO_PTR(argv[0]); + bc_arr = JS_VALUE_TO_PTR(p1->u.regexp.byte_code); + re_flags = lre_get_flags(bc_arr->buf); + + if (s == 0) { + p1 = JS_VALUE_TO_PTR(argv[0]); + p1->u.regexp.last_index = 0; + *z = js_regexp_exec(ctx, &argv[0], 1, this_val, MAGIC_REGEXP_FORCE_GLOBAL); + if (JS_IsException(*z)) + goto exception; + if (JS_IsNull(*z)) + goto add_tail; + goto done; + } + q = 0; + while (q < s) { + p1 = JS_VALUE_TO_PTR(argv[0]); + p1->u.regexp.last_index = q; + /* XXX: need sticky behavior */ + *z = js_regexp_exec(ctx, &argv[0], 1, this_val, MAGIC_REGEXP_FORCE_GLOBAL); + if (JS_IsException(*z)) + goto exception; + if (JS_IsNull(*z)) { + if (!(re_flags & LRE_FLAG_STICKY)) { + break; + } else { + int c = string_getcp(ctx, *this_val, q, TRUE); + /* since regexp are unicode by default, split is also unicode by default */ + q += 1 + (c >= 0x10000); + } + } else { + if (!(re_flags & LRE_FLAG_STICKY)) { + JSValue res; + res = JS_GetProperty(ctx, *z, js_get_atom(ctx, JS_ATOM_index)); + if (JS_IsException(res)) + goto exception; + q = JS_VALUE_GET_INT(res); + } + p1 = JS_VALUE_TO_PTR(argv[0]); + e = p1->u.regexp.last_index; + if (e > s) + e = s; + if (e == p) { + int c = string_getcp(ctx, *this_val, q, TRUE); + /* since regexp are unicode by default, split is also unicode by default */ + q += 1 + (c >= 0x10000); + } else { + T = js_sub_string(ctx, *this_val, p, q); + if (JS_IsException(T)) + goto exception; + ret = JS_SetPropertyUint32(ctx, *A, lengthA++, T); + if (JS_IsException(ret)) + goto exception; + if (lengthA == lim) + goto done; + p1 = JS_VALUE_TO_PTR(*z); + numberOfCaptures = p1->u.array.len; + for(i = 1; i < numberOfCaptures; i++) { + p1 = JS_VALUE_TO_PTR(*z); + arr = JS_VALUE_TO_PTR(p1->u.array.tab); + T = arr->arr[i]; + ret = JS_SetPropertyUint32(ctx, *A, lengthA++, T); + if (JS_IsException(ret)) + goto exception; + } + q = p = e; + } + } + } + } else { + int r = js_string_len(ctx, argv[0]); + if (s == 0) { + if (r != 0) + goto add_tail; + goto done; + } + + for (q = 0; (q += !r) <= s - r - !r; q = p = e + r) { + + e = js_string_indexof(ctx, *this_val, argv[0], q, s, r); + if (e < 0) + break; + T = js_sub_string(ctx, *this_val, p, e); + if (JS_IsException(T)) + goto exception; + ret = JS_SetPropertyUint32(ctx, *A, lengthA++, T); + if (JS_IsException(ret)) + goto exception; + if (lengthA == lim) + goto done; + } + } +add_tail: + T = js_sub_string(ctx, *this_val, p, s); + if (JS_IsException(T)) + goto exception; + ret = JS_SetPropertyUint32(ctx, *A, lengthA++, T); + if (JS_IsException(ret)) + goto exception; + +done: + JS_PopGCRef(ctx, &z_ref); + return JS_PopGCRef(ctx, &A_ref); + +exception: + JS_PopGCRef(ctx, &z_ref); + JS_PopGCRef(ctx, &A_ref); + return JS_EXCEPTION; +} + +JSValue js_string_match(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + JSRegExp *re; + int global, n; + JSValue *A, *result, ret; + JSObject *p; + JSValueArray *arr; + JSByteArray *barr; + JSGCRef A_ref, result_ref; + + re = js_get_regexp(ctx, argv[0]); + if (!re) + return JS_EXCEPTION; + barr = JS_VALUE_TO_PTR(re->byte_code); + global = lre_get_flags(barr->buf) & LRE_FLAG_GLOBAL; + if (!global) + return js_regexp_exec(ctx, &argv[0], 1, this_val, 0); + + p = JS_VALUE_TO_PTR(argv[0]); + re = &p->u.regexp; + re->last_index = 0; + + A = JS_PushGCRef(ctx, &A_ref); + result = JS_PushGCRef(ctx, &result_ref); + *A = JS_NULL; + n = 0; + for(;;) { + *result = js_regexp_exec(ctx, &argv[0], 1, this_val, 0); + if (JS_IsException(*result)) + goto fail; + if (*result == JS_NULL) + break; + if (*A == JS_NULL) { + *A = JS_NewArray(ctx, 1); + if (JS_IsException(*A)) + goto fail; + } + + p = JS_VALUE_TO_PTR(*result); + arr = JS_VALUE_TO_PTR(p->u.array.tab); + + ret = JS_SetPropertyUint32(ctx, *A, n++, arr->arr[0]); + if (JS_IsException(ret)) { + fail: + *A = JS_EXCEPTION; + break; + } + } + JS_PopGCRef(ctx, &result_ref); + return JS_PopGCRef(ctx, &A_ref); +} + +JSValue js_string_search(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv) +{ + return js_regexp_exec(ctx, &argv[0], 1, this_val, MAGIC_REGEXP_SEARCH); +} diff --git a/lib/mquickjs/mquickjs.h b/lib/mquickjs/mquickjs.h new file mode 100644 index 00000000..a1557fe9 --- /dev/null +++ b/lib/mquickjs/mquickjs.h @@ -0,0 +1,382 @@ +/* + * Micro QuickJS Javascript Engine + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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 MQUICKJS_H +#define MQUICKJS_H + +#include + +#if defined(__GNUC__) || defined(__clang__) +#define __js_printf_like(f, a) __attribute__((format(printf, f, a))) +#else +#define __js_printf_like(a, b) +#endif + +#if INTPTR_MAX >= INT64_MAX +#define JS_PTR64 /* pointers are 64 bit wide instead of 32 bit wide */ +#endif + +typedef struct JSContext JSContext; + +#ifdef JS_PTR64 +typedef uint64_t JSWord; +typedef uint64_t JSValue; +#define JSW 8 +#define JSValue_PRI PRIo64 +#define JS_USE_SHORT_FLOAT +#else +typedef uint32_t JSWord; +typedef uint32_t JSValue; +#define JSW 4 +#define JSValue_PRI PRIo32 +#endif + +#define JS_BOOL int + +enum { + JS_TAG_INT = 0, /* 31 bit integer (1 bit) */ + JS_TAG_PTR = 1, /* pointer (2 bits) */ + JS_TAG_SPECIAL = 3, /* other special values (2 bits) */ + JS_TAG_BOOL = JS_TAG_SPECIAL | (0 << 2), /* (5 bits) */ + JS_TAG_NULL = JS_TAG_SPECIAL | (1 << 2), /* (5 bits) */ + JS_TAG_UNDEFINED = JS_TAG_SPECIAL | (2 << 2), /* (5 bits) */ + JS_TAG_EXCEPTION = JS_TAG_SPECIAL | (3 << 2), /* (5 bits) */ + JS_TAG_SHORT_FUNC = JS_TAG_SPECIAL | (4 << 2), /* (5 bits) */ + JS_TAG_UNINITIALIZED = JS_TAG_SPECIAL | (5 << 2), /* (5 bits) */ + JS_TAG_STRING_CHAR = JS_TAG_SPECIAL | (6 << 2), /* (5 bits) */ + JS_TAG_CATCH_OFFSET = JS_TAG_SPECIAL | (7 << 2), /* (5 bits) */ +#ifdef JS_USE_SHORT_FLOAT + JS_TAG_SHORT_FLOAT = 5, /* 3 bits */ +#endif +}; + +#define JS_TAG_SPECIAL_BITS 5 + +#define JS_VALUE_GET_INT(v) ((int)(v) >> 1) +#define JS_VALUE_GET_SPECIAL_VALUE(v) ((int)(v) >> JS_TAG_SPECIAL_BITS) +#define JS_VALUE_GET_SPECIAL_TAG(v) ((v) & ((1 << JS_TAG_SPECIAL_BITS) - 1)) +#define JS_VALUE_MAKE_SPECIAL(tag, v) ((tag) | ((v) << JS_TAG_SPECIAL_BITS)) + +#define JS_NULL JS_VALUE_MAKE_SPECIAL(JS_TAG_NULL, 0) +#define JS_UNDEFINED JS_VALUE_MAKE_SPECIAL(JS_TAG_UNDEFINED, 0) +#define JS_UNINITIALIZED JS_VALUE_MAKE_SPECIAL(JS_TAG_UNINITIALIZED, 0) +#define JS_FALSE JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, 0) +#define JS_TRUE JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, 1) + +#define JS_EX_NORMAL 0 /* all exceptions except not enough memory */ +#define JS_EX_CALL 1 /* specific exception to generate a tail call. The call flags are added */ +#define JS_EXCEPTION JS_VALUE_MAKE_SPECIAL(JS_TAG_EXCEPTION, JS_EX_NORMAL) + +typedef enum { + JS_CLASS_OBJECT, + JS_CLASS_ARRAY, + JS_CLASS_C_FUNCTION, + JS_CLASS_CLOSURE, + JS_CLASS_NUMBER, + JS_CLASS_BOOLEAN, + JS_CLASS_STRING, + JS_CLASS_DATE, + JS_CLASS_REGEXP, + + JS_CLASS_ERROR, + JS_CLASS_EVAL_ERROR, + JS_CLASS_RANGE_ERROR, + JS_CLASS_REFERENCE_ERROR, + JS_CLASS_SYNTAX_ERROR, + JS_CLASS_TYPE_ERROR, + JS_CLASS_URI_ERROR, + JS_CLASS_INTERNAL_ERROR, + + JS_CLASS_ARRAY_BUFFER, + JS_CLASS_TYPED_ARRAY, + + JS_CLASS_UINT8C_ARRAY, + JS_CLASS_INT8_ARRAY, + JS_CLASS_UINT8_ARRAY, + JS_CLASS_INT16_ARRAY, + JS_CLASS_UINT16_ARRAY, + JS_CLASS_INT32_ARRAY, + JS_CLASS_UINT32_ARRAY, + JS_CLASS_FLOAT32_ARRAY, + JS_CLASS_FLOAT64_ARRAY, + + JS_CLASS_USER, /* user classes start from this value */ +} JSObjectClassEnum; + +/* predefined functions */ +typedef enum { + JS_CFUNCTION_bound, + JS_CFUNCTION_USER, /* user functions start from this value */ +} JSCFunctionEnum; + +/* temporary buffer to hold C strings */ +typedef struct { + uint8_t buf[5]; +} JSCStringBuf; + +typedef struct JSGCRef { + JSValue val; + struct JSGCRef *prev; +} JSGCRef; + +/* stack of JSGCRef */ +JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref); +JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref); + +#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0) +#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref) + +/* list of JSGCRef (they can be removed in any order, slower) */ +JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref); +void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref); + +JSValue JS_NewFloat64(JSContext *ctx, double d); +JSValue JS_NewInt32(JSContext *ctx, int32_t val); +JSValue JS_NewUint32(JSContext *ctx, uint32_t val); +JSValue JS_NewInt64(JSContext *ctx, int64_t val); + +static inline JS_BOOL JS_IsInt(JSValue v) +{ + return (v & 1) == JS_TAG_INT; +} + +static inline JS_BOOL JS_IsPtr(JSValue v) +{ + return (v & (JSW - 1)) == JS_TAG_PTR; +} + +#ifdef JS_USE_SHORT_FLOAT +static inline JS_BOOL JS_IsShortFloat(JSValue v) +{ + return (v & (JSW - 1)) == JS_TAG_SHORT_FLOAT; +} +#endif + +static inline JS_BOOL JS_IsBool(JSValue v) +{ + return JS_VALUE_GET_SPECIAL_TAG(v) == JS_TAG_BOOL; +} + +static inline JS_BOOL JS_IsNull(JSValue v) +{ + return v == JS_NULL; +} + +static inline JS_BOOL JS_IsUndefined(JSValue v) +{ + return v == JS_UNDEFINED; +} + +static inline JS_BOOL JS_IsUninitialized(JSValue v) +{ + return v == JS_UNINITIALIZED; +} + +static inline JS_BOOL JS_IsException(JSValue v) +{ + return v == JS_EXCEPTION; +} + +static inline JSValue JS_NewBool(int val) +{ + return JS_VALUE_MAKE_SPECIAL(JS_TAG_BOOL, (val != 0)); +} + +JS_BOOL JS_IsNumber(JSContext *ctx, JSValue val); +JS_BOOL JS_IsString(JSContext *ctx, JSValue val); +JS_BOOL JS_IsError(JSContext *ctx, JSValue val); +JS_BOOL JS_IsFunction(JSContext *ctx, JSValue val); + +int JS_GetClassID(JSContext *ctx, JSValue val); +void JS_SetOpaque(JSContext *ctx, JSValue val, void *opaque); +void *JS_GetOpaque(JSContext *ctx, JSValue val); + +typedef JSValue JSCFunction(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +/* no JS function call be called from a C finalizer */ +typedef void (*JSCFinalizer)(JSContext *ctx, void *opaque); + +typedef enum JSCFunctionDefEnum { /* XXX: should rename for namespace isolation */ + JS_CFUNC_generic, + JS_CFUNC_generic_magic, + JS_CFUNC_constructor, + JS_CFUNC_constructor_magic, + JS_CFUNC_generic_params, + JS_CFUNC_f_f, +} JSCFunctionDefEnum; + +typedef union JSCFunctionType { + JSCFunction *generic; + JSValue (*generic_magic)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, int magic); + JSCFunction *constructor; + JSValue (*constructor_magic)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, int magic); + JSValue (*generic_params)(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv, JSValue params); + double (*f_f)(double f); +} JSCFunctionType; + +typedef struct JSCFunctionDef { + JSCFunctionType func; + JSValue name; + uint8_t def_type; + uint8_t arg_count; + int16_t magic; +} JSCFunctionDef; + +typedef struct { + const JSWord *stdlib_table; + const JSCFunctionDef *c_function_table; + const JSCFinalizer *c_finalizer_table; + uint32_t stdlib_table_len; + uint32_t stdlib_table_align; + uint32_t sorted_atoms_offset; + uint32_t global_object_offset; + uint32_t class_count; +} JSSTDLibraryDef; + +typedef void JSWriteFunc(void *opaque, const void *buf, size_t buf_len); +/* return != 0 if the JS code needs to be interrupted */ +typedef int JSInterruptHandler(JSContext *ctx, void *opaque); + +JSContext *JS_NewContext(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def); +/* if prepare_compilation is true, the context will be used to compile + to a binary file. JS_NewContext2() is not expected to be used in + the embedded version */ +JSContext *JS_NewContext2(void *mem_start, size_t mem_size, const JSSTDLibraryDef *stdlib_def, JS_BOOL prepare_compilation); +void JS_FreeContext(JSContext *ctx); +void JS_SetContextOpaque(JSContext *ctx, void *opaque); +void JS_SetInterruptHandler(JSContext *ctx, JSInterruptHandler *interrupt_handler); +void JS_SetRandomSeed(JSContext *ctx, uint64_t seed); +JSValue JS_GetGlobalObject(JSContext *ctx); +JSValue JS_Throw(JSContext *ctx, JSValue obj); +JSValue __js_printf_like(3, 4) JS_ThrowError(JSContext *ctx, JSObjectClassEnum error_num, + const char *fmt, ...); +#define JS_ThrowTypeError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_TYPE_ERROR, fmt, ##__VA_ARGS__) +#define JS_ThrowReferenceError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_REFERENCE_ERROR, fmt, ##__VA_ARGS__) +#define JS_ThrowInternalError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_INTERNAL_ERROR, fmt, ##__VA_ARGS__) +#define JS_ThrowRangeError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_RANGE_ERROR, fmt, ##__VA_ARGS__) +#define JS_ThrowSyntaxError(ctx, fmt, ...) JS_ThrowError(ctx, JS_CLASS_SYNTAX_ERROR, fmt, ##__VA_ARGS__) +JSValue JS_ThrowOutOfMemory(JSContext *ctx); +JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, const char *str); +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue obj, uint32_t idx); +JSValue JS_SetPropertyStr(JSContext *ctx, JSValue this_obj, + const char *str, JSValue val); +JSValue JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx, JSValue val); +JSValue JS_NewObjectClassUser(JSContext *ctx, int class_id); +JSValue JS_NewObject(JSContext *ctx); +JSValue JS_NewArray(JSContext *ctx, int initial_len); +/* create a C function with an object parameter (closure) */ +JSValue JS_NewCFunctionParams(JSContext *ctx, int func_idx, JSValue params); + +#define JS_EVAL_RETVAL (1 << 0) /* return the last value instead of undefined (slower code) */ +#define JS_EVAL_REPL (1 << 1) /* implicitly defined global variables in assignments */ +#define JS_EVAL_STRIP_COL (1 << 2) /* strip column number debug information (save memory) */ +#define JS_EVAL_JSON (1 << 3) /* parse as JSON and return the object */ +#define JS_EVAL_REGEXP (1 << 4) /* internal use */ +#define JS_EVAL_REGEXP_FLAGS_SHIFT 8 /* internal use */ +JSValue JS_Parse(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags); +JSValue JS_Run(JSContext *ctx, JSValue val); +JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, + const char *filename, int eval_flags); +void JS_GC(JSContext *ctx); +JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len); +JSValue JS_NewString(JSContext *ctx, const char *buf); +const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValue val, JSCStringBuf *buf); +const char *JS_ToCString(JSContext *ctx, JSValue val, JSCStringBuf *buf); +JSValue JS_ToString(JSContext *ctx, JSValue val); +int JS_ToInt32(JSContext *ctx, int *pres, JSValue val); +int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValue val); +int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValue val); +int JS_ToNumber(JSContext *ctx, double *pres, JSValue val); + +JSValue JS_GetException(JSContext *ctx); +int JS_StackCheck(JSContext *ctx, uint32_t len); +void JS_PushArg(JSContext *ctx, JSValue val); +#define FRAME_CF_CTOR (1 << 16) /* also ored with argc in + C constructors */ +JSValue JS_Call(JSContext *ctx, int call_flags); + +#define JS_BYTECODE_MAGIC 0xacfb + +typedef struct { + uint16_t magic; /* JS_BYTECODE_MAGIC */ + uint16_t version; + uintptr_t base_addr; + JSValue unique_strings; + JSValue main_func; +} JSBytecodeHeader; + +/* only used on the host when compiling to file */ +void JS_PrepareBytecode(JSContext *ctx, + JSBytecodeHeader *hdr, + const uint8_t **pdata_buf, uint32_t *pdata_len, + JSValue eval_code); +/* only used on the host when compiling to file */ +int JS_RelocateBytecode2(JSContext *ctx, JSBytecodeHeader *hdr, + uint8_t *buf, uint32_t buf_len, + uintptr_t new_base_addr, JS_BOOL update_atoms); +#if JSW == 8 +typedef struct { + uint16_t magic; /* JS_BYTECODE_MAGIC */ + uint16_t version; + uint32_t base_addr; + uint32_t unique_strings; + uint32_t main_func; +} JSBytecodeHeader32; + +/* only used on the host when compiling to file. A 32 bit bytecode is generated on a 64 bit host. */ +int JS_PrepareBytecode64to32(JSContext *ctx, + JSBytecodeHeader32 *hdr, + const uint8_t **pdata_buf, uint32_t *pdata_len, + JSValue eval_code); +#endif + +JS_BOOL JS_IsBytecode(const uint8_t *buf, size_t buf_len); +/* Relocate the bytecode in 'buf' so that it can be executed + later. Return 0 if OK, != 0 if error */ +int JS_RelocateBytecode(JSContext *ctx, + uint8_t *buf, uint32_t buf_len); +/* Load the precompiled bytecode from 'buf'. 'buf' must be allocated + as long as the JSContext exists. Use JS_Run() to execute + it. warning: the bytecode is not checked so it should come from a + trusted source. */ +JSValue JS_LoadBytecode(JSContext *ctx, const uint8_t *buf); + +/* debug functions */ +void JS_SetLogFunc(JSContext *ctx, JSWriteFunc *write_func); +void JS_PrintValue(JSContext *ctx, JSValue val); +#define JS_DUMP_LONG (1 << 0) /* display object/array content */ +#define JS_DUMP_NOQUOTE (1 << 1) /* strings: no quote for identifiers */ +/* for low level dumps: don't dump special properties and use specific + quotes to distinguish string chars, unique strings and normal + strings */ +#define JS_DUMP_RAW (1 << 2) +void JS_PrintValueF(JSContext *ctx, JSValue val, int flags); +void JS_DumpValueF(JSContext *ctx, const char *str, + JSValue val, int flags); +void JS_DumpValue(JSContext *ctx, const char *str, + JSValue val); +void JS_DumpMemory(JSContext *ctx, JS_BOOL is_long); + +#endif /* MQUICKJS_H */ diff --git a/lib/mquickjs/mquickjs_atom.h b/lib/mquickjs/mquickjs_atom.h new file mode 100644 index 00000000..f81edcb1 --- /dev/null +++ b/lib/mquickjs/mquickjs_atom.h @@ -0,0 +1,76 @@ +#define JS_ATOM_null 0 +#define JS_ATOM_false 3 +#define JS_ATOM_true 6 +#define JS_ATOM_if 9 +#define JS_ATOM_else 11 +#define JS_ATOM_return 14 +#define JS_ATOM_var 17 +#define JS_ATOM_this 19 +#define JS_ATOM_delete 22 +#define JS_ATOM_void 25 +#define JS_ATOM_typeof 28 +#define JS_ATOM_new 31 +#define JS_ATOM_in 33 +#define JS_ATOM_instanceof 35 +#define JS_ATOM_do 39 +#define JS_ATOM_while 41 +#define JS_ATOM_for 44 +#define JS_ATOM_break 46 +#define JS_ATOM_continue 49 +#define JS_ATOM_switch 53 +#define JS_ATOM_case 56 +#define JS_ATOM_default 59 +#define JS_ATOM_throw 62 +#define JS_ATOM_try 65 +#define JS_ATOM_catch 67 +#define JS_ATOM_finally 70 +#define JS_ATOM_function 73 +#define JS_ATOM_debugger 77 +#define JS_ATOM_with 81 +#define JS_ATOM_class 84 +#define JS_ATOM_const 87 +#define JS_ATOM_enum 90 +#define JS_ATOM_export 93 +#define JS_ATOM_extends 96 +#define JS_ATOM_import 99 +#define JS_ATOM_super 102 +#define JS_ATOM_implements 105 +#define JS_ATOM_interface 109 +#define JS_ATOM_let 113 +#define JS_ATOM_package 115 +#define JS_ATOM_private 118 +#define JS_ATOM_protected 121 +#define JS_ATOM_public 125 +#define JS_ATOM_static 128 +#define JS_ATOM_yield 131 +#define JS_ATOM_empty 134 +#define JS_ATOM_toString 136 +#define JS_ATOM_valueOf 140 +#define JS_ATOM_number 143 +#define JS_ATOM_object 146 +#define JS_ATOM_undefined 149 +#define JS_ATOM_string 153 +#define JS_ATOM_boolean 156 +#define JS_ATOM__ret_ 159 +#define JS_ATOM__eval_ 162 +#define JS_ATOM_eval 165 +#define JS_ATOM_arguments 168 +#define JS_ATOM_value 172 +#define JS_ATOM_get 175 +#define JS_ATOM_set 177 +#define JS_ATOM_prototype 179 +#define JS_ATOM_constructor 183 +#define JS_ATOM_length 187 +#define JS_ATOM_target 190 +#define JS_ATOM_of 193 +#define JS_ATOM_NaN 195 +#define JS_ATOM_Infinity 197 +#define JS_ATOM__Infinity 201 +#define JS_ATOM_name 205 +#define JS_ATOM_Error 208 +#define JS_ATOM___proto__ 211 +#define JS_ATOM_index 215 +#define JS_ATOM_input 218 + +#define JS_ATOM_END 221 + diff --git a/lib/mquickjs/mquickjs_build.c b/lib/mquickjs/mquickjs_build.c new file mode 100644 index 00000000..61732715 --- /dev/null +++ b/lib/mquickjs/mquickjs_build.c @@ -0,0 +1,932 @@ +/* + * Micro QuickJS build utility + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "list.h" +#include "mquickjs_build.h" + +static unsigned JSW = 4; // override this with -m64 + +typedef struct { + char *str; + int offset; +} AtomDef; + +typedef struct { + AtomDef *tab; + int count; + int size; + int offset; +} AtomList; + +typedef struct { + char *name; + int length; + char *magic; + char *cproto_name; + char *cfunc_name; +} CFuncDef; + +typedef struct { + CFuncDef *tab; + int count; + int size; +} CFuncList; + +typedef struct { + struct list_head link; + const JSClassDef *class1; + int class_idx; + char *finalizer_name; + char *class_id; +} ClassDefEntry; + +typedef struct { + AtomList atom_list; + CFuncList cfunc_list; + int cur_offset; + int sorted_atom_table_offset; + int global_object_offset; + struct list_head class_list; +} BuildContext; + +static const char *atoms[] = { +#define DEF(a, b) b, + /* keywords */ + 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") +#undef DEF + + /* other atoms */ + "", + "toString", + "valueOf", + "number", + "object", + "undefined", + "string", + "boolean", + "", + "", + "eval", + "arguments", + "value", + "get", + "set", + "prototype", + "constructor", + "length", + "target", + "of", + "NaN", + "Infinity", + "-Infinity", + "name", + "Error", + "__proto__", + "index", + "input", +}; + + +static char *cvt_name(char *buf, size_t buf_size, const char *str) +{ + size_t i, len = strlen(str); + assert(len < buf_size); + if (len == 0) { + strcpy(buf, "empty"); + } else { + strcpy(buf, str); + for(i = 0; i < len; i++) { + if (buf[i] == '<' || buf[i] == '>' || buf[i] == '-') + buf[i] = '_'; + } + } + return buf; +} + +static BOOL is_ascii_string(const char *buf, size_t len) +{ + size_t i; + for(i = 0; i < len; i++) { + if ((uint8_t)buf[i] > 0x7f) + return FALSE; + } + return TRUE; +} + +static BOOL is_numeric_string(const char *buf, size_t len) +{ + return (!strcmp(buf, "NaN") || + !strcmp(buf, "Infinity") || + !strcmp(buf, "-Infinity")); +} + +static int find_atom(AtomList *s, const char *str) +{ + int i; + for(i = 0; i < s->count; i++) { + if (!strcmp(str, s->tab[i].str)) + return i; + } + return -1; +} + +static int add_atom(AtomList *s, const char *str) +{ + int i; + AtomDef *e; + i = find_atom(s, str); + if (i >= 0) + return s->tab[i].offset; + if ((s->count + 1) > s->size) { + s->size = max_int(s->count + 1, s->size * 3 / 2); + s->tab = realloc(s->tab, sizeof(s->tab[0]) * s->size); + } + e = &s->tab[s->count++]; + e->str = strdup(str); + e->offset = s->offset; + s->offset += 1 + ((strlen(str) + JSW) / JSW); + return s->count - 1; +} + +static int add_cfunc(CFuncList *s, const char *name, int length, const char *magic, const char *cproto_name, const char *cfunc_name) +{ + int i; + CFuncDef *e; + + for(i = 0; i < s->count; i++) { + e = &s->tab[i]; + if (!strcmp(name, e->name) && + length == e->length && + !strcmp(magic, e->magic) && + !strcmp(cproto_name, e->cproto_name) && + !strcmp(cfunc_name, e->cfunc_name)) { + return i; + } + } + if ((s->count + 1) > s->size) { + s->size = max_int(s->count + 1, s->size * 3 / 2); + s->tab = realloc(s->tab, sizeof(s->tab[0]) * s->size); + } + e = &s->tab[s->count++]; + e->name = strdup(name); + e->magic = strdup(magic); + e->length = length; + e->cproto_name = strdup(cproto_name); + e->cfunc_name = strdup(cfunc_name); + return s->count - 1; +} + +static void dump_atom_defines(void) +{ + AtomList atom_list_s, *s = &atom_list_s; + AtomDef *e; + int i; + char buf[256]; + + memset(s, 0, sizeof(*s)); + + /* add the predefined atoms (they have a corresponding define) */ + for(i = 0; i < countof(atoms); i++) { + add_atom(s, atoms[i]); + } + + for(i = 0; i < s->count; i++) { + e = &s->tab[i]; + printf("#define JS_ATOM_%s %d\n", + cvt_name(buf, sizeof(buf), e->str), e->offset); + } + printf("\n"); + printf("#define JS_ATOM_END %d\n", s->offset); + printf("\n"); +} + +static int atom_cmp(const void *p1, const void *p2) +{ + const AtomDef *a1 = (const AtomDef *)p1; + const AtomDef *a2 = (const AtomDef *)p2; + return strcmp(a1->str, a2->str); +} + +/* js_atom_table must be properly aligned because the property hash + table uses the low bits of the atom pointer value */ +#define ATOM_ALIGN 64 + +static void dump_atoms(BuildContext *ctx) +{ + AtomList *s = &ctx->atom_list; + int i, j, k, l, len, len1, is_ascii, is_numeric; + uint64_t v; + const char *str; + AtomDef *sorted_atoms; + char buf[256]; + + sorted_atoms = malloc(sizeof(sorted_atoms[0]) * s->count); + memcpy(sorted_atoms, s->tab, sizeof(sorted_atoms[0]) * s->count); + qsort(sorted_atoms, s->count, sizeof(sorted_atoms[0]), atom_cmp); + + printf(" /* atom_table */\n"); + for(i = 0; i < s->count; i++) { + str = s->tab[i].str; + len = strlen(str); + is_ascii = is_ascii_string(str, len); + is_numeric = is_numeric_string(str, len); + printf(" (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (%d << (JS_MTAG_BITS + 1)) | (%d << (JS_MTAG_BITS + 2)) | (%d << (JS_MTAG_BITS + 3)), /* \"%s\" (offset=%d) */\n", + is_ascii, is_numeric, len, str, ctx->cur_offset); + len1 = (len + JSW) / JSW; + for(j = 0; j < len1; j++) { + l = min_uint32(JSW, len - j * JSW); + v = 0; + for(k = 0; k < l; k++) + v |= (uint64_t)(uint8_t)str[j * JSW + k] << (k * 8); + printf(" 0x%0*" PRIx64 ",\n", JSW * 2, v); + } + assert(ctx->cur_offset == s->tab[i].offset); + ctx->cur_offset += len1 + 1; + } + printf("\n"); + + ctx->sorted_atom_table_offset = ctx->cur_offset; + + printf(" /* sorted atom table (offset=%d) */\n", ctx->cur_offset); + printf(" JS_VALUE_ARRAY_HEADER(%d),\n", s->count); + for(i = 0; i < s->count; i++) { + AtomDef *e = &sorted_atoms[i]; + printf(" JS_ROM_VALUE(%d), /* %s */\n", + e->offset, cvt_name(buf, sizeof(buf), e->str)); + } + ctx->cur_offset += s->count + 1; + printf("\n"); + + free(sorted_atoms); +} + +static int define_value(BuildContext *s, const JSPropDef *d); + +static uint32_t dump_atom(BuildContext *s, const char *str, BOOL value_only) +{ + int len, idx, i, offset; + + len = strlen(str); + for(i = 0; i < len; i++) { + if ((uint8_t)str[i] >= 128) { + fprintf(stderr, "unicode property names are not supported yet (%s)\n", str); + exit(1); + } + } + if (len >= 1 && (str[0] >= '0' && str[0] <= '9')) { + fprintf(stderr, "numeric property names are not supported yet (%s)\n", str); + exit(1); + } + if (len == 1) { + if (value_only) { + /* XXX: hardcoded */ + return ((uint8_t)str[0] << 5) | 0x1b; + } + printf("JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, %d)", + (uint8_t)str[0]); + } else { + idx = find_atom(&s->atom_list, str); + if (idx < 0) { + fprintf(stderr, "atom '%s' is undefined\n", str); + exit(1); + } + offset = s->atom_list.tab[idx].offset; + if (value_only) + return (offset * JSW) + 1; /* correct modulo ATOM_ALIGN */ + printf("JS_ROM_VALUE(%d)", offset); + } + printf(" /* %s */", str); + return 0; +} + +static void dump_cfuncs(BuildContext *s) +{ + int i; + CFuncDef *e; + + printf("static const JSCFunctionDef js_c_function_table[] = {\n"); + for(i = 0; i < s->cfunc_list.count; i++) { + e = &s->cfunc_list.tab[i]; + printf(" { { .%s = %s },\n", e->cproto_name, e->cfunc_name); + printf(" "); + dump_atom(s, e->name, FALSE); + printf(",\n"); + printf(" JS_CFUNC_%s, %d, %s },\n", + e->cproto_name, e->length, e->magic); + } + printf("};\n\n"); +} + +static void dump_cfinalizers(BuildContext *s) +{ + struct list_head *el; + ClassDefEntry *e; + + printf("static const JSCFinalizer js_c_finalizer_table[JS_CLASS_COUNT - JS_CLASS_USER] = {\n"); + list_for_each(el, &s->class_list) { + e = list_entry(el, ClassDefEntry, link); + if (e->finalizer_name && + strcmp(e->finalizer_name, "NULL") != 0) { + printf(" [%s - JS_CLASS_USER] = %s,\n", e->class_id, e->finalizer_name); + } + } + printf("};\n\n"); +} + +typedef enum { + PROPS_KIND_GLOBAL, + PROPS_KIND_PROTO, + PROPS_KIND_CLASS, + PROPS_KIND_OBJECT, +} JSPropsKindEnum; + +static inline uint32_t hash_prop(BuildContext *s, const char *name) +{ + /* Compute the hash for a symbol, must be consistent with + mquickjs.c implementation. + */ + uint32_t prop = dump_atom(s, name, TRUE); + return (prop / JSW) ^ (prop % JSW); /* XXX: improve */ +} + +static int define_props(BuildContext *s, const JSPropDef *props_def, + JSPropsKindEnum props_kind, const char *class_id_str) +{ + int i, *ident_tab, idx, props_ident, n_props; + int prop_idx; + const JSPropDef *d; + uint32_t *prop_hash; + BOOL is_global_object = (props_kind == PROPS_KIND_GLOBAL); + static const JSPropDef dummy_props[] = { + { JS_DEF_END }, + }; + + if (!props_def) + props_def = dummy_props; + + n_props = 0; + for(d = props_def; d->def_type != JS_DEF_END; d++) { + n_props++; + } + if (props_kind == PROPS_KIND_PROTO || + props_kind == PROPS_KIND_CLASS) + n_props++; + ident_tab = malloc(sizeof(ident_tab[0]) * n_props); + + /* define the various objects */ + for(d = props_def, i = 0; d->def_type != JS_DEF_END; d++, i++) { + ident_tab[i] = define_value(s, d); + } + + props_ident = -1; + prop_hash = NULL; + if (is_global_object) { + props_ident = s->cur_offset; + printf(" /* global object properties (offset=%d) */\n", props_ident); + printf(" JS_VALUE_ARRAY_HEADER(%d),\n", 2 * n_props); + s->cur_offset += 2 * n_props + 1; + } else { + int hash_size_log2; + uint32_t hash_size, hash_mask; + uint32_t *hash_table, h; + + if (n_props <= 1) + hash_size_log2 = 0; + else + hash_size_log2 = (32 - clz32(n_props - 1)) - 1; + hash_size = 1 << hash_size_log2; + if (hash_size > ATOM_ALIGN / JSW) { +#if !defined __APPLE__ + // XXX: Cannot request data alignment larger than 64 bytes on Darwin + fprintf(stderr, "Too many properties, consider increasing ATOM_ALIGN\n"); +#endif + hash_size = ATOM_ALIGN / JSW; + } + hash_mask = hash_size - 1; + + hash_table = malloc(sizeof(hash_table[0]) * hash_size); + prop_hash = malloc(sizeof(prop_hash[0]) * n_props); + /* build the hash table */ + for(i = 0; i < hash_size; i++) + hash_table[i] = 0; + prop_idx = 0; + for(i = 0, d = props_def; i < n_props; i++, d++) { + const char *name; + if (d->def_type != JS_DEF_END) { + name = d->name; + } else { + if (props_kind == PROPS_KIND_PROTO) + name = "constructor"; + else + name = "prototype"; + } + h = hash_prop(s, name) & hash_mask; + prop_hash[prop_idx] = hash_table[h]; + hash_table[h] = 2 + hash_size + 3 * prop_idx; + prop_idx++; + } + + props_ident = s->cur_offset; + printf(" /* properties (offset=%d) */\n", props_ident); + printf(" JS_VALUE_ARRAY_HEADER(%d),\n", 2 + hash_size + n_props * 3); + printf(" %d << 1, /* n_props */\n", n_props); + printf(" %d << 1, /* hash_mask */\n", hash_mask); + for(i = 0; i < hash_size; i++) { + printf(" %d << 1,\n", hash_table[i]); + } + s->cur_offset += hash_size + 3 + 3 * n_props; + free(hash_table); + } + prop_idx = 0; + for(d = props_def, i = 0; i < n_props; d++, i++) { + const char *name, *prop_type; + /* name */ + printf(" "); + if (d->def_type != JS_DEF_END) { + name = d->name; + } else { + if (props_kind == PROPS_KIND_PROTO) + name = "constructor"; + else + name = "prototype"; + } + dump_atom(s, name, FALSE); + printf(",\n"); + + printf(" "); + prop_type = "NORMAL"; + switch(d->def_type) { + case JS_DEF_PROP_DOUBLE: + if (ident_tab[i] >= 0) + goto value_ptr; + /* short int */ + printf("%d << 1,", (int32_t)d->u.f64); + break; + case JS_DEF_CGETSET: + if (is_global_object) { + fprintf(stderr, "getter/setter forbidden in global object\n"); + exit(1); + } + prop_type = "GETSET"; + goto value_ptr; + case JS_DEF_CLASS: + value_ptr: + assert(ident_tab[i] >= 0); + printf("JS_ROM_VALUE(%d),", ident_tab[i]); + break; + case JS_DEF_PROP_UNDEFINED: + printf("JS_UNDEFINED,"); + break; + case JS_DEF_PROP_NULL: + printf("JS_NULL,"); + break; + case JS_DEF_PROP_STRING: + dump_atom(s, d->u.str, FALSE); + printf(","); + break; + case JS_DEF_CFUNC: + idx = add_cfunc(&s->cfunc_list, + d->name, + d->u.func.length, + d->u.func.magic, + d->u.func.cproto_name, + d->u.func.func_name); + printf("JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),", idx); + break; + case JS_DEF_END: + if (props_kind == PROPS_KIND_PROTO) { + /* constructor property */ + printf("(uint32_t)(-%s - 1) << 1,", class_id_str); + } else { + /* prototype property */ + printf("%s << 1,", class_id_str); + } + prop_type = "SPECIAL"; + break; + default: + abort(); + } + printf("\n"); + if (!is_global_object) { + printf(" (%d << 1) | (JS_PROP_%s << 30),\n", + prop_hash[prop_idx], prop_type); + } + prop_idx++; + } + + free(prop_hash); + free(ident_tab); + return props_ident; +} + +static ClassDefEntry *find_class(BuildContext *s, const JSClassDef *d) +{ + struct list_head *el; + ClassDefEntry *e; + + list_for_each(el, &s->class_list) { + e = list_entry(el, ClassDefEntry, link); + if (e->class1 == d) + return e; + } + return NULL; +} + +static void free_class_entries(BuildContext *s) +{ + struct list_head *el, *el1; + ClassDefEntry *e; + list_for_each_safe(el, el1, &s->class_list) { + e = list_entry(el, ClassDefEntry, link); + free(e->class_id); + free(e->finalizer_name); + free(e); + } + init_list_head(&s->class_list); +} + +static int define_class(BuildContext *s, const JSClassDef *d) +{ + int ctor_func_idx = -1, class_props_idx = -1, proto_props_idx = -1; + int ident, parent_class_idx = -1; + ClassDefEntry *e; + + /* check if the class is already defined */ + e = find_class(s, d); + if (e) + return e->class_idx; + + if (d->parent_class) + parent_class_idx = define_class(s, d->parent_class); + + if (d->func_name) { + ctor_func_idx = add_cfunc(&s->cfunc_list, + d->name, + d->length, + d->class_id, + d->cproto_name, + d->func_name); + } + + if (ctor_func_idx >= 0) { + class_props_idx = define_props(s, d->class_props, PROPS_KIND_CLASS, d->class_id); + proto_props_idx = define_props(s, d->proto_props, PROPS_KIND_PROTO, d->class_id); + } else { + if (d->class_props) + class_props_idx = define_props(s, d->class_props, PROPS_KIND_OBJECT, d->class_id); + } + + ident = s->cur_offset; + printf(" /* class (offset=%d) */\n", ident); + printf(" JS_MB_HEADER_DEF(JS_MTAG_OBJECT),\n"); + if (class_props_idx >= 0) + printf(" JS_ROM_VALUE(%d),\n", class_props_idx); + else + printf(" JS_NULL,\n"); + printf(" %d,\n", ctor_func_idx); + if (proto_props_idx >= 0) + printf(" JS_ROM_VALUE(%d),\n", proto_props_idx); + else + printf(" JS_NULL,\n"); + if (parent_class_idx >= 0) { + printf(" JS_ROM_VALUE(%d),\n", parent_class_idx); + } else { + printf(" JS_NULL,\n"); + } + printf("\n"); + + s->cur_offset += 5; + + e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->class_idx = ident; + e->class1 = d; + if (ctor_func_idx >= 0) { + e->class_id = strdup(d->class_id); + e->finalizer_name = strdup(d->finalizer_name); + } + list_add_tail(&e->link, &s->class_list); + return ident; +} + +#define JS_SHORTINT_MIN (-(1 << 30)) +#define JS_SHORTINT_MAX ((1 << 30) - 1) + +static BOOL is_short_int(double d) +{ + return (d >= JS_SHORTINT_MIN && d <= JS_SHORTINT_MAX && (int32_t)d == d); +} + +static int define_value(BuildContext *s, const JSPropDef *d) +{ + int ident; + ident = -1; + switch(d->def_type) { + case JS_DEF_PROP_DOUBLE: + { + uint64_t v; + if (!is_short_int(d->u.f64)) { + ident = s->cur_offset; + printf(" /* float64 (offset=%d) */\n", ident); + printf(" JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),\n"); + v = float64_as_uint64(d->u.f64); + if (JSW == 8) { + printf(" 0x%016zx,\n", (size_t)v); + printf("\n"); + s->cur_offset += 2; + } else { + /* XXX: little endian assumed */ + printf(" 0x%08x,\n", (uint32_t)v); + printf(" 0x%08x,\n", (uint32_t)(v >> 32)); + printf("\n"); + s->cur_offset += 3; + } + } + } + break; + case JS_DEF_CLASS: + ident = define_class(s, d->u.class1); + break; + case JS_DEF_CGETSET: + { + int get_idx = -1, set_idx = -1; + char buf[256]; + if (strcmp(d->u.getset.get_func_name, "NULL") != 0) { + snprintf(buf, sizeof(buf), "get %s", d->name); + get_idx = add_cfunc(&s->cfunc_list, + buf, + 0, /* length */ + d->u.getset.magic, + d->u.getset.cproto_name, + d->u.getset.get_func_name); + } + if (strcmp(d->u.getset.set_func_name, "NULL") != 0) { + snprintf(buf, sizeof(buf), "set %s", d->name); + set_idx = add_cfunc(&s->cfunc_list, + buf, + 1, /* length */ + d->u.getset.magic, + d->u.getset.cproto_name, + d->u.getset.set_func_name); + } + ident = s->cur_offset; + printf(" /* getset (offset=%d) */\n", ident); + printf(" JS_VALUE_ARRAY_HEADER(2),\n"); + if (get_idx >= 0) + printf(" JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),\n", get_idx); + else + printf(" JS_UNDEFINED,\n"); + if (set_idx >= 0) + printf(" JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),\n", set_idx); + else + printf(" JS_UNDEFINED,\n"); + printf("\n"); + s->cur_offset += 3; + } + break; + default: + break; + } + return ident; +} + +static void define_atoms_props(BuildContext *s, const JSPropDef *props_def, JSPropsKindEnum props_kind); + +static void define_atoms_class(BuildContext *s, const JSClassDef *d) +{ + ClassDefEntry *e; + /* check if the class is already defined */ + e = find_class(s, d); + if (e) + return; + if (d->parent_class) + define_atoms_class(s, d->parent_class); + if (d->func_name) + add_atom(&s->atom_list, d->name); + if (d->class_props) + define_atoms_props(s, d->class_props, d->func_name ? PROPS_KIND_CLASS : PROPS_KIND_OBJECT); + if (d->proto_props) + define_atoms_props(s, d->proto_props, PROPS_KIND_PROTO); +} + +static void define_atoms_props(BuildContext *s, const JSPropDef *props_def, JSPropsKindEnum props_kind) +{ + const JSPropDef *d; + for(d = props_def; d->def_type != JS_DEF_END; d++) { + add_atom(&s->atom_list, d->name); + switch(d->def_type) { + case JS_DEF_PROP_STRING: + add_atom(&s->atom_list, d->u.str); + break; + case JS_DEF_CLASS: + define_atoms_class(s, d->u.class1); + break; + case JS_DEF_CGETSET: + { + char buf[256]; + if (strcmp(d->u.getset.get_func_name, "NULL") != 0) { + snprintf(buf, sizeof(buf), "get %s", d->name); + add_atom(&s->atom_list, buf); + } + if (strcmp(d->u.getset.set_func_name, "NULL") != 0) { + snprintf(buf, sizeof(buf), "set %s", d->name); + add_atom(&s->atom_list, buf); + } + } + break; + default: + break; + } + } +} + +static int usage(const char *name) +{ + fprintf(stderr, "usage: %s {-m32 | -m64} [-a]\n", name); + fprintf(stderr, + " create a ROM file for the mquickjs standard library\n" + "--help list options\n" + "-m32 force generation for a 32 bit target\n" + "-m64 force generation for a 64 bit target\n" + "-a generate the mquickjs_atom.h header\n" + ); + return 1; +} + +int build_atoms(const char *stdlib_name, const JSPropDef *global_obj, + const JSPropDef *c_function_decl, int argc, char **argv) +{ + int i; + unsigned jsw; + BuildContext ss, *s = &ss; + BOOL build_atom_defines = FALSE; + +#if INTPTR_MAX >= INT64_MAX + jsw = 8; +#else + jsw = 4; +#endif + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-m64")) { + jsw = 8; + } else if (!strcmp(argv[i], "-m32")) { + jsw = 4; + } else if (!strcmp(argv[i], "-a")) { + build_atom_defines = TRUE; + } else if (!strcmp(argv[i], "--help")) { + return usage(argv[0]); + } else { + fprintf(stderr, "invalid argument '%s'\n", argv[i]); + return usage(argv[0]); + } + } + + JSW = jsw; + + if (build_atom_defines) { + dump_atom_defines(); + return 0; + } + + memset(s, 0, sizeof(*s)); + init_list_head(&s->class_list); + + /* add the predefined atoms (they have a corresponding define) */ + for(i = 0; i < countof(atoms); i++) { + add_atom(&s->atom_list, atoms[i]); + } + + /* add the predefined functions */ + if (c_function_decl) { + const JSPropDef *d; + for(d = c_function_decl; d->def_type != JS_DEF_END; d++) { + if (d->def_type != JS_DEF_CFUNC) { + fprintf(stderr, "only C functions are allowed in c_function_decl[]\n"); + exit(1); + } + add_atom(&s->atom_list, d->name); + add_cfunc(&s->cfunc_list, + d->name, + d->u.func.length, + d->u.func.magic, + d->u.func.cproto_name, + d->u.func.func_name); + } + } + + /* first pass to define the atoms */ + define_atoms_props(s, global_obj, PROPS_KIND_GLOBAL); + free_class_entries(s); + + printf("/* this file is automatically generated - do not edit */\n\n"); + printf("#include \"mquickjs_priv.h\"\n\n"); + + printf("static const uint%u_t __attribute((aligned(%d))) js_stdlib_table[] = {\n", + JSW * 8, ATOM_ALIGN); + + dump_atoms(s); + + s->global_object_offset = define_props(s, global_obj, PROPS_KIND_GLOBAL, NULL); + + printf("};\n\n"); + + dump_cfuncs(s); + + printf("#ifndef JS_CLASS_COUNT\n" + "#define JS_CLASS_COUNT JS_CLASS_USER /* total number of classes */\n" + "#endif\n\n"); + + dump_cfinalizers(s); + + free_class_entries(s); + + printf("const JSSTDLibraryDef %s = {\n", stdlib_name); + printf(" js_stdlib_table,\n"); + printf(" js_c_function_table,\n"); + printf(" js_c_finalizer_table,\n"); + printf(" %d,\n", s->cur_offset); + printf(" %d,\n", ATOM_ALIGN); + printf(" %d,\n", s->sorted_atom_table_offset); + printf(" %d,\n", s->global_object_offset); + printf(" JS_CLASS_COUNT,\n"); + printf("};\n\n"); + + return 0; +} diff --git a/lib/mquickjs/mquickjs_build.h b/lib/mquickjs/mquickjs_build.h new file mode 100644 index 00000000..51be6b44 --- /dev/null +++ b/lib/mquickjs/mquickjs_build.h @@ -0,0 +1,97 @@ +/* + * Micro QuickJS build utility + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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 MQUICKJS_BUILD_H +#define MQUICKJS_BUILD_H + +#include +#include + +enum { + JS_DEF_END, + JS_DEF_CFUNC, + JS_DEF_CGETSET, + JS_DEF_PROP_DOUBLE, + JS_DEF_PROP_UNDEFINED, + JS_DEF_PROP_STRING, + JS_DEF_PROP_NULL, + JS_DEF_CLASS, +}; + +typedef struct JSClassDef JSClassDef; + +typedef struct JSPropDef { + int def_type; + const char *name; + union { + struct { + uint8_t length; + const char *magic; + const char *cproto_name; + const char *func_name; + } func; + struct { + const char *magic; + const char *cproto_name; + const char *get_func_name; + const char *set_func_name; + } getset; + double f64; + const JSClassDef *class1; + const char *str; + } u; +} JSPropDef; + +typedef struct JSClassDef { + const char *name; + int length; + const char *cproto_name; + const char *func_name; + const char *class_id; + const JSPropDef *class_props; /* NULL if none */ + const JSPropDef *proto_props; /* NULL if none */ + const JSClassDef *parent_class; /* NULL if none */ + const char *finalizer_name; /* "NULL" if none */ +} JSClassDef; + +#define JS_PROP_END { JS_DEF_END } +#define JS_CFUNC_DEF(name, length, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", "generic", #func_name } } } +#define JS_CFUNC_MAGIC_DEF(name, length, func_name, magic) { JS_DEF_CFUNC, name, { .func = { length, #magic, "generic_magic", #func_name } } } +#define JS_CFUNC_SPECIAL_DEF(name, length, proto, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", #proto, #func_name } } } +#define JS_CGETSET_DEF(name, get_name, set_name) { JS_DEF_CGETSET, name, { .getset = { "0", "generic", #get_name, #set_name } } } +#define JS_CGETSET_MAGIC_DEF(name, get_name, set_name, magic) { JS_DEF_CGETSET, name, { .getset = { #magic, "generic_magic", #get_name, #set_name } } } +#define JS_PROP_CLASS_DEF(name, cl) { JS_DEF_CLASS, name, { .class1 = cl } } +#define JS_PROP_DOUBLE_DEF(name, val, flags) { JS_DEF_PROP_DOUBLE, name, { .f64 = val } } +#define JS_PROP_UNDEFINED_DEF(name, flags) { JS_DEF_PROP_UNDEFINED, name } +#define JS_PROP_NULL_DEF(name, flags) { JS_DEF_PROP_NULL, name } +#define JS_PROP_STRING_DEF(name, cstr, flags) { JS_DEF_PROP_STRING, name, { .str = cstr } } + +#define JS_CLASS_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name } +#define JS_CLASS_MAGIC_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor_magic", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name } +#define JS_OBJECT_DEF(name, obj_props) { name, 0, NULL, NULL, NULL, obj_props, NULL, NULL, NULL } + +int build_atoms(const char *stdlib_name, const JSPropDef *global_obj, + const JSPropDef *c_function_decl, int argc, char **argv); + +#endif /* MQUICKJS_BUILD_H */ diff --git a/lib/mquickjs/mquickjs_opcode.h b/lib/mquickjs/mquickjs_opcode.h new file mode 100644 index 00000000..2ccd6050 --- /dev/null +++ b/lib/mquickjs/mquickjs_opcode.h @@ -0,0 +1,264 @@ +/* + * Micro QuickJS opcode definitions + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(i32) +FMT(const16) +FMT(label) +FMT(value) +#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_value, 5, 0, 1, value) +DEF( push_const, 3, 0, 1, const16) +DEF( fclosure, 3, 0, 1, const16) +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, 3, 0, 1, u16) +DEF( this_func, 1, 0, 1, none) +DEF( arguments, 1, 0, 1, none) +DEF( new_target, 1, 0, 1, none) + +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, 1, 1, npop) /* func args... -> ret (arguments are not counted in n_pop) */ +DEF( call, 3, 1, 1, npop) /* func args... -> ret (arguments are not counted in n_pop) */ +DEF( call_method, 3, 2, 1, npop) /* this func args.. -> ret (arguments are not counted in n_pop) */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a bytecode string */ + +DEF( get_field, 3, 1, 1, const16) /* obj -> val */ +DEF( get_field2, 3, 1, 2, const16) /* obj -> obj val */ +DEF( put_field, 3, 2, 0, const16) /* obj val -> */ +DEF( get_array_el, 1, 2, 1, none) /* obj prop -> val */ +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) /* obj prop val -> */ +DEF( get_length, 1, 1, 1, none) /* obj -> val */ +DEF( get_length2, 1, 1, 2, none) /* obj -> obj val */ +DEF( define_field, 3, 2, 1, const16) /* obj val -> obj */ +DEF( define_getter, 3, 2, 1, const16) /* obj val -> obj */ +DEF( define_setter, 3, 2, 1, const16) /* obj val -> obj */ +DEF( set_proto, 1, 2, 1, none) /* obj proto -> obj */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_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(get_var_ref_nocheck, 3, 0, 1, var_ref) +DEF(put_var_ref_nocheck, 3, 1, 0, var_ref) +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( for_in_start, 1, 1, 1, none) /* obj -> iter */ +DEF( for_of_start, 1, 1, 1, none) /* obj -> iter */ +DEF( for_of_next, 1, 1, 3, none) /* iter -> iter val done */ + +/* 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( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) /* obj prop -> ret */ + +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) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +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) /* must follow get_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) /* must follow get_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( 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) /* must follow get_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) +#if 0 +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) +#endif + +#undef DEF +#undef def +#endif /* DEF */ + +#ifdef REDEF + +/* regular expression bytecode */ +REDEF(invalid, 1) /* never used */ +REDEF(char1, 2) +REDEF(char2, 3) +REDEF(char3, 4) +REDEF(char4, 5) +REDEF(dot, 1) +REDEF(any, 1) /* same as dot but match any character including line terminator */ +REDEF(space, 1) +REDEF(not_space, 1) /* must come after */ +REDEF(line_start, 1) +REDEF(line_start_m, 1) +REDEF(line_end, 1) +REDEF(line_end_m, 1) +REDEF(goto, 5) +REDEF(split_goto_first, 5) +REDEF(split_next_first, 5) +REDEF(match, 1) +REDEF(lookahead_match, 1) +REDEF(negative_lookahead_match, 1) /* must come after */ +REDEF(save_start, 2) /* save start position */ +REDEF(save_end, 2) /* save end position, must come after saved_start */ +REDEF(save_reset, 3) /* reset save positions */ +REDEF(loop, 6) /* decrement the top the stack and goto if != 0 */ +REDEF(loop_split_goto_first, 10) /* loop and then split */ +REDEF(loop_split_next_first, 10) +REDEF(loop_check_adv_split_goto_first, 10) /* loop and then check advance and split */ +REDEF(loop_check_adv_split_next_first, 10) +REDEF(set_i32, 6) /* store the immediate value to a register */ +REDEF(word_boundary, 1) +REDEF(not_word_boundary, 1) +REDEF(back_reference, 2) +REDEF(back_reference_i, 2) +REDEF(range8, 2) /* variable length */ +REDEF(range, 3) /* variable length */ +REDEF(lookahead, 5) +REDEF(negative_lookahead, 5) /* must come after */ +REDEF(set_char_pos, 2) /* store the character position to a register */ +REDEF(check_advance, 2) /* check that the register is different from the character position */ + +#endif /* REDEF */ diff --git a/lib/mquickjs/mquickjs_priv.h b/lib/mquickjs/mquickjs_priv.h new file mode 100644 index 00000000..37bc1535 --- /dev/null +++ b/lib/mquickjs/mquickjs_priv.h @@ -0,0 +1,268 @@ +/* microj private definitions */ +#ifndef MICROJS_PRIV_H +#define MICROJS_PRIV_H + +#include "mquickjs.h" +#include "libm.h" + +#define JS_DUMP /* 2.6 kB */ +//#define DUMP_EXEC +//#define DUMP_FUNC_BYTECODE /* dump the bytecode of each compiled function */ +//#define DUMP_REOP /* dump regexp bytecode */ +//#define DUMP_GC +//#define DUMP_TOKEN /* dump parsed tokens */ +/* run GC before at each malloc() and modify the allocated data pointers */ +//#define DEBUG_GC +#if defined(DUMP_FUNC_BYTECODE) || defined(DUMP_EXEC) +#define DUMP_BYTECODE /* include the dump_byte_code() function */ +#endif + +#define JS_VALUE_TO_PTR(v) (void *)((uintptr_t)(v) - 1) +#define JS_VALUE_FROM_PTR(ptr) (JSWord)((uintptr_t)(ptr) + 1) + +#define JS_IS_ROM_PTR(ctx, ptr) ((uintptr_t)(ptr) < (uintptr_t)ctx || (uintptr_t)(ptr) >= (uintptr_t)ctx->stack_top) + +enum { + JS_MTAG_FREE, + /* javascript values */ + JS_MTAG_OBJECT, + JS_MTAG_FLOAT64, + JS_MTAG_STRING, + /* other special memory blocks */ + JS_MTAG_FUNCTION_BYTECODE, + JS_MTAG_VALUE_ARRAY, + JS_MTAG_BYTE_ARRAY, + JS_MTAG_VARREF, + + JS_MTAG_COUNT, +}; + +/* JS_MTAG_BITS bits are reserved at the start of every memory block */ +#define JS_MTAG_BITS 4 + +#define JS_MB_HEADER \ + JSWord gc_mark: 1; \ + JSWord mtag: (JS_MTAG_BITS - 1) + +typedef enum { + JS_PROP_NORMAL, + JS_PROP_GETSET, /* value is a two element JSValueArray */ + JS_PROP_VARREF, /* value is a JSVarRef (used for global variables) */ + JS_PROP_SPECIAL, /* for the prototype and constructor properties in ROM */ +} JSPropTypeEnum; + +#define JS_MB_HEADER_DEF(tag) ((tag) << 1) +#define JS_VALUE_ARRAY_HEADER(size) (JS_MB_HEADER_DEF(JS_MTAG_VALUE_ARRAY) | ((size) << JS_MTAG_BITS)) + +#define JS_ROM_VALUE(offset) JS_VALUE_FROM_PTR(&js_stdlib_table[offset]) + +/* runtime helpers */ +JSValue js_function_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_function_get_prototype(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_function_set_prototype(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_function_get_length_name(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_name); +JSValue js_function_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_function_call(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_function_apply(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_function_bind(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_function_bound(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, JSValue params); + +JSValue js_array_get_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_set_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_number_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_number_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_number_toFixed(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_number_toExponential(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_number_toPrecision(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_number_parseInt(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_number_parseFloat(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_boolean_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_string_get_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_set_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_slice(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_substring(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +enum { + magic_internalAt, + magic_charAt, + magic_charCodeAt, + magic_codePointAt, +}; +JSValue js_string_charAt(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic); +JSValue js_string_concat(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_indexOf(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int lastIndexOf); +JSValue js_string_match(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_replace(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_replaceAll); +JSValue js_string_search(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_split(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_toLowerCase(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int to_lower); +JSValue js_string_trim(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic); +JSValue js_string_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_repeat(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_object_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_object_defineProperty(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_object_getPrototypeOf(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_object_setPrototypeOf(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_object_create(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_object_keys(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_object_hasOwnProperty(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_object_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_string_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_string_fromCharCode(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_fromCodePoint); + +JSValue js_error_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic); +JSValue js_error_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_error_get_message(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic); + +JSValue js_array_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_push(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_unshift); +JSValue js_array_pop(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_shift(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_join(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_toString(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_isArray(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_reverse(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_concat(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_indexOf(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_lastIndexOf); +JSValue js_array_slice(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_splice(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_sort(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +#define js_special_every 0 +#define js_special_some 1 +#define js_special_forEach 2 +#define js_special_map 3 +#define js_special_filter 4 + +JSValue js_array_every(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int special); + +#define js_special_reduce 0 +#define js_special_reduceRight 1 + +JSValue js_array_reduce(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int special); + +JSValue js_math_min_max(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic); +double js_math_sign(double a); +double js_math_fround(double a); +JSValue js_math_imul(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_math_clz32(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_math_atan2(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_math_pow(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_math_random(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_array_buffer_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_array_buffer_get_byteLength(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_typed_array_base_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_typed_array_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic); +JSValue js_typed_array_get_length(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int magic); +JSValue js_typed_array_subarray(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_typed_array_set(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_date_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_global_eval(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_global_isNaN(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_global_isFinite(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_json_parse(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_json_stringify(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); + +JSValue js_regexp_constructor(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_regexp_get_lastIndex(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_regexp_set_lastIndex(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_regexp_get_source(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_regexp_get_flags(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv); +JSValue js_regexp_exec(JSContext *ctx, JSValue *this_val, + int argc, JSValue *argv, int is_test); + +#endif /* MICROJS_PRIV_H */ diff --git a/lib/mquickjs/scripts/build.sh b/lib/mquickjs/scripts/build.sh new file mode 100755 index 00000000..a14a9770 --- /dev/null +++ b/lib/mquickjs/scripts/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +cd $SCRIPT_DIR/.. # mquickjs directory + +make clean + +CONFIG_X86_32=1 CONFIG_SOFTFLOAT=1 make mqjs_stdlib.h +CONFIG_X86_32=1 CONFIG_SOFTFLOAT=1 make mquickjs_atom.h + +mv mqjs_stdlib.h crosspoint_stdlib.h + +mv mquickjs_atom.h crosspoint_atom.h # avoid being deleted in the next step +make clean +mv crosspoint_atom.h mquickjs_atom.h diff --git a/lib/mquickjs/scripts/crosspoint_stdlib.c b/lib/mquickjs/scripts/crosspoint_stdlib.c new file mode 100644 index 00000000..918b9623 --- /dev/null +++ b/lib/mquickjs/scripts/crosspoint_stdlib.c @@ -0,0 +1,566 @@ +/* + * Micro QuickJS REPL library + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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 "mquickjs_build.h" + +/* defined in mqjs_example.c */ +//#define CONFIG_CLASS_EXAMPLE + +static const JSPropDef js_object_proto[] = { + JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty), + JS_CFUNC_DEF("toString", 0, js_object_toString), + JS_PROP_END, +}; + +static const JSPropDef js_object[] = { + JS_CFUNC_DEF("defineProperty", 3, js_object_defineProperty), + JS_CFUNC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf), + JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf), + JS_CFUNC_DEF("create", 2, js_object_create), + JS_CFUNC_DEF("keys", 1, js_object_keys), + JS_PROP_END, +}; + +static const JSClassDef js_object_class = + JS_CLASS_DEF("Object", 1, js_object_constructor, JS_CLASS_OBJECT, + js_object, js_object_proto, NULL, NULL); + +static const JSPropDef js_function_proto[] = { + JS_CGETSET_DEF("prototype", js_function_get_prototype, js_function_set_prototype ), + JS_CFUNC_DEF("call", 1, js_function_call ), + JS_CFUNC_DEF("apply", 2, js_function_apply ), + JS_CFUNC_DEF("bind", 1, js_function_bind ), + JS_CFUNC_DEF("toString", 0, js_function_toString ), + JS_CGETSET_MAGIC_DEF("length", js_function_get_length_name, NULL, 0 ), + JS_CGETSET_MAGIC_DEF("name", js_function_get_length_name, NULL, 1 ), + JS_PROP_END, +}; + +static const JSClassDef js_function_class = + JS_CLASS_DEF("Function", 1, js_function_constructor, JS_CLASS_CLOSURE, NULL, js_function_proto, NULL, NULL); + +static const JSPropDef js_number_proto[] = { + JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ), + JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ), + JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ), + JS_CFUNC_DEF("toString", 1, js_number_toString ), + JS_PROP_END, +}; + +static const JSPropDef js_number[] = { + JS_CFUNC_DEF("parseInt", 2, js_number_parseInt ), + JS_CFUNC_DEF("parseFloat", 1, js_number_parseFloat ), + JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ), + JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ), + JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), + JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ), + JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ), + JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */ + JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */ + JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */ + JS_PROP_END, +}; + +static const JSClassDef js_number_class = + JS_CLASS_DEF("Number", 1, js_number_constructor, JS_CLASS_NUMBER, js_number, js_number_proto, NULL, NULL); + +static const JSClassDef js_boolean_class = + JS_CLASS_DEF("Boolean", 1, js_boolean_constructor, JS_CLASS_BOOLEAN, NULL, NULL, NULL, NULL); + +static const JSPropDef js_string_proto[] = { + JS_CGETSET_DEF("length", js_string_get_length, js_string_set_length ), + JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, magic_charAt ), + JS_CFUNC_MAGIC_DEF("charCodeAt", 1, js_string_charAt, magic_charCodeAt ), + JS_CFUNC_MAGIC_DEF("codePointAt", 1, js_string_charAt, magic_codePointAt ), + JS_CFUNC_DEF("slice", 2, js_string_slice ), + JS_CFUNC_DEF("substring", 2, js_string_substring ), + JS_CFUNC_DEF("concat", 1, js_string_concat ), + JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ), + JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ), + JS_CFUNC_DEF("match", 1, js_string_match ), + JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ), + JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ), + JS_CFUNC_DEF("search", 1, js_string_search ), + JS_CFUNC_DEF("split", 2, js_string_split ), + JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), + JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ), + JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ), + JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ), + JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ), + JS_CFUNC_DEF("toString", 0, js_string_toString ), + JS_CFUNC_DEF("repeat", 1, js_string_repeat ), + JS_PROP_END, +}; + +static const JSPropDef js_string[] = { + JS_CFUNC_MAGIC_DEF("fromCharCode", 1, js_string_fromCharCode, 0 ), + JS_CFUNC_MAGIC_DEF("fromCodePoint", 1, js_string_fromCharCode, 1 ), + JS_PROP_END, +}; + +static const JSClassDef js_string_class = + JS_CLASS_DEF("String", 1, js_string_constructor, JS_CLASS_STRING, js_string, js_string_proto, NULL, NULL); + +static const JSPropDef js_array_proto[] = { + JS_CFUNC_DEF("concat", 1, js_array_concat ), + JS_CGETSET_DEF("length", js_array_get_length, js_array_set_length ), + JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ), + JS_CFUNC_DEF("pop", 0, js_array_pop ), + JS_CFUNC_DEF("join", 1, js_array_join ), + JS_CFUNC_DEF("toString", 0, js_array_toString ), + JS_CFUNC_DEF("reverse", 0, js_array_reverse ), + JS_CFUNC_DEF("shift", 0, js_array_shift ), + JS_CFUNC_DEF("slice", 2, js_array_slice ), + JS_CFUNC_DEF("splice", 2, js_array_splice ), + JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), + JS_CFUNC_MAGIC_DEF("indexOf", 1, js_array_indexOf, 0 ), + JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_array_indexOf, 1 ), + JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, js_special_every ), + JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, js_special_some ), + JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, js_special_forEach ), + JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, js_special_map ), + JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, js_special_filter ), + JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, js_special_reduce ), + JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, js_special_reduceRight ), + JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, js_special_reduce ), + JS_CFUNC_DEF("sort", 1, js_array_sort ), + JS_PROP_END, +}; + +static const JSPropDef js_array[] = { + JS_CFUNC_DEF("isArray", 1, js_array_isArray ), + JS_PROP_END, +}; + +static const JSClassDef js_array_class = + JS_CLASS_DEF("Array", 1, js_array_constructor, JS_CLASS_ARRAY, js_array, js_array_proto, NULL, NULL); + +static const JSPropDef js_error_proto[] = { + JS_CFUNC_DEF("toString", 0, js_error_toString ), + JS_PROP_STRING_DEF("name", "Error", 0 ), + JS_CGETSET_MAGIC_DEF("message", js_error_get_message, NULL, 0 ), + JS_CGETSET_MAGIC_DEF("stack", js_error_get_message, NULL, 1 ), + JS_PROP_END, +}; + +static const JSClassDef js_error_class = + JS_CLASS_MAGIC_DEF("Error", 1, js_error_constructor, JS_CLASS_ERROR, NULL, js_error_proto, NULL, NULL); + +#define ERROR_DEF(cname, name, class_id) \ + static const JSPropDef js_ ## cname ## _proto[] = { \ + JS_PROP_STRING_DEF("name", name, 0 ), \ + JS_PROP_END, \ + }; \ + static const JSClassDef js_ ## cname ## _class = \ + JS_CLASS_MAGIC_DEF(name, 1, js_error_constructor, class_id, NULL, js_ ## cname ## _proto, &js_error_class, NULL); + +ERROR_DEF(eval_error, "EvalError", JS_CLASS_EVAL_ERROR) +ERROR_DEF(range_error, "RangeError", JS_CLASS_RANGE_ERROR) +ERROR_DEF(reference_error, "ReferenceError", JS_CLASS_REFERENCE_ERROR) +ERROR_DEF(syntax_error, "SyntaxError", JS_CLASS_SYNTAX_ERROR) +ERROR_DEF(type_error, "TypeError", JS_CLASS_TYPE_ERROR) +ERROR_DEF(uri_error, "URIError", JS_CLASS_URI_ERROR) +ERROR_DEF(internal_error, "InternalError", JS_CLASS_INTERNAL_ERROR) + +static const JSPropDef js_math[] = { + JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), + JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), + JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ), + JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, js_fabs ), + JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, js_floor ), + JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, js_ceil ), + JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_round_inf ), + JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, js_sqrt ), + + JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ), + JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ), + JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ), + JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ), + JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ), + JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ), + JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ), + JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ), + + JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, js_sin ), + JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, js_cos ), + JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, js_tan ), + JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, js_asin ), + JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, js_acos ), + JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, js_atan ), + JS_CFUNC_DEF("atan2", 2, js_math_atan2 ), + JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, js_exp ), + JS_CFUNC_SPECIAL_DEF("log", 1, f_f, js_log ), + JS_CFUNC_DEF("pow", 2, js_math_pow ), + JS_CFUNC_DEF("random", 0, js_math_random ), + + /* some ES6 functions */ + JS_CFUNC_DEF("imul", 2, js_math_imul ), + JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), + JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), + JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, js_trunc ), + JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, js_log2 ), + JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, js_log10 ), + + JS_PROP_END, +}; + +static const JSClassDef js_math_obj = + JS_OBJECT_DEF("Math", js_math); + +static const JSPropDef js_json[] = { + JS_CFUNC_DEF("parse", 2, js_json_parse ), + JS_CFUNC_DEF("stringify", 3, js_json_stringify ), + JS_PROP_END, +}; + +static const JSClassDef js_json_obj = + JS_OBJECT_DEF("JSON", js_json); + +/* typed arrays */ +static const JSPropDef js_array_buffer_proto[] = { + JS_CGETSET_DEF("byteLength", js_array_buffer_get_byteLength, NULL ), + JS_PROP_END, +}; + +static const JSClassDef js_array_buffer_class = + JS_CLASS_DEF("ArrayBuffer", 1, js_array_buffer_constructor, JS_CLASS_ARRAY_BUFFER, NULL, js_array_buffer_proto, NULL, NULL); + +static const JSPropDef js_typed_array_base_proto[] = { + JS_CGETSET_MAGIC_DEF("length", js_typed_array_get_length, NULL, 0 ), + JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_length, NULL, 1 ), + JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_length, NULL, 2 ), + JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_length, NULL, 3 ), + JS_CFUNC_DEF("join", 1, js_array_join ), + JS_CFUNC_DEF("toString", 0, js_array_toString ), + JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ), + JS_CFUNC_DEF("set", 1, js_typed_array_set ), + JS_PROP_END, +}; + +static const JSClassDef js_typed_array_base_class = + JS_CLASS_DEF("TypedArray", 0, js_typed_array_base_constructor, JS_CLASS_TYPED_ARRAY, NULL, js_typed_array_base_proto, NULL, NULL); + +#define TA_DEF(name, class_name, bpe)\ +static const JSPropDef js_ ## name [] = {\ + JS_PROP_DOUBLE_DEF("BYTES_PER_ELEMENT", bpe, 0),\ + JS_PROP_END,\ +};\ +static const JSPropDef js_ ## name ## _proto[] = {\ + JS_PROP_DOUBLE_DEF("BYTES_PER_ELEMENT", bpe, 0),\ + JS_PROP_END,\ +};\ +static const JSClassDef js_ ## name ## _class =\ + JS_CLASS_MAGIC_DEF(#name, 3, js_typed_array_constructor, class_name, js_ ## name, js_ ## name ## _proto, &js_typed_array_base_class, NULL); + +TA_DEF(Uint8ClampedArray, JS_CLASS_UINT8C_ARRAY, 1) +TA_DEF(Int8Array, JS_CLASS_INT8_ARRAY, 1) +TA_DEF(Uint8Array, JS_CLASS_UINT8_ARRAY, 1) +TA_DEF(Int16Array, JS_CLASS_INT16_ARRAY, 2) +TA_DEF(Uint16Array, JS_CLASS_UINT16_ARRAY, 2) +TA_DEF(Int32Array, JS_CLASS_INT32_ARRAY, 4) +TA_DEF(Uint32Array, JS_CLASS_UINT32_ARRAY, 4) +TA_DEF(Float32Array, JS_CLASS_FLOAT32_ARRAY, 4) +TA_DEF(Float64Array, JS_CLASS_FLOAT64_ARRAY, 8) + +/* regexp */ + +static const JSPropDef js_regexp_proto[] = { + JS_CGETSET_DEF("lastIndex", js_regexp_get_lastIndex, js_regexp_set_lastIndex ), + JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), + JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), + JS_CFUNC_MAGIC_DEF("exec", 1, js_regexp_exec, 0 ), + JS_CFUNC_MAGIC_DEF("test", 1, js_regexp_exec, 1 ), + JS_PROP_END, +}; + +static const JSClassDef js_regexp_class = + JS_CLASS_DEF("RegExp", 2, js_regexp_constructor, JS_CLASS_REGEXP, NULL, js_regexp_proto, NULL, NULL); + +/* other objects */ + +static const JSPropDef js_date[] = { + JS_CFUNC_DEF("now", 0, js_date_now), + JS_PROP_END, +}; + +static const JSClassDef js_date_class = + JS_CLASS_DEF("Date", 7, js_date_constructor, JS_CLASS_DATE, js_date, NULL, NULL, NULL); + +static const JSPropDef js_console[] = { + JS_CFUNC_DEF("log", 1, js_print), + JS_PROP_END, +}; + +static const JSClassDef js_console_obj = + JS_OBJECT_DEF("Console", js_console); + +static const JSPropDef js_performance[] = { + JS_CFUNC_DEF("now", 0, js_performance_now), + JS_PROP_END, +}; +static const JSClassDef js_performance_obj = + JS_OBJECT_DEF("Performance", js_performance); + +// Crosspoint-specific functions (will invoke native C++ code) +#define JS_PROP_INT_DEF(name, val, flags) { JS_DEF_PROP_DOUBLE, name, { .f64 = val } } +static const JSPropDef js_cp[] = { + JS_PROP_STRING_DEF("FAST_REFRESH", "F", 0), + JS_PROP_STRING_DEF("HALF_REFRESH", "H", 0), + JS_PROP_STRING_DEF("FULL_REFRESH", "A", 0), + + JS_PROP_STRING_DEF("FONT_UI_10", "UI10", 0), + JS_PROP_STRING_DEF("FONT_UI_12", "UI12", 0), + JS_PROP_STRING_DEF("FONT_SMALL", "SM", 0), + + JS_PROP_STRING_DEF("TEXT_REGULAR", "R", 0), + JS_PROP_STRING_DEF("TEXT_BOLD", "B", 0), + JS_PROP_STRING_DEF("TEXT_ITALIC", "I", 0), + JS_PROP_STRING_DEF("TEXT_BOLD_ITALIC", "J", 0), + + JS_PROP_STRING_DEF("BTN_BACK", "B", 0), + JS_PROP_STRING_DEF("BTN_CONFIRM", "C", 0), + JS_PROP_STRING_DEF("BTN_LEFT", "L", 0), + JS_PROP_STRING_DEF("BTN_RIGHT", "R", 0), + JS_PROP_STRING_DEF("BTN_UP", "U", 0), + JS_PROP_STRING_DEF("BTN_DOWN", "D", 0), + + /** + * @brief Get the number of milliseconds since the device was powered on + * @return {number} Milliseconds since power on + */ + JS_CFUNC_DEF("millis", 0, js_millis), + + /** + * @brief Check if a button is currently pressed + * @param {string} buttonId The ID of the button to check (e.g., CP.BTN_BACK, CP.BTN_CONFIRM, etc.) + * @return {boolean} True if the button is pressed, false otherwise + */ + JS_CFUNC_DEF("btnIsPressed", 0, js_btnIsPressed), + + /** + * @brief Get screen width, ben changed based on orientation + * @return {number} Screen width in pixels + */ + JS_CFUNC_DEF("getScreenWidth", 0, js_getScreenWidth), + + /** + * @brief Get screen height, ben changed based on orientation + * @return {number} Screen height in pixels + */ + JS_CFUNC_DEF("getScreenHeight", 0, js_getScreenHeight), + + /** + * @brief Clear the screen buffer + * @param {number} color The color to clear the screen to (0-255, where 0 = black, 255 = white) + * @return {void} + */ + JS_CFUNC_DEF("clearScreen", 1, js_clearScreen), + + /** + * @brief Display the screen buffer + * @param {string} refreshMode The refresh mode to use (0 = CP.FAST_REFRESH, 1 = CP.FULL_REFRESH, etc.) + * @return {void} + */ + JS_CFUNC_DEF("displayBuffer", 1, js_displayBuffer), + + + /** + * @brief Draws a line from (x1, y1) to (x2, y2). + * @param x1 {number} The x-coordinate of the starting point. + * @param y1 {number} The y-coordinate of the starting point. + * @param x2 {number} The x-coordinate of the ending point. + * @param y2 {number} The y-coordinate of the ending point. + * @param state {boolean} The state to set the pixels to (true for on, false for off). Defaults to true. + */ + JS_CFUNC_DEF("drawLine", 5, js_drawLine), + + /** + * @brief Draws the outline of a rectangle at the specified position with the given dimensions. + * @param x {number} The x-coordinate of the top-left corner of the rectangle. + * @param y {number} The y-coordinate of the top-left corner of the rectangle. + * @param width {number} The width of the rectangle. + * @param height {number} The height of the rectangle. + * @param state {boolean} The state to set the pixels to (true for on, false for off). Defaults to true. + */ + JS_CFUNC_DEF("drawRect", 5, js_drawRect), + + /** + * @brief Fills a rectangle at the specified position with the given dimensions. + * @param x {number} The x-coordinate of the top-left corner of the rectangle. + * @param y {number} The y-coordinate of the top-left corner of the rectangle. + * @param width {number} The width of the rectangle. + * @param height {number} The height of the rectangle. + * @param state {boolean} The state to set the pixels to (true for on, false for off). Defaults to true. + */ + JS_CFUNC_DEF("fillRect", 5, js_fillRect), + + /** + * @brief Draws a bitmap image at the specified position with the given dimensions. + * @param bitmap {ArrayBuffer} The array of bytes representing the bitmap data, 1bpp, must have total size of (width * height) / 8. + * @param x {number} The x-coordinate of the top-left corner where the image will be drawn. + * @param y {number} The y-coordinate of the top-left corner where the image will be drawn. + * @param width {number} The width of the image. + * @param height {number} The height of the image. + */ + JS_CFUNC_DEF("drawImage", 5, js_drawImage), + + + /** + * @brief Get the width in pixels of the text. + * @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.) + * @param text {string} The text to measure. + * @param style {string} The style of the font (CP.TEXT_REGULAR, CP.TEXT_BOLD, etc.) + */ + JS_CFUNC_DEF("getTextWidth", 3, js_getTextWidth), + + /** + * @brief Draws centered text at the specified y-coordinate (non-wrapped). + * @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.) + * @param y {number} The y-coordinate where the text will be drawn. + * @param text {string} The text to draw. + * @param black {boolean} Whether to draw the text in black (true) or white (false). + * @param style {string} The style of the font (CP.TEXT_REGULAR, CP.TEXT_BOLD, etc.) + */ + JS_CFUNC_DEF("drawCenteredText", 5, js_drawCenteredText), + + /** + * @brief Draws text at the specified position (non-wrapped). + * @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.) + * @param x {number} The x-coordinate where the text will be drawn. + * @param y {number} The y-coordinate where the text will be drawn. + * @param text {string} The text to draw. + * @param black {boolean} Whether to draw the text in black (true) or white (false). + * @param style {string} The style of the font (CP.TEXT_REGULAR, CP.TEXT_BOLD, etc.) + */ + JS_CFUNC_DEF("drawText", 6, js_drawText), + + // JS_CFUNC_DEF("drawTextBox", 6, js_drawText), // TODO: implement drawTextBox + + + /** + * @brief Draws button hints at the bottom of the screen. + * @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.) + * @param btn1Text {string} The text for button 1. + * @param btn2Text {string} The text for button 2. + * @param btn3Text {string} The text for button 3. + * @param btn4Text {string} The text for button 4. + */ + JS_CFUNC_DEF("drawButtonHints", 5, js_drawButtonHints), + + /** + * @brief Draws side button hints on the left and right sides of the screen. + * @param fontId {string} The ID of the font to use (e.g., CP.FONT_UI_10, CP.FONT_SMALL, etc.) + * @param topBtnText {string} The text for the top side button. + * @param bottomBtnText {string} The text for the bottom side button. + */ + JS_CFUNC_DEF("drawSideButtonHints", 3, js_drawSideButtonHints), + + JS_PROP_END, +}; + +static const JSClassDef js_cp_obj = + JS_OBJECT_DEF("CP", js_cp); +// End of Crosspoint-specific functions + +static const JSPropDef js_global_object[] = { + JS_PROP_CLASS_DEF("Object", &js_object_class), + JS_PROP_CLASS_DEF("Function", &js_function_class), + JS_PROP_CLASS_DEF("Number", &js_number_class), + JS_PROP_CLASS_DEF("Boolean", &js_boolean_class), + JS_PROP_CLASS_DEF("String", &js_string_class), + JS_PROP_CLASS_DEF("Array", &js_array_class), + JS_PROP_CLASS_DEF("Math", &js_math_obj), + JS_PROP_CLASS_DEF("Date", &js_date_class), + JS_PROP_CLASS_DEF("JSON", &js_json_obj), + JS_PROP_CLASS_DEF("RegExp", &js_regexp_class), + + JS_PROP_CLASS_DEF("Error", &js_error_class), + JS_PROP_CLASS_DEF("EvalError", &js_eval_error_class), + JS_PROP_CLASS_DEF("RangeError", &js_range_error_class), + JS_PROP_CLASS_DEF("ReferenceError", &js_reference_error_class), + JS_PROP_CLASS_DEF("SyntaxError", &js_syntax_error_class), + JS_PROP_CLASS_DEF("TypeError", &js_type_error_class), + JS_PROP_CLASS_DEF("URIError", &js_uri_error_class), + JS_PROP_CLASS_DEF("InternalError", &js_internal_error_class), + + JS_PROP_CLASS_DEF("ArrayBuffer", &js_array_buffer_class), + JS_PROP_CLASS_DEF("Uint8ClampedArray", &js_Uint8ClampedArray_class), + JS_PROP_CLASS_DEF("Int8Array", &js_Int8Array_class), + JS_PROP_CLASS_DEF("Uint8Array", &js_Uint8Array_class), + JS_PROP_CLASS_DEF("Int16Array", &js_Int16Array_class), + JS_PROP_CLASS_DEF("Uint16Array", &js_Uint16Array_class), + JS_PROP_CLASS_DEF("Int32Array", &js_Int32Array_class), + JS_PROP_CLASS_DEF("Uint32Array", &js_Uint32Array_class), + JS_PROP_CLASS_DEF("Float32Array", &js_Float32Array_class), + JS_PROP_CLASS_DEF("Float64Array", &js_Float64Array_class), + + JS_CFUNC_DEF("parseInt", 2, js_number_parseInt ), + JS_CFUNC_DEF("parseFloat", 1, js_number_parseFloat ), + JS_CFUNC_DEF("eval", 1, js_global_eval), + JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ), + JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ), + + JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), + JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), + JS_PROP_UNDEFINED_DEF("undefined", 0 ), + /* Note: null is expanded as the global object in js_global_object[] */ + JS_PROP_NULL_DEF("globalThis", 0 ), + + JS_PROP_CLASS_DEF("console", &js_console_obj), + JS_PROP_CLASS_DEF("performance", &js_performance_obj), + JS_CFUNC_DEF("print", 1, js_print), +#ifdef CONFIG_CLASS_EXAMPLE + JS_PROP_CLASS_DEF("Rectangle", &js_rectangle_class), + JS_PROP_CLASS_DEF("FilledRectangle", &js_filled_rectangle_class), +#else + JS_CFUNC_DEF("gc", 0, js_gc), + JS_CFUNC_DEF("load", 1, js_load), + JS_CFUNC_DEF("setTimeout", 2, js_setTimeout), + JS_CFUNC_DEF("clearTimeout", 1, js_clearTimeout), +#endif + + JS_PROP_CLASS_DEF("CP", &js_cp_obj), // crosspoint-specific object + + JS_PROP_END, +}; + +/* Additional C function declarations (only useful for C + closures). They are always defined first. */ +static const JSPropDef js_c_function_decl[] = { + /* must come first if "bind" is defined */ + JS_CFUNC_SPECIAL_DEF("bound", 0, generic_params, js_function_bound ), +#ifdef CONFIG_CLASS_EXAMPLE + JS_CFUNC_SPECIAL_DEF("rectangle_closure_test", 0, generic_params, js_rectangle_closure_test ), +#endif + JS_PROP_END, +}; + +int main(int argc, char **argv) +{ + return build_atoms("js_stdlib", js_global_object, js_c_function_decl, argc, argv); +} diff --git a/lib/mquickjs/scripts/mquickjs_build.c b/lib/mquickjs/scripts/mquickjs_build.c new file mode 100644 index 00000000..61732715 --- /dev/null +++ b/lib/mquickjs/scripts/mquickjs_build.c @@ -0,0 +1,932 @@ +/* + * Micro QuickJS build utility + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "list.h" +#include "mquickjs_build.h" + +static unsigned JSW = 4; // override this with -m64 + +typedef struct { + char *str; + int offset; +} AtomDef; + +typedef struct { + AtomDef *tab; + int count; + int size; + int offset; +} AtomList; + +typedef struct { + char *name; + int length; + char *magic; + char *cproto_name; + char *cfunc_name; +} CFuncDef; + +typedef struct { + CFuncDef *tab; + int count; + int size; +} CFuncList; + +typedef struct { + struct list_head link; + const JSClassDef *class1; + int class_idx; + char *finalizer_name; + char *class_id; +} ClassDefEntry; + +typedef struct { + AtomList atom_list; + CFuncList cfunc_list; + int cur_offset; + int sorted_atom_table_offset; + int global_object_offset; + struct list_head class_list; +} BuildContext; + +static const char *atoms[] = { +#define DEF(a, b) b, + /* keywords */ + 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") +#undef DEF + + /* other atoms */ + "", + "toString", + "valueOf", + "number", + "object", + "undefined", + "string", + "boolean", + "", + "", + "eval", + "arguments", + "value", + "get", + "set", + "prototype", + "constructor", + "length", + "target", + "of", + "NaN", + "Infinity", + "-Infinity", + "name", + "Error", + "__proto__", + "index", + "input", +}; + + +static char *cvt_name(char *buf, size_t buf_size, const char *str) +{ + size_t i, len = strlen(str); + assert(len < buf_size); + if (len == 0) { + strcpy(buf, "empty"); + } else { + strcpy(buf, str); + for(i = 0; i < len; i++) { + if (buf[i] == '<' || buf[i] == '>' || buf[i] == '-') + buf[i] = '_'; + } + } + return buf; +} + +static BOOL is_ascii_string(const char *buf, size_t len) +{ + size_t i; + for(i = 0; i < len; i++) { + if ((uint8_t)buf[i] > 0x7f) + return FALSE; + } + return TRUE; +} + +static BOOL is_numeric_string(const char *buf, size_t len) +{ + return (!strcmp(buf, "NaN") || + !strcmp(buf, "Infinity") || + !strcmp(buf, "-Infinity")); +} + +static int find_atom(AtomList *s, const char *str) +{ + int i; + for(i = 0; i < s->count; i++) { + if (!strcmp(str, s->tab[i].str)) + return i; + } + return -1; +} + +static int add_atom(AtomList *s, const char *str) +{ + int i; + AtomDef *e; + i = find_atom(s, str); + if (i >= 0) + return s->tab[i].offset; + if ((s->count + 1) > s->size) { + s->size = max_int(s->count + 1, s->size * 3 / 2); + s->tab = realloc(s->tab, sizeof(s->tab[0]) * s->size); + } + e = &s->tab[s->count++]; + e->str = strdup(str); + e->offset = s->offset; + s->offset += 1 + ((strlen(str) + JSW) / JSW); + return s->count - 1; +} + +static int add_cfunc(CFuncList *s, const char *name, int length, const char *magic, const char *cproto_name, const char *cfunc_name) +{ + int i; + CFuncDef *e; + + for(i = 0; i < s->count; i++) { + e = &s->tab[i]; + if (!strcmp(name, e->name) && + length == e->length && + !strcmp(magic, e->magic) && + !strcmp(cproto_name, e->cproto_name) && + !strcmp(cfunc_name, e->cfunc_name)) { + return i; + } + } + if ((s->count + 1) > s->size) { + s->size = max_int(s->count + 1, s->size * 3 / 2); + s->tab = realloc(s->tab, sizeof(s->tab[0]) * s->size); + } + e = &s->tab[s->count++]; + e->name = strdup(name); + e->magic = strdup(magic); + e->length = length; + e->cproto_name = strdup(cproto_name); + e->cfunc_name = strdup(cfunc_name); + return s->count - 1; +} + +static void dump_atom_defines(void) +{ + AtomList atom_list_s, *s = &atom_list_s; + AtomDef *e; + int i; + char buf[256]; + + memset(s, 0, sizeof(*s)); + + /* add the predefined atoms (they have a corresponding define) */ + for(i = 0; i < countof(atoms); i++) { + add_atom(s, atoms[i]); + } + + for(i = 0; i < s->count; i++) { + e = &s->tab[i]; + printf("#define JS_ATOM_%s %d\n", + cvt_name(buf, sizeof(buf), e->str), e->offset); + } + printf("\n"); + printf("#define JS_ATOM_END %d\n", s->offset); + printf("\n"); +} + +static int atom_cmp(const void *p1, const void *p2) +{ + const AtomDef *a1 = (const AtomDef *)p1; + const AtomDef *a2 = (const AtomDef *)p2; + return strcmp(a1->str, a2->str); +} + +/* js_atom_table must be properly aligned because the property hash + table uses the low bits of the atom pointer value */ +#define ATOM_ALIGN 64 + +static void dump_atoms(BuildContext *ctx) +{ + AtomList *s = &ctx->atom_list; + int i, j, k, l, len, len1, is_ascii, is_numeric; + uint64_t v; + const char *str; + AtomDef *sorted_atoms; + char buf[256]; + + sorted_atoms = malloc(sizeof(sorted_atoms[0]) * s->count); + memcpy(sorted_atoms, s->tab, sizeof(sorted_atoms[0]) * s->count); + qsort(sorted_atoms, s->count, sizeof(sorted_atoms[0]), atom_cmp); + + printf(" /* atom_table */\n"); + for(i = 0; i < s->count; i++) { + str = s->tab[i].str; + len = strlen(str); + is_ascii = is_ascii_string(str, len); + is_numeric = is_numeric_string(str, len); + printf(" (JS_MTAG_STRING << 1) | (1 << JS_MTAG_BITS) | (%d << (JS_MTAG_BITS + 1)) | (%d << (JS_MTAG_BITS + 2)) | (%d << (JS_MTAG_BITS + 3)), /* \"%s\" (offset=%d) */\n", + is_ascii, is_numeric, len, str, ctx->cur_offset); + len1 = (len + JSW) / JSW; + for(j = 0; j < len1; j++) { + l = min_uint32(JSW, len - j * JSW); + v = 0; + for(k = 0; k < l; k++) + v |= (uint64_t)(uint8_t)str[j * JSW + k] << (k * 8); + printf(" 0x%0*" PRIx64 ",\n", JSW * 2, v); + } + assert(ctx->cur_offset == s->tab[i].offset); + ctx->cur_offset += len1 + 1; + } + printf("\n"); + + ctx->sorted_atom_table_offset = ctx->cur_offset; + + printf(" /* sorted atom table (offset=%d) */\n", ctx->cur_offset); + printf(" JS_VALUE_ARRAY_HEADER(%d),\n", s->count); + for(i = 0; i < s->count; i++) { + AtomDef *e = &sorted_atoms[i]; + printf(" JS_ROM_VALUE(%d), /* %s */\n", + e->offset, cvt_name(buf, sizeof(buf), e->str)); + } + ctx->cur_offset += s->count + 1; + printf("\n"); + + free(sorted_atoms); +} + +static int define_value(BuildContext *s, const JSPropDef *d); + +static uint32_t dump_atom(BuildContext *s, const char *str, BOOL value_only) +{ + int len, idx, i, offset; + + len = strlen(str); + for(i = 0; i < len; i++) { + if ((uint8_t)str[i] >= 128) { + fprintf(stderr, "unicode property names are not supported yet (%s)\n", str); + exit(1); + } + } + if (len >= 1 && (str[0] >= '0' && str[0] <= '9')) { + fprintf(stderr, "numeric property names are not supported yet (%s)\n", str); + exit(1); + } + if (len == 1) { + if (value_only) { + /* XXX: hardcoded */ + return ((uint8_t)str[0] << 5) | 0x1b; + } + printf("JS_VALUE_MAKE_SPECIAL(JS_TAG_STRING_CHAR, %d)", + (uint8_t)str[0]); + } else { + idx = find_atom(&s->atom_list, str); + if (idx < 0) { + fprintf(stderr, "atom '%s' is undefined\n", str); + exit(1); + } + offset = s->atom_list.tab[idx].offset; + if (value_only) + return (offset * JSW) + 1; /* correct modulo ATOM_ALIGN */ + printf("JS_ROM_VALUE(%d)", offset); + } + printf(" /* %s */", str); + return 0; +} + +static void dump_cfuncs(BuildContext *s) +{ + int i; + CFuncDef *e; + + printf("static const JSCFunctionDef js_c_function_table[] = {\n"); + for(i = 0; i < s->cfunc_list.count; i++) { + e = &s->cfunc_list.tab[i]; + printf(" { { .%s = %s },\n", e->cproto_name, e->cfunc_name); + printf(" "); + dump_atom(s, e->name, FALSE); + printf(",\n"); + printf(" JS_CFUNC_%s, %d, %s },\n", + e->cproto_name, e->length, e->magic); + } + printf("};\n\n"); +} + +static void dump_cfinalizers(BuildContext *s) +{ + struct list_head *el; + ClassDefEntry *e; + + printf("static const JSCFinalizer js_c_finalizer_table[JS_CLASS_COUNT - JS_CLASS_USER] = {\n"); + list_for_each(el, &s->class_list) { + e = list_entry(el, ClassDefEntry, link); + if (e->finalizer_name && + strcmp(e->finalizer_name, "NULL") != 0) { + printf(" [%s - JS_CLASS_USER] = %s,\n", e->class_id, e->finalizer_name); + } + } + printf("};\n\n"); +} + +typedef enum { + PROPS_KIND_GLOBAL, + PROPS_KIND_PROTO, + PROPS_KIND_CLASS, + PROPS_KIND_OBJECT, +} JSPropsKindEnum; + +static inline uint32_t hash_prop(BuildContext *s, const char *name) +{ + /* Compute the hash for a symbol, must be consistent with + mquickjs.c implementation. + */ + uint32_t prop = dump_atom(s, name, TRUE); + return (prop / JSW) ^ (prop % JSW); /* XXX: improve */ +} + +static int define_props(BuildContext *s, const JSPropDef *props_def, + JSPropsKindEnum props_kind, const char *class_id_str) +{ + int i, *ident_tab, idx, props_ident, n_props; + int prop_idx; + const JSPropDef *d; + uint32_t *prop_hash; + BOOL is_global_object = (props_kind == PROPS_KIND_GLOBAL); + static const JSPropDef dummy_props[] = { + { JS_DEF_END }, + }; + + if (!props_def) + props_def = dummy_props; + + n_props = 0; + for(d = props_def; d->def_type != JS_DEF_END; d++) { + n_props++; + } + if (props_kind == PROPS_KIND_PROTO || + props_kind == PROPS_KIND_CLASS) + n_props++; + ident_tab = malloc(sizeof(ident_tab[0]) * n_props); + + /* define the various objects */ + for(d = props_def, i = 0; d->def_type != JS_DEF_END; d++, i++) { + ident_tab[i] = define_value(s, d); + } + + props_ident = -1; + prop_hash = NULL; + if (is_global_object) { + props_ident = s->cur_offset; + printf(" /* global object properties (offset=%d) */\n", props_ident); + printf(" JS_VALUE_ARRAY_HEADER(%d),\n", 2 * n_props); + s->cur_offset += 2 * n_props + 1; + } else { + int hash_size_log2; + uint32_t hash_size, hash_mask; + uint32_t *hash_table, h; + + if (n_props <= 1) + hash_size_log2 = 0; + else + hash_size_log2 = (32 - clz32(n_props - 1)) - 1; + hash_size = 1 << hash_size_log2; + if (hash_size > ATOM_ALIGN / JSW) { +#if !defined __APPLE__ + // XXX: Cannot request data alignment larger than 64 bytes on Darwin + fprintf(stderr, "Too many properties, consider increasing ATOM_ALIGN\n"); +#endif + hash_size = ATOM_ALIGN / JSW; + } + hash_mask = hash_size - 1; + + hash_table = malloc(sizeof(hash_table[0]) * hash_size); + prop_hash = malloc(sizeof(prop_hash[0]) * n_props); + /* build the hash table */ + for(i = 0; i < hash_size; i++) + hash_table[i] = 0; + prop_idx = 0; + for(i = 0, d = props_def; i < n_props; i++, d++) { + const char *name; + if (d->def_type != JS_DEF_END) { + name = d->name; + } else { + if (props_kind == PROPS_KIND_PROTO) + name = "constructor"; + else + name = "prototype"; + } + h = hash_prop(s, name) & hash_mask; + prop_hash[prop_idx] = hash_table[h]; + hash_table[h] = 2 + hash_size + 3 * prop_idx; + prop_idx++; + } + + props_ident = s->cur_offset; + printf(" /* properties (offset=%d) */\n", props_ident); + printf(" JS_VALUE_ARRAY_HEADER(%d),\n", 2 + hash_size + n_props * 3); + printf(" %d << 1, /* n_props */\n", n_props); + printf(" %d << 1, /* hash_mask */\n", hash_mask); + for(i = 0; i < hash_size; i++) { + printf(" %d << 1,\n", hash_table[i]); + } + s->cur_offset += hash_size + 3 + 3 * n_props; + free(hash_table); + } + prop_idx = 0; + for(d = props_def, i = 0; i < n_props; d++, i++) { + const char *name, *prop_type; + /* name */ + printf(" "); + if (d->def_type != JS_DEF_END) { + name = d->name; + } else { + if (props_kind == PROPS_KIND_PROTO) + name = "constructor"; + else + name = "prototype"; + } + dump_atom(s, name, FALSE); + printf(",\n"); + + printf(" "); + prop_type = "NORMAL"; + switch(d->def_type) { + case JS_DEF_PROP_DOUBLE: + if (ident_tab[i] >= 0) + goto value_ptr; + /* short int */ + printf("%d << 1,", (int32_t)d->u.f64); + break; + case JS_DEF_CGETSET: + if (is_global_object) { + fprintf(stderr, "getter/setter forbidden in global object\n"); + exit(1); + } + prop_type = "GETSET"; + goto value_ptr; + case JS_DEF_CLASS: + value_ptr: + assert(ident_tab[i] >= 0); + printf("JS_ROM_VALUE(%d),", ident_tab[i]); + break; + case JS_DEF_PROP_UNDEFINED: + printf("JS_UNDEFINED,"); + break; + case JS_DEF_PROP_NULL: + printf("JS_NULL,"); + break; + case JS_DEF_PROP_STRING: + dump_atom(s, d->u.str, FALSE); + printf(","); + break; + case JS_DEF_CFUNC: + idx = add_cfunc(&s->cfunc_list, + d->name, + d->u.func.length, + d->u.func.magic, + d->u.func.cproto_name, + d->u.func.func_name); + printf("JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),", idx); + break; + case JS_DEF_END: + if (props_kind == PROPS_KIND_PROTO) { + /* constructor property */ + printf("(uint32_t)(-%s - 1) << 1,", class_id_str); + } else { + /* prototype property */ + printf("%s << 1,", class_id_str); + } + prop_type = "SPECIAL"; + break; + default: + abort(); + } + printf("\n"); + if (!is_global_object) { + printf(" (%d << 1) | (JS_PROP_%s << 30),\n", + prop_hash[prop_idx], prop_type); + } + prop_idx++; + } + + free(prop_hash); + free(ident_tab); + return props_ident; +} + +static ClassDefEntry *find_class(BuildContext *s, const JSClassDef *d) +{ + struct list_head *el; + ClassDefEntry *e; + + list_for_each(el, &s->class_list) { + e = list_entry(el, ClassDefEntry, link); + if (e->class1 == d) + return e; + } + return NULL; +} + +static void free_class_entries(BuildContext *s) +{ + struct list_head *el, *el1; + ClassDefEntry *e; + list_for_each_safe(el, el1, &s->class_list) { + e = list_entry(el, ClassDefEntry, link); + free(e->class_id); + free(e->finalizer_name); + free(e); + } + init_list_head(&s->class_list); +} + +static int define_class(BuildContext *s, const JSClassDef *d) +{ + int ctor_func_idx = -1, class_props_idx = -1, proto_props_idx = -1; + int ident, parent_class_idx = -1; + ClassDefEntry *e; + + /* check if the class is already defined */ + e = find_class(s, d); + if (e) + return e->class_idx; + + if (d->parent_class) + parent_class_idx = define_class(s, d->parent_class); + + if (d->func_name) { + ctor_func_idx = add_cfunc(&s->cfunc_list, + d->name, + d->length, + d->class_id, + d->cproto_name, + d->func_name); + } + + if (ctor_func_idx >= 0) { + class_props_idx = define_props(s, d->class_props, PROPS_KIND_CLASS, d->class_id); + proto_props_idx = define_props(s, d->proto_props, PROPS_KIND_PROTO, d->class_id); + } else { + if (d->class_props) + class_props_idx = define_props(s, d->class_props, PROPS_KIND_OBJECT, d->class_id); + } + + ident = s->cur_offset; + printf(" /* class (offset=%d) */\n", ident); + printf(" JS_MB_HEADER_DEF(JS_MTAG_OBJECT),\n"); + if (class_props_idx >= 0) + printf(" JS_ROM_VALUE(%d),\n", class_props_idx); + else + printf(" JS_NULL,\n"); + printf(" %d,\n", ctor_func_idx); + if (proto_props_idx >= 0) + printf(" JS_ROM_VALUE(%d),\n", proto_props_idx); + else + printf(" JS_NULL,\n"); + if (parent_class_idx >= 0) { + printf(" JS_ROM_VALUE(%d),\n", parent_class_idx); + } else { + printf(" JS_NULL,\n"); + } + printf("\n"); + + s->cur_offset += 5; + + e = malloc(sizeof(*e)); + memset(e, 0, sizeof(*e)); + e->class_idx = ident; + e->class1 = d; + if (ctor_func_idx >= 0) { + e->class_id = strdup(d->class_id); + e->finalizer_name = strdup(d->finalizer_name); + } + list_add_tail(&e->link, &s->class_list); + return ident; +} + +#define JS_SHORTINT_MIN (-(1 << 30)) +#define JS_SHORTINT_MAX ((1 << 30) - 1) + +static BOOL is_short_int(double d) +{ + return (d >= JS_SHORTINT_MIN && d <= JS_SHORTINT_MAX && (int32_t)d == d); +} + +static int define_value(BuildContext *s, const JSPropDef *d) +{ + int ident; + ident = -1; + switch(d->def_type) { + case JS_DEF_PROP_DOUBLE: + { + uint64_t v; + if (!is_short_int(d->u.f64)) { + ident = s->cur_offset; + printf(" /* float64 (offset=%d) */\n", ident); + printf(" JS_MB_HEADER_DEF(JS_MTAG_FLOAT64),\n"); + v = float64_as_uint64(d->u.f64); + if (JSW == 8) { + printf(" 0x%016zx,\n", (size_t)v); + printf("\n"); + s->cur_offset += 2; + } else { + /* XXX: little endian assumed */ + printf(" 0x%08x,\n", (uint32_t)v); + printf(" 0x%08x,\n", (uint32_t)(v >> 32)); + printf("\n"); + s->cur_offset += 3; + } + } + } + break; + case JS_DEF_CLASS: + ident = define_class(s, d->u.class1); + break; + case JS_DEF_CGETSET: + { + int get_idx = -1, set_idx = -1; + char buf[256]; + if (strcmp(d->u.getset.get_func_name, "NULL") != 0) { + snprintf(buf, sizeof(buf), "get %s", d->name); + get_idx = add_cfunc(&s->cfunc_list, + buf, + 0, /* length */ + d->u.getset.magic, + d->u.getset.cproto_name, + d->u.getset.get_func_name); + } + if (strcmp(d->u.getset.set_func_name, "NULL") != 0) { + snprintf(buf, sizeof(buf), "set %s", d->name); + set_idx = add_cfunc(&s->cfunc_list, + buf, + 1, /* length */ + d->u.getset.magic, + d->u.getset.cproto_name, + d->u.getset.set_func_name); + } + ident = s->cur_offset; + printf(" /* getset (offset=%d) */\n", ident); + printf(" JS_VALUE_ARRAY_HEADER(2),\n"); + if (get_idx >= 0) + printf(" JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),\n", get_idx); + else + printf(" JS_UNDEFINED,\n"); + if (set_idx >= 0) + printf(" JS_VALUE_MAKE_SPECIAL(JS_TAG_SHORT_FUNC, %d),\n", set_idx); + else + printf(" JS_UNDEFINED,\n"); + printf("\n"); + s->cur_offset += 3; + } + break; + default: + break; + } + return ident; +} + +static void define_atoms_props(BuildContext *s, const JSPropDef *props_def, JSPropsKindEnum props_kind); + +static void define_atoms_class(BuildContext *s, const JSClassDef *d) +{ + ClassDefEntry *e; + /* check if the class is already defined */ + e = find_class(s, d); + if (e) + return; + if (d->parent_class) + define_atoms_class(s, d->parent_class); + if (d->func_name) + add_atom(&s->atom_list, d->name); + if (d->class_props) + define_atoms_props(s, d->class_props, d->func_name ? PROPS_KIND_CLASS : PROPS_KIND_OBJECT); + if (d->proto_props) + define_atoms_props(s, d->proto_props, PROPS_KIND_PROTO); +} + +static void define_atoms_props(BuildContext *s, const JSPropDef *props_def, JSPropsKindEnum props_kind) +{ + const JSPropDef *d; + for(d = props_def; d->def_type != JS_DEF_END; d++) { + add_atom(&s->atom_list, d->name); + switch(d->def_type) { + case JS_DEF_PROP_STRING: + add_atom(&s->atom_list, d->u.str); + break; + case JS_DEF_CLASS: + define_atoms_class(s, d->u.class1); + break; + case JS_DEF_CGETSET: + { + char buf[256]; + if (strcmp(d->u.getset.get_func_name, "NULL") != 0) { + snprintf(buf, sizeof(buf), "get %s", d->name); + add_atom(&s->atom_list, buf); + } + if (strcmp(d->u.getset.set_func_name, "NULL") != 0) { + snprintf(buf, sizeof(buf), "set %s", d->name); + add_atom(&s->atom_list, buf); + } + } + break; + default: + break; + } + } +} + +static int usage(const char *name) +{ + fprintf(stderr, "usage: %s {-m32 | -m64} [-a]\n", name); + fprintf(stderr, + " create a ROM file for the mquickjs standard library\n" + "--help list options\n" + "-m32 force generation for a 32 bit target\n" + "-m64 force generation for a 64 bit target\n" + "-a generate the mquickjs_atom.h header\n" + ); + return 1; +} + +int build_atoms(const char *stdlib_name, const JSPropDef *global_obj, + const JSPropDef *c_function_decl, int argc, char **argv) +{ + int i; + unsigned jsw; + BuildContext ss, *s = &ss; + BOOL build_atom_defines = FALSE; + +#if INTPTR_MAX >= INT64_MAX + jsw = 8; +#else + jsw = 4; +#endif + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-m64")) { + jsw = 8; + } else if (!strcmp(argv[i], "-m32")) { + jsw = 4; + } else if (!strcmp(argv[i], "-a")) { + build_atom_defines = TRUE; + } else if (!strcmp(argv[i], "--help")) { + return usage(argv[0]); + } else { + fprintf(stderr, "invalid argument '%s'\n", argv[i]); + return usage(argv[0]); + } + } + + JSW = jsw; + + if (build_atom_defines) { + dump_atom_defines(); + return 0; + } + + memset(s, 0, sizeof(*s)); + init_list_head(&s->class_list); + + /* add the predefined atoms (they have a corresponding define) */ + for(i = 0; i < countof(atoms); i++) { + add_atom(&s->atom_list, atoms[i]); + } + + /* add the predefined functions */ + if (c_function_decl) { + const JSPropDef *d; + for(d = c_function_decl; d->def_type != JS_DEF_END; d++) { + if (d->def_type != JS_DEF_CFUNC) { + fprintf(stderr, "only C functions are allowed in c_function_decl[]\n"); + exit(1); + } + add_atom(&s->atom_list, d->name); + add_cfunc(&s->cfunc_list, + d->name, + d->u.func.length, + d->u.func.magic, + d->u.func.cproto_name, + d->u.func.func_name); + } + } + + /* first pass to define the atoms */ + define_atoms_props(s, global_obj, PROPS_KIND_GLOBAL); + free_class_entries(s); + + printf("/* this file is automatically generated - do not edit */\n\n"); + printf("#include \"mquickjs_priv.h\"\n\n"); + + printf("static const uint%u_t __attribute((aligned(%d))) js_stdlib_table[] = {\n", + JSW * 8, ATOM_ALIGN); + + dump_atoms(s); + + s->global_object_offset = define_props(s, global_obj, PROPS_KIND_GLOBAL, NULL); + + printf("};\n\n"); + + dump_cfuncs(s); + + printf("#ifndef JS_CLASS_COUNT\n" + "#define JS_CLASS_COUNT JS_CLASS_USER /* total number of classes */\n" + "#endif\n\n"); + + dump_cfinalizers(s); + + free_class_entries(s); + + printf("const JSSTDLibraryDef %s = {\n", stdlib_name); + printf(" js_stdlib_table,\n"); + printf(" js_c_function_table,\n"); + printf(" js_c_finalizer_table,\n"); + printf(" %d,\n", s->cur_offset); + printf(" %d,\n", ATOM_ALIGN); + printf(" %d,\n", s->sorted_atom_table_offset); + printf(" %d,\n", s->global_object_offset); + printf(" JS_CLASS_COUNT,\n"); + printf("};\n\n"); + + return 0; +} diff --git a/lib/mquickjs/scripts/mquickjs_build.h b/lib/mquickjs/scripts/mquickjs_build.h new file mode 100644 index 00000000..51be6b44 --- /dev/null +++ b/lib/mquickjs/scripts/mquickjs_build.h @@ -0,0 +1,97 @@ +/* + * Micro QuickJS build utility + * + * Copyright (c) 2017-2025 Fabrice Bellard + * Copyright (c) 2017-2025 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * 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 MQUICKJS_BUILD_H +#define MQUICKJS_BUILD_H + +#include +#include + +enum { + JS_DEF_END, + JS_DEF_CFUNC, + JS_DEF_CGETSET, + JS_DEF_PROP_DOUBLE, + JS_DEF_PROP_UNDEFINED, + JS_DEF_PROP_STRING, + JS_DEF_PROP_NULL, + JS_DEF_CLASS, +}; + +typedef struct JSClassDef JSClassDef; + +typedef struct JSPropDef { + int def_type; + const char *name; + union { + struct { + uint8_t length; + const char *magic; + const char *cproto_name; + const char *func_name; + } func; + struct { + const char *magic; + const char *cproto_name; + const char *get_func_name; + const char *set_func_name; + } getset; + double f64; + const JSClassDef *class1; + const char *str; + } u; +} JSPropDef; + +typedef struct JSClassDef { + const char *name; + int length; + const char *cproto_name; + const char *func_name; + const char *class_id; + const JSPropDef *class_props; /* NULL if none */ + const JSPropDef *proto_props; /* NULL if none */ + const JSClassDef *parent_class; /* NULL if none */ + const char *finalizer_name; /* "NULL" if none */ +} JSClassDef; + +#define JS_PROP_END { JS_DEF_END } +#define JS_CFUNC_DEF(name, length, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", "generic", #func_name } } } +#define JS_CFUNC_MAGIC_DEF(name, length, func_name, magic) { JS_DEF_CFUNC, name, { .func = { length, #magic, "generic_magic", #func_name } } } +#define JS_CFUNC_SPECIAL_DEF(name, length, proto, func_name) { JS_DEF_CFUNC, name, { .func = { length, "0", #proto, #func_name } } } +#define JS_CGETSET_DEF(name, get_name, set_name) { JS_DEF_CGETSET, name, { .getset = { "0", "generic", #get_name, #set_name } } } +#define JS_CGETSET_MAGIC_DEF(name, get_name, set_name, magic) { JS_DEF_CGETSET, name, { .getset = { #magic, "generic_magic", #get_name, #set_name } } } +#define JS_PROP_CLASS_DEF(name, cl) { JS_DEF_CLASS, name, { .class1 = cl } } +#define JS_PROP_DOUBLE_DEF(name, val, flags) { JS_DEF_PROP_DOUBLE, name, { .f64 = val } } +#define JS_PROP_UNDEFINED_DEF(name, flags) { JS_DEF_PROP_UNDEFINED, name } +#define JS_PROP_NULL_DEF(name, flags) { JS_DEF_PROP_NULL, name } +#define JS_PROP_STRING_DEF(name, cstr, flags) { JS_DEF_PROP_STRING, name, { .str = cstr } } + +#define JS_CLASS_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name } +#define JS_CLASS_MAGIC_DEF(name, length, func_name, class_id, class_props, proto_props, parent_class, finalizer_name) { name, length, "constructor_magic", #func_name, #class_id, class_props, proto_props, parent_class, #finalizer_name } +#define JS_OBJECT_DEF(name, obj_props) { name, 0, NULL, NULL, NULL, obj_props, NULL, NULL, NULL } + +int build_atoms(const char *stdlib_name, const JSPropDef *global_obj, + const JSPropDef *c_function_decl, int argc, char **argv); + +#endif /* MQUICKJS_BUILD_H */ diff --git a/lib/mquickjs/softfp_template.h b/lib/mquickjs/softfp_template.h new file mode 100644 index 00000000..34034fd8 --- /dev/null +++ b/lib/mquickjs/softfp_template.h @@ -0,0 +1,970 @@ +/* + * SoftFP Library + * + * Copyright (c) 2016 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. + */ +#if F_SIZE == 32 +#define F_UINT uint32_t +#define F_ULONG uint64_t +#define MANT_SIZE 23 +#define EXP_SIZE 8 +#elif F_SIZE == 64 +#define F_UHALF uint32_t +#define F_UINT uint64_t +#ifdef HAVE_INT128 +#define F_ULONG uint128_t +#endif +#define MANT_SIZE 52 +#define EXP_SIZE 11 +#elif F_SIZE == 128 +#define F_UHALF uint64_t +#define F_UINT uint128_t +#define MANT_SIZE 112 +#define EXP_SIZE 15 +#else +#error unsupported F_SIZE +#endif + +#define EXP_MASK ((1 << EXP_SIZE) - 1) +#define MANT_MASK (((F_UINT)1 << MANT_SIZE) - 1) +#define SIGN_MASK ((F_UINT)1 << (F_SIZE - 1)) +#define IMANT_SIZE (F_SIZE - 2) /* internal mantissa size */ +#define RND_SIZE (IMANT_SIZE - MANT_SIZE) +#define QNAN_MASK ((F_UINT)1 << (MANT_SIZE - 1)) +#define EXP_BIAS ((1 << (EXP_SIZE - 1)) - 1) + +/* quiet NaN */ +#define F_QNAN glue(F_QNAN, F_SIZE) +#define clz glue(clz, F_SIZE) +#define pack_sf glue(pack_sf, F_SIZE) +#define unpack_sf glue(unpack_sf, F_SIZE) +#define rshift_rnd glue(rshift_rnd, F_SIZE) +#define round_pack_sf glue(roundpack_sf, F_SIZE) +#define normalize_sf glue(normalize_sf, F_SIZE) +#define normalize2_sf glue(normalize2_sf, F_SIZE) +#define issignan_sf glue(issignan_sf, F_SIZE) +#define isnan_sf glue(isnan_sf, F_SIZE) +#define add_sf glue(add_sf, F_SIZE) +#define mul_sf glue(mul_sf, F_SIZE) +#define fma_sf glue(fma_sf, F_SIZE) +#define div_sf glue(div_sf, F_SIZE) +#define sqrt_sf glue(sqrt_sf, F_SIZE) +#define normalize_subnormal_sf glue(normalize_subnormal_sf, F_SIZE) +#define divrem_u glue(divrem_u, F_SIZE) +#define sqrtrem_u glue(sqrtrem_u, F_SIZE) +#define mul_u glue(mul_u, F_SIZE) +#define cvt_sf32_sf glue(cvt_sf32_sf, F_SIZE) +#define cvt_sf64_sf glue(cvt_sf64_sf, F_SIZE) + +static const F_UINT F_QNAN = (((F_UINT)EXP_MASK << MANT_SIZE) | ((F_UINT)1 << (MANT_SIZE - 1))); + +static inline F_UINT pack_sf(uint32_t a_sign, uint32_t a_exp, F_UINT a_mant) +{ + return ((F_UINT)a_sign << (F_SIZE - 1)) | + ((F_UINT)a_exp << MANT_SIZE) | + (a_mant & MANT_MASK); +} + +static inline F_UINT unpack_sf(uint32_t *pa_sign, int32_t *pa_exp, + F_UINT a) +{ + *pa_sign = a >> (F_SIZE - 1); + *pa_exp = (a >> MANT_SIZE) & EXP_MASK; + return a & MANT_MASK; +} + +static F_UINT rshift_rnd(F_UINT a, int d) +{ + F_UINT mask; + if (d != 0) { + if (d >= F_SIZE) { + a = (a != 0); + } else { + mask = ((F_UINT)1 << d) - 1; + a = (a >> d) | ((a & mask) != 0); + } + } + return a; +} + +#if F_USE_FFLAGS +#define FFLAGS_PARAM , uint32_t *pfflags +#define FFLAGS_ARG , pfflags +#else +#define FFLAGS_PARAM +#define FFLAGS_ARG +#endif + +/* a_mant is considered to have its MSB at F_SIZE - 2 bits */ +static F_UINT round_pack_sf(uint32_t a_sign, int a_exp, F_UINT a_mant, + RoundingModeEnum rm FFLAGS_PARAM) +{ + int diff; + uint32_t addend, rnd_bits; + + switch(rm) { + case RM_RNE: + case RM_RMM: + addend = (1 << (RND_SIZE - 1)); + break; + case RM_RTZ: + addend = 0; + break; + default: + case RM_RDN: + case RM_RUP: + // printf("s=%d rm=%d m=%x\n", a_sign, rm, a_mant); + if (a_sign ^ (rm & 1)) + addend = (1 << RND_SIZE) - 1; + else + addend = 0; + break; + } + + /* potentially subnormal */ + if (a_exp <= 0) { + BOOL is_subnormal; + /* Note: we set the underflow flag if the rounded result + is subnormal and inexact */ + is_subnormal = (a_exp < 0 || + (a_mant + addend) < ((F_UINT)1 << (F_SIZE - 1))); + diff = 1 - a_exp; + a_mant = rshift_rnd(a_mant, diff); + rnd_bits = a_mant & ((1 << RND_SIZE ) - 1); + if (is_subnormal && rnd_bits != 0) { +#if F_USE_FFLAGS + *pfflags |= FFLAG_UNDERFLOW; +#endif + } + a_exp = 1; + } else { + rnd_bits = a_mant & ((1 << RND_SIZE ) - 1); + } +#if F_USE_FFLAGS + if (rnd_bits != 0) + *pfflags |= FFLAG_INEXACT; +#endif + a_mant = (a_mant + addend) >> RND_SIZE; + /* half way: select even result */ + if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1))) + a_mant &= ~1; + /* Note the rounding adds at least 1, so this is the maximum + value */ + a_exp += a_mant >> (MANT_SIZE + 1); + if (a_mant <= MANT_MASK) { + /* denormalized or zero */ + a_exp = 0; + } else if (a_exp >= EXP_MASK) { + /* overflow */ + if (addend == 0) { + a_exp = EXP_MASK - 1; + a_mant = MANT_MASK; + } else { + /* infinity */ + a_exp = EXP_MASK; + a_mant = 0; + } +#if F_USE_FFLAGS + *pfflags |= FFLAG_OVERFLOW | FFLAG_INEXACT; +#endif + } + return pack_sf(a_sign, a_exp, a_mant); +} + +/* a_mant is considered to have at most F_SIZE - 1 bits */ +static F_UINT normalize_sf(uint32_t a_sign, int a_exp, F_UINT a_mant, + RoundingModeEnum rm FFLAGS_PARAM) +{ + int shift; + shift = clz(a_mant) - (F_SIZE - 1 - IMANT_SIZE); + assert(shift >= 0); + a_exp -= shift; + a_mant <<= shift; + return round_pack_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG); +} + +static inline F_UINT normalize_subnormal_sf(int32_t *pa_exp, F_UINT a_mant) +{ + int shift; + shift = MANT_SIZE - ((F_SIZE - 1 - clz(a_mant))); + *pa_exp = 1 - shift; + return a_mant << shift; +} + +#if F_USE_FFLAGS +F_STATIC BOOL issignan_sf(F_UINT a) +{ + uint32_t a_exp1; + F_UINT a_mant; + a_exp1 = (a >> (MANT_SIZE - 1)) & ((1 << (EXP_SIZE + 1)) - 1); + a_mant = a & MANT_MASK; + return (a_exp1 == (2 * EXP_MASK) && a_mant != 0); +} +#endif + +#ifndef F_NORMALIZE_ONLY + +F_STATIC BOOL isnan_sf(F_UINT a) +{ + uint32_t a_exp; + F_UINT a_mant; + a_exp = (a >> MANT_SIZE) & EXP_MASK; + a_mant = a & MANT_MASK; + return (a_exp == EXP_MASK && a_mant != 0); +} + + +F_STATIC F_UINT add_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM) +{ + uint32_t a_sign, b_sign, a_exp, b_exp; + F_UINT tmp, a_mant, b_mant; + + /* swap so that abs(a) >= abs(b) */ + if ((a & ~SIGN_MASK) < (b & ~SIGN_MASK)) { + tmp = a; + a = b; + b = tmp; + } + a_sign = a >> (F_SIZE - 1); + b_sign = b >> (F_SIZE - 1); + a_exp = (a >> MANT_SIZE) & EXP_MASK; + b_exp = (b >> MANT_SIZE) & EXP_MASK; + a_mant = (a & MANT_MASK) << 3; + b_mant = (b & MANT_MASK) << 3; + if (unlikely(a_exp == EXP_MASK)) { + if (a_mant != 0) { + /* NaN result */ +#if F_USE_FFLAGS + if (!(a_mant & (QNAN_MASK << 3)) || issignan_sf(b)) + *pfflags |= FFLAG_INVALID_OP; +#endif + return F_QNAN; + } else if (b_exp == EXP_MASK && a_sign != b_sign) { +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return F_QNAN; + } else { + /* infinity */ + return a; + } + } + if (a_exp == 0) { + a_exp = 1; + } else { + a_mant |= (F_UINT)1 << (MANT_SIZE + 3); + } + if (b_exp == 0) { + b_exp = 1; + } else { + b_mant |= (F_UINT)1 << (MANT_SIZE + 3); + } + b_mant = rshift_rnd(b_mant, a_exp - b_exp); + if (a_sign == b_sign) { + /* same signs : add the absolute values */ + a_mant += b_mant; + } else { + /* different signs : subtract the absolute values */ + a_mant -= b_mant; + if (a_mant == 0) { + /* zero result : the sign needs a specific handling */ + a_sign = (rm == RM_RDN); + } + } + a_exp += (RND_SIZE - 3); + return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG); +} + +F_STATIC F_UINT glue(sub_sf, F_SIZE)(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM) +{ + return add_sf(a, b ^ SIGN_MASK, rm FFLAGS_ARG); +} + +#ifdef F_ULONG + +static F_UINT mul_u(F_UINT *plow, F_UINT a, F_UINT b) +{ + F_ULONG r; + r = (F_ULONG)a * (F_ULONG)b; + *plow = r; + return r >> F_SIZE; +} + +#else + +#define FH_SIZE (F_SIZE / 2) + +static F_UINT mul_u(F_UINT *plow, F_UINT a, F_UINT b) +{ + F_UHALF a0, a1, b0, b1, r0, r1, r2, r3; + F_UINT r00, r01, r10, r11, c; + a0 = a; + a1 = a >> FH_SIZE; + b0 = b; + b1 = b >> FH_SIZE; + + r00 = (F_UINT)a0 * (F_UINT)b0; + r01 = (F_UINT)a0 * (F_UINT)b1; + r10 = (F_UINT)a1 * (F_UINT)b0; + r11 = (F_UINT)a1 * (F_UINT)b1; + + r0 = r00; + c = (r00 >> FH_SIZE) + (F_UHALF)r01 + (F_UHALF)r10; + r1 = c; + c = (c >> FH_SIZE) + (r01 >> FH_SIZE) + (r10 >> FH_SIZE) + (F_UHALF)r11; + r2 = c; + r3 = (c >> FH_SIZE) + (r11 >> FH_SIZE); + + *plow = ((F_UINT)r1 << FH_SIZE) | r0; + return ((F_UINT)r3 << FH_SIZE) | r2; +} + +#undef FH_SIZE + +#endif + +F_STATIC F_UINT mul_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM) +{ + uint32_t a_sign, b_sign, r_sign; + int32_t a_exp, b_exp, r_exp; + F_UINT a_mant, b_mant, r_mant, r_mant_low; + + a_sign = a >> (F_SIZE - 1); + b_sign = b >> (F_SIZE - 1); + r_sign = a_sign ^ b_sign; + a_exp = (a >> MANT_SIZE) & EXP_MASK; + b_exp = (b >> MANT_SIZE) & EXP_MASK; + a_mant = a & MANT_MASK; + b_mant = b & MANT_MASK; + if (a_exp == EXP_MASK || b_exp == EXP_MASK) { + if (isnan_sf(a) || isnan_sf(b)) { +#if F_USE_FFLAGS + if (issignan_sf(a) || issignan_sf(b)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return F_QNAN; + } else { + /* infinity */ + if ((a_exp == EXP_MASK && (b_exp == 0 && b_mant == 0)) || + (b_exp == EXP_MASK && (a_exp == 0 && a_mant == 0))) { +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return F_QNAN; + } else { + return pack_sf(r_sign, EXP_MASK, 0); + } + } + } + if (a_exp == 0) { + if (a_mant == 0) + return pack_sf(r_sign, 0, 0); /* zero */ + a_mant = normalize_subnormal_sf(&a_exp, a_mant); + } else { + a_mant |= (F_UINT)1 << MANT_SIZE; + } + if (b_exp == 0) { + if (b_mant == 0) + return pack_sf(r_sign, 0, 0); /* zero */ + b_mant = normalize_subnormal_sf(&b_exp, b_mant); + } else { + b_mant |= (F_UINT)1 << MANT_SIZE; + } + r_exp = a_exp + b_exp - (1 << (EXP_SIZE - 1)) + 2; + + r_mant = mul_u(&r_mant_low,a_mant << RND_SIZE, b_mant << (RND_SIZE + 1)); + r_mant |= (r_mant_low != 0); + return normalize_sf(r_sign, r_exp, r_mant, rm FFLAGS_ARG); +} + +#ifdef F_ULONG + +static F_UINT divrem_u(F_UINT *pr, F_UINT ah, F_UINT al, F_UINT b) +{ + F_ULONG a; + a = ((F_ULONG)ah << F_SIZE) | al; + *pr = a % b; + return a / b; +} + +#else + +/* XXX: optimize */ +static F_UINT divrem_u(F_UINT *pr, F_UINT a1, F_UINT a0, F_UINT b) +{ + int i, qb, ab; + + assert(a1 < b); + for(i = 0; i < F_SIZE; i++) { + ab = a1 >> (F_SIZE - 1); + a1 = (a1 << 1) | (a0 >> (F_SIZE - 1)); + if (ab || a1 >= b) { + a1 -= b; + qb = 1; + } else { + qb = 0; + } + a0 = (a0 << 1) | qb; + } + *pr = a1; + return a0; +} + +#endif + +F_STATIC F_UINT div_sf(F_UINT a, F_UINT b, RoundingModeEnum rm FFLAGS_PARAM) +{ + uint32_t a_sign, b_sign, r_sign; + int32_t a_exp, b_exp, r_exp; + F_UINT a_mant, b_mant, r_mant, r; + + a_sign = a >> (F_SIZE - 1); + b_sign = b >> (F_SIZE - 1); + r_sign = a_sign ^ b_sign; + a_exp = (a >> MANT_SIZE) & EXP_MASK; + b_exp = (b >> MANT_SIZE) & EXP_MASK; + a_mant = a & MANT_MASK; + b_mant = b & MANT_MASK; + if (a_exp == EXP_MASK) { + if (a_mant != 0 || isnan_sf(b)) { +#if F_USE_FFLAGS + if (issignan_sf(a) || issignan_sf(b)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return F_QNAN; + } else if (b_exp == EXP_MASK) { +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return F_QNAN; + } else { + return pack_sf(r_sign, EXP_MASK, 0); + } + } else if (b_exp == EXP_MASK) { + if (b_mant != 0) { +#if F_USE_FFLAGS + if (issignan_sf(a) || issignan_sf(b)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return F_QNAN; + } else { + return pack_sf(r_sign, 0, 0); + } + } + + if (b_exp == 0) { + if (b_mant == 0) { + /* zero */ + if (a_exp == 0 && a_mant == 0) { +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return F_QNAN; + } else { +#if F_USE_FFLAGS + *pfflags |= FFLAG_DIVIDE_ZERO; +#endif + return pack_sf(r_sign, EXP_MASK, 0); + } + } + b_mant = normalize_subnormal_sf(&b_exp, b_mant); + } else { + b_mant |= (F_UINT)1 << MANT_SIZE; + } + if (a_exp == 0) { + if (a_mant == 0) + return pack_sf(r_sign, 0, 0); /* zero */ + a_mant = normalize_subnormal_sf(&a_exp, a_mant); + } else { + a_mant |= (F_UINT)1 << MANT_SIZE; + } + r_exp = a_exp - b_exp + (1 << (EXP_SIZE - 1)) - 1; + r_mant = divrem_u(&r, a_mant, 0, b_mant << 2); + if (r != 0) + r_mant |= 1; + return normalize_sf(r_sign, r_exp, r_mant, rm FFLAGS_ARG); +} + +#ifdef F_ULONG + +/* compute sqrt(a) with a = ah*2^F_SIZE+al and a < 2^(F_SIZE - 2) + return true if not exact square. */ +static int sqrtrem_u(F_UINT *pr, F_UINT ah, F_UINT al) +{ + F_ULONG a, u, s; + int l, inexact; + + /* 2^l >= a */ + if (ah != 0) { + l = 2 * F_SIZE - clz(ah - 1); + } else { + if (al == 0) { + *pr = 0; + return 0; + } + l = F_SIZE - clz(al - 1); + } + a = ((F_ULONG)ah << F_SIZE) | al; + u = (F_ULONG)1 << ((l + 1) / 2); + for(;;) { + s = u; + u = ((a / s) + s) / 2; + if (u >= s) + break; + } + inexact = (a - s * s) != 0; + *pr = s; + return inexact; +} + +#else + +static int sqrtrem_u(F_UINT *pr, F_UINT a1, F_UINT a0) +{ + int l, inexact; + F_UINT u, s, r, q, sq0, sq1; + + /* 2^l >= a */ + if (a1 != 0) { + l = 2 * F_SIZE - clz(a1 - 1); + } else { + if (a0 == 0) { + *pr = 0; + return 0; + } + l = F_SIZE - clz(a0 - 1); + } + u = (F_UINT)1 << ((l + 1) / 2); + for(;;) { + s = u; + q = divrem_u(&r, a1, a0, s); + u = (q + s) / 2; + if (u >= s) + break; + } + sq1 = mul_u(&sq0, s, s); + inexact = (sq0 != a0 || sq1 != a1); + *pr = s; + return inexact; +} + +#endif + +F_STATIC F_UINT sqrt_sf(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM) +{ + uint32_t a_sign; + int32_t a_exp; + F_UINT a_mant; + + a_sign = a >> (F_SIZE - 1); + a_exp = (a >> MANT_SIZE) & EXP_MASK; + a_mant = a & MANT_MASK; + if (a_exp == EXP_MASK) { + if (a_mant != 0) { +#if F_USE_FFLAGS + if (issignan_sf(a)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return F_QNAN; + } else if (a_sign) { + goto neg_error; + } else { + return a; /* +infinity */ + } + } + if (a_sign) { + if (a_exp == 0 && a_mant == 0) + return a; /* -zero */ + neg_error: +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return F_QNAN; + } + if (a_exp == 0) { + if (a_mant == 0) + return pack_sf(0, 0, 0); /* zero */ + a_mant = normalize_subnormal_sf(&a_exp, a_mant); + } else { + a_mant |= (F_UINT)1 << MANT_SIZE; + } + a_exp -= EXP_MASK / 2; + /* simpler to handle an even exponent */ + if (a_exp & 1) { + a_exp--; + a_mant <<= 1; + } + a_exp = (a_exp >> 1) + EXP_MASK / 2; + a_mant <<= (F_SIZE - 4 - MANT_SIZE); + if (sqrtrem_u(&a_mant, a_mant, 0)) + a_mant |= 1; + return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG); +} + +/* comparisons */ + +F_STATIC int glue(eq_quiet_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM) +{ + if (isnan_sf(a) || isnan_sf(b)) { +#if F_USE_FFLAGS + if (issignan_sf(a) || issignan_sf(b)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return 0; + } + + if ((F_UINT)((a | b) << 1) == 0) + return 1; /* zero case */ + return (a == b); +} + +F_STATIC int glue(le_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM) +{ + uint32_t a_sign, b_sign; + + if (isnan_sf(a) || isnan_sf(b)) { +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return 0; + } + + a_sign = a >> (F_SIZE - 1); + b_sign = b >> (F_SIZE - 1); + if (a_sign != b_sign) { + return (a_sign || ((F_UINT)((a | b) << 1) == 0)); + } else { + if (a_sign) { + return (a >= b); + } else { + return (a <= b); + } + } +} + +F_STATIC int glue(lt_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM) +{ + uint32_t a_sign, b_sign; + + if (isnan_sf(a) || isnan_sf(b)) { +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return 0; + } + + a_sign = a >> (F_SIZE - 1); + b_sign = b >> (F_SIZE - 1); + if (a_sign != b_sign) { + return (a_sign && ((F_UINT)((a | b) << 1) != 0)); + } else { + if (a_sign) { + return (a > b); + } else { + return (a < b); + } + } +} + +/* return -1 (a < b), 0 (a = b), 1 (a > b) or 2 (a = nan or b = + nan) */ +F_STATIC int glue(cmp_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM) +{ + uint32_t a_sign, b_sign; + + if (isnan_sf(a) || isnan_sf(b)) { +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return 2; + } + + a_sign = a >> (F_SIZE - 1); + b_sign = b >> (F_SIZE - 1); + if (a_sign != b_sign) { + if ((F_UINT)((a | b) << 1) != 0) + return 1 - 2 * a_sign; + else + return 0; /* -0 = +0 */ + } else { + if (a < b) + return 2 * a_sign - 1; + else if (a > b) + return 1 - 2 * a_sign; + else + return 0; + } +} + +/* conversions between floats */ + +#if F_SIZE >= 64 + +F_STATIC F_UINT cvt_sf32_sf(uint32_t a FFLAGS_PARAM) +{ + uint32_t a_sign; + int32_t a_exp; + F_UINT a_mant; + + a_mant = unpack_sf32(&a_sign, &a_exp, a); + if (a_exp == 0xff) { + if (a_mant != 0) { + /* NaN */ +#if F_USE_FFLAGS + if (issignan_sf32(a)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return F_QNAN; + } else { + /* infinity */ + return pack_sf(a_sign, EXP_MASK, 0); + } + } + if (a_exp == 0) { + if (a_mant == 0) + return pack_sf(a_sign, 0, 0); /* zero */ + a_mant = normalize_subnormal_sf32(&a_exp, a_mant); + } + /* convert the exponent value */ + a_exp = a_exp - 0x7f + (EXP_MASK / 2); + /* shift the mantissa */ + a_mant <<= (MANT_SIZE - 23); + /* We assume the target float is large enough to that no + normalization is necessary */ + return pack_sf(a_sign, a_exp, a_mant); +} + +F_STATIC uint32_t glue(glue(cvt_sf, F_SIZE), _sf32)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM) +{ + uint32_t a_sign; + int32_t a_exp; + F_UINT a_mant; + + a_mant = unpack_sf(&a_sign, &a_exp, a); + if (a_exp == EXP_MASK) { + if (a_mant != 0) { + /* NaN */ +#if F_USE_FFLAGS + if (issignan_sf(a)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return F_QNAN32; + } else { + /* infinity */ + return pack_sf32(a_sign, 0xff, 0); + } + } + if (a_exp == 0) { + if (a_mant == 0) + return pack_sf32(a_sign, 0, 0); /* zero */ + normalize_subnormal_sf(&a_exp, a_mant); + } else { + a_mant |= (F_UINT)1 << MANT_SIZE; + } + /* convert the exponent value */ + a_exp = a_exp - (EXP_MASK / 2) + 0x7f; + /* shift the mantissa */ + a_mant = rshift_rnd(a_mant, MANT_SIZE - (32 - 2)); + return normalize_sf32(a_sign, a_exp, a_mant, rm FFLAGS_ARG); +} + +#endif + +#if F_SIZE >= 128 + +F_STATIC F_UINT cvt_sf64_sf(uint64_t a FFLAGS_PARAM) +{ + uint32_t a_sign; + int32_t a_exp; + F_UINT a_mant; + + a_mant = unpack_sf64(&a_sign, &a_exp, a); + + if (a_exp == 0x7ff) { + if (a_mant != 0) { + /* NaN */ +#if F_USE_FFLAGS + if (issignan_sf64(a)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return F_QNAN; + } else { + /* infinity */ + return pack_sf(a_sign, EXP_MASK, 0); + } + } + if (a_exp == 0) { + if (a_mant == 0) + return pack_sf(a_sign, 0, 0); /* zero */ + a_mant = normalize_subnormal_sf64(&a_exp, a_mant); + } + /* convert the exponent value */ + a_exp = a_exp - 0x3ff + (EXP_MASK / 2); + /* shift the mantissa */ + a_mant <<= (MANT_SIZE - 52); + return pack_sf(a_sign, a_exp, a_mant); +} + +F_STATIC uint64_t glue(glue(cvt_sf, F_SIZE), _sf64)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM) +{ + uint32_t a_sign; + int32_t a_exp; + F_UINT a_mant; + + a_mant = unpack_sf(&a_sign, &a_exp, a); + if (a_exp == EXP_MASK) { + if (a_mant != 0) { + /* NaN */ +#if F_USE_FFLAGS + if (issignan_sf(a)) { + *pfflags |= FFLAG_INVALID_OP; + } +#endif + return F_QNAN64; + } else { + /* infinity */ + return pack_sf64(a_sign, 0x7ff, 0); + } + } + if (a_exp == 0) { + if (a_mant == 0) + return pack_sf64(a_sign, 0, 0); /* zero */ + normalize_subnormal_sf(&a_exp, a_mant); + } else { + a_mant |= (F_UINT)1 << MANT_SIZE; + } + /* convert the exponent value */ + a_exp = a_exp - (EXP_MASK / 2) + 0x3ff; + /* shift the mantissa */ + a_mant = rshift_rnd(a_mant, MANT_SIZE - (64 - 2)); + return normalize_sf64(a_sign, a_exp, a_mant, rm, pfflags); +} + +#endif + +#undef clz + +#define ICVT_SIZE 32 +#include "softfp_template_icvt.h" + +#define ICVT_SIZE 64 +#include "softfp_template_icvt.h" + +/* additional libm functions */ + +/* return a mod b (exact) */ +F_STATIC F_UINT glue(fmod_sf, F_SIZE)(F_UINT a, F_UINT b FFLAGS_PARAM) +{ + uint32_t a_sign; + int32_t a_exp, b_exp, n; + F_UINT a_mant, b_mant, a_abs, b_abs; + + a_abs = a & ~SIGN_MASK; + b_abs = b & ~SIGN_MASK; + if (b_abs == 0 || + a_abs >= ((F_UINT)EXP_MASK << MANT_SIZE) || + b_abs > ((F_UINT)EXP_MASK << MANT_SIZE)) { + /* XXX: flags */ + return F_QNAN; + } + if (a_abs < b_abs) { + return a; /* |a| < |b| return a */ + } else if (a_abs == b_abs) { + return a & SIGN_MASK; /* |a| = |b| return copy_sign(0, a) */ + } + + a_sign = a >> (F_SIZE - 1); + a_exp = (a >> MANT_SIZE) & EXP_MASK; + b_exp = (b >> MANT_SIZE) & EXP_MASK; + a_mant = (a & MANT_MASK); + b_mant = (b & MANT_MASK); + + if (a_exp == 0) { + a_mant = normalize_subnormal_sf(&a_exp, a_mant); + } else { + a_mant |= (F_UINT)1 << MANT_SIZE; + } + if (b_exp == 0) { + b_mant = normalize_subnormal_sf(&b_exp, b_mant); + } else { + b_mant |= (F_UINT)1 << MANT_SIZE; + } + n = a_exp - b_exp; + if (a_mant >= b_mant) + a_mant -= b_mant; + /* here a_mant < b_mant and n >= 0 */ + /* multiply a_mant by 2^n */ + /* XXX: do it faster */ + while (n != 0) { + a_mant <<= 1; + if (a_mant >= b_mant) + a_mant -= b_mant; + n--; + } + /* Note: the rounding mode does not matter because the result is + exact */ + return normalize_sf(a_sign, b_exp, a_mant << RND_SIZE, RM_RNE FFLAGS_ARG); +} +#endif /* F_NORMALIZE_ONLY */ + +#undef F_SIZE +#undef F_UINT +#undef F_ULONG +#undef F_UHALF +#undef MANT_SIZE +#undef EXP_SIZE +#undef EXP_MASK +#undef MANT_MASK +#undef SIGN_MASK +#undef IMANT_SIZE +#undef RND_SIZE +#undef QNAN_MASK +#undef F_QNAN +#undef F_NORMALIZE_ONLY +#undef EXP_BIAS + +#undef pack_sf +#undef unpack_sf +#undef rshift_rnd +#undef round_pack_sf +#undef normalize_sf +#undef normalize2_sf +#undef issignan_sf +#undef isnan_sf +#undef add_sf +#undef mul_sf +#undef fma_sf +#undef div_sf +#undef sqrt_sf +#undef normalize_subnormal_sf +#undef divrem_u +#undef sqrtrem_u +#undef mul_u +#undef cvt_sf32_sf +#undef cvt_sf64_sf diff --git a/lib/mquickjs/softfp_template_icvt.h b/lib/mquickjs/softfp_template_icvt.h new file mode 100644 index 00000000..02fee109 --- /dev/null +++ b/lib/mquickjs/softfp_template_icvt.h @@ -0,0 +1,172 @@ +/* + * SoftFP Library + * + * Copyright (c) 2016 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. + */ +#if ICVT_SIZE == 32 +#define ICVT_UINT uint32_t +#define ICVT_INT int32_t +#elif ICVT_SIZE == 64 +#define ICVT_UINT uint64_t +#define ICVT_INT int64_t +#elif ICVT_SIZE == 128 +#define ICVT_UINT uint128_t +#define ICVT_INT int128_t +#else +#error unsupported icvt +#endif + +/* conversions between float and integers */ +static ICVT_INT glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm, + BOOL is_unsigned FFLAGS_PARAM) +{ + uint32_t a_sign, addend, rnd_bits; + int32_t a_exp; + F_UINT a_mant; + ICVT_UINT r, r_max; + + a_sign = a >> (F_SIZE - 1); + a_exp = (a >> MANT_SIZE) & EXP_MASK; + a_mant = a & MANT_MASK; + if (a_exp == EXP_MASK && a_mant != 0) + a_sign = 0; /* NaN is like +infinity */ + if (a_exp == 0) { + a_exp = 1; + } else { + a_mant |= (F_UINT)1 << MANT_SIZE; + } + a_mant <<= RND_SIZE; + a_exp = a_exp - (EXP_MASK / 2) - MANT_SIZE; + + if (is_unsigned) + r_max = (ICVT_UINT)a_sign - 1; + else + r_max = ((ICVT_UINT)1 << (ICVT_SIZE - 1)) - (ICVT_UINT)(a_sign ^ 1); + if (a_exp >= 0) { + if (a_exp <= (ICVT_SIZE - 1 - MANT_SIZE)) { + r = (ICVT_UINT)(a_mant >> RND_SIZE) << a_exp; + if (r > r_max) + goto overflow; + } else { + overflow: +#if F_USE_FFLAGS + *pfflags |= FFLAG_INVALID_OP; +#endif + return r_max; + } + } else { + a_mant = rshift_rnd(a_mant, -a_exp); + + switch(rm) { + case RM_RNE: + case RM_RMM: + addend = (1 << (RND_SIZE - 1)); + break; + case RM_RTZ: + addend = 0; + break; + default: + case RM_RDN: + case RM_RUP: + if (a_sign ^ (rm & 1)) + addend = (1 << RND_SIZE) - 1; + else + addend = 0; + break; + } + + rnd_bits = a_mant & ((1 << RND_SIZE ) - 1); + a_mant = (a_mant + addend) >> RND_SIZE; + /* half way: select even result */ + if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1))) + a_mant &= ~1; + if (a_mant > r_max) + goto overflow; + r = a_mant; +#if F_USE_FFLAGS + if (rnd_bits != 0) + *pfflags |= FFLAG_INEXACT; +#endif + } + if (a_sign) + r = -r; + return r; +} + +F_STATIC ICVT_INT __maybe_unused glue(glue(glue(cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM) +{ + return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(a, rm, + FALSE FFLAGS_ARG); +} + +F_STATIC ICVT_UINT __maybe_unused glue(glue(glue(cvt_sf, F_SIZE), _u), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm FFLAGS_PARAM) +{ + return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE) (a, rm, + TRUE FFLAGS_ARG); +} + +/* conversions between float and integers */ +static F_UINT glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a, + RoundingModeEnum rm, + BOOL is_unsigned FFLAGS_PARAM) +{ + uint32_t a_sign; + int32_t a_exp; + F_UINT a_mant; + ICVT_UINT r, mask; + int l; + + if (!is_unsigned && a < 0) { + a_sign = 1; + r = -(ICVT_UINT)a; + } else { + a_sign = 0; + r = a; + } + a_exp = (EXP_MASK / 2) + F_SIZE - 2; + /* need to reduce range before generic float normalization */ + l = ICVT_SIZE - glue(clz, ICVT_SIZE)(r) - (F_SIZE - 1); + if (l > 0) { + mask = r & (((ICVT_UINT)1 << l) - 1); + r = (r >> l) | ((r & mask) != 0); + a_exp += l; + } + a_mant = r; + return normalize_sf(a_sign, a_exp, a_mant, rm FFLAGS_ARG); +} + +F_STATIC F_UINT __maybe_unused glue(glue(glue(cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a, + RoundingModeEnum rm + FFLAGS_PARAM) +{ + return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, FALSE FFLAGS_ARG); +} + +F_STATIC F_UINT __maybe_unused glue(glue(glue(cvt_u, ICVT_SIZE), _sf), F_SIZE)(ICVT_UINT a, + RoundingModeEnum rm + FFLAGS_PARAM) +{ + return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, TRUE FFLAGS_ARG); +} + +#undef ICVT_SIZE +#undef ICVT_INT +#undef ICVT_UINT diff --git a/platformio.ini b/platformio.ini index e8574470..ee87e176 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,6 +30,8 @@ build_flags = -std=c++2a # Enable UTF-8 long file names in SdFat -DUSE_UTF8_LONG_NAMES=1 +# Ignore error for mquickjs + -Wno-narrowing ; Board configuration board_build.flash_mode = dio diff --git a/src/activities/app/AppActivity.cpp b/src/activities/app/AppActivity.cpp new file mode 100644 index 00000000..b861fac7 --- /dev/null +++ b/src/activities/app/AppActivity.cpp @@ -0,0 +1,251 @@ +#include "AppActivity.h" + +#include +#include +#include + +#include "../../util/StringUtils.h" +#include "fontIds.h" + +void AppActivity::taskTrampoline(void* param) { + auto* self = static_cast(param); + self->displayTaskLoop(); +} + +void AppActivity::taskAppTrampoline(void* param) { + auto* self = static_cast(param); + self->appTaskLoop(); +} + +void AppActivity::onEnter() { + Activity::onEnter(); + renderingMutex = xSemaphoreCreateMutex(); + + selectedIdx = 0; + programs.clear(); + + // load available applications from /apps directory + FsFile dir = SdMan.open("/apps"); + if (dir && dir.isDirectory()) { + dir.rewindDirectory(); + for (FsFile file = dir.openNextFile(); file; file = dir.openNextFile()) { + char name[256]; + file.getName(name, sizeof(name)); + std::string filename(name); + // only accept .js files + if (StringUtils::checkFileExtension(filename, ".js")) { + programs.emplace_back(std::move(filename)); + } + file.close(); + } + } + dir.close(); + + // Trigger first update + updateRequired = true; + + xTaskCreate(&AppActivity::taskTrampoline, "AppActivityTask", + 4096, // Stack size + this, // Parameters + 1, // Priority + &displayTaskHandle // Task handle + ); +} + +void AppActivity::onExit() { + Activity::onExit(); + + auto& runner = AppRunner::getInstance(); + runner.reset(); + + xSemaphoreTake(renderingMutex, portMAX_DELAY); + if (displayTaskHandle) { + vTaskDelete(displayTaskHandle); + displayTaskHandle = nullptr; + } + vSemaphoreDelete(renderingMutex); + renderingMutex = nullptr; +} + +void AppActivity::loop() { + auto& runner = AppRunner::getInstance(); + if (runner.running) { + return; + } + + if (runner.exited) { + if (appTaskHandle) { + vTaskDelete(appTaskHandle); + appTaskHandle = nullptr; + } + runner.reset(); + updateRequired = true; + // give back rendering control + xSemaphoreGive(renderingMutex); + return; + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + // delegate rendering to the app + xSemaphoreTake(renderingMutex, portMAX_DELAY); + startProgram(programs[selectedIdx]); // TODO: handle errors + return; + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { + onGoHome(); + return; + } + + // Handle navigation + if (mappedInput.wasPressed(MappedInputManager::Button::Up) || + mappedInput.wasPressed(MappedInputManager::Button::Left)) { + // Move selection up (with wrap-around) + selectedIdx = (selectedIdx > 0) ? (selectedIdx - 1) : (programs.size() - 1); + updateRequired = true; + } else if (mappedInput.wasPressed(MappedInputManager::Button::Down) || + mappedInput.wasPressed(MappedInputManager::Button::Right)) { + // Move selection down (with wrap around) + selectedIdx = (selectedIdx + 1) % programs.size(); + updateRequired = true; + } +} + +void AppActivity::displayTaskLoop() { + while (true) { + if (updateRequired) { //&& !subActivity) { + updateRequired = false; + xSemaphoreTake(renderingMutex, portMAX_DELAY); + render(); + xSemaphoreGive(renderingMutex); + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } +} + +void AppActivity::render() const { + renderer.clearScreen(); + + const auto pageWidth = renderer.getScreenWidth(); + const auto pageHeight = renderer.getScreenHeight(); + + // Draw header + renderer.drawCenteredText(UI_12_FONT_ID, 15, "Applications", true, EpdFontFamily::BOLD); + + if (programs.empty()) { + renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2, "No applications found", true); + } else { + // Draw selection + renderer.fillRect(0, 60 + selectedIdx * 30 - 2, pageWidth - 1, 30); + + // Draw all programs + for (int i = 0; i < programs.size(); i++) { + const int programY = 60 + i * 30; // 30 pixels between programs + + // Draw program name + renderer.drawText(UI_10_FONT_ID, 20, programY, programs[i].c_str(), i != selectedIdx); + } + } + + // Draw help text + const auto labels = mappedInput.mapLabels("« Back", "Select", "", ""); + renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + + // Always use standard refresh for settings screen + renderer.displayBuffer(); +} + +// +// APP RUNNER +// + +// singleton for now +AppActivity* instance = nullptr; + +int fontIdFromString(const std::string& fontStr) { + if (fontStr == "BOOKERLY_12") return BOOKERLY_12_FONT_ID; + if (fontStr == "BOOKERLY_14") return BOOKERLY_14_FONT_ID; + if (fontStr == "BOOKERLY_16") return BOOKERLY_16_FONT_ID; + if (fontStr == "BOOKERLY_18") return BOOKERLY_18_FONT_ID; + if (fontStr == "NOTOSANS_12") return NOTOSANS_12_FONT_ID; + if (fontStr == "NOTOSANS_14") return NOTOSANS_14_FONT_ID; + if (fontStr == "NOTOSANS_16") return NOTOSANS_16_FONT_ID; + if (fontStr == "NOTOSANS_18") return NOTOSANS_18_FONT_ID; + if (fontStr == "OPENDYSLEXIC_8") return OPENDYSLEXIC_8_FONT_ID; + if (fontStr == "OPENDYSLEXIC_10") return OPENDYSLEXIC_10_FONT_ID; + if (fontStr == "OPENDYSLEXIC_12") return OPENDYSLEXIC_12_FONT_ID; + if (fontStr == "OPENDYSLEXIC_14") return OPENDYSLEXIC_14_FONT_ID; + if (fontStr == "UI_10") return UI_10_FONT_ID; + if (fontStr == "UI_12") return UI_12_FONT_ID; + if (fontStr == "SMALL") return SMALL_FONT_ID; + return -1; +} + +EpdFontFamily::Style styleFromString(const std::string& styleStr) { + if (styleStr == "REGULAR") return EpdFontFamily::REGULAR; + if (styleStr == "BOLD") return EpdFontFamily::BOLD; + if (styleStr == "ITALIC") return EpdFontFamily::ITALIC; + if (styleStr == "BOLD_ITALIC") return EpdFontFamily::BOLD_ITALIC; + return EpdFontFamily::REGULAR; +} + +void AppActivity::startProgram(std::string programName) { + std::string fullPath = "/apps/" + programName; + FsFile file = SdMan.open(fullPath.c_str(), O_RDONLY); + assert(file && file.isOpen()); + size_t fileSize = file.size(); + + if (fileSize == 0 || fileSize > AppRunner::MAX_PROG_SIZE) { + // TODO: show as a dialog message + Serial.printf("[%lu] [APP] Invalid program size: %u bytes, max supported = %u\n", millis(), (unsigned)fileSize, + (unsigned)AppRunner::MAX_PROG_SIZE); + file.close(); + return; + } + + // prepare runner + auto& runner = AppRunner::getInstance(); + runner.reset(); + + // load program code + runner.prog.resize(fileSize + 1); + size_t bytesRead = file.read(&runner.prog[0], fileSize); + assert(bytesRead == fileSize); + runner.prog[fileSize] = '\0'; + file.close(); + Serial.printf("[%lu] [APP] Starting program: %s (%u bytes)\n", millis(), programName.c_str(), + (unsigned)runner.prog.size()); + + // clear screen before running + renderer.clearScreen(); + renderer.displayBuffer(); + + // start new task + runner.running = true; + xTaskCreate(&AppActivity::taskAppTrampoline, "AppRuntimeTask", + 4096, // Stack size + this, // Parameters + 1, // Priority + &appTaskHandle // Task handle + ); + + Serial.printf("[%lu] [APP] Program started\n", millis()); +} + +void AppActivity::appTaskLoop() { + auto& runner = AppRunner::getInstance(); + assert(runner.running && "program not running"); + + // run program code + runner.run(&renderer, &mappedInput); + + // program ended + Serial.printf("[%lu] [APP] Program ended\n", millis()); + runner.running = false; + runner.exited = true; + + // keep task alive until main loop cleans up + while (true) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} diff --git a/src/activities/app/AppActivity.h b/src/activities/app/AppActivity.h new file mode 100644 index 00000000..5533a0ca --- /dev/null +++ b/src/activities/app/AppActivity.h @@ -0,0 +1,39 @@ +#pragma once +#include + +#include +#include +#include +#include + +#include "../../activities/Activity.h" +#include "AppRunner.h" + +class AppActivity final : public Activity { + TaskHandle_t displayTaskHandle = nullptr; + TaskHandle_t appTaskHandle = nullptr; + SemaphoreHandle_t renderingMutex = nullptr; + bool updateRequired = false; + const std::function onGoHome; + + static void taskTrampoline(void* param); + static void taskAppTrampoline(void* param); + [[noreturn]] void displayTaskLoop(); + [[noreturn]] void appTaskLoop(); + void render() const; + void startProgram(std::string programName); + + public: + explicit AppActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::function& onGoHome) + : Activity("Settings", renderer, mappedInput), onGoHome(onGoHome) {} + void onEnter() override; + void onExit() override; + void loop() override; + + GfxRenderer& getRenderer() { return renderer; } + MappedInputManager& getMappedInput() { return mappedInput; } + + // state + std::vector programs; + size_t selectedIdx = 0; +}; diff --git a/src/activities/app/AppRunner.cpp b/src/activities/app/AppRunner.cpp new file mode 100644 index 00000000..b07b36fa --- /dev/null +++ b/src/activities/app/AppRunner.cpp @@ -0,0 +1,331 @@ + +#include "AppRunner.h" + +#include + +#include +#include +#include + +#include "../../fontIds.h" + +extern "C" { + +#include + +static JSValue js_print(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + int i; + JSValue v; + + for (i = 0; i < argc; i++) { + if (i != 0) Serial.write(' '); + v = argv[i]; + if (JS_IsString(ctx, v)) { + JSCStringBuf buf; + const char* str; + size_t len; + str = JS_ToCStringLen(ctx, &len, v, &buf); + Serial.write((const uint8_t*)str, len); + } else { + JS_PrintValueF(ctx, argv[i], JS_DUMP_LONG); + } + } + Serial.println(); + return JS_UNDEFINED; +} + +static JSValue js_date_now(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + struct timeval tv; + gettimeofday(&tv, NULL); + return JS_NewInt64(ctx, (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000)); +} + +static JSValue js_performance_now(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + return JS_ThrowInternalError(ctx, "js_performance_now not implemented"); +} + +static JSValue js_gc(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + return JS_ThrowInternalError(ctx, "js_gc not implemented"); +} + +static JSValue js_load(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + return JS_ThrowInternalError(ctx, "js_load not implemented"); +} + +static JSValue js_setTimeout(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + return JS_ThrowInternalError(ctx, "js_setTimeout not implemented"); +} + +static JSValue js_clearTimeout(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + return JS_ThrowInternalError(ctx, "js_clearTimeout not implemented"); +} + +// Crosspoint-specific functions + +AppRunner& appInstance() { return AppRunner::getInstance(); } + +const char* argc_err_msg = "Expected at least %d arguments, but got %d"; +#define CHECK_ARGC(minArgs) \ + if (argc < minArgs) { \ + return JS_ThrowTypeError(ctx, argc_err_msg, minArgs, argc); \ + } + +#define GET_STRING_ARG(index, varName) \ + JSCStringBuf varName##Buf; \ + size_t varName##Len; \ + const char* varName; \ + varName = JS_ToCStringLen(ctx, &varName##Len, argv[index], &varName##Buf); + +static JSValue js_millis(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + return JS_NewInt64(ctx, millis()); +} + +static JSValue js_btnIsPressed(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(1); + GET_STRING_ARG(0, buttonStr); + if (!buttonStr) { + return JS_EXCEPTION; + } + MappedInputManager::Button button; + if (strcmp(buttonStr, "B") == 0) { + button = MappedInputManager::Button::Back; + } else if (strcmp(buttonStr, "C") == 0) { + button = MappedInputManager::Button::Confirm; + } else if (strcmp(buttonStr, "L") == 0) { + button = MappedInputManager::Button::Left; + } else if (strcmp(buttonStr, "R") == 0) { + button = MappedInputManager::Button::Right; + } else if (strcmp(buttonStr, "U") == 0) { + button = MappedInputManager::Button::Up; + } else if (strcmp(buttonStr, "D") == 0) { + button = MappedInputManager::Button::Down; + } else { + return JS_ThrowRangeError(ctx, "invalid button id '%s'", buttonStr); + } + bool isPressed = appInstance().mappedInput->isPressed(button); + return JS_NewBool(isPressed); +} + +static JSValue js_getScreenWidth(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + return appInstance().renderer->getScreenWidth(); +} + +static JSValue js_getScreenHeight(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + return appInstance().renderer->getScreenHeight(); +} + +static JSValue js_clearScreen(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(1); + int color; + if (JS_ToInt32(ctx, &color, argv[0])) return JS_EXCEPTION; + if (color < 0 || color > 255) { + return JS_ThrowRangeError(ctx, "color must be between 0 and 255"); + } + appInstance().renderer->clearScreen((uint8_t)color); + return JS_UNDEFINED; +} + +static JSValue js_displayBuffer(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(1); + int refreshMode = HalDisplay::FAST_REFRESH; + if (JS_ToInt32(ctx, &refreshMode, argv[0])) return JS_EXCEPTION; + appInstance().renderer->displayBuffer((HalDisplay::RefreshMode)refreshMode); + return JS_UNDEFINED; +} + +static JSValue js_drawLine(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(5); + int x1, y1, x2, y2, state; + if (JS_ToInt32(ctx, &x1, argv[0]) || JS_ToInt32(ctx, &y1, argv[1]) || JS_ToInt32(ctx, &x2, argv[2]) || + JS_ToInt32(ctx, &y2, argv[3]) || JS_ToInt32(ctx, &state, argv[4])) { + return JS_EXCEPTION; + } + appInstance().renderer->drawLine(x1, y1, x2, y2, state != 0); + return JS_UNDEFINED; +} + +static JSValue js_drawRect(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(5); + int x, y, width, height, state; + if (JS_ToInt32(ctx, &x, argv[0]) || JS_ToInt32(ctx, &y, argv[1]) || JS_ToInt32(ctx, &width, argv[2]) || + JS_ToInt32(ctx, &height, argv[3]) || JS_ToInt32(ctx, &state, argv[4])) { + return JS_EXCEPTION; + } + appInstance().renderer->drawRect(x, y, width, height, state != 0); + return JS_UNDEFINED; +} + +static JSValue js_fillRect(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(5); + int x, y, width, height, state; + if (JS_ToInt32(ctx, &x, argv[0]) || JS_ToInt32(ctx, &y, argv[1]) || JS_ToInt32(ctx, &width, argv[2]) || + JS_ToInt32(ctx, &height, argv[3]) || JS_ToInt32(ctx, &state, argv[4])) { + return JS_EXCEPTION; + } + appInstance().renderer->fillRect(x, y, width, height, state != 0); + return JS_UNDEFINED; +} + +static JSValue js_drawImage(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(5); + JSCStringBuf buf; + size_t len; // unused for now + const char* bitmapData = JS_ToCStringLen(ctx, &len, argv[0], &buf); + int x, y, width, height; + if (!bitmapData || JS_ToInt32(ctx, &x, argv[1]) || JS_ToInt32(ctx, &y, argv[2]) || JS_ToInt32(ctx, &width, argv[3]) || + JS_ToInt32(ctx, &height, argv[4])) { + return JS_EXCEPTION; + } + appInstance().renderer->drawImage((const uint8_t*)bitmapData, x, y, width, height); + return JS_UNDEFINED; +} + +static int fontIdFromString(const char* fontIdStr) { + if (strcmp(fontIdStr, "UI10") == 0) { + return UI_10_FONT_ID; + } else if (strcmp(fontIdStr, "UI12") == 0) { + return UI_12_FONT_ID; + } else if (strcmp(fontIdStr, "SM") == 0) { + return SMALL_FONT_ID; + } + return UI_10_FONT_ID; // default +} + +static EpdFontFamily::Style textStyleFromString(const char* styleStr) { + EpdFontFamily::Style style = EpdFontFamily::REGULAR; + if (strcmp(styleStr, "B") == 0) { + style = EpdFontFamily::BOLD; + } else if (strcmp(styleStr, "I") == 0) { + style = EpdFontFamily::ITALIC; + } else if (strcmp(styleStr, "J") == 0) { + style = EpdFontFamily::BOLD_ITALIC; + } + return style; +} + +static JSValue js_getTextWidth(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(3); + GET_STRING_ARG(0, fontIdStr); + GET_STRING_ARG(1, text); + GET_STRING_ARG(2, styleStr); + if (!fontIdStr || !text || !styleStr) { + return JS_EXCEPTION; + } + int fontId = fontIdFromString(fontIdStr); + EpdFontFamily::Style style = textStyleFromString(styleStr); + int width = appInstance().renderer->getTextWidth(fontId, text, style); + return JS_NewInt32(ctx, width); +} + +static JSValue js_drawCenteredText(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(5); + int y, black; + GET_STRING_ARG(0, fontIdStr); + GET_STRING_ARG(2, text); + GET_STRING_ARG(4, styleStr); + if (!fontIdStr || JS_ToInt32(ctx, &y, argv[1]) || !text || JS_ToInt32(ctx, &black, argv[3]) || !styleStr) { + return JS_EXCEPTION; + } + int fontId = fontIdFromString(fontIdStr); + EpdFontFamily::Style style = textStyleFromString(styleStr); + appInstance().renderer->drawCenteredText(fontId, y, text, black != 0, style); + return JS_UNDEFINED; +} + +static JSValue js_drawText(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(6); + int x, y, black; + GET_STRING_ARG(0, fontIdStr); + GET_STRING_ARG(3, text); + GET_STRING_ARG(5, styleStr); + if (!fontIdStr || JS_ToInt32(ctx, &y, argv[2]) || !text || JS_ToInt32(ctx, &black, argv[4]) || !styleStr) { + return JS_EXCEPTION; + } + int fontId = fontIdFromString(fontIdStr); + EpdFontFamily::Style style = textStyleFromString(styleStr); + appInstance().renderer->drawText(fontId, x, y, text, black != 0, style); + return JS_UNDEFINED; +} + +static JSValue js_drawButtonHints(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(5); + GET_STRING_ARG(0, fontIdStr); + GET_STRING_ARG(1, btn1Text); + GET_STRING_ARG(2, btn2Text); + GET_STRING_ARG(3, btn3Text); + GET_STRING_ARG(4, btn4Text); + if (!fontIdStr || !btn1Text || !btn2Text || !btn3Text || !btn4Text) { + return JS_EXCEPTION; + } + int fontId = fontIdFromString(fontIdStr); + appInstance().renderer->drawButtonHints(fontId, btn1Text, btn2Text, btn3Text, btn4Text); + return JS_UNDEFINED; +} + +static JSValue js_drawSideButtonHints(JSContext* ctx, JSValue* this_val, int argc, JSValue* argv) { + CHECK_ARGC(3); + GET_STRING_ARG(0, fontIdStr); + GET_STRING_ARG(1, topBtnText); + GET_STRING_ARG(2, bottomBtnText); + if (!fontIdStr || !topBtnText || !bottomBtnText) { + return JS_EXCEPTION; + } + int fontId = fontIdFromString(fontIdStr); + appInstance().renderer->drawSideButtonHints(fontId, topBtnText, bottomBtnText); + return JS_UNDEFINED; +} + +#include +} + +AppRunner AppRunner::instance; + +static void dump_error(JSContext* jsCtx) { + JSValue obj = JS_GetException(jsCtx); + JS_PrintValueF(jsCtx, obj, JS_DUMP_LONG); +} + +static void serial_log_write_func(void* opaque, const void* buf, size_t buf_len) { + Serial.printf("[%lu] [MJS] %.*s", millis(), (int)buf_len, (const char*)buf); + Serial.write((const uint8_t*)buf, buf_len); + Serial.println(); +} + +void AppRunner::run(GfxRenderer* renderer, MappedInputManager* mappedInput) { + this->renderer = renderer; + this->mappedInput = mappedInput; + + this->mem.resize(MAX_MEM_SIZE); + this->jsCtx = JS_NewContext(mem.data(), mem.size(), &js_stdlib); + JS_SetLogFunc(jsCtx, serial_log_write_func); + + JSValue val; + + if (JS_IsBytecode((const uint8_t*)prog.data(), prog.size())) { + Serial.printf("[%lu] [APP] Loading bytecode...\n", millis()); + if (JS_RelocateBytecode(jsCtx, (uint8_t*)prog.data(), prog.size())) { + Serial.printf("[%lu] [APP] Failed to relocate bytecode\n", millis()); + } + val = JS_LoadBytecode(jsCtx, (const uint8_t*)prog.data()); + } else { + Serial.printf("[%lu] [APP] Parsing program from source...\n", millis()); + int parse_flags = 0; + val = JS_Parse(jsCtx, prog.data(), prog.size(), "app", parse_flags); + } + + if (JS_IsException(val)) { + dump_error(jsCtx); + Serial.printf("[%lu] [APP] Got exception on parsing program\n", millis()); + return; + } + + val = JS_Run(jsCtx, val); + + if (JS_IsException(val)) { + dump_error(jsCtx); + Serial.printf("[%lu] [APP] Program exited with exception\n", millis()); + return; + } + + // normal exit +} diff --git a/src/activities/app/AppRunner.h b/src/activities/app/AppRunner.h new file mode 100644 index 00000000..ae0a365b --- /dev/null +++ b/src/activities/app/AppRunner.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include +#include + +extern "C" { +#include +} + +class AppRunner { + public: + static constexpr size_t MAX_PROG_SIZE = 32 * 1024; // 32KB + static constexpr size_t MAX_MEM_SIZE = 64 * 1024; // 64KB + + bool running = false; + bool exited = false; + std::vector prog; // need to be alloc and set before run() + std::vector mem; + JSContext* jsCtx = nullptr; + + GfxRenderer* renderer = nullptr; + MappedInputManager* mappedInput = nullptr; + + void reset() { + this->running = false; + this->exited = false; + this->prog.clear(); + this->mem.clear(); + this->jsCtx = nullptr; + } + + void run(GfxRenderer* renderer, MappedInputManager* mappedInput); + + // need to be singleton so that the binded JS functions can access it + static AppRunner& getInstance() { return instance; } + + private: + static AppRunner instance; +}; diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 678af7cb..2998bc0d 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -175,6 +175,7 @@ void HomeActivity::loop() { const int continueIdx = hasContinueReading ? idx++ : -1; const int myLibraryIdx = idx++; const int opdsLibraryIdx = hasOpdsUrl ? idx++ : -1; + const int appsIdx = idx++; const int fileTransferIdx = idx++; const int settingsIdx = idx; @@ -184,6 +185,8 @@ void HomeActivity::loop() { onMyLibraryOpen(); } else if (selectorIndex == opdsLibraryIdx) { onOpdsBrowserOpen(); + } else if (selectorIndex == appsIdx) { + onAppsOpen(); } else if (selectorIndex == fileTransferIdx) { onFileTransferOpen(); } else if (selectorIndex == settingsIdx) { @@ -504,7 +507,7 @@ void HomeActivity::render() { // --- Bottom menu tiles --- // Build menu items dynamically - std::vector menuItems = {"My Library", "File Transfer", "Settings"}; + std::vector menuItems = {"My Library", "Applications", "File Transfer", "Settings"}; if (hasOpdsUrl) { // Insert OPDS Browser after My Library menuItems.insert(menuItems.begin() + 1, "OPDS Browser"); diff --git a/src/activities/home/HomeActivity.h b/src/activities/home/HomeActivity.h index 52963514..4d305f84 100644 --- a/src/activities/home/HomeActivity.h +++ b/src/activities/home/HomeActivity.h @@ -26,6 +26,7 @@ class HomeActivity final : public Activity { const std::function onSettingsOpen; const std::function onFileTransferOpen; const std::function onOpdsBrowserOpen; + const std::function onAppsOpen; static void taskTrampoline(void* param); [[noreturn]] void displayTaskLoop(); @@ -39,13 +40,14 @@ class HomeActivity final : public Activity { explicit HomeActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, const std::function& onContinueReading, const std::function& onMyLibraryOpen, const std::function& onSettingsOpen, const std::function& onFileTransferOpen, - const std::function& onOpdsBrowserOpen) + const std::function& onOpdsBrowserOpen, const std::function& onAppsOpen) : Activity("Home", renderer, mappedInput), onContinueReading(onContinueReading), onMyLibraryOpen(onMyLibraryOpen), onSettingsOpen(onSettingsOpen), onFileTransferOpen(onFileTransferOpen), - onOpdsBrowserOpen(onOpdsBrowserOpen) {} + onOpdsBrowserOpen(onOpdsBrowserOpen), + onAppsOpen(onAppsOpen) {} void onEnter() override; void onExit() override; void loop() override; diff --git a/src/main.cpp b/src/main.cpp index 89c4e13c..48b21129 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,7 @@ #include "KOReaderCredentialStore.h" #include "MappedInputManager.h" #include "RecentBooksStore.h" +#include "activities/app/AppActivity.h" #include "activities/boot_sleep/BootActivity.h" #include "activities/boot_sleep/SleepActivity.h" #include "activities/browser/OpdsBookBrowserActivity.h" @@ -236,10 +237,15 @@ void onGoToBrowser() { enterNewActivity(new OpdsBookBrowserActivity(renderer, mappedInputManager, onGoHome)); } +void onGoToApps() { + exitActivity(); + enterNewActivity(new AppActivity(renderer, mappedInputManager, onGoHome)); +} + void onGoHome() { exitActivity(); enterNewActivity(new HomeActivity(renderer, mappedInputManager, onContinueReading, onGoToMyLibrary, onGoToSettings, - onGoToFileTransfer, onGoToBrowser)); + onGoToFileTransfer, onGoToBrowser, onGoToApps)); } void setupDisplayAndFonts() {