Merge commit '894ce9d' into upgrade-20250910

This commit is contained in:
Nicolas Duteil 2025-09-11 00:40:21 +02:00
commit a555f9d893
72 changed files with 14660 additions and 23866 deletions

285
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,285 @@
name: ci
on:
pull_request:
paths:
- '**'
- '!.gitignore'
- '!LICENSE'
- '!TODO'
- '!doc/**'
- '!examples/**'
- '.github/workflows/ci.yml'
push:
branches:
- '*'
jobs:
linux:
name: Linux (Ubuntu)
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y
- name: Stats
run: |
./qjs -qd
- name: Run built-in tests
run: |
make test
- name: Run microbench
run: |
make microbench
linux-lto:
name: Linux LTO
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_LTO=y
- name: Run built-in tests
run: |
make test
- name: Run microbench
run: |
make microbench
linux-32bit:
name: Linux 32bit
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install gcc-multilib
run: |
sudo apt install -y gcc-multilib
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_M32=y
- name: Run built-in tests
run: |
make CONFIG_M32=y test
linux-asan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_ASAN=y
- name: Run built-in tests
env:
ASAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_ASAN=y test
linux-msan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
env:
CC: clang
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_MSAN=y CONFIG_CLANG=y
- name: Run built-in tests
env:
MSAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_MSAN=y CONFIG_CLANG=y test
linux-ubsan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_UBSAN=y
- name: Run built-in tests
env:
UBSAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_UBSAN=y test
macos:
name: macOS
runs-on: macos-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y
- name: Stats
run: |
./qjs -qd
- name: Run built-in tests
run: |
make test
macos-asan:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_ASAN=y
- name: Run built-in tests
env:
ASAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_ASAN=y test
macos-ubsan:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y CONFIG_UBSAN=y
- name: Run built-in tests
env:
UBSAN_OPTIONS: halt_on_error=1
run: |
make CONFIG_UBSAN=y test
freebsd:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build + test
uses: vmactions/freebsd-vm@v1
with:
usesh: true
prepare: |
pkg install -y gmake
run: |
gmake
./qjs -qd
gmake test
cosmopolitan:
name: Cosmopolitan
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install Cosmopolitan
run: |
mkdir ~/cosmocc
cd ~/cosmocc
wget https://cosmo.zip/pub/cosmocc/cosmocc.zip
unzip cosmocc.zip
echo "$HOME/cosmocc/bin" >> "$GITHUB_PATH"
- name: Build
run: |
make CONFIG_COSMO=y
- name: Run built-in tests
run: |
make CONFIG_COSMO=y test
mingw-windows:
name: MinGW Windows target
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install MinGW and Wine
run: |
sudo apt install -y wine mingw-w64
cp /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll .
- name: Setup Wine
run: |
wine --version
winecfg /v
# binfmt doesn't work in GitHub Actions
#sudo apt install -y binfmt-support wine-binfmt
#echo ":Wine:M::MZ::/usr/bin/wine:" > /etc/binfmt.d/wine.conf
#sudo systemctl restart systemd-binfmt
- name: Build
run: |
make CONFIG_WIN32=y
- name: Run built-in tests
run: |
# If binfmt support worked, could just run `make CONFIG_WIN32=y test`
make WINE=/usr/bin/wine CONFIG_WIN32=y test
windows-msys:
name: Windows MSYS2
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: git make mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-dlfcn
- name: Build
run: |
make -j$(getconf _NPROCESSORS_ONLN) CONFIG_WERROR=y
- name: Stats
run: |
./qjs -qd
- name: Run built-in tests
run: |
make test
- name: Run microbench
run: |
make microbench
qemu-alpine:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- linux/386
- linux/riscv64
- linux/arm64
- linux/arm/v6
- linux/arm/v7
- linux/s390x
- linux/ppc64le
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Get qemu
# See https://github.com/tonistiigi/binfmt/issues/215#issuecomment-2613004741
run: docker run --privileged --rm tonistiigi/binfmt:master --install all
- name: Run tests on ${{ matrix.platform }}
run: docker run --rm --interactive --mount type=bind,source=$(pwd),target=/host --platform ${{ matrix.platform }} alpine sh -c "apk add git patch make gcc libc-dev && cd /host && make test"

31
.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
*.a
.obj/
tests/bjson.so
examples/test_fib
test_fib.c
examples/*.so
examples/hello
examples/hello_module
examples/hello.exe
examples/test_fib.exe
hello.c
microbench*.txt
qjs
qjs.exe
qjsc
qjsc.exe
host-qjsc
qjscalc
qjscalc.c
repl.c
run-test262
run-test262.exe
test262
test262_*.txt
test262o
test262o_*.txt
unicode
unicode_gen
run_octane
run_sunspider_like
libwinpthread*.dll

View File

@ -1,3 +1,19 @@
2025-04-26:
- removed the bignum extensions and qjscalc
- new BigInt implementation optimized for small numbers
- added WeakRef, FinalizationRegistry and symbols as weakrefs
- added builtin float64 printing and parsing functions for more correctness
- faster repeated string concatenation
- qjs: promise unhandled rejections are fatal errors by default
- added column number in debug information
- removed the "use strip" extension
- qjs: added -s and --strip-source options
- qjsc: added -s and --keep-source options
- added JS_GetAnyOpaque()
- added more callbacks for exotic objects in JSClassExoticMethods
- misc bug fixes
2024-01-13: 2024-01-13:
- top-level-await support in modules - top-level-await support in modules
@ -13,7 +29,7 @@ TypedArray.prototype.{with,toReversed,toSorted}
- added RegExp 'd' flag - added RegExp 'd' flag
- fixed RegExp zero length match logic - fixed RegExp zero length match logic
- fixed RegExp case insensitive flag - fixed RegExp case insensitive flag
- added os.getpid() and os.now() - added os.sleepAsync(), os.getpid() and os.now()
- added cosmopolitan build - added cosmopolitan build
- misc bug fixes - misc bug fixes

304
Makefile
View File

@ -25,13 +25,19 @@
ifeq ($(shell uname -s),Darwin) ifeq ($(shell uname -s),Darwin)
CONFIG_DARWIN=y CONFIG_DARWIN=y
endif endif
ifeq ($(shell uname -s),FreeBSD)
CONFIG_FREEBSD=y
endif
# Windows cross compilation from Linux # Windows cross compilation from Linux
# May need to have libwinpthread*.dll alongside the executable
# (On Ubuntu/Debian may be installed with mingw-w64-x86-64-dev
# to /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll)
#CONFIG_WIN32=y #CONFIG_WIN32=y
# use link time optimization (smaller and faster executables but slower build) # use link time optimization (smaller and faster executables but slower build)
CONFIG_LTO=y #CONFIG_LTO=y
# consider warnings as errors (for development) # consider warnings as errors (for development)
#CONFIG_WERROR=y #CONFIG_WERROR=y
# force 32 bit build for some utilities # force 32 bit build on x86_64
#CONFIG_M32=y #CONFIG_M32=y
# cosmopolitan build (see https://github.com/jart/cosmopolitan) # cosmopolitan build (see https://github.com/jart/cosmopolitan)
#CONFIG_COSMO=y #CONFIG_COSMO=y
@ -43,16 +49,34 @@ PREFIX?=/usr/local
#CONFIG_PROFILE=y #CONFIG_PROFILE=y
# use address sanitizer # use address sanitizer
#CONFIG_ASAN=y #CONFIG_ASAN=y
# include the code for BigFloat/BigDecimal, math mode and faster large integers # use memory sanitizer
CONFIG_BIGNUM=y #CONFIG_MSAN=y
# use UB sanitizer
#CONFIG_UBSAN=y
OBJDIR=.obj OBJDIR=.obj
ifdef CONFIG_ASAN
OBJDIR:=$(OBJDIR)/asan
endif
ifdef CONFIG_MSAN
OBJDIR:=$(OBJDIR)/msan
endif
ifdef CONFIG_UBSAN
OBJDIR:=$(OBJDIR)/ubsan
endif
ifdef CONFIG_DARWIN ifdef CONFIG_DARWIN
# use clang instead of gcc # use clang instead of gcc
CONFIG_CLANG=y CONFIG_CLANG=y
CONFIG_DEFAULT_AR=y CONFIG_DEFAULT_AR=y
endif endif
ifdef CONFIG_FREEBSD
# use clang instead of gcc
CONFIG_CLANG=y
CONFIG_DEFAULT_AR=y
CONFIG_LTO=
endif
ifdef CONFIG_WIN32 ifdef CONFIG_WIN32
ifdef CONFIG_M32 ifdef CONFIG_M32
@ -61,6 +85,10 @@ ifdef CONFIG_WIN32
CROSS_PREFIX?=x86_64-w64-mingw32- CROSS_PREFIX?=x86_64-w64-mingw32-
endif endif
EXE=.exe EXE=.exe
else ifdef MSYSTEM
CONFIG_WIN32=y
CROSS_PREFIX?=
EXE=.exe
else else
CROSS_PREFIX?= CROSS_PREFIX?=
EXE= EXE=
@ -87,6 +115,7 @@ ifdef CONFIG_CLANG
AR=$(CROSS_PREFIX)ar AR=$(CROSS_PREFIX)ar
endif endif
endif endif
LIB_FUZZING_ENGINE ?= "-fsanitize=fuzzer"
else ifdef CONFIG_COSMO else ifdef CONFIG_COSMO
CONFIG_LTO= CONFIG_LTO=
HOST_CC=gcc HOST_CC=gcc
@ -99,25 +128,34 @@ else
HOST_CC=gcc HOST_CC=gcc
CC=$(CROSS_PREFIX)gcc CC=$(CROSS_PREFIX)gcc
CFLAGS+=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d CFLAGS+=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS += -Wno-array-bounds -Wno-format-truncation CFLAGS += -Wno-array-bounds -Wno-format-truncation -Wno-infinite-recursion
ifdef CONFIG_LTO ifdef CONFIG_LTO
AR=$(CROSS_PREFIX)gcc-ar AR=$(CROSS_PREFIX)gcc-ar
else else
AR=$(CROSS_PREFIX)ar AR=$(CROSS_PREFIX)ar
endif endif
endif endif
STRIP=$(CROSS_PREFIX)strip STRIP?=$(CROSS_PREFIX)strip
ifdef CONFIG_M32
CFLAGS+=-msse2 -mfpmath=sse # use SSE math for correct FP rounding
ifndef CONFIG_WIN32
CFLAGS+=-m32
LDFLAGS+=-m32
endif
endif
CFLAGS+=-fwrapv # ensure that signed overflows behave as expected CFLAGS+=-fwrapv # ensure that signed overflows behave as expected
ifdef CONFIG_WERROR ifdef CONFIG_WERROR
CFLAGS+=-Werror CFLAGS+=-Werror
endif endif
DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\" DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
ifdef CONFIG_BIGNUM
DEFINES+=-DCONFIG_BIGNUM
endif
ifdef CONFIG_WIN32 ifdef CONFIG_WIN32
DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior
endif endif
ifndef CONFIG_WIN32
ifeq ($(shell $(CC) -o /dev/null compat/test-closefrom.c 2>/dev/null && echo 1),1)
DEFINES+=-DHAVE_CLOSEFROM
endif
endif
CFLAGS+=$(DEFINES) CFLAGS+=$(DEFINES)
CFLAGS_DEBUG=$(CFLAGS) -O0 CFLAGS_DEBUG=$(CFLAGS) -O0
@ -142,6 +180,14 @@ ifdef CONFIG_ASAN
CFLAGS+=-fsanitize=address -fno-omit-frame-pointer CFLAGS+=-fsanitize=address -fno-omit-frame-pointer
LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer
endif endif
ifdef CONFIG_MSAN
CFLAGS+=-fsanitize=memory -fno-omit-frame-pointer
LDFLAGS+=-fsanitize=memory -fno-omit-frame-pointer
endif
ifdef CONFIG_UBSAN
CFLAGS+=-fsanitize=undefined -fno-omit-frame-pointer
LDFLAGS+=-fsanitize=undefined -fno-omit-frame-pointer
endif
ifdef CONFIG_WIN32 ifdef CONFIG_WIN32
LDEXPORT= LDEXPORT=
else else
@ -150,11 +196,14 @@ endif
ifndef CONFIG_COSMO ifndef CONFIG_COSMO
ifndef CONFIG_DARWIN ifndef CONFIG_DARWIN
ifndef CONFIG_WIN32
CONFIG_SHARED_LIBS=y # building shared libraries is supported CONFIG_SHARED_LIBS=y # building shared libraries is supported
endif endif
endif endif
endif
PROGS=qjs$(EXE) qjsc$(EXE) run-test262$(EXE)
PROGS=qjs$(EXE) qjsc$(EXE) run-test262
ifneq ($(CROSS_PREFIX),) ifneq ($(CROSS_PREFIX),)
QJSC_CC=gcc QJSC_CC=gcc
QJSC=./host-qjsc QJSC=./host-qjsc
@ -163,12 +212,6 @@ else
QJSC_CC=$(CC) QJSC_CC=$(CC)
QJSC=./qjsc$(EXE) QJSC=./qjsc$(EXE)
endif endif
ifndef CONFIG_WIN32
PROGS+=qjscalc
endif
ifdef CONFIG_M32
PROGS+=qjs32 qjs32_s
endif
PROGS+=libquickjs.a PROGS+=libquickjs.a
ifdef CONFIG_LTO ifdef CONFIG_LTO
PROGS+=libquickjs.lto.a PROGS+=libquickjs.lto.a
@ -176,28 +219,34 @@ endif
# examples # examples
ifeq ($(CROSS_PREFIX),) ifeq ($(CROSS_PREFIX),)
PROGS+=examples/hello
ifndef CONFIG_ASAN ifndef CONFIG_ASAN
ifndef CONFIG_MSAN
ifndef CONFIG_UBSAN
PROGS+=examples/hello examples/test_fib
# no -m32 option in qjsc
ifndef CONFIG_M32
ifndef CONFIG_WIN32
PROGS+=examples/hello_module PROGS+=examples/hello_module
endif endif
endif
ifdef CONFIG_SHARED_LIBS ifdef CONFIG_SHARED_LIBS
PROGS+=examples/test_fib examples/fib.so examples/point.so PROGS+=examples/fib.so examples/point.so
endif
endif
endif
endif endif
endif endif
all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS)
QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o $(OBJDIR)/libbf.o QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/dtoa.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o
QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS) QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
ifdef CONFIG_BIGNUM
QJS_OBJS+=$(OBJDIR)/qjscalc.o
endif
HOST_LIBS=-lm -ldl -lpthread HOST_LIBS=-lm -ldl -lpthread
LIBS=-lm LIBS=-lm -lpthread
ifndef CONFIG_WIN32 ifndef CONFIG_WIN32
LIBS+=-ldl -lpthread LIBS+=-ldl
endif endif
LIBS+=$(EXTRA_LIBS) LIBS+=$(EXTRA_LIBS)
@ -213,6 +262,17 @@ qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS))
qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS) qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
fuzz_eval: $(OBJDIR)/fuzz_eval.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a
$(CC) $(CFLAGS_OPT) $^ -o fuzz_eval $(LIB_FUZZING_ENGINE)
fuzz_compile: $(OBJDIR)/fuzz_compile.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a
$(CC) $(CFLAGS_OPT) $^ -o fuzz_compile $(LIB_FUZZING_ENGINE)
fuzz_regexp: $(OBJDIR)/fuzz_regexp.o $(OBJDIR)/libregexp.fuzz.o $(OBJDIR)/cutils.fuzz.o $(OBJDIR)/libunicode.fuzz.o
$(CC) $(CFLAGS_OPT) $^ -o fuzz_regexp $(LIB_FUZZING_ENGINE)
libfuzzer: fuzz_eval fuzz_compile fuzz_regexp
ifneq ($(CROSS_PREFIX),) ifneq ($(CROSS_PREFIX),)
$(QJSC): $(OBJDIR)/qjsc.host.o \ $(QJSC): $(OBJDIR)/qjsc.host.o \
@ -230,16 +290,6 @@ QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(PREFIX)\"
$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES) $(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES)
$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES) $(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES)
qjs32: $(patsubst %.o, %.m32.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
@size $@
qjscalc: qjs
ln -sf $< $@
ifdef CONFIG_LTO ifdef CONFIG_LTO
LTOEXT=.lto LTOEXT=.lto
else else
@ -254,34 +304,33 @@ libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^ $(AR) rcs $@ $^
endif # CONFIG_LTO endif # CONFIG_LTO
repl.c: $(QJSC) repl.js libquickjs.fuzz.a: $(patsubst %.o, %.fuzz.o, $(QJS_LIB_OBJS))
$(QJSC) -c -o $@ -m repl.js $(AR) rcs $@ $^
qjscalc.c: $(QJSC) qjscalc.js repl.c: $(QJSC) repl.js
$(QJSC) -fbignum -c -o $@ qjscalc.js $(QJSC) -s -c -o $@ -m repl.js
ifneq ($(wildcard unicode/UnicodeData.txt),) ifneq ($(wildcard unicode/UnicodeData.txt),)
$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o \ $(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.nolto.o: libunicode-table.h
$(OBJDIR)/libunicode.nolto.o: libunicode-table.h
libunicode-table.h: unicode_gen libunicode-table.h: unicode_gen
./unicode_gen unicode $@ ./unicode_gen unicode $@
endif endif
run-test262: $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS) run-test262$(EXE): $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) # object suffix order: nolto
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
# object suffix order: nolto, [m32|m32s]
$(OBJDIR)/%.o: %.c | $(OBJDIR) $(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -c -o $@ $< $(CC) $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/fuzz_%.o: fuzz/fuzz_%.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -c -I. -o $@ $<
$(OBJDIR)/%.host.o: %.c | $(OBJDIR) $(OBJDIR)/%.host.o: %.c | $(OBJDIR)
$(HOST_CC) $(CFLAGS_OPT) -c -o $@ $< $(HOST_CC) $(CFLAGS_OPT) -c -o $@ $<
@ -291,15 +340,12 @@ $(OBJDIR)/%.pic.o: %.c | $(OBJDIR)
$(OBJDIR)/%.nolto.o: %.c | $(OBJDIR) $(OBJDIR)/%.nolto.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_NOLTO) -c -o $@ $< $(CC) $(CFLAGS_NOLTO) -c -o $@ $<
$(OBJDIR)/%.m32.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.m32s.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_SMALL) -c -o $@ $<
$(OBJDIR)/%.debug.o: %.c | $(OBJDIR) $(OBJDIR)/%.debug.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_DEBUG) -c -o $@ $< $(CC) $(CFLAGS_DEBUG) -c -o $@ $<
$(OBJDIR)/%.fuzz.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -fsanitize=fuzzer-no-link -c -o $@ $<
$(OBJDIR)/%.check.o: %.c | $(OBJDIR) $(OBJDIR)/%.check.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $< $(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $<
@ -310,18 +356,18 @@ unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c u
$(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o $(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o
clean: clean:
rm -f repl.c qjscalc.c out.c rm -f repl.c out.c
rm -f *.a *.o *.d *~ unicode_gen regexp_test $(PROGS) rm -f *.a *.o *.d *~ unicode_gen regexp_test fuzz_eval fuzz_compile fuzz_regexp $(PROGS)
rm -f hello.c test_fib.c rm -f hello.c test_fib.c
rm -f examples/*.so tests/*.so rm -f examples/*.so tests/*.so
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug$(EXE)
rm -rf run-test262-debug run-test262-32 rm -rf run-test262-debug$(EXE)
rm -f run_octane run_sunspider_like
install: all install: all
mkdir -p "$(DESTDIR)$(PREFIX)/bin" mkdir -p "$(DESTDIR)$(PREFIX)/bin"
$(STRIP) qjs qjsc $(STRIP) qjs$(EXE) qjsc$(EXE)
install -m755 qjs qjsc "$(DESTDIR)$(PREFIX)/bin" install -m755 qjs$(EXE) qjsc$(EXE) "$(DESTDIR)$(PREFIX)/bin"
ln -sf qjs "$(DESTDIR)$(PREFIX)/bin/qjscalc"
mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs" mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs"
install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs" install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs"
ifdef CONFIG_LTO ifdef CONFIG_LTO
@ -337,22 +383,17 @@ endif
HELLO_SRCS=examples/hello.js HELLO_SRCS=examples/hello.js
HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \
-fno-date -fno-module-loader -fno-bigint -fno-date -fno-module-loader
hello.c: $(QJSC) $(HELLO_SRCS) hello.c: $(QJSC) $(HELLO_SRCS)
$(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS) $(QJSC) -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS)
ifdef CONFIG_M32
examples/hello: $(OBJDIR)/hello.m32s.o $(patsubst %.o, %.m32s.o, $(QJS_LIB_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
else
examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS) examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
endif
# example of static JS compilation with modules # example of static JS compilation with modules
HELLO_MODULE_SRCS=examples/hello_module.js HELLO_MODULE_SRCS=examples/hello_module.js
HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-typedarray \
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \ -fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy \
-fno-date -m -fno-date -m
examples/hello_module: $(QJSC) libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS) examples/hello_module: $(QJSC) libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS)
@ -375,17 +416,20 @@ examples/point.so: $(OBJDIR)/examples/point.pic.o
############################################################################### ###############################################################################
# documentation # documentation
DOCS=doc/quickjs.pdf doc/quickjs.html doc/jsbignum.pdf doc/jsbignum.html DOCS=doc/quickjs.pdf doc/quickjs.html
build_doc: $(DOCS) build_doc: $(DOCS)
clean_doc: clean_doc:
rm -f $(DOCS) rm -f $(DOCS)
doc/%.pdf: doc/%.texi doc/version.texi: VERSION
@echo "@set VERSION `cat $<`" > $@
doc/%.pdf: doc/%.texi doc/version.texi
texi2pdf --clean -o $@ -q $< texi2pdf --clean -o $@ -q $<
doc/%.html.pre: doc/%.texi doc/%.html.pre: doc/%.texi doc/version.texi
makeinfo --html --no-headers --no-split --number-sections -o $@ $< makeinfo --html --no-headers --no-split --number-sections -o $@ $<
doc/%.html: doc/%.html.pre doc/%.html: doc/%.html.pre
@ -397,91 +441,97 @@ doc/%.html: doc/%.html.pre
ifdef CONFIG_SHARED_LIBS ifdef CONFIG_SHARED_LIBS
test: tests/bjson.so examples/point.so test: tests/bjson.so examples/point.so
endif endif
ifdef CONFIG_M32
test: qjs32
endif
test: qjs test: qjs$(EXE)
./qjs tests/test_closure.js $(WINE) ./qjs$(EXE) tests/test_closure.js
./qjs tests/test_language.js $(WINE) ./qjs$(EXE) tests/test_language.js
./qjs tests/test_builtin.js $(WINE) ./qjs$(EXE) --std tests/test_builtin.js
./qjs tests/test_loop.js $(WINE) ./qjs$(EXE) tests/test_loop.js
./qjs tests/test_std.js $(WINE) ./qjs$(EXE) tests/test_bigint.js
./qjs tests/test_worker.js $(WINE) ./qjs$(EXE) tests/test_cyclic_import.js
$(WINE) ./qjs$(EXE) tests/test_worker.js
ifndef CONFIG_WIN32
$(WINE) ./qjs$(EXE) tests/test_std.js
endif
ifdef CONFIG_SHARED_LIBS ifdef CONFIG_SHARED_LIBS
ifdef CONFIG_BIGNUM $(WINE) ./qjs$(EXE) tests/test_bjson.js
./qjs --bignum tests/test_bjson.js $(WINE) ./qjs$(EXE) examples/test_point.js
endif
stats: qjs$(EXE)
$(WINE) ./qjs$(EXE) -qd
microbench: qjs$(EXE)
$(WINE) ./qjs$(EXE) --std tests/microbench.js
ifeq ($(wildcard test262o/tests.txt),)
test2o test2o-update:
@echo test262o tests not installed
else else
./qjs tests/test_bjson.js
endif
./qjs examples/test_point.js
endif
ifdef CONFIG_BIGNUM
./qjs --bignum tests/test_op_overloading.js
./qjs --bignum tests/test_bignum.js
./qjs --qjscalc tests/test_qjscalc.js
endif
ifdef CONFIG_M32
./qjs32 tests/test_closure.js
./qjs32 tests/test_language.js
./qjs32 tests/test_builtin.js
./qjs32 tests/test_loop.js
./qjs32 tests/test_std.js
./qjs32 tests/test_worker.js
ifdef CONFIG_BIGNUM
./qjs32 --bignum tests/test_op_overloading.js
./qjs32 --bignum tests/test_bignum.js
./qjs32 --qjscalc tests/test_qjscalc.js
endif
endif
stats: qjs qjs32
./qjs -qd
./qjs32 -qd
microbench: qjs
./qjs tests/microbench.js
microbench-32: qjs32
./qjs32 tests/microbench.js
# ES5 tests (obsolete) # ES5 tests (obsolete)
test2o: run-test262 test2o: run-test262
time ./run-test262 -m -c test262o.conf time ./run-test262 -t -m -c test262o.conf
test2o-32: run-test262-32
time ./run-test262-32 -m -c test262o.conf
test2o-update: run-test262 test2o-update: run-test262
./run-test262 -u -c test262o.conf ./run-test262 -t -u -c test262o.conf
endif
ifeq ($(wildcard test262/features.txt),)
test2 test2-update test2-default test2-check:
@echo test262 tests not installed
else
# Test262 tests # Test262 tests
test2-default: run-test262 test2-default: run-test262
time ./run-test262 -m -c test262.conf time ./run-test262 -t -m -c test262.conf
test2: run-test262 test2: run-test262
time ./run-test262 -m -c test262.conf -a time ./run-test262 -t -m -c test262.conf -a
test2-32: run-test262-32
time ./run-test262-32 -m -c test262.conf -a
test2-update: run-test262 test2-update: run-test262
./run-test262 -u -c test262.conf -a ./run-test262 -t -u -c test262.conf -a
test2-check: run-test262 test2-check: run-test262
time ./run-test262 -m -c test262.conf -E -a time ./run-test262 -t -m -c test262.conf -E -a
endif
testall: all test microbench test2o test2 testall: all test microbench test2o test2
testall-32: all test-32 microbench-32 test2o-32 test2-32 testall-complete: testall
testall-complete: testall testall-32 node-test:
node tests/test_closure.js
node tests/test_language.js
node tests/test_builtin.js
node tests/test_loop.js
node tests/test_bigint.js
node-microbench:
node tests/microbench.js -s microbench-node.txt
node --jitless tests/microbench.js -s microbench-node-jitless.txt
bench-v8: qjs bench-v8: qjs
make -C tests/bench-v8 make -C tests/bench-v8
./qjs -d tests/bench-v8/combined.js ./qjs -d tests/bench-v8/combined.js
node-bench-v8:
make -C tests/bench-v8
node --jitless tests/bench-v8/combined.js
tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o
$(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS)
BENCHMARKDIR=../quickjs-benchmarks
run_sunspider_like: $(BENCHMARKDIR)/run_sunspider_like.c
$(CC) $(CFLAGS) $(LDFLAGS) -DNO_INCLUDE_DIR -I. -o $@ $< libquickjs$(LTOEXT).a $(LIBS)
run_octane: $(BENCHMARKDIR)/run_octane.c
$(CC) $(CFLAGS) $(LDFLAGS) -DNO_INCLUDE_DIR -I. -o $@ $< libquickjs$(LTOEXT).a $(LIBS)
benchmarks: run_sunspider_like run_octane
./run_sunspider_like $(BENCHMARKDIR)/kraken-1.0/
./run_sunspider_like $(BENCHMARKDIR)/kraken-1.1/
./run_sunspider_like $(BENCHMARKDIR)/sunspider-1.0/
./run_octane $(BENCHMARKDIR)/
-include $(wildcard $(OBJDIR)/*.d) -include $(wildcard $(OBJDIR)/*.d)

14
TODO
View File

@ -37,17 +37,16 @@ REPL:
- close all predefined methods in repl.js and jscalc.js - close all predefined methods in repl.js and jscalc.js
Optimization ideas: Optimization ideas:
- 64-bit atoms in 64-bit mode ? - use 64 bit JSValue in 64 bit mode
- 64-bit small bigint in 64-bit mode ? - use JSValue as atoms and use a specific constant pool in functions to
reference atoms from the bytecode
- reuse stack slots for disjoint scopes, if strip - reuse stack slots for disjoint scopes, if strip
- add heuristic to avoid some cycles in closures - add heuristic to avoid some cycles in closures
- small String (0-2 charcodes) with immediate storage - small String (1 codepoint) with immediate storage
- perform static string concatenation at compile time - perform static string concatenation at compile time
- optimize string concatenation with ropes or miniropes?
- add implicit numeric strings for Uint32 numbers? - add implicit numeric strings for Uint32 numbers?
- optimize `s += a + b`, `s += a.b` and similar simple expressions - optimize `s += a + b`, `s += a.b` and similar simple expressions
- ensure string canonical representation and optimise comparisons and hashes? - ensure string canonical representation and optimise comparisons and hashes?
- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references
- property access optimization on the global object, functions, - property access optimization on the global object, functions,
prototypes and special non extensible objects. prototypes and special non extensible objects.
- create object literals with the correct length by backpatching length argument - create object literals with the correct length by backpatching length argument
@ -63,5 +62,6 @@ Optimization ideas:
Test262o: 0/11262 errors, 463 excluded Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Result: 10/76947 errors, 1497 excluded, 8117 skipped Result: 70/78178 errors, 1610 excluded, 7236 skipped
Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb Test262 commit: 56e77d6325067a545ea7e8ff5be5d9284334e33c

View File

@ -1 +1 @@
2024-01-13 2025-04-26

6
compat/test-closefrom.c Normal file
View File

@ -0,0 +1,6 @@
#include <unistd.h>
int main(void) {
closefrom(3);
return 0;
}

View File

@ -140,7 +140,7 @@ int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
if (dbuf_realloc(s, s->size + len)) if (dbuf_realloc(s, s->size + len))
return -1; return -1;
} }
memcpy(s->buf + s->size, data, len); memcpy_no_ub(s->buf + s->size, data, len);
s->size += len; s->size += len;
return 0; return 0;
} }
@ -176,6 +176,8 @@ int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
va_start(ap, fmt); va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap); len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap); va_end(ap);
if (len < 0)
return -1;
if (len < sizeof(buf)) { if (len < sizeof(buf)) {
/* fast case */ /* fast case */
return dbuf_put(s, (uint8_t *)buf, len); return dbuf_put(s, (uint8_t *)buf, len);

View File

@ -26,11 +26,9 @@
#define CUTILS_H #define CUTILS_H
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <inttypes.h> #include <inttypes.h>
/* set if CPU is big endian */
#undef WORDS_BIGENDIAN
#define likely(x) __builtin_expect(!!(x), 1) #define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0) #define unlikely(x) __builtin_expect(!!(x), 0)
#define force_inline inline __attribute__((always_inline)) #define force_inline inline __attribute__((always_inline))
@ -48,9 +46,16 @@
#ifndef countof #ifndef countof
#define countof(x) (sizeof(x) / sizeof((x)[0])) #define countof(x) (sizeof(x) / sizeof((x)[0]))
#endif #endif
#ifndef container_of
/* return the pointer of type 'type *' containing 'ptr' as field 'member' */ /* return the pointer of type 'type *' containing 'ptr' as field 'member' */
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) #define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
#endif
#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define minimum_length(n) static n
#else
#define minimum_length(n) n
#endif
typedef int BOOL; typedef int BOOL;
@ -66,6 +71,12 @@ char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr); int strstart(const char *str, const char *val, const char **ptr);
int has_suffix(const char *str, const char *suffix); int has_suffix(const char *str, const char *suffix);
/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */
static inline void memcpy_no_ub(void *dest, const void *src, size_t n) {
if (n)
memcpy(dest, src, n);
}
static inline int max_int(int a, int b) static inline int max_int(int a, int b)
{ {
if (a > b) if (a > b)
@ -210,17 +221,22 @@ static inline void put_u8(uint8_t *tab, uint8_t val)
*tab = val; *tab = val;
} }
#ifndef bswap16
static inline uint16_t bswap16(uint16_t x) static inline uint16_t bswap16(uint16_t x)
{ {
return (x >> 8) | (x << 8); return (x >> 8) | (x << 8);
} }
#endif
#ifndef bswap32
static inline uint32_t bswap32(uint32_t v) static inline uint32_t bswap32(uint32_t v)
{ {
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
} }
#endif
#ifndef bswap64
static inline uint64_t bswap64(uint64_t v) static inline uint64_t bswap64(uint64_t v)
{ {
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
@ -232,6 +248,7 @@ static inline uint64_t bswap64(uint64_t v)
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
} }
#endif
/* XXX: should take an extra argument to pass slack information to the caller */ /* XXX: should take an extra argument to pass slack information to the caller */
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
@ -281,6 +298,36 @@ static inline void dbuf_set_error(DynBuf *s)
int unicode_to_utf8(uint8_t *buf, unsigned int c); int unicode_to_utf8(uint8_t *buf, unsigned int c);
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp); int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
static inline BOOL is_surrogate(uint32_t c)
{
return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF
}
static inline BOOL is_hi_surrogate(uint32_t c)
{
return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF
}
static inline BOOL is_lo_surrogate(uint32_t c)
{
return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF
}
static inline uint32_t get_hi_surrogate(uint32_t c)
{
return (c >> 10) - (0x10000 >> 10) + 0xD800;
}
static inline uint32_t get_lo_surrogate(uint32_t c)
{
return (c & 0x3FF) | 0xDC00;
}
static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo)
{
return 0x10000 + 0x400 * (hi - 0xD800) + (lo - 0xDC00);
}
static inline int from_hex(int c) static inline int from_hex(int c)
{ {
if (c >= '0' && c <= '9') if (c >= '0' && c <= '9')
@ -297,4 +344,24 @@ void rqsort(void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *, void *), int (*cmp)(const void *, const void *, void *),
void *arg); void *arg);
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;
}
#endif /* CUTILS_H */ #endif /* CUTILS_H */

View File

@ -1,589 +0,0 @@
\input texinfo
@iftex
@afourpaper
@headings double
@end iftex
@titlepage
@afourpaper
@sp 7
@center @titlefont{Javascript Bignum Extensions}
@sp 3
@center Version 2020-01-11
@sp 3
@center Author: Fabrice Bellard
@end titlepage
@setfilename jsbignum.info
@settitle Javascript Bignum Extensions
@contents
@chapter Introduction
The Bignum extensions add the following features to the Javascript
language while being 100% backward compatible:
@itemize
@item Operator overloading with a dispatch logic inspired from the proposal available at @url{https://github.com/tc39/proposal-operator-overloading/}.
@item Arbitrarily large floating point numbers (@code{BigFloat}) in base 2 using the IEEE 754 semantics.
@item Arbitrarily large floating point numbers (@code{BigDecimal}) in base 10 based on the proposal available at
@url{https://github.com/littledan/proposal-bigdecimal}.
@item @code{math} mode: arbitrarily large integers and floating point numbers are available by default. The integer division and power can be overloaded for example to return a fraction. The modulo operator (@code{%}) is defined as the Euclidian
remainder. @code{^} is an alias to the power operator
(@code{**}). @code{^^} is used as the exclusive or operator.
@end itemize
The extensions are independent from each other except the @code{math}
mode which relies on BigFloat and operator overloading.
@chapter Operator overloading
Operator overloading is inspired from the proposal available at
@url{https://github.com/tc39/proposal-operator-overloading/}. It
implements the same dispatch logic but finds the operator sets by
looking at the @code{Symbol.operatorSet} property in the objects. The
changes were done in order to simplify the implementation.
More precisely, the following modifications were made:
@itemize
@item @code{with operators from} is not supported. Operator overloading is always enabled.
@item The dispatch is not based on a static @code{[[OperatorSet]]} field in all instances. Instead, a dynamic lookup of the @code{Symbol.operatorSet} property is done. This property is typically added in the prototype of each object.
@item @code{Operators.create(...dictionaries)} is used to create a new OperatorSet object. The @code{Operators} function is supported as an helper to be closer to the TC39 proposal.
@item @code{[]} cannot be overloaded.
@item In math mode, the BigInt division and power operators can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}.
@end itemize
@chapter BigInt extensions
A few properties are added to the BigInt object:
@table @code
@item tdiv(a, b)
Return @math{trunc(a/b)}. @code{b = 0} raises a RangeError
exception.
@item fdiv(a, b)
Return @math{\lfloor a/b \rfloor}. @code{b = 0} raises a RangeError
exception.
@item cdiv(a, b)
Return @math{\lceil a/b \rceil}. @code{b = 0} raises a RangeError
exception.
@item ediv(a, b)
Return @math{sgn(b) \lfloor a/{|b|} \rfloor} (Euclidian
division). @code{b = 0} raises a RangeError exception.
@item tdivrem(a, b)
@item fdivrem(a, b)
@item cdivrem(a, b)
@item edivrem(a, b)
Return an array of two elements. The first element is the quotient,
the second is the remainder. The same rounding is done as the
corresponding division operation.
@item sqrt(a)
Return @math{\lfloor \sqrt(a) \rfloor}. A RangeError exception is
raised if @math{a < 0}.
@item sqrtrem(a)
Return an array of two elements. The first element is @math{\lfloor
\sqrt{a} \rfloor}. The second element is @math{a-\lfloor \sqrt{a}
\rfloor^2}. A RangeError exception is raised if @math{a < 0}.
@item floorLog2(a)
Return -1 if @math{a \leq 0} otherwise return @math{\lfloor \log2(a) \rfloor}.
@item ctz(a)
Return the number of trailing zeros in the two's complement binary representation of a. Return -1 if @math{a=0}.
@end table
@chapter BigFloat
@section Introduction
This extension adds the @code{BigFloat} primitive type. The
@code{BigFloat} type represents floating point numbers in base 2
with the IEEE 754 semantics. A floating
point number is represented as a sign, mantissa and exponent. The
special values @code{NaN}, @code{+/-Infinity}, @code{+0} and @code{-0}
are supported. The mantissa and exponent can have any bit length with
an implementation specific minimum and maximum.
@section Floating point rounding
Each floating point operation operates with infinite precision and
then rounds the result according to the specified floating point
environment (@code{BigFloatEnv} object). The status flags of the
environment are also set according to the result of the operation.
If no floating point environment is provided, the global floating
point environment is used.
The rounding mode of the global floating point environment is always
@code{RNDN} (``round to nearest with ties to even'')@footnote{The
rationale is that the rounding mode changes must always be
explicit.}. The status flags of the global environment cannot be
read@footnote{The rationale is to avoid side effects for the built-in
operators.}. The precision of the global environment is
@code{BigFloatEnv.prec}. The number of exponent bits of the global
environment is @code{BigFloatEnv.expBits}. The global environment
subnormal flag is set to @code{true}.
For example, @code{prec = 53} and @code{ expBits = 11} exactly give
the same precision as the IEEE 754 64 bit floating point format. The
default precision is @code{prec = 113} and @code{ expBits = 15} (IEEE
754 128 bit floating point format).
The global floating point environment can only be modified temporarily
when calling a function (see @code{BigFloatEnv.setPrec}). Hence a
function can change the global floating point environment for its
callees but not for its caller.
@section Operators
The builtin operators are extended so that a BigFloat is returned if
at least one operand is a BigFloat. The computations are always done
with infinite precision and rounded according to the global floating
point environment.
@code{typeof} applied on a @code{BigFloat} returns @code{bigfloat}.
BigFloat can be compared with all the other numeric types and the
result follows the expected mathematical relations.
However, since BigFloat and Number are different types they are never
equal when using the strict comparison operators (e.g. @code{0.0 ===
0.0l} is false).
@section BigFloat literals
BigFloat literals are floating point numbers with a trailing @code{l}
suffix. BigFloat literals have an infinite precision. They are rounded
according to the global floating point environment when they are
evaluated.@footnote{Base 10 floating point literals cannot usually be
exactly represented as base 2 floating point number. In order to
ensure that the literal is represented accurately with the current
precision, it must be evaluated at runtime.}
@section Builtin Object changes
@subsection @code{BigFloat} function
The @code{BigFloat} function cannot be invoked as a constructor. When
invoked as a function: the parameter is converted to a primitive
type. If the result is a numeric type, it is converted to BigFloat
without rounding. If the result is a string, it is converted to
BigFloat using the precision of the global floating point environment.
@code{BigFloat} properties:
@table @code
@item LN2
@item PI
Getter. Return the value of the corresponding mathematical constant
rounded to nearest, ties to even with the current global
precision. The constant values are cached for small precisions.
@item MIN_VALUE
@item MAX_VALUE
@item EPSILON
Getter. Return the minimum, maximum and epsilon @code{BigFloat} values
(same definition as the corresponding @code{Number} constants).
@item fpRound(a[, e])
Round the floating point number @code{a} according to the floating
point environment @code{e} or the global environment if @code{e} is
undefined.
@item parseFloat(a[, radix[, e]])
Parse the string @code{a} as a floating point number in radix
@code{radix}. The radix is 0 (default) or from 2 to 36. The radix 0
means radix 10 unless there is a hexadecimal or binary prefix. The
result is rounded according to the floating point environment @code{e}
or the global environment if @code{e} is undefined.
@item isFinite(a)
Return true if @code{a} is a finite bigfloat.
@item isNaN(a)
Return true if @code{a} is a NaN bigfloat.
@item add(a, b[, e])
@item sub(a, b[, e])
@item mul(a, b[, e])
@item div(a, b[, e])
Perform the specified floating point operation and round the floating
point number @code{a} according to the floating point environment
@code{e} or the global environment if @code{e} is undefined. If
@code{e} is specified, the floating point status flags are updated.
@item floor(x)
@item ceil(x)
@item round(x)
@item trunc(x)
Round to an integer. No additional rounding is performed.
@item abs(x)
Return the absolute value of x. No additional rounding is performed.
@item fmod(x, y[, e])
@item remainder(x, y[, e])
Floating point remainder. The quotient is truncated to zero (fmod) or
to the nearest integer with ties to even (remainder). @code{e} is an
optional floating point environment.
@item sqrt(x[, e])
Square root. Return a rounded floating point number. @code{e} is an
optional floating point environment.
@item sin(x[, e])
@item cos(x[, e])
@item tan(x[, e])
@item asin(x[, e])
@item acos(x[, e])
@item atan(x[, e])
@item atan2(x, y[, e])
@item exp(x[, e])
@item log(x[, e])
@item pow(x, y[, e])
Transcendental operations. Return a rounded floating point
number. @code{e} is an optional floating point environment.
@end table
@subsection @code{BigFloat.prototype}
The following properties are modified:
@table @code
@item valueOf()
Return the bigfloat primitive value corresponding to @code{this}.
@item toString(radix)
For floating point numbers:
@itemize
@item
If the radix is a power of two, the conversion is done with infinite
precision.
@item
Otherwise, the number is rounded to nearest with ties to even using
the global precision. It is then converted to string using the minimum
number of digits so that its conversion back to a floating point using
the global precision and round to nearest gives the same number.
@end itemize
The exponent letter is @code{e} for base 10, @code{p} for bases 2, 8,
16 with a binary exponent and @code{@@} for the other bases.
@item toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
@item toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
@item toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)
Same semantics as the corresponding @code{Number} functions with
BigFloats. There is no limit on the accepted precision @code{p}. The
rounding mode and radix can be optionally specified. The radix must be
between 2 and 36.
@end table
@subsection @code{BigFloatEnv} constructor
The @code{BigFloatEnv([p, [,rndMode]]} constructor cannot be invoked as a
function. The floating point environment contains:
@itemize
@item the mantissa precision in bits
@item the exponent size in bits assuming an IEEE 754 representation;
@item the subnormal flag (if true, subnormal floating point numbers can
be generated by the floating point operations).
@item the rounding mode
@item the floating point status. The status flags can only be set by the floating point operations. They can be reset with @code{BigFloatEnv.prototype.clearStatus()} or with the various status flag setters.
@end itemize
@code{new BigFloatEnv([p, [,rndMode]]} creates a new floating point
environment. The status flags are reset. If no parameter is given the
precision, exponent bits and subnormal flags are copied from the
global floating point environment. Otherwise, the precision is set to
@code{p}, the number of exponent bits is set to @code{expBitsMax} and the
subnormal flags is set to @code{false}. If @code{rndMode} is
@code{undefined}, the rounding mode is set to @code{RNDN}.
@code{BigFloatEnv} properties:
@table @code
@item prec
Getter. Return the precision in bits of the global floating point
environment. The initial value is @code{113}.
@item expBits
Getter. Return the exponent size in bits of the global floating point
environment assuming an IEEE 754 representation. The initial value is
@code{15}.
@item setPrec(f, p[, e])
Set the precision of the global floating point environment to @code{p}
and the exponent size to @code{e} then call the function
@code{f}. Then the Float precision and exponent size are reset to
their precious value and the return value of @code{f} is returned (or
an exception is raised if @code{f} raised an exception). If @code{e}
is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}.
@item precMin
Read-only integer. Return the minimum allowed precision. Must be at least 2.
@item precMax
Read-only integer. Return the maximum allowed precision. Must be at least 113.
@item expBitsMin
Read-only integer. Return the minimum allowed exponent size in
bits. Must be at least 3.
@item expBitsMax
Read-only integer. Return the maximum allowed exponent size in
bits. Must be at least 15.
@item RNDN
Read-only integer. Round to nearest, with ties to even rounding mode.
@item RNDZ
Read-only integer. Round to zero rounding mode.
@item RNDD
Read-only integer. Round to -Infinity rounding mode.
@item RNDU
Read-only integer. Round to +Infinity rounding mode.
@item RNDNA
Read-only integer. Round to nearest, with ties away from zero rounding mode.
@item RNDA
Read-only integer. Round away from zero rounding mode.
@item RNDF@footnote{Could be removed in case a deterministic behavior for floating point operations is required.}
Read-only integer. Faithful rounding mode. The result is
non-deterministically rounded to -Infinity or +Infinity. This rounding
mode usually gives a faster and deterministic running time for the
floating point operations.
@end table
@code{BigFloatEnv.prototype} properties:
@table @code
@item prec
Getter and setter (Integer). Return or set the precision in bits.
@item expBits
Getter and setter (Integer). Return or set the exponent size in bits
assuming an IEEE 754 representation.
@item rndMode
Getter and setter (Integer). Return or set the rounding mode.
@item subnormal
Getter and setter (Boolean). subnormal flag. It is false when
@code{expBits = expBitsMax}.
@item clearStatus()
Clear the status flags.
@item invalidOperation
@item divideByZero
@item overflow
@item underflow
@item inexact
Getter and setter (Boolean). Status flags.
@end table
@chapter BigDecimal
This extension adds the @code{BigDecimal} primitive type. The
@code{BigDecimal} type represents floating point numbers in base
10. It is inspired from the proposal available at
@url{https://github.com/littledan/proposal-bigdecimal}.
The @code{BigDecimal} floating point numbers are always normalized and
finite. There is no concept of @code{-0}, @code{Infinity} or
@code{NaN}. By default, all the computations are done with infinite
precision.
@section Operators
The following builtin operators support BigDecimal:
@table @code
@item +
@item -
@item *
Both operands must be BigDecimal. The result is computed with infinite
precision.
@item %
Both operands must be BigDecimal. The result is computed with infinite
precision. A range error is throws in case of division by zero.
@item /
Both operands must be BigDecimal. A range error is throws in case of
division by zero or if the result cannot be represented with infinite
precision (use @code{BigDecimal.div} to specify the rounding).
@item **
Both operands must be BigDecimal. The exponent must be a positive
integer. The result is computed with infinite precision.
@item ===
When one of the operand is a BigDecimal, return true if both operands
are a BigDecimal and if they are equal.
@item ==
@item !=
@item <=
@item >=
@item <
@item >
Numerical comparison. When one of the operand is not a BigDecimal, it is
converted to BigDecimal by using ToString(). Hence comparisons between
Number and BigDecimal do not use the exact mathematical value of the
Number value.
@end table
@section BigDecimal literals
BigDecimal literals are decimal floating point numbers with a trailing
@code{m} suffix.
@section Builtin Object changes
@subsection The @code{BigDecimal} function.
It returns @code{0m} if no parameter is provided. Otherwise the first
parameter is converted to a bigdecimal by using ToString(). Hence
Number values are not converted to their exact numerical value as
BigDecimal.
@subsection Properties of the @code{BigDecimal} object
@table @code
@item add(a, b[, e])
@item sub(a, b[, e])
@item mul(a, b[, e])
@item div(a, b[, e])
@item mod(a, b[, e])
@item sqrt(a, e)
@item round(a, e)
Perform the specified floating point operation and round the floating
point result according to the rounding object @code{e}. If the
rounding object is not present, the operation is executed with
infinite precision.
For @code{div}, a @code{RangeError} exception is thrown in case of
division by zero or if the result cannot be represented with infinite
precision if no rounding object is present.
For @code{sqrt}, a range error is thrown if @code{a} is less than
zero.
The rounding object must contain the following properties:
@code{roundingMode} is a string specifying the rounding mode
(@code{"floor"}, @code{"ceiling"}, @code{"down"}, @code{"up"},
@code{"half-even"}, @code{"half-up"}). Either
@code{maximumSignificantDigits} or @code{maximumFractionDigits} must
be present to specify respectively the number of significant digits
(must be >= 1) or the number of digits after the decimal point (must
be >= 0).
@end table
@subsection Properties of the @code{BigDecimal.prototype} object
@table @code
@item valueOf()
Return the bigdecimal primitive value corresponding to @code{this}.
@item toString()
Convert @code{this} to a string with infinite precision in base 10.
@item toPrecision(p, rnd_mode = "half-up")
@item toFixed(p, rnd_mode = "half-up")
@item toExponential(p, rnd_mode = "half-up")
Convert the BigDecimal @code{this} to string with the specified
precision @code{p}. There is no limit on the accepted precision
@code{p}. The rounding mode can be optionally
specified. @code{toPrecision} outputs either in decimal fixed notation
or in decimal exponential notation with a @code{p} digits of
precision. @code{toExponential} outputs in decimal exponential
notation with @code{p} digits after the decimal point. @code{toFixed}
outputs in decimal notation with @code{p} digits after the decimal
point.
@end table
@chapter Math mode
A new @emph{math mode} is enabled with the @code{"use math"}
directive. It propagates the same way as the @emph{strict mode}. It is
designed so that arbitrarily large integers and floating point numbers
are available by default. In order to minimize the number of changes
in the Javascript semantics, integers are represented either as Number
or BigInt depending on their magnitude. Floating point numbers are
always represented as BigFloat.
The following changes are made to the Javascript semantics:
@itemize
@item Floating point literals (i.e. number with a decimal point or an exponent) are @code{BigFloat} by default (i.e. a @code{l} suffix is implied). Hence @code{typeof 1.0 === "bigfloat"}.
@item Integer literals (i.e. numbers without a decimal point or an exponent) with or without the @code{n} suffix are @code{BigInt} if their value cannot be represented as a safe integer. A safe integer is defined as a integer whose absolute value is smaller or equal to @code{2**53-1}. Hence @code{typeof 1 === "number "}, @code{typeof 1n === "number"} but @code{typeof 9007199254740992 === "bigint" }.
@item All the bigint builtin operators and functions are modified so that their result is returned as a Number if it is a safe integer. Otherwise the result stays a BigInt.
@item The builtin operators are modified so that they return an exact result (which can be a BigInt) if their operands are safe integers. Operands between Number and BigInt are accepted provided the Number operand is a safe integer. The integer power with a negative exponent returns a BigFloat as result. The integer division returns a BigFloat as result.
@item The @code{^} operator is an alias to the power operator (@code{**}).
@item The power operator (both @code{^} and @code{**}) grammar is modified so that @code{-2^2} is allowed and yields @code{-4}.
@item The logical xor operator is still available with the @code{^^} operator.
@item The modulo operator (@code{%}) returns the Euclidian remainder (always positive) instead of the truncated remainder.
@item The integer division operator can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}.
@item The integer power operator with a non zero negative exponent can be overloaded with @code{Operators.updateBigIntOperators(dictionary)}.
@end itemize
@bye

View File

@ -5,11 +5,14 @@
@headings double @headings double
@end iftex @end iftex
@include version.texi
@titlepage @titlepage
@afourpaper @afourpaper
@sp 7 @sp 7
@center @titlefont{QuickJS Javascript Engine} @center @titlefont{QuickJS Javascript Engine}
@sp 3 @sp 3
@center Version: @value{VERSION}
@end titlepage @end titlepage
@setfilename spec.info @setfilename spec.info
@ -19,14 +22,10 @@
@chapter Introduction @chapter Introduction
QuickJS is a small and embeddable Javascript engine. It supports most of the QuickJS (version @value{VERSION}) is a small and embeddable Javascript
ES2023 specification engine. It supports most of the ES2023 specification
@footnote{@url{https://tc39.es/ecma262/2023 }} @footnote{@url{https://tc39.es/ecma262/2023 }} including modules,
including modules, asynchronous generators, proxies and BigInt. asynchronous generators, proxies and BigInt.
It supports mathematical extensions such as big decimal float float
numbers (BigDecimal), big binary floating point numbers (BigFloat),
and operator overloading.
@section Main Features @section Main Features
@ -47,8 +46,6 @@ features from the upcoming ES2024 specification
@item Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal. @item Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal.
@item Mathematical extensions: BigDecimal, BigFloat, operator overloading, bigint mode, math mode.
@item Command line interpreter with contextual colorization and completion implemented in Javascript. @item Command line interpreter with contextual colorization and completion implemented in Javascript.
@item Small built-in standard library with C library wrappers. @item Small built-in standard library with C library wrappers.
@ -123,10 +120,6 @@ source is @code{import}.
@item --script @item --script
Load as ES6 script (default=autodetect). Load as ES6 script (default=autodetect).
@item --bignum
Enable the bignum extensions: BigDecimal object, BigFloat object and
the @code{"use math"} directive.
@item -I file @item -I file
@item --include file @item --include file
Include an additional file. Include an additional file.
@ -193,21 +186,8 @@ when the @code{-fno-x} options are used.
@item -fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint] @item -fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint]
Disable selected language features to produce a smaller executable file. Disable selected language features to produce a smaller executable file.
@item -fbignum
Enable the bignum extensions: BigDecimal object, BigFloat object and
the @code{"use math"} directive.
@end table @end table
@section @code{qjscalc} application
The @code{qjscalc} application is a superset of the @code{qjs}
command line interpreter implementing a Javascript calculator with
arbitrarily large integer and floating point numbers, fractions,
complex numbers, polynomials and matrices. The source code is in
@file{qjscalc.js}. More documentation and a web version are available at
@url{http://numcalc.com}.
@section Built-in tests @section Built-in tests
Run @code{make test} to run the few built-in tests included in the Run @code{make test} to run the few built-in tests included in the
@ -281,45 +261,12 @@ The following features are not supported yet:
@item Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.} @item Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.}
@item WeakRef and FinalizationRegistry objects
@item Symbols as WeakMap keys
@end itemize @end itemize
@subsection ECMA402 @subsection ECMA402
ECMA402 (Internationalization API) is not supported. ECMA402 (Internationalization API) is not supported.
@subsection Extensions
@itemize
@item The directive @code{"use strip"} indicates that the debug information (including the source code of the functions) should not be retained to save memory. As @code{"use strict"}, the directive can be global to a script or local to a function.
@item The first line of a script beginning with @code{#!} is ignored.
@end itemize
@subsection Mathematical extensions
The mathematical extensions are fully backward compatible with
standard Javascript. See @code{jsbignum.pdf} for more information.
@itemize
@item @code{BigDecimal} support: arbitrary large floating point numbers in base 10.
@item @code{BigFloat} support: arbitrary large floating point numbers in base 2.
@item Operator overloading.
@item The directive @code{"use bigint"} enables the bigint mode where integers are @code{BigInt} by default.
@item The directive @code{"use math"} enables the math mode where the division and power operators on integers produce fractions. Floating point literals are @code{BigFloat} by default and integers are @code{BigInt} by default.
@end itemize
@section Modules @section Modules
ES6 modules are fully supported. The default name resolution is the ES6 modules are fully supported. The default name resolution is the
@ -379,7 +326,9 @@ optional properties:
stack frames below the evalScript. stack frames below the evalScript.
@item async @item async
Boolean (default = false). If true, @code{await} is accepted in the Boolean (default = false). If true, @code{await} is accepted in the
script and a promise is returned. script and a promise is returned. The promise is resolved with an
object whose @code{value} property holds the value returned by the
script.
@end table @end table
@item loadScript(filename) @item loadScript(filename)
@ -1103,12 +1052,11 @@ binary properties.
The full Unicode library weights about 45 KiB (x86 code). The full Unicode library weights about 45 KiB (x86 code).
@section BigInt, BigFloat, BigDecimal @section BigInt
BigInt, BigFloat and BigDecimal are implemented with the @code{libbf} BigInts are represented using binary two's complement notation. An
library@footnote{@url{https://bellard.org/libbf}}. It weights about 90 additional short bigint value is used to optimize the performance on
KiB (x86 code) and provides arbitrary precision IEEE 754 floating small BigInt values.
point operations and transcendental functions with exact rounding.
@chapter License @chapter License

1620
dtoa.c Normal file

File diff suppressed because it is too large Load Diff

83
dtoa.h Normal file
View File

@ -0,0 +1,83 @@
/*
* Tiny float64 printing and parsing 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.
*/
//#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);

View File

@ -1,68 +0,0 @@
/*
* PI computation in Javascript using the QuickJS bigdecimal type
* (decimal floating point)
*/
"use strict";
/* compute PI with a precision of 'prec' digits */
function calc_pi(prec) {
const CHUD_A = 13591409m;
const CHUD_B = 545140134m;
const CHUD_C = 640320m;
const CHUD_C3 = 10939058860032000m; /* C^3/24 */
const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(C/12)*3 */
/* return [P, Q, G] */
function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1;
if (a == (b - 1n)) {
b1 = BigDecimal(b);
G = (2m * b1 - 1m) * (6m * b1 - 1m) * (6m * b1 - 5m);
P = G * (CHUD_B * b1 + CHUD_A);
if (b & 1n)
P = -P;
G = G;
Q = b1 * b1 * b1 * CHUD_C3;
} else {
c = (a + b) >> 1n;
[P1, Q1, G1] = chud_bs(a, c, true);
[P2, Q2, G2] = chud_bs(c, b, need_G);
P = P1 * Q2 + P2 * G1;
Q = Q1 * Q2;
if (need_G)
G = G1 * G2;
else
G = 0m;
}
return [P, Q, G];
}
var n, P, Q, G;
/* number of serie terms */
n = BigInt(Math.ceil(prec / CHUD_DIGITS_PER_TERM)) + 10n;
[P, Q, G] = chud_bs(0n, n, false);
Q = BigDecimal.div(Q, (P + Q * CHUD_A),
{ roundingMode: "half-even",
maximumSignificantDigits: prec });
G = (CHUD_C / 12m) * BigDecimal.sqrt(CHUD_C,
{ roundingMode: "half-even",
maximumSignificantDigits: prec });
return Q * G;
}
(function() {
var r, n_digits, n_bits;
if (typeof scriptArgs != "undefined") {
if (scriptArgs.length < 2) {
print("usage: pi n_digits");
return;
}
n_digits = scriptArgs[1] | 0;
} else {
n_digits = 1000;
}
/* we add more digits to reduce the probability of bad rounding for
the last digits */
r = calc_pi(n_digits + 20);
print(r.toFixed(n_digits, "down"));
})();

View File

@ -1,66 +0,0 @@
/*
* PI computation in Javascript using the QuickJS bigfloat type
* (binary floating point)
*/
"use strict";
/* compute PI with a precision of 'prec' bits */
function calc_pi() {
const CHUD_A = 13591409n;
const CHUD_B = 545140134n;
const CHUD_C = 640320n;
const CHUD_C3 = 10939058860032000n; /* C^3/24 */
const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
/* return [P, Q, G] */
function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
if (a == (b - 1n)) {
G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
P = BigFloat(G * (CHUD_B * b + CHUD_A));
if (b & 1n)
P = -P;
G = BigFloat(G);
Q = BigFloat(b * b * b * CHUD_C3);
} else {
c = (a + b) >> 1n;
[P1, Q1, G1] = chud_bs(a, c, true);
[P2, Q2, G2] = chud_bs(c, b, need_G);
P = P1 * Q2 + P2 * G1;
Q = Q1 * Q2;
if (need_G)
G = G1 * G2;
else
G = 0l;
}
return [P, Q, G];
}
var n, P, Q, G;
/* number of serie terms */
n = BigInt(Math.ceil(BigFloatEnv.prec / CHUD_BITS_PER_TERM)) + 10n;
[P, Q, G] = chud_bs(0n, n, false);
Q = Q / (P + Q * BigFloat(CHUD_A));
G = BigFloat((CHUD_C / 12n)) * BigFloat.sqrt(BigFloat(CHUD_C));
return Q * G;
}
(function() {
var r, n_digits, n_bits;
if (typeof scriptArgs != "undefined") {
if (scriptArgs.length < 2) {
print("usage: pi n_digits");
return;
}
n_digits = scriptArgs[1];
} else {
n_digits = 1000;
}
n_bits = Math.ceil(n_digits * Math.log2(10));
/* we add more bits to reduce the probability of bad rounding for
the last digits */
BigFloatEnv.setPrec( () => {
r = calc_pi();
print(r.toFixed(n_digits, BigFloatEnv.RNDZ));
}, n_bits + 32);
})();

27
fuzz/README Normal file
View File

@ -0,0 +1,27 @@
libFuzzer support for QuickJS
=============================
Build QuickJS with libFuzzer support as follows:
CONFIG_CLANG=y make libfuzzer
This can be extended with sanitizer support to improve efficacy:
CONFIG_CLANG=y CONFIG_ASAN=y make libfuzzer
Currently, there are three fuzzing targets defined: fuzz_eval, fuzz_compile and fuzz_regexp.
The above build command will produce an executable binary for each of them, which can be
simply executed as:
./fuzz_eval
or with an initial corpus:
./fuzz_compile corpus_dir/
or with a predefined dictionary to improve its efficacy:
./fuzz_eval -dict fuzz/fuzz.dict
or with arbitrary CLI arguments provided by libFuzzer (https://llvm.org/docs/LibFuzzer.html).

254
fuzz/fuzz.dict Normal file
View File

@ -0,0 +1,254 @@
"__loadScript"
"abs"
"acos"
"acosh"
"add"
"AggregateError"
"and"
"apply"
"Array"
"ArrayBuffer"
"asin"
"asinh"
"atan"
"atan2"
"atanh"
"Atomics"
"BigInt"
"BigInt64Array"
"BigUint64Array"
"Boolean"
"cbrt"
"ceil"
"chdir"
"clearTimeout"
"close"
"clz32"
"compareExchange"
"console"
"construct"
"cos"
"cosh"
"DataView"
"Date"
"decodeURI"
"decodeURIComponent"
"defineProperty"
"deleteProperty"
"dup"
"dup2"
"E"
"encodeURI"
"encodeURIComponent"
"err"
"Error"
"escape"
"eval"
"EvalError"
"evalScript"
"exchange"
"exec"
"exit"
"exp"
"expm1"
"fdopen"
"Float32Array"
"Float64Array"
"floor"
"fround"
"Function"
"gc"
"get"
"getcwd"
"getenv"
"getenviron"
"getOwnPropertyDescriptor"
"getpid"
"getPrototypeOf"
"globalThis"
"has"
"hypot"
"imul"
"in"
"Infinity"
"Int16Array"
"Int32Array"
"Int8Array"
"InternalError"
"isatty"
"isExtensible"
"isFinite"
"isLockFree"
"isNaN"
"iterateBuiltIns"
"JSON"
"kill"
"length"
"LN10"
"LN2"
"load"
"loadFile"
"loadScript"
"log"
"log10"
"LOG10E"
"log1p"
"log2"
"LOG2E"
"lstat"
"Map"
"Math"
"max"
"min"
"mkdir"
"NaN"
"notify"
"now"
"Number"
"O_APPEND"
"O_CREAT"
"O_EXCL"
"O_RDONLY"
"O_RDWR"
"O_TRUNC"
"O_WRONLY"
"Object"
"open"
"Operators"
"or"
"os"
"out"
"ownKeys"
"parse"
"parseExtJSON"
"parseFloat"
"parseInt"
"PI"
"pipe"
"platform"
"popen"
"pow"
"preventExtensions"
"print"
"printf"
"Promise"
"Proxy"
"puts"
"random"
"RangeError"
"read"
"readdir"
"readlink"
"realpath"
"ReferenceError"
"Reflect"
"RegExp"
"remove"
"rename"
"round"
"S_IFBLK"
"S_IFCHR"
"S_IFDIR"
"S_IFIFO"
"S_IFLNK"
"S_IFMT"
"S_IFREG"
"S_IFSOCK"
"S_ISGID"
"S_ISUID"
"scriptArgs"
"seek"
"SEEK_CUR"
"SEEK_END"
"SEEK_SET"
"set"
"Set"
"setenv"
"setPrototypeOf"
"setReadHandler"
"setTimeout"
"setWriteHandler"
"SharedArrayBuffer"
"SIGABRT"
"SIGALRM"
"SIGCHLD"
"SIGCONT"
"SIGFPE"
"SIGILL"
"SIGINT"
"sign"
"signal"
"SIGPIPE"
"SIGQUIT"
"SIGSEGV"
"SIGSTOP"
"SIGTERM"
"SIGTSTP"
"SIGTTIN"
"SIGTTOU"
"SIGUSR1"
"SIGUSR2"
"sin"
"sinh"
"sleep"
"sleepAsync"
"sprintf"
"sqrt"
"SQRT1_2"
"SQRT2"
"stat"
"std"
"store"
"strerror"
"String"
"stringify"
"sub"
"Symbol"
"symlink"
"SyntaxError"
"tan"
"tanh"
"tmpfile"
"trunc"
"ttyGetWinSize"
"ttySetRaw"
"TypeError"
"Uint16Array"
"Uint32Array"
"Uint8Array"
"Uint8ClampedArray"
"undefined"
"unescape"
"unsetenv"
"URIError"
"urlGet"
"utimes"
"wait"
"waitpid"
"WeakMap"
"WeakSet"
"WNOHANG"
"Worker"
"write"
"xor"
"v0"
"v1"
"v2"
"v3"
"v4"
"v5"
"v6"
"v7"
"v8"
"v9"
"v10"
"v11"
"v12"
"v13"
"v14"
"v15"
"v16"
"v17"
"v18"
"v19"
"v20"

58
fuzz/fuzz_common.c Normal file
View File

@ -0,0 +1,58 @@
/* Copyright 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <string.h>
#include "fuzz/fuzz_common.h"
// handle timeouts from infinite loops
static int interrupt_handler(JSRuntime *rt, void *opaque)
{
nbinterrupts++;
return (nbinterrupts > 100);
}
void reset_nbinterrupts() {
nbinterrupts = 0;
}
void test_one_input_init(JSRuntime *rt, JSContext *ctx) {
// 64 Mo
JS_SetMemoryLimit(rt, 0x4000000);
// 64 Kb
JS_SetMaxStackSize(rt, 0x10000);
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL);
js_std_add_helpers(ctx, 0, NULL);
// Load os and std
js_std_init_handlers(rt);
js_init_module_std(ctx, "std");
js_init_module_os(ctx, "os");
const char *str = "import * as std from 'std';\n"
"import * as os from 'os';\n"
"globalThis.std = std;\n"
"globalThis.os = os;\n";
JSValue std_val = JS_Eval(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
if (!JS_IsException(std_val)) {
js_module_set_import_meta(ctx, std_val, 1, 1);
std_val = JS_EvalFunction(ctx, std_val);
} else {
js_std_dump_error(ctx);
}
std_val = js_std_await(ctx, std_val);
JS_FreeValue(ctx, std_val);
}

22
fuzz/fuzz_common.h Normal file
View File

@ -0,0 +1,22 @@
/* Copyright 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "quickjs.h"
#include "quickjs-libc.h"
static int nbinterrupts = 0;
void reset_nbinterrupts();
void test_one_input_init(JSRuntime *rt, JSContext *ctx);

93
fuzz/fuzz_compile.c Normal file
View File

@ -0,0 +1,93 @@
/* Copyright 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "quickjs.h"
#include "quickjs-libc.h"
#include "cutils.h"
#include "fuzz/fuzz_common.h"
#include <stdint.h>
#include <stdio.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size == 0)
return 0;
JSRuntime *rt = JS_NewRuntime();
JSContext *ctx = JS_NewContext(rt);
test_one_input_init(rt, ctx);
uint8_t *null_terminated_data = malloc(size + 1);
memcpy(null_terminated_data, data, size);
null_terminated_data[size] = 0;
JSValue obj = JS_Eval(ctx, (const char *)null_terminated_data, size, "<none>", JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_TYPE_MODULE);
free(null_terminated_data);
//TODO target with JS_ParseJSON
if (JS_IsException(obj)) {
js_std_free_handlers(rt);
JS_FreeValue(ctx, obj);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 0;
}
obj = js_std_await(ctx, obj);
size_t bytecode_size;
uint8_t* bytecode = JS_WriteObject(ctx, &bytecode_size, obj, JS_WRITE_OBJ_BYTECODE);
JS_FreeValue(ctx, obj);
if (!bytecode) {
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 0;
}
obj = JS_ReadObject(ctx, bytecode, bytecode_size, JS_READ_OBJ_BYTECODE);
js_free(ctx, bytecode);
if (JS_IsException(obj)) {
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 0;
}
reset_nbinterrupts();
/* this is based on
* js_std_eval_binary(ctx, bytecode, bytecode_size, 0);
* modified so as not to exit on JS exception
*/
JSValue val;
if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
if (JS_ResolveModule(ctx, obj) < 0) {
JS_FreeValue(ctx, obj);
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 0;
}
js_module_set_import_meta(ctx, obj, FALSE, TRUE);
}
val = JS_EvalFunction(ctx, obj);
if (JS_IsException(val)) {
js_std_dump_error(ctx);
} else {
js_std_loop(ctx);
}
JS_FreeValue(ctx, val);
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 0;
}

49
fuzz/fuzz_eval.c Normal file
View File

@ -0,0 +1,49 @@
/* Copyright 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "quickjs.h"
#include "quickjs-libc.h"
#include "fuzz/fuzz_common.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size == 0)
return 0;
JSRuntime *rt = JS_NewRuntime();
JSContext *ctx = JS_NewContext(rt);
test_one_input_init(rt, ctx);
uint8_t *null_terminated_data = malloc(size + 1);
memcpy(null_terminated_data, data, size);
null_terminated_data[size] = 0;
reset_nbinterrupts();
//the final 0 does not count (as in strlen)
JSValue val = JS_Eval(ctx, (const char *)null_terminated_data, size, "<none>", JS_EVAL_TYPE_GLOBAL);
free(null_terminated_data);
//TODO targets with JS_ParseJSON, JS_ReadObject
if (!JS_IsException(val)) {
js_std_loop(ctx);
JS_FreeValue(ctx, val);
}
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 0;
}

66
fuzz/fuzz_regexp.c Normal file
View File

@ -0,0 +1,66 @@
/* Copyright 2020 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libregexp.h"
#include "quickjs-libc.h"
static int nbinterrupts = 0;
int lre_check_stack_overflow(void *opaque, size_t alloca_size) { return 0; }
void *lre_realloc(void *opaque, void *ptr, size_t size)
{
return realloc(ptr, size);
}
int lre_check_timeout(void *opaque)
{
nbinterrupts++;
return (nbinterrupts > 100);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
int len, ret, i;
uint8_t *bc;
char error_msg[64];
const uint8_t *input;
uint8_t *capture[255 * 2];
size_t size1 = size;
//Splits buffer into 2 sub buffers delimited by null character
for (i = 0; i < size; i++) {
if (data[i] == 0) {
size1 = i;
break;
}
}
if (size1 == size) {
//missing delimiter
return 0;
}
bc = lre_compile(&len, error_msg, sizeof(error_msg), (const char *) data,
size1, 0, NULL);
if (!bc) {
return 0;
}
input = data + size1 + 1;
ret = lre_exec(capture, bc, input, 0, size - (size1 + 1), 0, NULL);
if (ret == 1) {
lre_get_capture_count(bc);
}
free(bc);
return 0;
}

24
fuzz/generate_dict.js Normal file
View File

@ -0,0 +1,24 @@
// Function to recursively iterate through built-in names.
function collectBuiltinNames(obj, visited = new Set(), result = new Set()) {
// Check if the object has already been visited to avoid infinite recursion.
if (visited.has(obj))
return;
// Add the current object to the set of visited objects
visited.add(obj);
// Get the property names of the current object
const properties = Object.getOwnPropertyNames(obj);
// Iterate through each property
for (var i=0; i < properties.length; i++) {
var property = properties[i];
if (property != "collectBuiltinNames" && typeof property != "number")
result.add(property);
// Check if the property is an object and if so, recursively iterate through its properties.
if (typeof obj[property] === 'object' && obj[property] !== null)
collectBuiltinNames(obj[property], visited, result);
}
return result;
}
// Start the recursive iteration with the global object.
console.log(Array.from(collectBuiltinNames(this)).join('\n'));

8473
libbf.c

File diff suppressed because it is too large Load Diff

535
libbf.h
View File

@ -1,535 +0,0 @@
/*
* Tiny arbitrary precision floating point library
*
* Copyright (c) 2017-2021 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBBF_H
#define LIBBF_H
#include <stddef.h>
#include <stdint.h>
#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
#define LIMB_LOG2_BITS 6
#else
#define LIMB_LOG2_BITS 5
#endif
#define LIMB_BITS (1 << LIMB_LOG2_BITS)
#if LIMB_BITS == 64
typedef __int128 int128_t;
typedef unsigned __int128 uint128_t;
typedef int64_t slimb_t;
typedef uint64_t limb_t;
typedef uint128_t dlimb_t;
#define BF_RAW_EXP_MIN INT64_MIN
#define BF_RAW_EXP_MAX INT64_MAX
#define LIMB_DIGITS 19
#define BF_DEC_BASE UINT64_C(10000000000000000000)
#else
typedef int32_t slimb_t;
typedef uint32_t limb_t;
typedef uint64_t dlimb_t;
#define BF_RAW_EXP_MIN INT32_MIN
#define BF_RAW_EXP_MAX INT32_MAX
#define LIMB_DIGITS 9
#define BF_DEC_BASE 1000000000U
#endif
/* in bits */
/* minimum number of bits for the exponent */
#define BF_EXP_BITS_MIN 3
/* maximum number of bits for the exponent */
#define BF_EXP_BITS_MAX (LIMB_BITS - 3)
/* extended range for exponent, used internally */
#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1)
/* minimum possible precision */
#define BF_PREC_MIN 2
/* minimum possible precision */
#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2)
/* some operations support infinite precision */
#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */
#if LIMB_BITS == 64
#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197))
#else
#define BF_CHKSUM_MOD 975620677U
#endif
#define BF_EXP_ZERO BF_RAW_EXP_MIN
#define BF_EXP_INF (BF_RAW_EXP_MAX - 1)
#define BF_EXP_NAN BF_RAW_EXP_MAX
/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0,
+/-infinity is represented with expn = BF_EXP_INF and len = 0,
NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored)
*/
typedef struct {
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bf_t;
typedef struct {
/* must be kept identical to bf_t */
struct bf_context_t *ctx;
int sign;
slimb_t expn;
limb_t len;
limb_t *tab;
} bfdec_t;
typedef enum {
BF_RNDN, /* round to nearest, ties to even */
BF_RNDZ, /* round to zero */
BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */
BF_RNDU, /* round to +inf */
BF_RNDNA, /* round to nearest, ties away from zero */
BF_RNDA, /* round away from zero */
BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU,
inexact flag is always set) */
} bf_rnd_t;
/* allow subnormal numbers. Only available if the number of exponent
bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */
#define BF_FLAG_SUBNORMAL (1 << 3)
/* 'prec' is the precision after the radix point instead of the whole
mantissa. Can only be used with bf_round() and
bfdec_[add|sub|mul|div|sqrt|round](). */
#define BF_FLAG_RADPNT_PREC (1 << 4)
#define BF_RND_MASK 0x7
#define BF_EXP_BITS_SHIFT 5
#define BF_EXP_BITS_MASK 0x3f
/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */
#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)
/* contains the rounding mode and number of exponents bits */
typedef uint32_t bf_flags_t;
typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size);
typedef struct {
bf_t val;
limb_t prec;
} BFConstCache;
typedef struct bf_context_t {
void *realloc_opaque;
bf_realloc_func_t *realloc_func;
BFConstCache log2_cache;
BFConstCache pi_cache;
struct BFNTTState *ntt_state;
} bf_context_t;
static inline int bf_get_exp_bits(bf_flags_t flags)
{
int e;
e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK;
if (e == BF_EXP_BITS_MASK)
return BF_EXP_BITS_MAX + 1;
else
return BF_EXP_BITS_MAX - e;
}
static inline bf_flags_t bf_set_exp_bits(int n)
{
return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT;
}
/* returned status */
#define BF_ST_INVALID_OP (1 << 0)
#define BF_ST_DIVIDE_ZERO (1 << 1)
#define BF_ST_OVERFLOW (1 << 2)
#define BF_ST_UNDERFLOW (1 << 3)
#define BF_ST_INEXACT (1 << 4)
/* indicate that a memory allocation error occured. NaN is returned */
#define BF_ST_MEM_ERROR (1 << 5)
#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */
static inline slimb_t bf_max(slimb_t a, slimb_t b)
{
if (a > b)
return a;
else
return b;
}
static inline slimb_t bf_min(slimb_t a, slimb_t b)
{
if (a < b)
return a;
else
return b;
}
void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func,
void *realloc_opaque);
void bf_context_end(bf_context_t *s);
/* free memory allocated for the bf cache data */
void bf_clear_cache(bf_context_t *s);
static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size)
{
return s->realloc_func(s->realloc_opaque, ptr, size);
}
/* 'size' must be != 0 */
static inline void *bf_malloc(bf_context_t *s, size_t size)
{
return bf_realloc(s, NULL, size);
}
static inline void bf_free(bf_context_t *s, void *ptr)
{
/* must test ptr otherwise equivalent to malloc(0) */
if (ptr)
bf_realloc(s, ptr, 0);
}
void bf_init(bf_context_t *s, bf_t *r);
static inline void bf_delete(bf_t *r)
{
bf_context_t *s = r->ctx;
/* we accept to delete a zeroed bf_t structure */
if (s && r->tab) {
bf_realloc(s, r->tab, 0);
}
}
static inline void bf_neg(bf_t *r)
{
r->sign ^= 1;
}
static inline int bf_is_finite(const bf_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bf_is_nan(const bf_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bf_is_zero(const bf_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
static inline void bf_memcpy(bf_t *r, const bf_t *a)
{
*r = *a;
}
int bf_set_ui(bf_t *r, uint64_t a);
int bf_set_si(bf_t *r, int64_t a);
void bf_set_nan(bf_t *r);
void bf_set_zero(bf_t *r, int is_neg);
void bf_set_inf(bf_t *r, int is_neg);
int bf_set(bf_t *r, const bf_t *a);
void bf_move(bf_t *r, bf_t *a);
int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode);
int bf_set_float64(bf_t *a, double d);
int bf_cmpu(const bf_t *a, const bf_t *b);
int bf_cmp_full(const bf_t *a, const bf_t *b);
int bf_cmp(const bf_t *a, const bf_t *b);
static inline int bf_cmp_eq(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) == 0;
}
static inline int bf_cmp_le(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) <= 0;
}
static inline int bf_cmp_lt(const bf_t *a, const bf_t *b)
{
return bf_cmp(a, b) < 0;
}
int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags);
int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags);
int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
#define BF_DIVREM_EUCLIDIAN BF_RNDF
int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
/* round to integer with infinite precision */
int bf_rint(bf_t *r, int rnd_mode);
int bf_round(bf_t *r, limb_t prec, bf_flags_t flags);
int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a);
int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
slimb_t bf_get_exp_min(const bf_t *a);
int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b);
int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b);
int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b);
/* additional flags for bf_atof */
/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */
#define BF_ATOF_NO_HEX (1 << 16)
/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */
#define BF_ATOF_BIN_OCT (1 << 17)
/* Do not parse NaN or Inf */
#define BF_ATOF_NO_NAN_INF (1 << 18)
/* return the exponent separately */
#define BF_ATOF_EXPONENT (1 << 19)
int bf_atof(bf_t *a, const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
/* this version accepts prec = BF_PREC_INF and returns the radix
exponent */
int bf_atof2(bf_t *r, slimb_t *pexponent,
const char *str, const char **pnext, int radix,
limb_t prec, bf_flags_t flags);
int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
slimb_t expn, limb_t prec, bf_flags_t flags);
/* Conversion of floating point number to string. Return a null
terminated string or NULL if memory error. *plen contains its
length if plen != NULL. The exponent letter is "e" for base 10,
"p" for bases 2, 8, 16 with a binary exponent and "@" for the other
bases. */
#define BF_FTOA_FORMAT_MASK (3 << 16)
/* fixed format: prec significant digits rounded with (flags &
BF_RND_MASK). Exponential notation is used if too many zeros are
needed.*/
#define BF_FTOA_FORMAT_FIXED (0 << 16)
/* fractional format: prec digits after the decimal point rounded with
(flags & BF_RND_MASK) */
#define BF_FTOA_FORMAT_FRAC (1 << 16)
/* free format:
For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum
number of digits to represent 'a'. The precision and the rounding
mode are ignored.
For the non binary radices with bf_ftoa(): use as many digits as
necessary so that bf_atof() return the same number when using
precision 'prec', rounding to nearest and the subnormal
configuration of 'flags'. The result is meaningful only if 'a' is
already rounded to 'prec' bits. If the subnormal flag is set, the
exponent in 'flags' must also be set to the desired exponent range.
*/
#define BF_FTOA_FORMAT_FREE (2 << 16)
/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits
(takes more computation time). Identical to BF_FTOA_FORMAT_FREE for
binary radices with bf_ftoa() and for bfdec_ftoa(). */
#define BF_FTOA_FORMAT_FREE_MIN (3 << 16)
/* force exponential notation for fixed or free format */
#define BF_FTOA_FORCE_EXP (1 << 20)
/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for
base 2 if non zero value */
#define BF_FTOA_ADD_PREFIX (1 << 21)
/* return "Infinity" instead of "Inf" and add a "+" for positive
exponents */
#define BF_FTOA_JS_QUIRKS (1 << 22)
char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,
bf_flags_t flags);
/* modulo 2^n instead of saturation. NaN and infinity return 0 */
#define BF_GET_INT_MOD (1 << 0)
int bf_get_int32(int *pres, const bf_t *a, int flags);
int bf_get_int64(int64_t *pres, const bf_t *a, int flags);
int bf_get_uint64(uint64_t *pres, const bf_t *a);
/* the following functions are exported for testing only. */
void mp_print_str(const char *str, const limb_t *tab, limb_t n);
void bf_print_str(const char *str, const bf_t *a);
int bf_resize(bf_t *r, limb_t len);
int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len);
int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags);
int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k);
slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
int is_ceil1);
int mp_mul(bf_context_t *s, limb_t *result,
const limb_t *op1, limb_t op1_size,
const limb_t *op2, limb_t op2_size);
limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2,
limb_t n, limb_t carry);
limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n);
int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n);
int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n);
limb_t bf_isqrt(limb_t a);
/* transcendental functions */
int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags);
int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */
int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags);
int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x,
limb_t prec, bf_flags_t flags);
int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags);
/* decimal floating point */
static inline void bfdec_init(bf_context_t *s, bfdec_t *r)
{
bf_init(s, (bf_t *)r);
}
static inline void bfdec_delete(bfdec_t *r)
{
bf_delete((bf_t *)r);
}
static inline void bfdec_neg(bfdec_t *r)
{
r->sign ^= 1;
}
static inline int bfdec_is_finite(const bfdec_t *a)
{
return (a->expn < BF_EXP_INF);
}
static inline int bfdec_is_nan(const bfdec_t *a)
{
return (a->expn == BF_EXP_NAN);
}
static inline int bfdec_is_zero(const bfdec_t *a)
{
return (a->expn == BF_EXP_ZERO);
}
static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a)
{
bf_memcpy((bf_t *)r, (const bf_t *)a);
}
int bfdec_set_ui(bfdec_t *r, uint64_t a);
int bfdec_set_si(bfdec_t *r, int64_t a);
static inline void bfdec_set_nan(bfdec_t *r)
{
bf_set_nan((bf_t *)r);
}
static inline void bfdec_set_zero(bfdec_t *r, int is_neg)
{
bf_set_zero((bf_t *)r, is_neg);
}
static inline void bfdec_set_inf(bfdec_t *r, int is_neg)
{
bf_set_inf((bf_t *)r, is_neg);
}
static inline int bfdec_set(bfdec_t *r, const bfdec_t *a)
{
return bf_set((bf_t *)r, (bf_t *)a);
}
static inline void bfdec_move(bfdec_t *r, bfdec_t *a)
{
bf_move((bf_t *)r, (bf_t *)a);
}
static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmpu((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmp_full((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b)
{
return bf_cmp((const bf_t *)a, (const bf_t *)b);
}
static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) == 0;
}
static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) <= 0;
}
static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b)
{
return bfdec_cmp(a, b) < 0;
}
int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec,
bf_flags_t flags);
int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags);
int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
limb_t prec, bf_flags_t flags, int rnd_mode);
int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec,
bf_flags_t flags, int rnd_mode);
int bfdec_rint(bfdec_t *r, int rnd_mode);
int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags);
int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags);
int bfdec_get_int32(int *pres, const bfdec_t *a);
int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b);
char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags);
int bfdec_atof(bfdec_t *r, const char *str, const char **pnext,
limb_t prec, bf_flags_t flags);
/* the following functions are exported for testing only. */
extern const limb_t mp_pow_dec[LIMB_DIGITS + 1];
void bfdec_print_str(const char *str, const bfdec_t *a);
static inline int bfdec_resize(bfdec_t *r, limb_t len)
{
return bf_resize((bf_t *)r, len);
}
int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags);
#endif /* LIBBF_H */

View File

@ -30,6 +30,7 @@
#include "cutils.h" #include "cutils.h"
#include "libregexp.h" #include "libregexp.h"
#include "libunicode.h"
/* /*
TODO: TODO:
@ -53,6 +54,9 @@ typedef enum {
#define CAPTURE_COUNT_MAX 255 #define CAPTURE_COUNT_MAX 255
#define STACK_SIZE_MAX 255 #define STACK_SIZE_MAX 255
/* must be large enough to have a negligible runtime cost and small
enough to call the interrupt callback often. */
#define INTERRUPT_COUNTER_INIT 10000
/* unicode code points */ /* unicode code points */
#define CP_LS 0x2028 #define CP_LS 0x2028
@ -66,7 +70,7 @@ typedef struct {
const uint8_t *buf_end; const uint8_t *buf_end;
const uint8_t *buf_start; const uint8_t *buf_start;
int re_flags; int re_flags;
BOOL is_utf16; BOOL is_unicode;
BOOL ignore_case; BOOL ignore_case;
BOOL dotall; BOOL dotall;
int capture_count; int capture_count;
@ -100,6 +104,7 @@ static const REOpCode reopcode_info[REOP_COUNT] = {
#define RE_HEADER_FLAGS 0 #define RE_HEADER_FLAGS 0
#define RE_HEADER_CAPTURE_COUNT 1 #define RE_HEADER_CAPTURE_COUNT 1
#define RE_HEADER_STACK_SIZE 2 #define RE_HEADER_STACK_SIZE 2
#define RE_HEADER_BYTECODE_LEN 3
#define RE_HEADER_LEN 7 #define RE_HEADER_LEN 7
@ -140,32 +145,6 @@ static const uint16_t char_range_s[] = {
0xFEFF, 0xFEFF + 1, 0xFEFF, 0xFEFF + 1,
}; };
BOOL lre_is_space(int c)
{
int i, n, low, high;
n = (countof(char_range_s) - 1) / 2;
for(i = 0; i < n; i++) {
low = char_range_s[2 * i + 1];
if (c < low)
return FALSE;
high = char_range_s[2 * i + 2];
if (c < high)
return TRUE;
}
return FALSE;
}
uint32_t const lre_id_start_table_ascii[4] = {
/* $ A-Z _ a-z */
0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE
};
uint32_t const lre_id_continue_table_ascii[4] = {
/* $ 0-9 A-Z _ a-z */
0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE
};
static const uint16_t char_range_w[] = { static const uint16_t char_range_w[] = {
4, 4,
0x0030, 0x0039 + 1, 0x0030, 0x0039 + 1,
@ -185,7 +164,7 @@ typedef enum {
CHAR_RANGE_W, CHAR_RANGE_W,
} CharRangeEnum; } CharRangeEnum;
static const uint16_t *char_range_table[] = { static const uint16_t * const char_range_table[] = {
char_range_d, char_range_d,
char_range_s, char_range_s,
char_range_w, char_range_w,
@ -224,16 +203,16 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
assert(buf_len >= RE_HEADER_LEN); assert(buf_len >= RE_HEADER_LEN);
re_flags= buf[0]; re_flags = lre_get_flags(buf);
bc_len = get_u32(buf + 3); bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN);
assert(bc_len + RE_HEADER_LEN <= buf_len); assert(bc_len + RE_HEADER_LEN <= buf_len);
printf("flags: 0x%x capture_count=%d stack_size=%d\n", printf("flags: 0x%x capture_count=%d stack_size=%d\n",
re_flags, buf[1], buf[2]); re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]);
if (re_flags & LRE_FLAG_NAMED_GROUPS) { if (re_flags & LRE_FLAG_NAMED_GROUPS) {
const char *p; const char *p;
p = (char *)buf + RE_HEADER_LEN + bc_len; p = (char *)buf + RE_HEADER_LEN + bc_len;
printf("named groups: "); printf("named groups: ");
for(i = 1; i < buf[1]; i++) { for(i = 1; i < buf[RE_HEADER_CAPTURE_COUNT]; i++) {
if (i != 1) if (i != 1)
printf(","); printf(",");
printf("<%s>", p); printf("<%s>", p);
@ -494,7 +473,7 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
} }
c = (c << 4) | h; c = (c << 4) | h;
} }
if (c >= 0xd800 && c < 0xdc00 && if (is_hi_surrogate(c) &&
allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') {
/* convert an escaped surrogate pair into a /* convert an escaped surrogate pair into a
unicode char */ unicode char */
@ -505,9 +484,9 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
break; break;
c1 = (c1 << 4) | h; c1 = (c1 << 4) | h;
} }
if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) { if (i == 4 && is_lo_surrogate(c1)) {
p += 6; p += 6;
c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; c = from_surrogate(c, c1);
} }
} }
} }
@ -696,10 +675,10 @@ static int get_class_atom(REParseState *s, CharRange *cr,
if ((c >= 'a' && c <= 'z') || if ((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') || (c >= 'A' && c <= 'Z') ||
(((c >= '0' && c <= '9') || c == '_') && (((c >= '0' && c <= '9') || c == '_') &&
inclass && !s->is_utf16)) { /* Annex B.1.4 */ inclass && !s->is_unicode)) { /* Annex B.1.4 */
c &= 0x1f; c &= 0x1f;
p++; p++;
} else if (s->is_utf16) { } else if (s->is_unicode) {
goto invalid_escape; goto invalid_escape;
} else { } else {
/* otherwise return '\' and 'c' */ /* otherwise return '\' and 'c' */
@ -707,10 +686,14 @@ static int get_class_atom(REParseState *s, CharRange *cr,
c = '\\'; c = '\\';
} }
break; break;
case '-':
if (!inclass && s->is_unicode)
goto invalid_escape;
break;
#ifdef CONFIG_ALL_UNICODE #ifdef CONFIG_ALL_UNICODE
case 'p': case 'p':
case 'P': case 'P':
if (s->is_utf16) { if (s->is_unicode) {
if (parse_unicode_property(s, cr, &p, (c == 'P'))) if (parse_unicode_property(s, cr, &p, (c == 'P')))
return -1; return -1;
c = CLASS_RANGE_BASE; c = CLASS_RANGE_BASE;
@ -720,14 +703,14 @@ static int get_class_atom(REParseState *s, CharRange *cr,
#endif #endif
default: default:
p--; p--;
ret = lre_parse_escape(&p, s->is_utf16 * 2); ret = lre_parse_escape(&p, s->is_unicode * 2);
if (ret >= 0) { if (ret >= 0) {
c = ret; c = ret;
} else { } else {
if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) { if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) {
/* always valid to escape these characters */ /* always valid to escape these characters */
goto normal_char; goto normal_char;
} else if (s->is_utf16) { } else if (s->is_unicode) {
invalid_escape: invalid_escape:
return re_parse_error(s, "invalid escape sequence in regular expression"); return re_parse_error(s, "invalid escape sequence in regular expression");
} else { } else {
@ -749,7 +732,7 @@ static int get_class_atom(REParseState *s, CharRange *cr,
/* normal char */ /* normal char */
if (c >= 128) { if (c >= 128) {
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
if ((unsigned)c > 0xffff && !s->is_utf16) { if ((unsigned)c > 0xffff && !s->is_unicode) {
/* XXX: should handle non BMP-1 code points */ /* XXX: should handle non BMP-1 code points */
return re_parse_error(s, "malformed unicode char"); return re_parse_error(s, "malformed unicode char");
} }
@ -811,11 +794,13 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
cr_init(cr, s->opaque, lre_realloc); cr_init(cr, s->opaque, lre_realloc);
p = *pp; p = *pp;
p++; /* skip '[' */ p++; /* skip '[' */
invert = FALSE; invert = FALSE;
if (*p == '^') { if (*p == '^') {
p++; p++;
invert = TRUE; invert = TRUE;
} }
for(;;) { for(;;) {
if (*p == ']') if (*p == ']')
break; break;
@ -825,7 +810,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
if (*p == '-' && p[1] != ']') { if (*p == '-' && p[1] != ']') {
const uint8_t *p0 = p + 1; const uint8_t *p0 = p + 1;
if (c1 >= CLASS_RANGE_BASE) { if (c1 >= CLASS_RANGE_BASE) {
if (s->is_utf16) { if (s->is_unicode) {
cr_free(cr1); cr_free(cr1);
goto invalid_class_range; goto invalid_class_range;
} }
@ -837,7 +822,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
goto fail; goto fail;
if (c2 >= CLASS_RANGE_BASE) { if (c2 >= CLASS_RANGE_BASE) {
cr_free(cr1); cr_free(cr1);
if (s->is_utf16) { if (s->is_unicode) {
goto invalid_class_range; goto invalid_class_range;
} }
/* Annex B: match '-' character */ /* Annex B: match '-' character */
@ -866,7 +851,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
} }
} }
if (s->ignore_case) { if (s->ignore_case) {
if (cr_regexp_canonicalize(cr, s->is_utf16)) if (cr_regexp_canonicalize(cr, s->is_unicode))
goto memory_error; goto memory_error;
} }
if (invert) { if (invert) {
@ -934,7 +919,7 @@ static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len)
case REOP_backward_back_reference: case REOP_backward_back_reference:
break; break;
default: default:
/* safe behvior: we cannot predict the outcome */ /* safe behavior: we cannot predict the outcome */
return TRUE; return TRUE;
} }
pos += len; pos += len;
@ -1003,10 +988,10 @@ static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp)
break; break;
} else if (c >= 128) { } else if (c >= 128) {
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
if (c >= 0xD800 && c <= 0xDBFF) { if (is_hi_surrogate(c)) {
d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
if (d >= 0xDC00 && d <= 0xDFFF) { if (is_lo_surrogate(d)) {
c = 0x10000 + 0x400 * (c - 0xD800) + (d - 0xDC00); c = from_surrogate(c, d);
p = p1; p = p1;
} }
} }
@ -1114,9 +1099,10 @@ static int find_group_name(REParseState *s, const char *name)
size_t len, name_len; size_t len, name_len;
int capture_index; int capture_index;
name_len = strlen(name);
p = (char *)s->group_names.buf; p = (char *)s->group_names.buf;
if (!p) return -1;
buf_end = (char *)s->group_names.buf + s->group_names.size; buf_end = (char *)s->group_names.buf + s->group_names.size;
name_len = strlen(name);
capture_index = 1; capture_index = 1;
while (p < buf_end) { while (p < buf_end) {
len = strlen(p); len = strlen(p);
@ -1161,7 +1147,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
re_emit_op(s, REOP_prev); re_emit_op(s, REOP_prev);
break; break;
case '{': case '{':
if (s->is_utf16) { if (s->is_unicode) {
return re_parse_error(s, "syntax error"); return re_parse_error(s, "syntax error");
} else if (!is_digit(p[1])) { } else if (!is_digit(p[1])) {
/* Annex B: we accept '{' not followed by digits as a /* Annex B: we accept '{' not followed by digits as a
@ -1213,7 +1199,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
lookahead: lookahead:
/* Annex B allows lookahead to be used as an atom for /* Annex B allows lookahead to be used as an atom for
the quantifiers */ the quantifiers */
if (!s->is_utf16 && !is_backward_lookahead) { if (!s->is_unicode && !is_backward_lookahead) {
last_atom_start = s->byte_code.size; last_atom_start = s->byte_code.size;
last_capture_count = s->capture_count; last_capture_count = s->capture_count;
} }
@ -1289,7 +1275,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
/* annex B: we tolerate invalid group names in non /* annex B: we tolerate invalid group names in non
unicode mode if there is no named capture unicode mode if there is no named capture
definition */ definition */
if (s->is_utf16 || re_has_named_captures(s)) if (s->is_unicode || re_has_named_captures(s))
return re_parse_error(s, "expecting group name"); return re_parse_error(s, "expecting group name");
else else
goto parse_class_atom; goto parse_class_atom;
@ -1297,7 +1283,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
p1 += 3; p1 += 3;
if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
&p1)) { &p1)) {
if (s->is_utf16 || re_has_named_captures(s)) if (s->is_unicode || re_has_named_captures(s))
return re_parse_error(s, "invalid group name"); return re_parse_error(s, "invalid group name");
else else
goto parse_class_atom; goto parse_class_atom;
@ -1308,7 +1294,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
after (inefficient, but hopefully not common */ after (inefficient, but hopefully not common */
c = re_parse_captures(s, &dummy_res, s->u.tmp_buf); c = re_parse_captures(s, &dummy_res, s->u.tmp_buf);
if (c < 0) { if (c < 0) {
if (s->is_utf16 || re_has_named_captures(s)) if (s->is_unicode || re_has_named_captures(s))
return re_parse_error(s, "group name not defined"); return re_parse_error(s, "group name not defined");
else else
goto parse_class_atom; goto parse_class_atom;
@ -1320,7 +1306,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
case '0': case '0':
p += 2; p += 2;
c = 0; c = 0;
if (s->is_utf16) { if (s->is_unicode) {
if (is_digit(*p)) { if (is_digit(*p)) {
return re_parse_error(s, "invalid decimal escape in regular expression"); return re_parse_error(s, "invalid decimal escape in regular expression");
} }
@ -1342,7 +1328,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
c = parse_digits(&p, FALSE); c = parse_digits(&p, FALSE);
if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) {
if (!s->is_utf16) { if (!s->is_unicode) {
/* Annex B.1.4: accept legacy octal */ /* Annex B.1.4: accept legacy octal */
p = q; p = q;
if (*p <= '7') { if (*p <= '7') {
@ -1384,7 +1370,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
break; break;
case ']': case ']':
case '}': case '}':
if (s->is_utf16) if (s->is_unicode)
return re_parse_error(s, "syntax error"); return re_parse_error(s, "syntax error");
goto parse_class_atom; goto parse_class_atom;
default: default:
@ -1406,7 +1392,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
return -1; return -1;
} else { } else {
if (s->ignore_case) if (s->ignore_case)
c = lre_canonicalize(c, s->is_utf16); c = lre_canonicalize(c, s->is_unicode);
if (c <= 0xffff) if (c <= 0xffff)
re_emit_op_u16(s, REOP_char, c); re_emit_op_u16(s, REOP_char, c);
else else
@ -1442,7 +1428,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
/* As an extension (see ES6 annex B), we accept '{' not /* As an extension (see ES6 annex B), we accept '{' not
followed by digits as a normal atom */ followed by digits as a normal atom */
if (!is_digit(p[1])) { if (!is_digit(p[1])) {
if (s->is_utf16) if (s->is_unicode)
goto invalid_quant_count; goto invalid_quant_count;
break; break;
} }
@ -1461,7 +1447,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
quant_max = INT32_MAX; /* infinity */ quant_max = INT32_MAX; /* infinity */
} }
} }
if (*p != '}' && !s->is_utf16) { if (*p != '}' && !s->is_unicode) {
/* Annex B: normal atom if invalid '{' syntax */ /* Annex B: normal atom if invalid '{' syntax */
p = p1; p = p1;
break; break;
@ -1509,15 +1495,13 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
if (dbuf_error(&s->byte_code)) if (dbuf_error(&s->byte_code))
goto out_of_memory; goto out_of_memory;
/* the spec tells that if there is no advance when
running the atom after the first quant_min times,
then there is no match. We remove this test when we
are sure the atom always advances the position. */
add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start,
s->byte_code.size - last_atom_start);
} else {
add_zero_advance_check = FALSE;
} }
/* the spec tells that if there is no advance when
running the atom after the first quant_min times,
then there is no match. We remove this test when we
are sure the atom always advances the position. */
add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start,
s->byte_code.size - last_atom_start);
{ {
int len, pos; int len, pos;
@ -1753,7 +1737,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
s->buf_end = s->buf_ptr + buf_len; s->buf_end = s->buf_ptr + buf_len;
s->buf_start = s->buf_ptr; s->buf_start = s->buf_ptr;
s->re_flags = re_flags; s->re_flags = re_flags;
s->is_utf16 = ((re_flags & LRE_FLAG_UTF16) != 0); s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0);
is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0); is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0);
s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0);
s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0);
@ -1811,7 +1795,8 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count;
s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size;
put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_LEN); put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN,
s->byte_code.size - RE_HEADER_LEN);
/* add the named groups if needed */ /* add the named groups if needed */
if (s->group_names.size > (s->capture_count - 1)) { if (s->group_names.size > (s->capture_count - 1)) {
@ -1842,93 +1827,86 @@ static BOOL is_word_char(uint32_t c)
(c == '_')); (c == '_'));
} }
#define GET_CHAR(c, cptr, cbuf_end) \ #define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
c = *cptr++; \ c = *cptr++; \
} else { \ } else { \
uint32_t __c1; \ const uint16_t *_p = (const uint16_t *)cptr; \
c = *(uint16_t *)cptr; \ const uint16_t *_end = (const uint16_t *)cbuf_end; \
cptr += 2; \ c = *_p++; \
if (c >= 0xd800 && c < 0xdc00 && \ if (is_hi_surrogate(c) && cbuf_type == 2) { \
cbuf_type == 2 && cptr < cbuf_end) { \ if (_p < _end && is_lo_surrogate(*_p)) { \
__c1 = *(uint16_t *)cptr; \ c = from_surrogate(c, *_p++); \
if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ } \
c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ } \
cptr += 2; \ cptr = (const void *)_p; \
} \
} while (0)
#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \
do { \
if (cbuf_type == 0) { \
c = cptr[0]; \
} else { \
const uint16_t *_p = (const uint16_t *)cptr; \
const uint16_t *_end = (const uint16_t *)cbuf_end; \
c = *_p++; \
if (is_hi_surrogate(c) && cbuf_type == 2) { \
if (_p < _end && is_lo_surrogate(*_p)) { \
c = from_surrogate(c, *_p); \
} \ } \
} \ } \
} \ } \
} while (0) } while (0)
#define PEEK_CHAR(c, cptr, cbuf_end) \ #define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
c = cptr[0]; \ c = cptr[-1]; \
} else { \ } else { \
uint32_t __c1; \ const uint16_t *_p = (const uint16_t *)cptr - 1; \
c = ((uint16_t *)cptr)[0]; \ const uint16_t *_start = (const uint16_t *)cbuf_start; \
if (c >= 0xd800 && c < 0xdc00 && \ c = *_p; \
cbuf_type == 2 && (cptr + 2) < cbuf_end) { \ if (is_lo_surrogate(c) && cbuf_type == 2) { \
__c1 = ((uint16_t *)cptr)[1]; \ if (_p > _start && is_hi_surrogate(_p[-1])) { \
if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ c = from_surrogate(*--_p, c); \
c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
} \
} \
} \
} while (0)
#define PEEK_PREV_CHAR(c, cptr, cbuf_start) \
do { \
if (cbuf_type == 0) { \
c = cptr[-1]; \
} else { \
uint32_t __c1; \
c = ((uint16_t *)cptr)[-1]; \
if (c >= 0xdc00 && c < 0xe000 && \
cbuf_type == 2 && (cptr - 4) >= cbuf_start) { \
__c1 = ((uint16_t *)cptr)[-2]; \
if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
} \ } \
} \ } \
} \ } \
} while (0) } while (0)
#define GET_PREV_CHAR(c, cptr, cbuf_start) \ #define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
cptr--; \ cptr--; \
c = cptr[0]; \ c = cptr[0]; \
} else { \ } else { \
uint32_t __c1; \ const uint16_t *_p = (const uint16_t *)cptr - 1; \
cptr -= 2; \ const uint16_t *_start = (const uint16_t *)cbuf_start; \
c = ((uint16_t *)cptr)[0]; \ c = *_p; \
if (c >= 0xdc00 && c < 0xe000 && \ if (is_lo_surrogate(c) && cbuf_type == 2) { \
cbuf_type == 2 && cptr > cbuf_start) { \ if (_p > _start && is_hi_surrogate(_p[-1])) { \
__c1 = ((uint16_t *)cptr)[-1]; \ c = from_surrogate(*--_p, c); \
if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
cptr -= 2; \
c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
} \ } \
} \ } \
cptr = (const void *)_p; \
} \ } \
} while (0) } while (0)
#define PREV_CHAR(cptr, cbuf_start) \ #define PREV_CHAR(cptr, cbuf_start, cbuf_type) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
cptr--; \ cptr--; \
} else { \ } else { \
cptr -= 2; \ const uint16_t *_p = (const uint16_t *)cptr - 1; \
if (cbuf_type == 2) { \ const uint16_t *_start = (const uint16_t *)cbuf_start; \
c = ((uint16_t *)cptr)[0]; \ if (is_lo_surrogate(*_p) && cbuf_type == 2) { \
if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) { \ if (_p > _start && is_hi_surrogate(_p[-1])) { \
c = ((uint16_t *)cptr)[-1]; \ --_p; \
if (c >= 0xd800 && c < 0xdc00) \
cptr -= 2; \
} \ } \
} \ } \
cptr = (const void *)_p; \
} \ } \
} while (0) } while (0)
@ -1959,7 +1937,8 @@ typedef struct {
int stack_size_max; int stack_size_max;
BOOL multi_line; BOOL multi_line;
BOOL ignore_case; BOOL ignore_case;
BOOL is_utf16; BOOL is_unicode;
int interrupt_counter;
void *opaque; /* used for stack overflow check */ void *opaque; /* used for stack overflow check */
size_t state_size; size_t state_size;
@ -2006,7 +1985,17 @@ static int push_state(REExecContext *s,
return 0; return 0;
} }
/* return 1 if match, 0 if not match or -1 if error. */ static int lre_poll_timeout(REExecContext *s)
{
if (unlikely(--s->interrupt_counter <= 0)) {
s->interrupt_counter = INTERRUPT_COUNTER_INIT;
if (lre_check_timeout(s->opaque))
return LRE_RET_TIMEOUT;
}
return 0;
}
/* return 1 if match, 0 if not match or < 0 if error. */
static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
StackInt *stack, int stack_len, StackInt *stack, int stack_len,
const uint8_t *pc, const uint8_t *cptr, const uint8_t *pc, const uint8_t *cptr,
@ -2037,6 +2026,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
ret = 0; ret = 0;
recurse: recurse:
for(;;) { for(;;) {
if (lre_poll_timeout(s))
return LRE_RET_TIMEOUT;
if (s->state_stack_len == 0) if (s->state_stack_len == 0)
return ret; return ret;
rs = (REExecState *)(s->state_stack + rs = (REExecState *)(s->state_stack +
@ -2068,7 +2059,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
/* go backward */ /* go backward */
char_count = get_u32(pc + 12); char_count = get_u32(pc + 12);
for(i = 0; i < char_count; i++) { for(i = 0; i < char_count; i++) {
PREV_CHAR(cptr, s->cbuf); PREV_CHAR(cptr, s->cbuf, cbuf_type);
} }
pc = (pc + 16) + (int)get_u32(pc); pc = (pc + 16) + (int)get_u32(pc);
rs->cptr = cptr; rs->cptr = cptr;
@ -2103,9 +2094,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
test_char: test_char:
if (cptr >= cbuf_end) if (cptr >= cbuf_end)
goto no_match; goto no_match;
GET_CHAR(c, cptr, cbuf_end); GET_CHAR(c, cptr, cbuf_end, cbuf_type);
if (s->ignore_case) { if (s->ignore_case) {
c = lre_canonicalize(c, s->is_utf16); c = lre_canonicalize(c, s->is_unicode);
} }
if (val != c) if (val != c)
goto no_match; goto no_match;
@ -2126,7 +2117,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
ret = push_state(s, capture, stack, stack_len, ret = push_state(s, capture, stack, stack_len,
pc1, cptr, RE_EXEC_STATE_SPLIT, 0); pc1, cptr, RE_EXEC_STATE_SPLIT, 0);
if (ret < 0) if (ret < 0)
return -1; return LRE_RET_MEMORY_ERROR;
break; break;
} }
case REOP_lookahead: case REOP_lookahead:
@ -2138,19 +2129,21 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead,
0); 0);
if (ret < 0) if (ret < 0)
return -1; return LRE_RET_MEMORY_ERROR;
break; break;
case REOP_goto: case REOP_goto:
val = get_u32(pc); val = get_u32(pc);
pc += 4 + (int)val; pc += 4 + (int)val;
if (lre_poll_timeout(s))
return LRE_RET_TIMEOUT;
break; break;
case REOP_line_start: case REOP_line_start:
if (cptr == s->cbuf) if (cptr == s->cbuf)
break; break;
if (!s->multi_line) if (!s->multi_line)
goto no_match; goto no_match;
PEEK_PREV_CHAR(c, cptr, s->cbuf); PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type);
if (!is_line_terminator(c)) if (!is_line_terminator(c))
goto no_match; goto no_match;
break; break;
@ -2159,21 +2152,21 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
break; break;
if (!s->multi_line) if (!s->multi_line)
goto no_match; goto no_match;
PEEK_CHAR(c, cptr, cbuf_end); PEEK_CHAR(c, cptr, cbuf_end, cbuf_type);
if (!is_line_terminator(c)) if (!is_line_terminator(c))
goto no_match; goto no_match;
break; break;
case REOP_dot: case REOP_dot:
if (cptr == cbuf_end) if (cptr == cbuf_end)
goto no_match; goto no_match;
GET_CHAR(c, cptr, cbuf_end); GET_CHAR(c, cptr, cbuf_end, cbuf_type);
if (is_line_terminator(c)) if (is_line_terminator(c))
goto no_match; goto no_match;
break; break;
case REOP_any: case REOP_any:
if (cptr == cbuf_end) if (cptr == cbuf_end)
goto no_match; goto no_match;
GET_CHAR(c, cptr, cbuf_end); GET_CHAR(c, cptr, cbuf_end, cbuf_type);
break; break;
case REOP_save_start: case REOP_save_start:
case REOP_save_end: case REOP_save_end:
@ -2208,6 +2201,8 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
pc += 4; pc += 4;
if (--stack[stack_len - 1] != 0) { if (--stack[stack_len - 1] != 0) {
pc += (int)val; pc += (int)val;
if (lre_poll_timeout(s))
return LRE_RET_TIMEOUT;
} }
break; break;
case REOP_push_char_pos: case REOP_push_char_pos:
@ -2225,14 +2220,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
if (cptr == s->cbuf) { if (cptr == s->cbuf) {
v1 = FALSE; v1 = FALSE;
} else { } else {
PEEK_PREV_CHAR(c, cptr, s->cbuf); PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type);
v1 = is_word_char(c); v1 = is_word_char(c);
} }
/* current char */ /* current char */
if (cptr >= cbuf_end) { if (cptr >= cbuf_end) {
v2 = FALSE; v2 = FALSE;
} else { } else {
PEEK_CHAR(c, cptr, cbuf_end); PEEK_CHAR(c, cptr, cbuf_end, cbuf_type);
v2 = is_word_char(c); v2 = is_word_char(c);
} }
if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode)) if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode))
@ -2257,11 +2252,11 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
while (cptr1 < cptr1_end) { while (cptr1 < cptr1_end) {
if (cptr >= cbuf_end) if (cptr >= cbuf_end)
goto no_match; goto no_match;
GET_CHAR(c1, cptr1, cptr1_end); GET_CHAR(c1, cptr1, cptr1_end, cbuf_type);
GET_CHAR(c2, cptr, cbuf_end); GET_CHAR(c2, cptr, cbuf_end, cbuf_type);
if (s->ignore_case) { if (s->ignore_case) {
c1 = lre_canonicalize(c1, s->is_utf16); c1 = lre_canonicalize(c1, s->is_unicode);
c2 = lre_canonicalize(c2, s->is_utf16); c2 = lre_canonicalize(c2, s->is_unicode);
} }
if (c1 != c2) if (c1 != c2)
goto no_match; goto no_match;
@ -2271,11 +2266,11 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
while (cptr1 > cptr1_start) { while (cptr1 > cptr1_start) {
if (cptr == s->cbuf) if (cptr == s->cbuf)
goto no_match; goto no_match;
GET_PREV_CHAR(c1, cptr1, cptr1_start); GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type);
GET_PREV_CHAR(c2, cptr, s->cbuf); GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type);
if (s->ignore_case) { if (s->ignore_case) {
c1 = lre_canonicalize(c1, s->is_utf16); c1 = lre_canonicalize(c1, s->is_unicode);
c2 = lre_canonicalize(c2, s->is_utf16); c2 = lre_canonicalize(c2, s->is_unicode);
} }
if (c1 != c2) if (c1 != c2)
goto no_match; goto no_match;
@ -2292,9 +2287,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
pc += 2; pc += 2;
if (cptr >= cbuf_end) if (cptr >= cbuf_end)
goto no_match; goto no_match;
GET_CHAR(c, cptr, cbuf_end); GET_CHAR(c, cptr, cbuf_end, cbuf_type);
if (s->ignore_case) { if (s->ignore_case) {
c = lre_canonicalize(c, s->is_utf16); c = lre_canonicalize(c, s->is_unicode);
} }
idx_min = 0; idx_min = 0;
low = get_u16(pc + 0 * 4); low = get_u16(pc + 0 * 4);
@ -2332,9 +2327,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
pc += 2; pc += 2;
if (cptr >= cbuf_end) if (cptr >= cbuf_end)
goto no_match; goto no_match;
GET_CHAR(c, cptr, cbuf_end); GET_CHAR(c, cptr, cbuf_end, cbuf_type);
if (s->ignore_case) { if (s->ignore_case) {
c = lre_canonicalize(c, s->is_utf16); c = lre_canonicalize(c, s->is_unicode);
} }
idx_min = 0; idx_min = 0;
low = get_u32(pc + 0 * 8); low = get_u32(pc + 0 * 8);
@ -2364,7 +2359,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
/* go to the previous char */ /* go to the previous char */
if (cptr == s->cbuf) if (cptr == s->cbuf)
goto no_match; goto no_match;
PREV_CHAR(cptr, s->cbuf); PREV_CHAR(cptr, s->cbuf, cbuf_type);
break; break;
case REOP_simple_greedy_quant: case REOP_simple_greedy_quant:
{ {
@ -2382,9 +2377,12 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
q = 0; q = 0;
for(;;) { for(;;) {
if (lre_poll_timeout(s))
return LRE_RET_TIMEOUT;
res = lre_exec_backtrack(s, capture, stack, stack_len, res = lre_exec_backtrack(s, capture, stack, stack_len,
pc1, cptr, TRUE); pc1, cptr, TRUE);
if (res == -1) if (res == LRE_RET_MEMORY_ERROR ||
res == LRE_RET_TIMEOUT)
return res; return res;
if (!res) if (!res)
break; break;
@ -2402,7 +2400,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
RE_EXEC_STATE_GREEDY_QUANT, RE_EXEC_STATE_GREEDY_QUANT,
q - quant_min); q - quant_min);
if (ret < 0) if (ret < 0)
return -1; return LRE_RET_MEMORY_ERROR;
} }
} }
break; break;
@ -2412,7 +2410,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
} }
} }
/* Return 1 if match, 0 if not match or -1 if error. cindex is the /* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the
starting position of the match and must be such as 0 <= cindex <= starting position of the match and must be such as 0 <= cindex <=
clen. */ clen. */
int lre_exec(uint8_t **capture, int lre_exec(uint8_t **capture,
@ -2423,17 +2421,18 @@ int lre_exec(uint8_t **capture,
int re_flags, i, alloca_size, ret; int re_flags, i, alloca_size, ret;
StackInt *stack_buf; StackInt *stack_buf;
re_flags = bc_buf[RE_HEADER_FLAGS]; re_flags = lre_get_flags(bc_buf);
s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0; s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0;
s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0; s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0;
s->is_utf16 = (re_flags & LRE_FLAG_UTF16) != 0; s->is_unicode = (re_flags & LRE_FLAG_UNICODE) != 0;
s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT]; s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT];
s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE]; s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE];
s->cbuf = cbuf; s->cbuf = cbuf;
s->cbuf_end = cbuf + (clen << cbuf_type); s->cbuf_end = cbuf + (clen << cbuf_type);
s->cbuf_type = cbuf_type; s->cbuf_type = cbuf_type;
if (s->cbuf_type == 1 && s->is_utf16) if (s->cbuf_type == 1 && s->is_unicode)
s->cbuf_type = 2; s->cbuf_type = 2;
s->interrupt_counter = INTERRUPT_COUNTER_INIT;
s->opaque = opaque; s->opaque = opaque;
s->state_size = sizeof(REExecState) + s->state_size = sizeof(REExecState) +
@ -2470,8 +2469,8 @@ const char *lre_get_groupnames(const uint8_t *bc_buf)
uint32_t re_bytecode_len; uint32_t re_bytecode_len;
if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0) if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0)
return NULL; return NULL;
re_bytecode_len = get_u32(bc_buf + 3); re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN);
return (const char *)(bc_buf + 7 + re_bytecode_len); return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len);
} }
#ifdef TEST #ifdef TEST
@ -2488,25 +2487,26 @@ void *lre_realloc(void *opaque, void *ptr, size_t size)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int len, ret, i; int len, flags, ret, i;
uint8_t *bc; uint8_t *bc;
char error_msg[64]; char error_msg[64];
uint8_t *capture[CAPTURE_COUNT_MAX * 2]; uint8_t *capture[CAPTURE_COUNT_MAX * 2];
const char *input; const char *input;
int input_len, capture_count; int input_len, capture_count;
if (argc < 3) { if (argc < 4) {
printf("usage: %s regexp input\n", argv[0]); printf("usage: %s regexp flags input\n", argv[0]);
exit(1); return 1;
} }
flags = atoi(argv[2]);
bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1],
strlen(argv[1]), 0, NULL); strlen(argv[1]), flags, NULL);
if (!bc) { if (!bc) {
fprintf(stderr, "error: %s\n", error_msg); fprintf(stderr, "error: %s\n", error_msg);
exit(1); exit(1);
} }
input = argv[2]; input = argv[3];
input_len = strlen(input); input_len = strlen(input);
ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL);

View File

@ -25,21 +25,20 @@
#define LIBREGEXP_H #define LIBREGEXP_H
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include "libunicode.h"
#define LRE_BOOL int /* for documentation purposes */
#define LRE_FLAG_GLOBAL (1 << 0) #define LRE_FLAG_GLOBAL (1 << 0)
#define LRE_FLAG_IGNORECASE (1 << 1) #define LRE_FLAG_IGNORECASE (1 << 1)
#define LRE_FLAG_MULTILINE (1 << 2) #define LRE_FLAG_MULTILINE (1 << 2)
#define LRE_FLAG_DOTALL (1 << 3) #define LRE_FLAG_DOTALL (1 << 3)
#define LRE_FLAG_UTF16 (1 << 4) #define LRE_FLAG_UNICODE (1 << 4)
#define LRE_FLAG_STICKY (1 << 5) #define LRE_FLAG_STICKY (1 << 5)
#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */ #define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */
#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ #define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
#define LRE_RET_MEMORY_ERROR (-1)
#define LRE_RET_TIMEOUT (-2)
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
const char *buf, size_t buf_len, int re_flags, const char *buf, size_t buf_len, int re_flags,
void *opaque); void *opaque);
@ -51,43 +50,11 @@ int lre_exec(uint8_t **capture,
int cbuf_type, void *opaque); int cbuf_type, void *opaque);
int lre_parse_escape(const uint8_t **pp, int allow_utf16); int lre_parse_escape(const uint8_t **pp, int allow_utf16);
LRE_BOOL lre_is_space(int c);
/* must be provided by the user */ /* must be provided by the user, return non zero if overflow */
LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); int lre_check_stack_overflow(void *opaque, size_t alloca_size);
/* must be provided by the user, return non zero if time out */
int lre_check_timeout(void *opaque);
void *lre_realloc(void *opaque, void *ptr, size_t size); void *lre_realloc(void *opaque, void *ptr, size_t size);
/* JS identifier test */
extern uint32_t const lre_id_start_table_ascii[4];
extern uint32_t const lre_id_continue_table_ascii[4];
static inline int lre_js_is_ident_first(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space(c);
#endif
}
}
static inline int lre_js_is_ident_next(int c)
{
if ((uint32_t)c < 128) {
return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1;
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c) || c == 0x200C || c == 0x200D;
#else
return !lre_is_space(c) || c == 0x200C || c == 0x200D;
#endif
}
}
#undef LRE_BOOL
#endif /* LIBREGEXP_H */ #endif /* LIBREGEXP_H */

File diff suppressed because it is too large Load Diff

View File

@ -262,11 +262,7 @@ int lre_canonicalize(uint32_t c, BOOL is_unicode)
static uint32_t get_le24(const uint8_t *ptr) static uint32_t get_le24(const uint8_t *ptr)
{ {
#if defined(__x86__) || defined(__x86_64__)
return *(uint16_t *)ptr | (ptr[2] << 16);
#else
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);
#endif
} }
#define UNICODE_INDEX_BLOCK_LEN 32 #define UNICODE_INDEX_BLOCK_LEN 32
@ -317,6 +313,14 @@ static BOOL lre_is_in_table(uint32_t c, const uint8_t *table,
return FALSE; /* outside the table */ return FALSE; /* outside the table */
p = table + pos; p = table + pos;
bit = 0; bit = 0;
/* Compressed run length encoding:
00..3F: 2 packed lengths: 3-bit + 3-bit
40..5F: 5-bits plus extra byte for length
60..7F: 5-bits plus 2 extra bytes for length
80..FF: 7-bit length
lengths must be incremented to get character count
Ranges alternate between false and true return value.
*/
for(;;) { for(;;) {
b = *p++; b = *p++;
if (b < 64) { if (b < 64) {
@ -533,6 +537,207 @@ int cr_invert(CharRange *cr)
return 0; return 0;
} }
#define CASE_U (1 << 0)
#define CASE_L (1 << 1)
#define CASE_F (1 << 2)
/* use the case conversion table to generate range of characters.
CASE_U: set char if modified by uppercasing,
CASE_L: set char if modified by lowercasing,
CASE_F: set char if modified by case folding,
*/
static int unicode_case1(CharRange *cr, int case_mask)
{
#define MR(x) (1 << RUN_TYPE_ ## x)
const uint32_t tab_run_mask[3] = {
MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) |
MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3),
MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2),
MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3),
};
#undef MR
uint32_t mask, v, code, type, len, i, idx;
if (case_mask == 0)
return 0;
mask = 0;
for(i = 0; i < 3; i++) {
if ((case_mask >> i) & 1)
mask |= tab_run_mask[i];
}
for(idx = 0; idx < countof(case_conv_table1); idx++) {
v = case_conv_table1[idx];
type = (v >> (32 - 17 - 7 - 4)) & 0xf;
code = v >> (32 - 17);
len = (v >> (32 - 17 - 7)) & 0x7f;
if ((mask >> type) & 1) {
// printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1);
switch(type) {
case RUN_TYPE_UL:
if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F)))
goto def_case;
code += ((case_mask & CASE_U) != 0);
for(i = 0; i < len; i += 2) {
if (cr_add_interval(cr, code + i, code + i + 1))
return -1;
}
break;
case RUN_TYPE_LSU:
if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F)))
goto def_case;
if (!(case_mask & CASE_U)) {
if (cr_add_interval(cr, code, code + 1))
return -1;
}
if (cr_add_interval(cr, code + 1, code + 2))
return -1;
if (case_mask & CASE_U) {
if (cr_add_interval(cr, code + 2, code + 3))
return -1;
}
break;
default:
def_case:
if (cr_add_interval(cr, code, code + len))
return -1;
break;
}
}
}
return 0;
}
static int point_cmp(const void *p1, const void *p2, void *arg)
{
uint32_t v1 = *(uint32_t *)p1;
uint32_t v2 = *(uint32_t *)p2;
return (v1 > v2) - (v1 < v2);
}
static void cr_sort_and_remove_overlap(CharRange *cr)
{
uint32_t start, end, start1, end1, i, j;
/* the resulting ranges are not necessarily sorted and may overlap */
rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL);
j = 0;
for(i = 0; i < cr->len; ) {
start = cr->points[i];
end = cr->points[i + 1];
i += 2;
while (i < cr->len) {
start1 = cr->points[i];
end1 = cr->points[i + 1];
if (start1 > end) {
/* |------|
* |-------| */
break;
} else if (end1 <= end) {
/* |------|
* |--| */
i += 2;
} else {
/* |------|
* |-------| */
end = end1;
i += 2;
}
}
cr->points[j] = start;
cr->points[j + 1] = end;
j += 2;
}
cr->len = j;
}
/* canonicalize a character set using the JS regex case folding rules
(see lre_canonicalize()) */
int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode)
{
CharRange cr_inter, cr_mask, cr_result, cr_sub;
uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d;
cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func);
cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func);
cr_init(&cr_result, cr->mem_opaque, cr->realloc_func);
cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func);
if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U))
goto fail;
if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER))
goto fail;
if (cr_invert(&cr_mask))
goto fail;
if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER))
goto fail;
/* cr_inter = cr & cr_mask */
/* cr_sub = cr & ~cr_mask */
/* use the case conversion table to compute the result */
d_start = -1;
d_end = -1;
idx = 0;
v = case_conv_table1[idx];
code = v >> (32 - 17);
len = (v >> (32 - 17 - 7)) & 0x7f;
for(i = 0; i < cr_inter.len; i += 2) {
start = cr_inter.points[i];
end = cr_inter.points[i + 1];
for(c = start; c < end; c++) {
for(;;) {
if (c >= code && c < code + len)
break;
idx++;
assert(idx < countof(case_conv_table1));
v = case_conv_table1[idx];
code = v >> (32 - 17);
len = (v >> (32 - 17 - 7)) & 0x7f;
}
d = lre_case_folding_entry(c, idx, v, is_unicode);
/* try to merge with the current interval */
if (d_start == -1) {
d_start = d;
d_end = d + 1;
} else if (d_end == d) {
d_end++;
} else {
cr_add_interval(&cr_result, d_start, d_end);
d_start = d;
d_end = d + 1;
}
}
}
if (d_start != -1) {
if (cr_add_interval(&cr_result, d_start, d_end))
goto fail;
}
/* the resulting ranges are not necessarily sorted and may overlap */
cr_sort_and_remove_overlap(&cr_result);
/* or with the character not affected by the case folding */
cr->len = 0;
if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION))
goto fail;
cr_free(&cr_inter);
cr_free(&cr_mask);
cr_free(&cr_result);
cr_free(&cr_sub);
return 0;
fail:
cr_free(&cr_inter);
cr_free(&cr_mask);
cr_free(&cr_result);
cr_free(&cr_sub);
return -1;
}
#ifdef CONFIG_ALL_UNICODE #ifdef CONFIG_ALL_UNICODE
BOOL lre_is_id_start(uint32_t c) BOOL lre_is_id_start(uint32_t c)
@ -833,6 +1038,13 @@ static int unicode_get_cc(uint32_t c)
if (pos < 0) if (pos < 0)
return 0; return 0;
p = unicode_cc_table + pos; p = unicode_cc_table + pos;
/* Compressed run length encoding:
- 2 high order bits are combining class type
- 0:0, 1:230, 2:extra byte linear progression, 3:extra byte
- 00..2F: range length (add 1)
- 30..37: 3-bit range-length + 1 extra byte
- 38..3F: 3-bit range-length + 2 extra byte
*/
for(;;) { for(;;) {
b = *p++; b = *p++;
type = b >> 6; type = b >> 6;
@ -1185,6 +1397,15 @@ static int unicode_general_category1(CharRange *cr, uint32_t gc_mask)
p = unicode_gc_table; p = unicode_gc_table;
p_end = unicode_gc_table + countof(unicode_gc_table); p_end = unicode_gc_table + countof(unicode_gc_table);
c = 0; c = 0;
/* Compressed range encoding:
initial byte:
bits 0..4: category number (special case 31)
bits 5..7: range length (add 1)
special case bits 5..7 == 7: read an extra byte
- 00..7F: range length (add 7 + 1)
- 80..BF: 6-bits plus extra byte for range length (add 7 + 128)
- C0..FF: 6-bits plus 2 extra bytes for range length (add 7 + 128 + 16384)
*/
while (p < p_end) { while (p < p_end) {
b = *p++; b = *p++;
n = b >> 5; n = b >> 5;
@ -1238,6 +1459,14 @@ static int unicode_prop1(CharRange *cr, int prop_idx)
p_end = p + unicode_prop_len_table[prop_idx]; p_end = p + unicode_prop_len_table[prop_idx];
c = 0; c = 0;
bit = 0; bit = 0;
/* Compressed range encoding:
00..3F: 2 packed lengths: 3-bit + 3-bit
40..5F: 5-bits plus extra byte for length
60..7F: 5-bits plus 2 extra bytes for length
80..FF: 7-bit length
lengths must be incremented to get character count
Ranges alternate between false and true return value.
*/
while (p < p_end) { while (p < p_end) {
c0 = c; c0 = c;
b = *p++; b = *p++;
@ -1268,207 +1497,6 @@ static int unicode_prop1(CharRange *cr, int prop_idx)
return 0; return 0;
} }
#define CASE_U (1 << 0)
#define CASE_L (1 << 1)
#define CASE_F (1 << 2)
/* use the case conversion table to generate range of characters.
CASE_U: set char if modified by uppercasing,
CASE_L: set char if modified by lowercasing,
CASE_F: set char if modified by case folding,
*/
static int unicode_case1(CharRange *cr, int case_mask)
{
#define MR(x) (1 << RUN_TYPE_ ## x)
const uint32_t tab_run_mask[3] = {
MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) |
MR(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3),
MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2),
MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3),
};
#undef MR
uint32_t mask, v, code, type, len, i, idx;
if (case_mask == 0)
return 0;
mask = 0;
for(i = 0; i < 3; i++) {
if ((case_mask >> i) & 1)
mask |= tab_run_mask[i];
}
for(idx = 0; idx < countof(case_conv_table1); idx++) {
v = case_conv_table1[idx];
type = (v >> (32 - 17 - 7 - 4)) & 0xf;
code = v >> (32 - 17);
len = (v >> (32 - 17 - 7)) & 0x7f;
if ((mask >> type) & 1) {
// printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1);
switch(type) {
case RUN_TYPE_UL:
if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F)))
goto def_case;
code += ((case_mask & CASE_U) != 0);
for(i = 0; i < len; i += 2) {
if (cr_add_interval(cr, code + i, code + i + 1))
return -1;
}
break;
case RUN_TYPE_LSU:
if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F)))
goto def_case;
if (!(case_mask & CASE_U)) {
if (cr_add_interval(cr, code, code + 1))
return -1;
}
if (cr_add_interval(cr, code + 1, code + 2))
return -1;
if (case_mask & CASE_U) {
if (cr_add_interval(cr, code + 2, code + 3))
return -1;
}
break;
default:
def_case:
if (cr_add_interval(cr, code, code + len))
return -1;
break;
}
}
}
return 0;
}
static int point_cmp(const void *p1, const void *p2, void *arg)
{
uint32_t v1 = *(uint32_t *)p1;
uint32_t v2 = *(uint32_t *)p2;
return (v1 > v2) - (v1 < v2);
}
static void cr_sort_and_remove_overlap(CharRange *cr)
{
uint32_t start, end, start1, end1, i, j;
/* the resulting ranges are not necessarily sorted and may overlap */
rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL);
j = 0;
for(i = 0; i < cr->len; ) {
start = cr->points[i];
end = cr->points[i + 1];
i += 2;
while (i < cr->len) {
start1 = cr->points[i];
end1 = cr->points[i + 1];
if (start1 > end) {
/* |------|
* |-------| */
break;
} else if (end1 <= end) {
/* |------|
* |--| */
i += 2;
} else {
/* |------|
* |-------| */
end = end1;
i += 2;
}
}
cr->points[j] = start;
cr->points[j + 1] = end;
j += 2;
}
cr->len = j;
}
/* canonicalize a character set using the JS regex case folding rules
(see lre_canonicalize()) */
int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode)
{
CharRange cr_inter, cr_mask, cr_result, cr_sub;
uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d;
cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func);
cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func);
cr_init(&cr_result, cr->mem_opaque, cr->realloc_func);
cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func);
if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U))
goto fail;
if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER))
goto fail;
if (cr_invert(&cr_mask))
goto fail;
if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER))
goto fail;
/* cr_inter = cr & cr_mask */
/* cr_sub = cr & ~cr_mask */
/* use the case conversion table to compute the result */
d_start = -1;
d_end = -1;
idx = 0;
v = case_conv_table1[idx];
code = v >> (32 - 17);
len = (v >> (32 - 17 - 7)) & 0x7f;
for(i = 0; i < cr_inter.len; i += 2) {
start = cr_inter.points[i];
end = cr_inter.points[i + 1];
for(c = start; c < end; c++) {
for(;;) {
if (c >= code && c < code + len)
break;
idx++;
assert(idx < countof(case_conv_table1));
v = case_conv_table1[idx];
code = v >> (32 - 17);
len = (v >> (32 - 17 - 7)) & 0x7f;
}
d = lre_case_folding_entry(c, idx, v, is_unicode);
/* try to merge with the current interval */
if (d_start == -1) {
d_start = d;
d_end = d + 1;
} else if (d_end == d) {
d_end++;
} else {
cr_add_interval(&cr_result, d_start, d_end);
d_start = d;
d_end = d + 1;
}
}
}
if (d_start != -1) {
if (cr_add_interval(&cr_result, d_start, d_end))
goto fail;
}
/* the resulting ranges are not necessarily sorted and may overlap */
cr_sort_and_remove_overlap(&cr_result);
/* or with the character not affected by the case folding */
cr->len = 0;
if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION))
goto fail;
cr_free(&cr_inter);
cr_free(&cr_mask);
cr_free(&cr_result);
cr_free(&cr_sub);
return 0;
fail:
cr_free(&cr_inter);
cr_free(&cr_mask);
cr_free(&cr_result);
cr_free(&cr_sub);
return -1;
}
typedef enum { typedef enum {
POP_GC, POP_GC,
POP_PROP, POP_PROP,
@ -1786,3 +1814,97 @@ int unicode_prop(CharRange *cr, const char *prop_name)
} }
#endif /* CONFIG_ALL_UNICODE */ #endif /* CONFIG_ALL_UNICODE */
/*---- lre codepoint categorizing functions ----*/
#define S UNICODE_C_SPACE
#define D UNICODE_C_DIGIT
#define X UNICODE_C_XDIGIT
#define U UNICODE_C_UPPER
#define L UNICODE_C_LOWER
#define _ UNICODE_C_UNDER
#define d UNICODE_C_DOLLAR
uint8_t const lre_ctype_bits[256] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, S, S, S, S, S, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
S, 0, 0, 0, d, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
X|D, X|D, X|D, X|D, X|D, X|D, X|D, X|D,
X|D, X|D, 0, 0, 0, 0, 0, 0,
0, X|U, X|U, X|U, X|U, X|U, X|U, U,
U, U, U, U, U, U, U, U,
U, U, U, U, U, U, U, U,
U, U, U, 0, 0, 0, 0, _,
0, X|L, X|L, X|L, X|L, X|L, X|L, L,
L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L,
L, L, L, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
S, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
#undef S
#undef D
#undef X
#undef U
#undef L
#undef _
#undef d
/* code point ranges for Zs,Zl or Zp property */
static const uint16_t char_range_s[] = {
10,
0x0009, 0x000D + 1,
0x0020, 0x0020 + 1,
0x00A0, 0x00A0 + 1,
0x1680, 0x1680 + 1,
0x2000, 0x200A + 1,
/* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */
/* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */
0x2028, 0x2029 + 1,
0x202F, 0x202F + 1,
0x205F, 0x205F + 1,
0x3000, 0x3000 + 1,
/* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */
0xFEFF, 0xFEFF + 1,
};
BOOL lre_is_space_non_ascii(uint32_t c)
{
size_t i, n;
n = countof(char_range_s);
for(i = 5; i < n; i += 2) {
uint32_t low = char_range_s[i];
uint32_t high = char_range_s[i + 1];
if (c < low)
return FALSE;
if (c < high)
return TRUE;
}
return FALSE;
}

View File

@ -24,27 +24,13 @@
#ifndef LIBUNICODE_H #ifndef LIBUNICODE_H
#define LIBUNICODE_H #define LIBUNICODE_H
#include <inttypes.h> #include <stdint.h>
#define LRE_BOOL int /* for documentation purposes */
/* define it to include all the unicode tables (40KB larger) */ /* define it to include all the unicode tables (40KB larger) */
#define CONFIG_ALL_UNICODE #define CONFIG_ALL_UNICODE
#define LRE_CC_RES_LEN_MAX 3 #define LRE_CC_RES_LEN_MAX 3
typedef enum {
UNICODE_NFC,
UNICODE_NFD,
UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
int lre_canonicalize(uint32_t c, BOOL is_unicode);
LRE_BOOL lre_is_cased(uint32_t c);
LRE_BOOL lre_is_case_ignorable(uint32_t c);
/* char ranges */ /* char ranges */
typedef struct { typedef struct {
@ -102,12 +88,14 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
int cr_invert(CharRange *cr); int cr_invert(CharRange *cr);
int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode); int cr_regexp_canonicalize(CharRange *cr, int is_unicode);
#ifdef CONFIG_ALL_UNICODE typedef enum {
UNICODE_NFC,
LRE_BOOL lre_is_id_start(uint32_t c); UNICODE_NFD,
LRE_BOOL lre_is_id_continue(uint32_t c); UNICODE_NFKC,
UNICODE_NFKD,
} UnicodeNormalizationEnum;
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
UnicodeNormalizationEnum n_type, UnicodeNormalizationEnum n_type,
@ -115,13 +103,80 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
/* Unicode character range functions */ /* Unicode character range functions */
int unicode_script(CharRange *cr, int unicode_script(CharRange *cr, const char *script_name, int is_ext);
const char *script_name, LRE_BOOL is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name); int unicode_general_category(CharRange *cr, const char *gc_name);
int unicode_prop(CharRange *cr, const char *prop_name); int unicode_prop(CharRange *cr, const char *prop_name);
#endif /* CONFIG_ALL_UNICODE */ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
int lre_canonicalize(uint32_t c, int is_unicode);
#undef LRE_BOOL /* Code point type categories */
enum {
UNICODE_C_SPACE = (1 << 0),
UNICODE_C_DIGIT = (1 << 1),
UNICODE_C_UPPER = (1 << 2),
UNICODE_C_LOWER = (1 << 3),
UNICODE_C_UNDER = (1 << 4),
UNICODE_C_DOLLAR = (1 << 5),
UNICODE_C_XDIGIT = (1 << 6),
};
extern uint8_t const lre_ctype_bits[256];
/* zero or non-zero return value */
int lre_is_cased(uint32_t c);
int lre_is_case_ignorable(uint32_t c);
int lre_is_id_start(uint32_t c);
int lre_is_id_continue(uint32_t c);
static inline int lre_is_space_byte(uint8_t c) {
return lre_ctype_bits[c] & UNICODE_C_SPACE;
}
static inline int lre_is_id_start_byte(uint8_t c) {
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
UNICODE_C_UNDER | UNICODE_C_DOLLAR);
}
static inline int lre_is_id_continue_byte(uint8_t c) {
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
UNICODE_C_UNDER | UNICODE_C_DOLLAR |
UNICODE_C_DIGIT);
}
int lre_is_space_non_ascii(uint32_t c);
static inline int lre_is_space(uint32_t c) {
if (c < 256)
return lre_is_space_byte(c);
else
return lre_is_space_non_ascii(c);
}
static inline int lre_js_is_ident_first(uint32_t c) {
if (c < 128) {
return lre_is_id_start_byte(c);
} else {
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_start(c);
#else
return !lre_is_space_non_ascii(c);
#endif
}
}
static inline int lre_js_is_ident_next(uint32_t c) {
if (c < 128) {
return lre_is_id_continue_byte(c);
} else {
/* ZWNJ and ZWJ are accepted in identifiers */
if (c >= 0x200C && c <= 0x200D)
return TRUE;
#ifdef CONFIG_ALL_UNICODE
return lre_is_id_continue(c);
#else
return !lre_is_space_non_ascii(c);
#endif
}
}
#endif /* LIBUNICODE_H */ #endif /* LIBUNICODE_H */

114
qjs.c
View File

@ -34,8 +34,10 @@
#include <time.h> #include <time.h>
#if defined(__APPLE__) #if defined(__APPLE__)
#include <malloc/malloc.h> #include <malloc/malloc.h>
#elif defined(__linux__) #elif defined(__linux__) || defined(__GLIBC__)
#include <malloc.h> #include <malloc.h>
#elif defined(__FreeBSD__)
#include <malloc_np.h>
#endif #endif
#include "cutils.h" #include "cutils.h"
@ -43,11 +45,6 @@
extern const uint8_t qjsc_repl[]; extern const uint8_t qjsc_repl[];
extern const uint32_t qjsc_repl_size; extern const uint32_t qjsc_repl_size;
#ifdef CONFIG_BIGNUM
extern const uint8_t qjsc_qjscalc[];
extern const uint32_t qjsc_qjscalc_size;
static int bignum_ext;
#endif
static int eval_buf(JSContext *ctx, const void *buf, int buf_len, static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
const char *filename, int eval_flags) const char *filename, int eval_flags)
@ -64,6 +61,7 @@ static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
js_module_set_import_meta(ctx, val, TRUE, TRUE); js_module_set_import_meta(ctx, val, TRUE, TRUE);
val = JS_EvalFunction(ctx, val); val = JS_EvalFunction(ctx, val);
} }
val = js_std_await(ctx, val);
} else { } else {
val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
} }
@ -109,14 +107,6 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt)
ctx = JS_NewContext(rt); ctx = JS_NewContext(rt);
if (!ctx) if (!ctx)
return NULL; return NULL;
#ifdef CONFIG_BIGNUM
if (bignum_ext) {
JS_AddIntrinsicBigFloat(ctx);
JS_AddIntrinsicBigDecimal(ctx);
JS_AddIntrinsicOperators(ctx);
JS_EnableBignumExt(ctx, TRUE);
}
#endif
/* system modules */ /* system modules */
js_init_module_std(ctx, "std"); js_init_module_std(ctx, "std");
js_init_module_os(ctx, "os"); js_init_module_os(ctx, "os");
@ -148,7 +138,7 @@ static size_t js_trace_malloc_usable_size(const void *ptr)
return _msize((void *)ptr); return _msize((void *)ptr);
#elif defined(EMSCRIPTEN) #elif defined(EMSCRIPTEN)
return 0; return 0;
#elif defined(__linux__) #elif defined(__linux__) || defined(__GLIBC__)
return malloc_usable_size((void *)ptr); return malloc_usable_size((void *)ptr);
#else #else
/* change this to `return 0;` if compilation fails */ /* change this to `return 0;` if compilation fails */
@ -267,6 +257,32 @@ static const JSMallocFunctions trace_mf = {
js_trace_malloc_usable_size, js_trace_malloc_usable_size,
}; };
static size_t get_suffixed_size(const char *str)
{
char *p;
size_t v;
v = (size_t)strtod(str, &p);
switch(*p) {
case 'G':
v <<= 30;
break;
case 'M':
v <<= 20;
break;
case 'k':
case 'K':
v <<= 10;
break;
default:
if (*p != '\0') {
fprintf(stderr, "qjs: invalid suffix: %s\n", p);
exit(1);
}
break;
}
return v;
}
#define PROG_NAME "qjs" #define PROG_NAME "qjs"
void help(void) void help(void)
@ -280,15 +296,13 @@ void help(void)
" --script load as ES6 script (default=autodetect)\n" " --script load as ES6 script (default=autodetect)\n"
"-I --include file include an additional file\n" "-I --include file include an additional file\n"
" --std make 'std' and 'os' available to the loaded script\n" " --std make 'std' and 'os' available to the loaded script\n"
#ifdef CONFIG_BIGNUM
" --bignum enable the bignum extensions (BigFloat, BigDecimal)\n"
" --qjscalc load the QJSCalc runtime (default if invoked as qjscalc)\n"
#endif
"-T --trace trace memory allocation\n" "-T --trace trace memory allocation\n"
"-d --dump dump the memory usage stats\n" "-d --dump dump the memory usage stats\n"
" --memory-limit n limit the memory usage to 'n' bytes\n" " --memory-limit n limit the memory usage to 'n' bytes (SI suffixes allowed)\n"
" --stack-size n limit the stack size to 'n' bytes\n" " --stack-size n limit the stack size to 'n' bytes (SI suffixes allowed)\n"
" --unhandled-rejection dump unhandled promise rejections\n" " --no-unhandled-rejection ignore unhandled promise rejections\n"
"-s strip all the debug info\n"
" --strip-source strip the source code\n"
"-q --quit just instantiate the interpreter and quit\n"); "-q --quit just instantiate the interpreter and quit\n");
exit(1); exit(1);
} }
@ -306,27 +320,13 @@ int main(int argc, char **argv)
int empty_run = 0; int empty_run = 0;
int module = -1; int module = -1;
int load_std = 0; int load_std = 0;
int dump_unhandled_promise_rejection = 0; int dump_unhandled_promise_rejection = 1;
size_t memory_limit = 0; size_t memory_limit = 0;
char *include_list[32]; char *include_list[32];
int i, include_count = 0; int i, include_count = 0;
#ifdef CONFIG_BIGNUM int strip_flags = 0;
int load_jscalc;
#endif
size_t stack_size = 0; size_t stack_size = 0;
#ifdef CONFIG_BIGNUM
/* load jscalc runtime if invoked as 'qjscalc' */
{
const char *p, *exename;
exename = argv[0];
p = strrchr(exename, '/');
if (p)
exename = p + 1;
load_jscalc = !strcmp(exename, "qjscalc");
}
#endif
/* cannot use getopt because we want to pass the command line to /* cannot use getopt because we want to pass the command line to
the script */ the script */
optind = 1; optind = 1;
@ -400,20 +400,10 @@ int main(int argc, char **argv)
load_std = 1; load_std = 1;
continue; continue;
} }
if (!strcmp(longopt, "unhandled-rejection")) { if (!strcmp(longopt, "no-unhandled-rejection")) {
dump_unhandled_promise_rejection = 1; dump_unhandled_promise_rejection = 0;
continue; continue;
} }
#ifdef CONFIG_BIGNUM
if (!strcmp(longopt, "bignum")) {
bignum_ext = 1;
continue;
}
if (!strcmp(longopt, "qjscalc")) {
load_jscalc = 1;
continue;
}
#endif
if (opt == 'q' || !strcmp(longopt, "quit")) { if (opt == 'q' || !strcmp(longopt, "quit")) {
empty_run++; empty_run++;
continue; continue;
@ -423,7 +413,7 @@ int main(int argc, char **argv)
fprintf(stderr, "expecting memory limit"); fprintf(stderr, "expecting memory limit");
exit(1); exit(1);
} }
memory_limit = (size_t)strtod(argv[optind++], NULL); memory_limit = get_suffixed_size(argv[optind++]);
continue; continue;
} }
if (!strcmp(longopt, "stack-size")) { if (!strcmp(longopt, "stack-size")) {
@ -431,7 +421,15 @@ int main(int argc, char **argv)
fprintf(stderr, "expecting stack size"); fprintf(stderr, "expecting stack size");
exit(1); exit(1);
} }
stack_size = (size_t)strtod(argv[optind++], NULL); stack_size = get_suffixed_size(argv[optind++]);
continue;
}
if (opt == 's') {
strip_flags = JS_STRIP_DEBUG;
continue;
}
if (!strcmp(longopt, "strip-source")) {
strip_flags = JS_STRIP_SOURCE;
continue; continue;
} }
if (opt) { if (opt) {
@ -443,11 +441,6 @@ int main(int argc, char **argv)
} }
} }
#ifdef CONFIG_BIGNUM
if (load_jscalc)
bignum_ext = 1;
#endif
if (trace_memory) { if (trace_memory) {
js_trace_malloc_init(&trace_data); js_trace_malloc_init(&trace_data);
rt = JS_NewRuntime2(&trace_mf, &trace_data); rt = JS_NewRuntime2(&trace_mf, &trace_data);
@ -462,6 +455,7 @@ int main(int argc, char **argv)
JS_SetMemoryLimit(rt, memory_limit); JS_SetMemoryLimit(rt, memory_limit);
if (stack_size != 0) if (stack_size != 0)
JS_SetMaxStackSize(rt, stack_size); JS_SetMaxStackSize(rt, stack_size);
JS_SetStripInfo(rt, strip_flags);
js_std_set_worker_new_context_func(JS_NewCustomContext); js_std_set_worker_new_context_func(JS_NewCustomContext);
js_std_init_handlers(rt); js_std_init_handlers(rt);
ctx = JS_NewCustomContext(rt); ctx = JS_NewCustomContext(rt);
@ -479,11 +473,6 @@ int main(int argc, char **argv)
} }
if (!empty_run) { if (!empty_run) {
#ifdef CONFIG_BIGNUM
if (load_jscalc) {
js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0);
}
#endif
js_std_add_helpers(ctx, argc - optind, argv + optind); js_std_add_helpers(ctx, argc - optind, argv + optind);
/* make 'std' and 'os' visible to non module code */ /* make 'std' and 'os' visible to non module code */
@ -514,6 +503,7 @@ int main(int argc, char **argv)
goto fail; goto fail;
} }
if (interactive) { if (interactive) {
JS_SetHostPromiseRejectionTracker(rt, NULL, NULL);
js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
} }
js_std_loop(ctx); js_std_loop(ctx);

212
qjsc.c
View File

@ -76,7 +76,7 @@ static const FeatureEntry feature_list[] = {
{ "promise", "Promise" }, { "promise", "Promise" },
#define FE_MODULE_LOADER 9 #define FE_MODULE_LOADER 9
{ "module-loader", NULL }, { "module-loader", NULL },
{ "bigint", "BigInt" }, { "weakref", "WeakRef" },
}; };
void namelist_add(namelist_t *lp, const char *name, const char *short_name, void namelist_add(namelist_t *lp, const char *name, const char *short_name,
@ -353,13 +353,14 @@ void help(void)
"-M module_name[,cname] add initialization code for an external C module\n" "-M module_name[,cname] add initialization code for an external C module\n"
"-x byte swapped output\n" "-x byte swapped output\n"
"-p prefix set the prefix of the generated C names\n" "-p prefix set the prefix of the generated C names\n"
"-S n set the maximum stack size to 'n' bytes (default=%d)\n", "-S n set the maximum stack size to 'n' bytes (default=%d)\n"
"-s strip all the debug info\n"
"--keep-source keep the source code\n",
JS_DEFAULT_STACK_SIZE); JS_DEFAULT_STACK_SIZE);
#ifdef CONFIG_LTO #ifdef CONFIG_LTO
{ {
int i; int i;
printf("-flto use link time optimization\n"); printf("-flto use link time optimization\n");
printf("-fbignum enable bignum extensions\n");
printf("-fno-["); printf("-fno-[");
for(i = 0; i < countof(feature_list); i++) { for(i = 0; i < countof(feature_list); i++) {
if (i != 0) if (i != 0)
@ -473,6 +474,31 @@ static int output_executable(const char *out_filename, const char *cfilename,
} }
#endif #endif
static size_t get_suffixed_size(const char *str)
{
char *p;
size_t v;
v = (size_t)strtod(str, &p);
switch(*p) {
case 'G':
v <<= 30;
break;
case 'M':
v <<= 20;
break;
case 'k':
case 'K':
v <<= 10;
break;
default:
if (*p != '\0') {
fprintf(stderr, "qjs: invalid suffix: %s\n", p);
exit(1);
}
break;
}
return v;
}
typedef enum { typedef enum {
OUTPUT_C, OUTPUT_C,
@ -480,9 +506,24 @@ typedef enum {
OUTPUT_EXECUTABLE, OUTPUT_EXECUTABLE,
} OutputTypeEnum; } OutputTypeEnum;
static const char *get_short_optarg(int *poptind, int opt,
const char *arg, int argc, char **argv)
{
const char *optarg;
if (*arg) {
optarg = arg;
} else if (*poptind < argc) {
optarg = argv[(*poptind)++];
} else {
fprintf(stderr, "qjsc: expecting parameter for -%c\n", opt);
exit(1);
}
return optarg;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int c, i, verbose; int i, verbose, strip_flags;
const char *out_filename, *cname; const char *out_filename, *cname;
char cfilename[1024]; char cfilename[1024];
FILE *fo; FILE *fo;
@ -492,9 +533,6 @@ int main(int argc, char **argv)
int module; int module;
OutputTypeEnum output_type; OutputTypeEnum output_type;
size_t stack_size; size_t stack_size;
#ifdef CONFIG_BIGNUM
BOOL bignum_ext = FALSE;
#endif
namelist_t dynamic_module_list; namelist_t dynamic_module_list;
out_filename = NULL; out_filename = NULL;
@ -504,6 +542,7 @@ int main(int argc, char **argv)
module = -1; module = -1;
byte_swap = FALSE; byte_swap = FALSE;
verbose = 0; verbose = 0;
strip_flags = JS_STRIP_SOURCE;
use_lto = FALSE; use_lto = FALSE;
stack_size = 0; stack_size = 0;
memset(&dynamic_module_list, 0, sizeof(dynamic_module_list)); memset(&dynamic_module_list, 0, sizeof(dynamic_module_list));
@ -512,30 +551,51 @@ int main(int argc, char **argv)
namelist_add(&cmodule_list, "std", "std", 0); namelist_add(&cmodule_list, "std", "std", 0);
namelist_add(&cmodule_list, "os", "os", 0); namelist_add(&cmodule_list, "os", "os", 0);
for(;;) { optind = 1;
c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:D:"); while (optind < argc && *argv[optind] == '-') {
if (c == -1) char *arg = argv[optind] + 1;
const char *longopt = "";
const char *optarg;
/* a single - is not an option, it also stops argument scanning */
if (!*arg)
break; break;
switch(c) { optind++;
case 'h': if (*arg == '-') {
help(); longopt = arg + 1;
case 'o': arg += strlen(arg);
out_filename = optarg; /* -- stops argument scanning */
break; if (!*longopt)
case 'c': break;
output_type = OUTPUT_C; }
break; for (; *arg || *longopt; longopt = "") {
case 'e': char opt = *arg;
output_type = OUTPUT_C_MAIN; if (opt)
break; arg++;
case 'N': if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) {
cname = optarg; help();
break; continue;
case 'f': }
{ if (opt == 'o') {
out_filename = get_short_optarg(&optind, opt, arg, argc, argv);
break;
}
if (opt == 'c') {
output_type = OUTPUT_C;
continue;
}
if (opt == 'e') {
output_type = OUTPUT_C_MAIN;
continue;
}
if (opt == 'N') {
cname = get_short_optarg(&optind, opt, arg, argc, argv);
break;
}
if (opt == 'f') {
const char *p; const char *p;
optarg = get_short_optarg(&optind, opt, arg, argc, argv);
p = optarg; p = optarg;
if (!strcmp(optarg, "lto")) { if (!strcmp(p, "lto")) {
use_lto = TRUE; use_lto = TRUE;
} else if (strstart(p, "no-", &p)) { } else if (strstart(p, "no-", &p)) {
use_lto = TRUE; use_lto = TRUE;
@ -547,27 +607,23 @@ int main(int argc, char **argv)
} }
if (i == countof(feature_list)) if (i == countof(feature_list))
goto bad_feature; goto bad_feature;
} else } else {
#ifdef CONFIG_BIGNUM
if (!strcmp(optarg, "bignum")) {
bignum_ext = TRUE;
} else
#endif
{
bad_feature: bad_feature:
fprintf(stderr, "unsupported feature: %s\n", optarg); fprintf(stderr, "unsupported feature: %s\n", optarg);
exit(1); exit(1);
} }
break;
} }
break; if (opt == 'm') {
case 'm': module = 1;
module = 1; continue;
break; }
case 'M': if (opt == 'M') {
{
char *p; char *p;
char path[1024]; char path[1024];
char cname[1024]; char cname[1024];
optarg = get_short_optarg(&optind, opt, arg, argc, argv);
pstrcpy(path, sizeof(path), optarg); pstrcpy(path, sizeof(path), optarg);
p = strchr(path, ','); p = strchr(path, ',');
if (p) { if (p) {
@ -577,25 +633,44 @@ int main(int argc, char **argv)
get_c_name(cname, sizeof(cname), path); get_c_name(cname, sizeof(cname), path);
} }
namelist_add(&cmodule_list, path, cname, 0); namelist_add(&cmodule_list, path, cname, 0);
break;
} }
break; if (opt == 'D') {
case 'D': optarg = get_short_optarg(&optind, opt, arg, argc, argv);
namelist_add(&dynamic_module_list, optarg, NULL, 0); namelist_add(&dynamic_module_list, optarg, NULL, 0);
break; break;
case 'x': }
byte_swap = TRUE; if (opt == 'x') {
break; byte_swap = 1;
case 'v': continue;
verbose++; }
break; if (opt == 'v') {
case 'p': verbose++;
c_ident_prefix = optarg; continue;
break; }
case 'S': if (opt == 'p') {
stack_size = (size_t)strtod(optarg, NULL); c_ident_prefix = get_short_optarg(&optind, opt, arg, argc, argv);
break; break;
default: }
break; if (opt == 'S') {
optarg = get_short_optarg(&optind, opt, arg, argc, argv);
stack_size = get_suffixed_size(optarg);
break;
}
if (opt == 's') {
strip_flags = JS_STRIP_DEBUG;
continue;
}
if (!strcmp(longopt, "keep-source")) {
strip_flags = 0;
continue;
}
if (opt) {
fprintf(stderr, "qjsc: unknown option '-%c'\n", opt);
} else {
fprintf(stderr, "qjsc: unknown option '--%s'\n", longopt);
}
help();
} }
} }
@ -630,14 +705,8 @@ int main(int argc, char **argv)
rt = JS_NewRuntime(); rt = JS_NewRuntime();
ctx = JS_NewContext(rt); ctx = JS_NewContext(rt);
#ifdef CONFIG_BIGNUM
if (bignum_ext) { JS_SetStripInfo(rt, strip_flags);
JS_AddIntrinsicBigFloat(ctx);
JS_AddIntrinsicBigDecimal(ctx);
JS_AddIntrinsicOperators(ctx);
JS_EnableBignumExt(ctx, TRUE);
}
#endif
/* loader for ES6 modules */ /* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL); JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
@ -686,15 +755,6 @@ int main(int argc, char **argv)
feature_list[i].init_name); feature_list[i].init_name);
} }
} }
#ifdef CONFIG_BIGNUM
if (bignum_ext) {
fprintf(fo,
" JS_AddIntrinsicBigFloat(ctx);\n"
" JS_AddIntrinsicBigDecimal(ctx);\n"
" JS_AddIntrinsicOperators(ctx);\n"
" JS_EnableBignumExt(ctx, 1);\n");
}
#endif
/* add the precompiled modules (XXX: could modify the module /* add the precompiled modules (XXX: could modify the module
loader instead) */ loader instead) */
for(i = 0; i < init_module_list.count; i++) { for(i = 0; i < init_module_list.count; i++) {

2657
qjscalc.js

File diff suppressed because it is too large Load Diff

View File

@ -81,6 +81,7 @@ DEF(empty_string, "")
DEF(length, "length") DEF(length, "length")
DEF(fileName, "fileName") DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber") DEF(lineNumber, "lineNumber")
DEF(columnNumber, "columnNumber")
DEF(message, "message") DEF(message, "message")
DEF(cause, "cause") DEF(cause, "cause")
DEF(errors, "errors") DEF(errors, "errors")
@ -172,13 +173,10 @@ DEF(status, "status")
DEF(reason, "reason") DEF(reason, "reason")
DEF(globalThis, "globalThis") DEF(globalThis, "globalThis")
DEF(bigint, "bigint") DEF(bigint, "bigint")
#ifdef CONFIG_BIGNUM DEF(minus_zero, "-0")
DEF(bigfloat, "bigfloat") DEF(Infinity, "Infinity")
DEF(bigdecimal, "bigdecimal") DEF(minus_Infinity, "-Infinity")
DEF(roundingMode, "roundingMode") DEF(NaN, "NaN")
DEF(maximumSignificantDigits, "maximumSignificantDigits")
DEF(maximumFractionDigits, "maximumFractionDigits")
#endif
/* the following 3 atoms are only used with CONFIG_ATOMICS */ /* the following 3 atoms are only used with CONFIG_ATOMICS */
DEF(not_equal, "not-equal") DEF(not_equal, "not-equal")
DEF(timed_out, "timed-out") DEF(timed_out, "timed-out")
@ -217,13 +215,8 @@ DEF(Float32Array, "Float32Array")
DEF(Float64Array, "Float64Array") DEF(Float64Array, "Float64Array")
DEF(DataView, "DataView") DEF(DataView, "DataView")
DEF(BigInt, "BigInt") DEF(BigInt, "BigInt")
#ifdef CONFIG_BIGNUM DEF(WeakRef, "WeakRef")
DEF(BigFloat, "BigFloat") DEF(FinalizationRegistry, "FinalizationRegistry")
DEF(BigFloatEnv, "BigFloatEnv")
DEF(BigDecimal, "BigDecimal")
DEF(OperatorSet, "OperatorSet")
DEF(Operators, "Operators")
#endif
DEF(Map, "Map") DEF(Map, "Map")
DEF(Set, "Set") /* Map + 1 */ DEF(Set, "Set") /* Map + 1 */
DEF(WeakMap, "WeakMap") /* Map + 2 */ DEF(WeakMap, "WeakMap") /* Map + 2 */
@ -266,8 +259,5 @@ DEF(Symbol_hasInstance, "Symbol.hasInstance")
DEF(Symbol_species, "Symbol.species") DEF(Symbol_species, "Symbol.species")
DEF(Symbol_unscopables, "Symbol.unscopables") DEF(Symbol_unscopables, "Symbol.unscopables")
DEF(Symbol_asyncIterator, "Symbol.asyncIterator") DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
#ifdef CONFIG_BIGNUM
DEF(Symbol_operatorSet, "Symbol.operatorSet")
#endif
#endif /* DEF */ #endif /* DEF */

View File

@ -47,8 +47,15 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/wait.h> #include <sys/wait.h>
#if defined(__APPLE__) #if defined(__FreeBSD__)
extern char **environ;
#endif
#if defined(__APPLE__) || defined(__FreeBSD__)
typedef sig_t sighandler_t; typedef sig_t sighandler_t;
#endif
#if defined(__APPLE__)
#if !defined(environ) #if !defined(environ)
#include <crt_externs.h> #include <crt_externs.h>
#define environ (*_NSGetEnviron()) #define environ (*_NSGetEnviron())
@ -57,10 +64,8 @@ typedef sig_t sighandler_t;
#endif #endif
#if !defined(_WIN32) /* enable the os.Worker API. It relies on POSIX threads */
/* enable the os.Worker API. IT relies on POSIX threads */
#define USE_WORKER #define USE_WORKER
#endif
#ifdef USE_WORKER #ifdef USE_WORKER
#include <pthread.h> #include <pthread.h>
@ -71,6 +76,10 @@ typedef sig_t sighandler_t;
#include "list.h" #include "list.h"
#include "quickjs-libc.h" #include "quickjs-libc.h"
#if !defined(PATH_MAX)
#define PATH_MAX 4096
#endif
/* TODO: /* TODO:
- add socket calls - add socket calls
*/ */
@ -89,7 +98,7 @@ typedef struct {
typedef struct { typedef struct {
struct list_head link; struct list_head link;
BOOL has_object; int timer_id;
int64_t timeout; int64_t timeout;
JSValue func; JSValue func;
} JSOSTimer; } JSOSTimer;
@ -103,14 +112,22 @@ typedef struct {
size_t sab_tab_len; size_t sab_tab_len;
} JSWorkerMessage; } JSWorkerMessage;
typedef struct JSWaker {
#ifdef _WIN32
HANDLE handle;
#else
int read_fd;
int write_fd;
#endif
} JSWaker;
typedef struct { typedef struct {
int ref_count; int ref_count;
#ifdef USE_WORKER #ifdef USE_WORKER
pthread_mutex_t mutex; pthread_mutex_t mutex;
#endif #endif
struct list_head msg_queue; /* list of JSWorkerMessage.link */ struct list_head msg_queue; /* list of JSWorkerMessage.link */
int read_fd; JSWaker waker;
int write_fd;
} JSWorkerMessagePipe; } JSWorkerMessagePipe;
typedef struct { typedef struct {
@ -125,6 +142,7 @@ typedef struct JSThreadState {
struct list_head os_timers; /* list of JSOSTimer.link */ struct list_head os_timers; /* list of JSOSTimer.link */
struct list_head port_list; /* list of JSWorkerMessageHandler.link */ struct list_head port_list; /* list of JSWorkerMessageHandler.link */
int eval_script_recurse; /* only used in the main thread */ int eval_script_recurse; /* only used in the main thread */
int next_timer_id; /* for setTimeout() */
/* not used in the main thread */ /* not used in the main thread */
JSWorkerMessagePipe *recv_pipe, *send_pipe; JSWorkerMessagePipe *recv_pipe, *send_pipe;
} JSThreadState; } JSThreadState;
@ -149,7 +167,7 @@ static JSValue js_printf_internal(JSContext *ctx,
uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; uint8_t cbuf[UTF8_CHAR_LEN_MAX+1];
JSValue res; JSValue res;
DynBuf dbuf; DynBuf dbuf;
const char *fmt_str; const char *fmt_str = NULL;
const uint8_t *fmt, *fmt_end; const uint8_t *fmt, *fmt_end;
const uint8_t *p; const uint8_t *p;
char *q; char *q;
@ -250,7 +268,7 @@ static JSValue js_printf_internal(JSContext *ctx,
string_arg = JS_ToCString(ctx, argv[i++]); string_arg = JS_ToCString(ctx, argv[i++]);
if (!string_arg) if (!string_arg)
goto fail; goto fail;
int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); int32_arg = unicode_from_utf8((const uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);
JS_FreeCString(ctx, string_arg); JS_FreeCString(ctx, string_arg);
} else { } else {
if (JS_ToInt32(ctx, &int32_arg, argv[i++])) if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
@ -354,6 +372,7 @@ static JSValue js_printf_internal(JSContext *ctx,
return res; return res;
fail: fail:
JS_FreeCString(ctx, fmt_str);
dbuf_free(&dbuf); dbuf_free(&dbuf);
return JS_EXCEPTION; return JS_EXCEPTION;
} }
@ -1081,7 +1100,7 @@ static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val,
int64_t pos; int64_t pos;
if (!f) if (!f)
return JS_EXCEPTION; return JS_EXCEPTION;
#if defined(__linux__) #if defined(__linux__) || defined(__GLIBC__)
pos = ftello(f); pos = ftello(f);
#else #else
pos = ftell(f); pos = ftell(f);
@ -1104,7 +1123,7 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val,
return JS_EXCEPTION; return JS_EXCEPTION;
if (JS_ToInt32(ctx, &whence, argv[1])) if (JS_ToInt32(ctx, &whence, argv[1]))
return JS_EXCEPTION; return JS_EXCEPTION;
#if defined(__linux__) #if defined(__linux__) || defined(__GLIBC__)
ret = fseeko(f, pos, whence); ret = fseeko(f, pos, whence);
#else #else
ret = fseek(f, pos, whence); ret = fseek(f, pos, whence);
@ -1280,7 +1299,7 @@ static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val,
/* urlGet */ /* urlGet */
#define URL_GET_PROGRAM "curl -s -i" #define URL_GET_PROGRAM "curl -s -i --"
#define URL_GET_BUF_SIZE 4096 #define URL_GET_BUF_SIZE 4096
static int http_get_header_line(FILE *f, char *buf, size_t buf_size, static int http_get_header_line(FILE *f, char *buf, size_t buf_size,
@ -1326,7 +1345,7 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,
DynBuf header_buf_s, *header_buf = &header_buf_s; DynBuf header_buf_s, *header_buf = &header_buf_s;
char *buf; char *buf;
size_t i, len; size_t i, len;
int c, status; int status;
JSValue response = JS_UNDEFINED, ret_obj; JSValue response = JS_UNDEFINED, ret_obj;
JSValueConst options_obj; JSValueConst options_obj;
FILE *f; FILE *f;
@ -1353,16 +1372,25 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,
} }
js_std_dbuf_init(ctx, &cmd_buf); js_std_dbuf_init(ctx, &cmd_buf);
dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM); dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM);
len = strlen(url); for(i = 0; url[i] != '\0'; i++) {
for(i = 0; i < len; i++) { unsigned char c = url[i];
c = url[i]; switch (c) {
if (c == '\'' || c == '\\') case '\'':
/* shell single quoted string does not support \' */
dbuf_putstr(&cmd_buf, "'\\''");
break;
case '[': case ']': case '{': case '}': case '\\':
/* prevent interpretation by curl as range or set specification */
dbuf_putc(&cmd_buf, '\\'); dbuf_putc(&cmd_buf, '\\');
dbuf_putc(&cmd_buf, c); /* FALLTHROUGH */
default:
dbuf_putc(&cmd_buf, c);
break;
}
} }
JS_FreeCString(ctx, url); JS_FreeCString(ctx, url);
dbuf_putstr(&cmd_buf, "''"); dbuf_putstr(&cmd_buf, "'");
dbuf_putc(&cmd_buf, '\0'); dbuf_putc(&cmd_buf, '\0');
if (dbuf_error(&cmd_buf)) { if (dbuf_error(&cmd_buf)) {
dbuf_free(&cmd_buf); dbuf_free(&cmd_buf);
@ -1637,7 +1665,7 @@ static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val,
} }
static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val, static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic) int argc, JSValueConst *argv, int magic)
{ {
int fd; int fd;
uint64_t pos, len; uint64_t pos, len;
@ -1669,7 +1697,7 @@ static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val,
int fd; int fd;
if (JS_ToInt32(ctx, &fd, argv[0])) if (JS_ToInt32(ctx, &fd, argv[0]))
return JS_EXCEPTION; return JS_EXCEPTION;
return JS_NewBool(ctx, (isatty(fd) != 0)); return JS_NewBool(ctx, isatty(fd));
} }
#if defined(_WIN32) #if defined(_WIN32)
@ -1778,7 +1806,7 @@ static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val,
#endif /* !_WIN32 */ #endif /* !_WIN32 */
static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
const char *filename; const char *filename;
int ret; int ret;
@ -2006,41 +2034,13 @@ static JSValue js_os_now(JSContext *ctx, JSValue this_val,
return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6); return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6);
} }
static void unlink_timer(JSRuntime *rt, JSOSTimer *th)
{
if (th->link.prev) {
list_del(&th->link);
th->link.prev = th->link.next = NULL;
}
}
static void free_timer(JSRuntime *rt, JSOSTimer *th) static void free_timer(JSRuntime *rt, JSOSTimer *th)
{ {
list_del(&th->link);
JS_FreeValueRT(rt, th->func); JS_FreeValueRT(rt, th->func);
js_free_rt(rt, th); js_free_rt(rt, th);
} }
static JSClassID js_os_timer_class_id;
static void js_os_timer_finalizer(JSRuntime *rt, JSValue val)
{
JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
if (th) {
th->has_object = FALSE;
if (!th->link.prev)
free_timer(rt, th);
}
}
static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func)
{
JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
if (th) {
JS_MarkValue(rt, th->func, mark_func);
}
}
static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
@ -2049,45 +2049,56 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
int64_t delay; int64_t delay;
JSValueConst func; JSValueConst func;
JSOSTimer *th; JSOSTimer *th;
JSValue obj;
func = argv[0]; func = argv[0];
if (!JS_IsFunction(ctx, func)) if (!JS_IsFunction(ctx, func))
return JS_ThrowTypeError(ctx, "not a function"); return JS_ThrowTypeError(ctx, "not a function");
if (JS_ToInt64(ctx, &delay, argv[1])) if (JS_ToInt64(ctx, &delay, argv[1]))
return JS_EXCEPTION; return JS_EXCEPTION;
obj = JS_NewObjectClass(ctx, js_os_timer_class_id);
if (JS_IsException(obj))
return obj;
th = js_mallocz(ctx, sizeof(*th)); th = js_mallocz(ctx, sizeof(*th));
if (!th) { if (!th)
JS_FreeValue(ctx, obj);
return JS_EXCEPTION; return JS_EXCEPTION;
} th->timer_id = ts->next_timer_id;
th->has_object = TRUE; if (ts->next_timer_id == INT32_MAX)
ts->next_timer_id = 1;
else
ts->next_timer_id++;
th->timeout = get_time_ms() + delay; th->timeout = get_time_ms() + delay;
th->func = JS_DupValue(ctx, func); th->func = JS_DupValue(ctx, func);
list_add_tail(&th->link, &ts->os_timers); list_add_tail(&th->link, &ts->os_timers);
JS_SetOpaque(obj, th); return JS_NewInt32(ctx, th->timer_id);
return obj; }
static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id)
{
struct list_head *el;
if (timer_id <= 0)
return NULL;
list_for_each(el, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link);
if (th->timer_id == timer_id)
return th;
}
return NULL;
} }
static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id); JSRuntime *rt = JS_GetRuntime(ctx);
if (!th) JSThreadState *ts = JS_GetRuntimeOpaque(rt);
JSOSTimer *th;
int timer_id;
if (JS_ToInt32(ctx, &timer_id, argv[0]))
return JS_EXCEPTION; return JS_EXCEPTION;
unlink_timer(JS_GetRuntime(ctx), th); th = find_timer_by_id(ts, timer_id);
if (!th)
return JS_UNDEFINED;
free_timer(rt, th);
return JS_UNDEFINED; return JS_UNDEFINED;
} }
static JSClassDef js_os_timer_class = {
"OSTimer",
.finalizer = js_os_timer_finalizer,
.gc_mark = js_os_timer_mark,
};
/* return a promise */ /* return a promise */
static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
@ -2111,7 +2122,7 @@ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
JS_FreeValue(ctx, resolving_funcs[1]); JS_FreeValue(ctx, resolving_funcs[1]);
return JS_EXCEPTION; return JS_EXCEPTION;
} }
th->has_object = FALSE; th->timer_id = -1;
th->timeout = get_time_ms() + delay; th->timeout = get_time_ms() + delay;
th->func = JS_DupValue(ctx, resolving_funcs[0]); th->func = JS_DupValue(ctx, resolving_funcs[0]);
list_add_tail(&th->link, &ts->os_timers); list_add_tail(&th->link, &ts->os_timers);
@ -2133,84 +2144,81 @@ static void call_handler(JSContext *ctx, JSValueConst func)
JS_FreeValue(ctx, ret); JS_FreeValue(ctx, ret);
} }
#if defined(_WIN32) #ifdef USE_WORKER
static int js_os_poll(JSContext *ctx) #ifdef _WIN32
static int js_waker_init(JSWaker *w)
{ {
JSRuntime *rt = JS_GetRuntime(ctx); w->handle = CreateEvent(NULL, TRUE, FALSE, NULL);
JSThreadState *ts = JS_GetRuntimeOpaque(rt); return w->handle ? 0 : -1;
int min_delay, console_fd; }
int64_t cur_time, delay;
JSOSRWHandler *rh;
struct list_head *el;
/* XXX: handle signals if useful */ static void js_waker_signal(JSWaker *w)
{
SetEvent(w->handle);
}
if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers)) static void js_waker_clear(JSWaker *w)
return -1; /* no more events */ {
ResetEvent(w->handle);
}
/* XXX: only timers and basic console input are supported */ static void js_waker_close(JSWaker *w)
if (!list_empty(&ts->os_timers)) { {
cur_time = get_time_ms(); CloseHandle(w->handle);
min_delay = 10000; w->handle = INVALID_HANDLE_VALUE;
list_for_each(el, &ts->os_timers) { }
JSOSTimer *th = list_entry(el, JSOSTimer, link);
delay = th->timeout - cur_time;
if (delay <= 0) {
JSValue func;
/* the timer expired */
func = th->func;
th->func = JS_UNDEFINED;
unlink_timer(rt, th);
if (!th->has_object)
free_timer(rt, th);
call_handler(ctx, func);
JS_FreeValue(ctx, func);
return 0;
} else if (delay < min_delay) {
min_delay = delay;
}
}
} else {
min_delay = -1;
}
console_fd = -1; #else // !_WIN32
list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {
console_fd = rh->fd;
break;
}
}
if (console_fd >= 0) { static int js_waker_init(JSWaker *w)
DWORD ti, ret; {
HANDLE handle; int fds[2];
if (min_delay == -1)
ti = INFINITE; if (pipe(fds) < 0)
else return -1;
ti = min_delay; w->read_fd = fds[0];
handle = (HANDLE)_get_osfhandle(console_fd); w->write_fd = fds[1];
ret = WaitForSingleObject(handle, ti);
if (ret == WAIT_OBJECT_0) {
list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) {
call_handler(ctx, rh->rw_func[0]);
/* must stop because the list may have been modified */
break;
}
}
}
} else {
Sleep(min_delay);
}
return 0; return 0;
} }
#else
#ifdef USE_WORKER static void js_waker_signal(JSWaker *w)
{
int ret;
for(;;) {
ret = write(w->write_fd, "", 1);
if (ret == 1)
break;
if (ret < 0 && (errno != EAGAIN || errno != EINTR))
break;
}
}
static void js_waker_clear(JSWaker *w)
{
uint8_t buf[16];
int ret;
for(;;) {
ret = read(w->read_fd, buf, sizeof(buf));
if (ret >= 0)
break;
if (errno != EAGAIN && errno != EINTR)
break;
}
}
static void js_waker_close(JSWaker *w)
{
close(w->read_fd);
close(w->write_fd);
w->read_fd = -1;
w->write_fd = -1;
}
#endif // _WIN32
static void js_free_message(JSWorkerMessage *msg); static void js_free_message(JSWorkerMessage *msg);
@ -2232,17 +2240,8 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
/* remove the message from the queue */ /* remove the message from the queue */
list_del(&msg->link); list_del(&msg->link);
if (list_empty(&ps->msg_queue)) { if (list_empty(&ps->msg_queue))
uint8_t buf[16]; js_waker_clear(&ps->waker);
int ret;
for(;;) {
ret = read(ps->read_fd, buf, sizeof(buf));
if (ret >= 0)
break;
if (errno != EAGAIN && errno != EINTR)
break;
}
}
pthread_mutex_unlock(&ps->mutex); pthread_mutex_unlock(&ps->mutex);
@ -2285,7 +2284,104 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
{ {
return 0; return 0;
} }
#endif #endif /* !USE_WORKER */
#if defined(_WIN32)
static int js_os_poll(JSContext *ctx)
{
JSRuntime *rt = JS_GetRuntime(ctx);
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
int min_delay, count;
int64_t cur_time, delay;
JSOSRWHandler *rh;
struct list_head *el;
HANDLE handles[MAXIMUM_WAIT_OBJECTS]; // 64
/* XXX: handle signals if useful */
if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->os_timers) &&
list_empty(&ts->port_list)) {
return -1; /* no more events */
}
if (!list_empty(&ts->os_timers)) {
cur_time = get_time_ms();
min_delay = 10000;
list_for_each(el, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link);
delay = th->timeout - cur_time;
if (delay <= 0) {
JSValue func;
/* the timer expired */
func = th->func;
th->func = JS_UNDEFINED;
free_timer(rt, th);
call_handler(ctx, func);
JS_FreeValue(ctx, func);
return 0;
} else if (delay < min_delay) {
min_delay = delay;
}
}
} else {
min_delay = -1;
}
count = 0;
list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {
handles[count++] = (HANDLE)_get_osfhandle(rh->fd); // stdin
if (count == (int)countof(handles))
break;
}
}
list_for_each(el, &ts->port_list) {
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
if (JS_IsNull(port->on_message_func))
continue;
handles[count++] = port->recv_pipe->waker.handle;
if (count == (int)countof(handles))
break;
}
if (count > 0) {
DWORD ret, timeout = INFINITE;
if (min_delay != -1)
timeout = min_delay;
ret = WaitForMultipleObjects(count, handles, FALSE, timeout);
if (ret < count) {
list_for_each(el, &ts->os_rw_handlers) {
rh = list_entry(el, JSOSRWHandler, link);
if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) {
call_handler(ctx, rh->rw_func[0]);
/* must stop because the list may have been modified */
goto done;
}
}
list_for_each(el, &ts->port_list) {
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
if (!JS_IsNull(port->on_message_func)) {
JSWorkerMessagePipe *ps = port->recv_pipe;
if (ps->waker.handle == handles[ret]) {
if (handle_posted_message(rt, ctx, port))
goto done;
}
}
}
}
} else {
Sleep(min_delay);
}
done:
return 0;
}
#else
static int js_os_poll(JSContext *ctx) static int js_os_poll(JSContext *ctx)
{ {
@ -2330,9 +2426,7 @@ static int js_os_poll(JSContext *ctx)
/* the timer expired */ /* the timer expired */
func = th->func; func = th->func;
th->func = JS_UNDEFINED; th->func = JS_UNDEFINED;
unlink_timer(rt, th); free_timer(rt, th);
if (!th->has_object)
free_timer(rt, th);
call_handler(ctx, func); call_handler(ctx, func);
JS_FreeValue(ctx, func); JS_FreeValue(ctx, func);
return 0; return 0;
@ -2363,8 +2457,8 @@ static int js_os_poll(JSContext *ctx)
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
if (!JS_IsNull(port->on_message_func)) { if (!JS_IsNull(port->on_message_func)) {
JSWorkerMessagePipe *ps = port->recv_pipe; JSWorkerMessagePipe *ps = port->recv_pipe;
fd_max = max_int(fd_max, ps->read_fd); fd_max = max_int(fd_max, ps->waker.read_fd);
FD_SET(ps->read_fd, &rfds); FD_SET(ps->waker.read_fd, &rfds);
} }
} }
@ -2390,14 +2484,14 @@ static int js_os_poll(JSContext *ctx)
JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link);
if (!JS_IsNull(port->on_message_func)) { if (!JS_IsNull(port->on_message_func)) {
JSWorkerMessagePipe *ps = port->recv_pipe; JSWorkerMessagePipe *ps = port->recv_pipe;
if (FD_ISSET(ps->read_fd, &rfds)) { if (FD_ISSET(ps->waker.read_fd, &rfds)) {
if (handle_posted_message(rt, ctx, port)) if (handle_posted_message(rt, ctx, port))
goto done; goto done;
} }
} }
} }
} }
done: done:
return 0; return 0;
} }
#endif /* !_WIN32 */ #endif /* !_WIN32 */
@ -2552,12 +2646,14 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
else else
res = stat(path, &st); res = stat(path, &st);
#endif #endif
if (res < 0)
err = errno;
else
err = 0;
JS_FreeCString(ctx, path); JS_FreeCString(ctx, path);
if (res < 0) { if (res < 0) {
err = errno;
obj = JS_NULL; obj = JS_NULL;
} else { } else {
err = 0;
obj = JS_NewObject(ctx); obj = JS_NewObject(ctx);
if (JS_IsException(obj)) if (JS_IsException(obj))
return JS_EXCEPTION; return JS_EXCEPTION;
@ -2668,7 +2764,7 @@ static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val,
/* sleep(delay_ms) */ /* sleep(delay_ms) */
static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
int64_t delay; int64_t delay;
int ret; int ret;
@ -2732,7 +2828,7 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,
#if !defined(_WIN32) #if !defined(_WIN32)
static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
const char *target, *linkpath; const char *target, *linkpath;
int err; int err;
@ -3016,7 +3112,6 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
} }
if (pid == 0) { if (pid == 0) {
/* child */ /* child */
int fd_max = sysconf(_SC_OPEN_MAX);
/* remap the stdin/stdout/stderr handles if necessary */ /* remap the stdin/stdout/stderr handles if necessary */
for(i = 0; i < 3; i++) { for(i = 0; i < 3; i++) {
@ -3025,6 +3120,12 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
_exit(127); _exit(127);
} }
} }
#if defined(HAVE_CLOSEFROM)
/* closefrom() is available on many recent unix systems:
Linux with glibc 2.34+, Solaris 9+, FreeBSD 7.3+,
NetBSD 3.0+, OpenBSD 3.5+.
Linux with the musl libc and macOS don't have it.
*/
#if defined(__linux__) #if defined(__linux__)
int pid = getpid(); int pid = getpid();
@ -3229,6 +3330,7 @@ typedef struct {
char *filename; /* module filename */ char *filename; /* module filename */
char *basename; /* module base name */ char *basename; /* module base name */
JSWorkerMessagePipe *recv_pipe, *send_pipe; JSWorkerMessagePipe *recv_pipe, *send_pipe;
int strip_flags;
} WorkerFuncArgs; } WorkerFuncArgs;
typedef struct { typedef struct {
@ -3277,22 +3379,17 @@ static void js_sab_dup(void *opaque, void *ptr)
static JSWorkerMessagePipe *js_new_message_pipe(void) static JSWorkerMessagePipe *js_new_message_pipe(void)
{ {
JSWorkerMessagePipe *ps; JSWorkerMessagePipe *ps;
int pipe_fds[2];
if (pipe(pipe_fds) < 0)
return NULL;
ps = malloc(sizeof(*ps)); ps = malloc(sizeof(*ps));
if (!ps) { if (!ps)
close(pipe_fds[0]); return NULL;
close(pipe_fds[1]); if (js_waker_init(&ps->waker)) {
free(ps);
return NULL; return NULL;
} }
ps->ref_count = 1; ps->ref_count = 1;
init_list_head(&ps->msg_queue); init_list_head(&ps->msg_queue);
pthread_mutex_init(&ps->mutex, NULL); pthread_mutex_init(&ps->mutex, NULL);
ps->read_fd = pipe_fds[0];
ps->write_fd = pipe_fds[1];
return ps; return ps;
} }
@ -3331,8 +3428,7 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps)
js_free_message(msg); js_free_message(msg);
} }
pthread_mutex_destroy(&ps->mutex); pthread_mutex_destroy(&ps->mutex);
close(ps->read_fd); js_waker_close(&ps->waker);
close(ps->write_fd);
free(ps); free(ps);
} }
} }
@ -3369,13 +3465,14 @@ static void *worker_func(void *opaque)
JSRuntime *rt; JSRuntime *rt;
JSThreadState *ts; JSThreadState *ts;
JSContext *ctx; JSContext *ctx;
JSValue promise; JSValue val;
rt = JS_NewRuntime(); rt = JS_NewRuntime();
if (rt == NULL) { if (rt == NULL) {
fprintf(stderr, "JS_NewRuntime failure"); fprintf(stderr, "JS_NewRuntime failure");
exit(1); exit(1);
} }
JS_SetStripInfo(rt, args->strip_flags);
js_std_init_handlers(rt); js_std_init_handlers(rt);
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
@ -3396,14 +3493,14 @@ static void *worker_func(void *opaque)
js_std_add_helpers(ctx, -1, NULL); js_std_add_helpers(ctx, -1, NULL);
promise = JS_LoadModule(ctx, args->basename, args->filename); val = JS_LoadModule(ctx, args->basename, args->filename);
if (JS_IsException(promise))
js_std_dump_error(ctx);
/* XXX: check */
JS_FreeValue(ctx, promise);
free(args->filename); free(args->filename);
free(args->basename); free(args->basename);
free(args); free(args);
val = js_std_await(ctx, val);
if (JS_IsException(val))
js_std_dump_error(ctx);
JS_FreeValue(ctx, val);
js_std_loop(ctx); js_std_loop(ctx);
@ -3493,6 +3590,8 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
if (!args->send_pipe) if (!args->send_pipe)
goto oom_fail; goto oom_fail;
args->strip_flags = JS_GetStripInfo(rt);
obj = js_worker_ctor_internal(ctx, new_target, obj = js_worker_ctor_internal(ctx, new_target,
args->send_pipe, args->recv_pipe); args->send_pipe, args->recv_pipe);
if (JS_IsException(obj)) if (JS_IsException(obj))
@ -3558,10 +3657,12 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
memcpy(msg->data, data, data_len); memcpy(msg->data, data, data_len);
msg->data_len = data_len; msg->data_len = data_len;
msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len); if (sab_tab_len > 0) {
if (!msg->sab_tab) msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len);
goto fail; if (!msg->sab_tab)
memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len); goto fail;
memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len);
}
msg->sab_tab_len = sab_tab_len; msg->sab_tab_len = sab_tab_len;
js_free(ctx, data); js_free(ctx, data);
@ -3575,17 +3676,8 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
ps = worker->send_pipe; ps = worker->send_pipe;
pthread_mutex_lock(&ps->mutex); pthread_mutex_lock(&ps->mutex);
/* indicate that data is present */ /* indicate that data is present */
if (list_empty(&ps->msg_queue)) { if (list_empty(&ps->msg_queue))
uint8_t ch = '\0'; js_waker_signal(&ps->waker);
int ret;
for(;;) {
ret = write(ps->write_fd, &ch, 1);
if (ret == 1)
break;
if (ret < 0 && (errno != EAGAIN || errno != EINTR))
break;
}
}
list_add_tail(&msg->link, &ps->msg_queue); list_add_tail(&msg->link, &ps->msg_queue);
pthread_mutex_unlock(&ps->mutex); pthread_mutex_unlock(&ps->mutex);
return JS_UNDEFINED; return JS_UNDEFINED;
@ -3765,10 +3857,6 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
{ {
os_poll_func = js_os_poll; os_poll_func = js_os_poll;
/* OSTimer class */
JS_NewClassID(&js_os_timer_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);
#ifdef USE_WORKER #ifdef USE_WORKER
{ {
JSRuntime *rt = JS_GetRuntime(ctx); JSRuntime *rt = JS_GetRuntime(ctx);
@ -3817,7 +3905,7 @@ JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name)
/**********************************************************/ /**********************************************************/
static JSValue js_print(JSContext *ctx, JSValueConst this_val, static JSValue js_print(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
int i; int i;
const char *str; const char *str;
@ -3836,9 +3924,18 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val,
return JS_UNDEFINED; return JS_UNDEFINED;
} }
static JSValue js_console_log(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue ret;
ret = js_print(ctx, this_val, argc, argv);
fflush(stdout);
return ret;
}
void js_std_add_helpers(JSContext *ctx, int argc, char **argv) void js_std_add_helpers(JSContext *ctx, int argc, char **argv)
{ {
JSValue global_obj, console, args; JSValue global_obj, console, args, performance;
int i; int i;
/* XXX: should these global definitions be enumerable? */ /* XXX: should these global definitions be enumerable? */
@ -3846,9 +3943,14 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv)
console = JS_NewObject(ctx); console = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, console, "log", JS_SetPropertyStr(ctx, console, "log",
JS_NewCFunction(ctx, js_print, "log", 1)); JS_NewCFunction(ctx, js_console_log, "log", 1));
JS_SetPropertyStr(ctx, global_obj, "console", console); JS_SetPropertyStr(ctx, global_obj, "console", console);
performance = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, performance, "now",
JS_NewCFunction(ctx, js_os_now, "now", 0));
JS_SetPropertyStr(ctx, global_obj, "performance", performance);
/* same methods as the mozilla JS shell */ /* same methods as the mozilla JS shell */
if (argc >= 0) { if (argc >= 0) {
args = JS_NewArray(ctx); args = JS_NewArray(ctx);
@ -3880,6 +3982,7 @@ void js_std_init_handlers(JSRuntime *rt)
init_list_head(&ts->os_signal_handlers); init_list_head(&ts->os_signal_handlers);
init_list_head(&ts->os_timers); init_list_head(&ts->os_timers);
init_list_head(&ts->port_list); init_list_head(&ts->port_list);
ts->next_timer_id = 1;
JS_SetRuntimeOpaque(rt, ts); JS_SetRuntimeOpaque(rt, ts);
@ -3913,9 +4016,7 @@ void js_std_free_handlers(JSRuntime *rt)
list_for_each_safe(el, el1, &ts->os_timers) { list_for_each_safe(el, el1, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link); JSOSTimer *th = list_entry(el, JSOSTimer, link);
unlink_timer(rt, th); free_timer(rt, th);
if (!th->has_object)
free_timer(rt, th);
} }
#ifdef USE_WORKER #ifdef USE_WORKER
@ -3973,6 +4074,7 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
if (!is_handled) { if (!is_handled) {
fprintf(stderr, "Possibly unhandled promise rejection: "); fprintf(stderr, "Possibly unhandled promise rejection: ");
js_std_dump_error1(ctx, reason); js_std_dump_error1(ctx, reason);
exit(1);
} }
} }
@ -3999,6 +4101,44 @@ void js_std_loop(JSContext *ctx)
} }
} }
/* Wait for a promise and execute pending jobs while waiting for
it. Return the promise result or JS_EXCEPTION in case of promise
rejection. */
JSValue js_std_await(JSContext *ctx, JSValue obj)
{
JSValue ret;
int state;
for(;;) {
state = JS_PromiseState(ctx, obj);
if (state == JS_PROMISE_FULFILLED) {
ret = JS_PromiseResult(ctx, obj);
JS_FreeValue(ctx, obj);
break;
} else if (state == JS_PROMISE_REJECTED) {
ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj));
JS_FreeValue(ctx, obj);
break;
} else if (state == JS_PROMISE_PENDING) {
JSContext *ctx1;
int err;
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
if (err < 0) {
js_std_dump_error(ctx1);
}
if (err == 0) {
if (os_poll_func)
os_poll_func(ctx);
}
} else {
/* not a promise */
ret = obj;
break;
}
}
return ret;
}
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int load_only) int load_only)
{ {
@ -4017,8 +4157,11 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
goto exception; goto exception;
} }
js_module_set_import_meta(ctx, obj, FALSE, TRUE); js_module_set_import_meta(ctx, obj, FALSE, TRUE);
val = JS_EvalFunction(ctx, obj);
val = js_std_await(ctx, val);
} else {
val = JS_EvalFunction(ctx, obj);
} }
val = JS_EvalFunction(ctx, obj);
if (JS_IsException(val)) { if (JS_IsException(val)) {
exception: exception:
js_std_dump_error(ctx); js_std_dump_error(ctx);

View File

@ -37,6 +37,7 @@ JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
void js_std_add_helpers(JSContext *ctx, int argc, char **argv); void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
void js_std_loop(JSContext *ctx); void js_std_loop(JSContext *ctx);
JSValue js_std_await(JSContext *ctx, JSValue obj);
void js_std_init_handlers(JSRuntime *rt); void js_std_init_handlers(JSRuntime *rt);
void js_std_free_handlers(JSRuntime *rt); void js_std_free_handlers(JSRuntime *rt);
void js_std_dump_error(JSContext *ctx); void js_std_dump_error(JSContext *ctx);

View File

@ -110,6 +110,7 @@ DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none) DEF( return_undef, 1, 0, 0, none)
DEF(check_ctor_return, 1, 1, 2, none) DEF(check_ctor_return, 1, 1, 2, none)
DEF( check_ctor, 1, 0, 0, none) DEF( check_ctor, 1, 0, 0, none)
DEF( init_ctor, 1, 0, 1, none)
DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
DEF( return_async, 1, 1, 0, none) DEF( return_async, 1, 1, 0, none)
@ -195,7 +196,6 @@ DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order a
DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8)
DEF( make_loc_ref, 7, 0, 2, atom_u16) DEF( make_loc_ref, 7, 0, 2, atom_u16)
DEF( make_arg_ref, 7, 0, 2, atom_u16) DEF( make_arg_ref, 7, 0, 2, atom_u16)
@ -207,8 +207,9 @@ DEF( for_of_start, 1, 1, 3, none)
DEF(for_await_of_start, 1, 1, 3, none) DEF(for_await_of_start, 1, 1, 3, none)
DEF( for_in_next, 1, 1, 3, none) DEF( for_in_next, 1, 1, 3, none)
DEF( for_of_next, 2, 3, 5, u8) DEF( for_of_next, 2, 3, 5, u8)
DEF(for_await_of_next, 1, 3, 4, none) /* iter next catch_offset -> iter next catch_offset obj */
DEF(iterator_check_object, 1, 1, 1, none) DEF(iterator_check_object, 1, 1, 1, none)
DEF(iterator_get_value_done, 1, 1, 2, none) DEF(iterator_get_value_done, 1, 2, 3, none) /* catch_offset obj -> catch_offset value done */
DEF( iterator_close, 1, 3, 0, none) DEF( iterator_close, 1, 3, 0, none)
DEF( iterator_next, 1, 4, 4, none) DEF( iterator_next, 1, 4, 4, none)
DEF( iterator_call, 2, 4, 5, u8) DEF( iterator_call, 2, 4, 5, u8)
@ -258,10 +259,7 @@ DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none) DEF( or, 1, 2, 1, none)
DEF(is_undefined_or_null, 1, 1, 1, none) DEF(is_undefined_or_null, 1, 1, 1, none)
DEF( private_in, 1, 2, 1, none) DEF( private_in, 1, 2, 1, none)
#ifdef CONFIG_BIGNUM DEF(push_bigint_i32, 5, 0, 1, i32)
DEF( mul_pow10, 1, 2, 1, none)
DEF( math_mod, 1, 2, 1, none)
#endif
/* must be the last non short and non temporary opcode */ /* must be the last non short and non temporary opcode */
DEF( nop, 1, 0, 0, none) DEF( nop, 1, 0, 0, none)

12494
quickjs.c

File diff suppressed because it is too large Load Diff

150
quickjs.h
View File

@ -27,6 +27,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -48,7 +49,6 @@ extern "C" {
typedef struct JSRuntime JSRuntime; typedef struct JSRuntime JSRuntime;
typedef struct JSContext JSContext; typedef struct JSContext JSContext;
typedef struct JSObject JSObject;
typedef struct JSClass JSClass; typedef struct JSClass JSClass;
typedef uint32_t JSClassID; typedef uint32_t JSClassID;
typedef uint32_t JSAtom; typedef uint32_t JSAtom;
@ -64,14 +64,21 @@ typedef uint32_t JSAtom;
#define JS_NAN_BOXING #define JS_NAN_BOXING
#endif #endif
#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
#define JS_LIMB_BITS 64
#else
#define JS_LIMB_BITS 32
#endif
#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS
enum { enum {
/* all tags with a reference count are negative */ /* all tags with a reference count are negative */
JS_TAG_FIRST = -11, /* first negative tag */ JS_TAG_FIRST = -9, /* first negative tag */
JS_TAG_BIG_DECIMAL = -11, JS_TAG_BIG_INT = -9,
JS_TAG_BIG_INT = -10,
JS_TAG_BIG_FLOAT = -9,
JS_TAG_SYMBOL = -8, JS_TAG_SYMBOL = -8,
JS_TAG_STRING = -7, JS_TAG_STRING = -7,
JS_TAG_STRING_ROPE = -6,
JS_TAG_MODULE = -3, /* used internally */ JS_TAG_MODULE = -3, /* used internally */
JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */
JS_TAG_OBJECT = -1, JS_TAG_OBJECT = -1,
@ -83,7 +90,8 @@ enum {
JS_TAG_UNINITIALIZED = 4, JS_TAG_UNINITIALIZED = 4,
JS_TAG_CATCH_OFFSET = 5, JS_TAG_CATCH_OFFSET = 5,
JS_TAG_EXCEPTION = 6, JS_TAG_EXCEPTION = 6,
JS_TAG_FLOAT64 = 7, JS_TAG_SHORT_BIG_INT = 7,
JS_TAG_FLOAT64 = 8,
/* any larger tag is FLOAT64 if JS_NAN_BOXING */ /* any larger tag is FLOAT64 if JS_NAN_BOXING */
}; };
@ -108,6 +116,7 @@ typedef const struct __JSValue *JSValueConst;
#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4) #define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)
#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v) #define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v) #define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_SHORT_BIG_INT(v) JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf) #define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)
#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag)) #define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))
@ -127,6 +136,11 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
return 0; return 0;
} }
static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d)
{
return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d);
}
#elif defined(JS_NAN_BOXING) #elif defined(JS_NAN_BOXING)
typedef uint64_t JSValue; typedef uint64_t JSValue;
@ -136,6 +150,7 @@ typedef uint64_t JSValue;
#define JS_VALUE_GET_TAG(v) (int)((v) >> 32) #define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
#define JS_VALUE_GET_INT(v) (int)(v) #define JS_VALUE_GET_INT(v) (int)(v)
#define JS_VALUE_GET_BOOL(v) (int)(v) #define JS_VALUE_GET_BOOL(v) (int)(v)
#define JS_VALUE_GET_SHORT_BIG_INT(v) (int)(v)
#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v) #define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val)) #define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
@ -192,12 +207,22 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
return tag == (JS_NAN >> 32); return tag == (JS_NAN >> 32);
} }
static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d)
{
return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d);
}
#else /* !JS_NAN_BOXING */ #else /* !JS_NAN_BOXING */
typedef union JSValueUnion { typedef union JSValueUnion {
int32_t int32; int32_t int32;
double float64; double float64;
void *ptr; void *ptr;
#if JS_SHORT_BIG_INT_BITS == 32
int32_t short_big_int;
#else
int64_t short_big_int;
#endif
} JSValueUnion; } JSValueUnion;
typedef struct JSValue { typedef struct JSValue {
@ -213,6 +238,7 @@ typedef struct JSValue {
#define JS_VALUE_GET_INT(v) ((v).u.int32) #define JS_VALUE_GET_INT(v) ((v).u.int32)
#define JS_VALUE_GET_BOOL(v) ((v).u.int32) #define JS_VALUE_GET_BOOL(v) ((v).u.int32)
#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
#define JS_VALUE_GET_SHORT_BIG_INT(v) ((v).u.short_big_int)
#define JS_VALUE_GET_PTR(v) ((v).u.ptr) #define JS_VALUE_GET_PTR(v) ((v).u.ptr)
#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } #define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
@ -242,13 +268,19 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000; return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000;
} }
static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int64_t d)
{
JSValue v;
v.tag = JS_TAG_SHORT_BIG_INT;
v.u.short_big_int = d;
return v;
}
#endif /* !JS_NAN_BOXING */ #endif /* !JS_NAN_BOXING */
#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) #define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)
#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) #define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2)))
#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))
#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))
#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) #define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST)
/* special values */ /* special values */
@ -290,7 +322,9 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
#define JS_PROP_NO_ADD (1 << 16) /* internal use */ #define JS_PROP_NO_ADD (1 << 16) /* internal use */
#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */ #define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */
#define JS_DEFAULT_STACK_SIZE (256 * 1024) #ifndef JS_DEFAULT_STACK_SIZE
#define JS_DEFAULT_STACK_SIZE (1024 * 1024)
#endif
/* JS_Eval() flags */ /* JS_Eval() flags */
#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */ #define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */
@ -300,7 +334,6 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
#define JS_EVAL_TYPE_MASK (3 << 0) #define JS_EVAL_TYPE_MASK (3 << 0)
#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */ #define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */
#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */
/* compile but do not run. The result is an object with a /* compile but do not run. The result is an object with a
JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
with JS_EvalFunction(). */ with JS_EvalFunction(). */
@ -373,13 +406,7 @@ void JS_AddIntrinsicProxy(JSContext *ctx);
void JS_AddIntrinsicMapSet(JSContext *ctx); void JS_AddIntrinsicMapSet(JSContext *ctx);
void JS_AddIntrinsicTypedArrays(JSContext *ctx); void JS_AddIntrinsicTypedArrays(JSContext *ctx);
void JS_AddIntrinsicPromise(JSContext *ctx); void JS_AddIntrinsicPromise(JSContext *ctx);
void JS_AddIntrinsicBigInt(JSContext *ctx); void JS_AddIntrinsicWeakRef(JSContext *ctx);
void JS_AddIntrinsicBigFloat(JSContext *ctx);
void JS_AddIntrinsicBigDecimal(JSContext *ctx);
/* enable operator overloading */
void JS_AddIntrinsicOperators(JSContext *ctx);
/* enable "use math" */
void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable);
JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv); int argc, JSValueConst *argv);
@ -474,6 +501,17 @@ typedef struct JSClassExoticMethods {
/* return < 0 if exception or TRUE/FALSE */ /* return < 0 if exception or TRUE/FALSE */
int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom, int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
JSValueConst value, JSValueConst receiver, int flags); JSValueConst value, JSValueConst receiver, int flags);
/* To get a consistent object behavior when get_prototype != NULL,
get_property, set_property and set_prototype must be != NULL
and the object must be created with a JS_NULL prototype. */
JSValue (*get_prototype)(JSContext *ctx, JSValueConst obj);
/* return < 0 if exception or TRUE/FALSE */
int (*set_prototype)(JSContext *ctx, JSValueConst obj, JSValueConst proto_val);
/* return < 0 if exception or TRUE/FALSE */
int (*is_extensible)(JSContext *ctx, JSValueConst obj);
/* return < 0 if exception or TRUE/FALSE */
int (*prevent_extensions)(JSContext *ctx, JSValueConst obj);
} JSClassExoticMethods; } JSClassExoticMethods;
typedef void JSClassFinalizer(JSRuntime *rt, JSValue val); typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);
@ -499,7 +537,10 @@ typedef struct JSClassDef {
JSClassExoticMethods *exotic; JSClassExoticMethods *exotic;
} JSClassDef; } JSClassDef;
#define JS_INVALID_CLASS_ID 0
JSClassID JS_NewClassID(JSClassID *pclass_id); JSClassID JS_NewClassID(JSClassID *pclass_id);
/* Returns the class ID if `v` is an object, otherwise returns JS_INVALID_CLASS_ID. */
JSClassID JS_GetClassID(JSValue v);
int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);
int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
@ -547,23 +588,21 @@ JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
{ {
JSValue v;
int32_t val; int32_t val;
union { union {
double d; double d;
uint64_t u; uint64_t u;
} u, t; } u, t;
u.d = d; if (d >= INT32_MIN && d <= INT32_MAX) {
val = (int32_t)d; u.d = d;
t.d = val; val = (int32_t)d;
/* -0 cannot be represented as integer, so we compare the bit t.d = val;
representation */ /* -0 cannot be represented as integer, so we compare the bit
if (u.u == t.u) { representation */
v = JS_MKVAL(JS_TAG_INT, val); if (u.u == t.u)
} else { return JS_MKVAL(JS_TAG_INT, val);
v = __JS_NewFloat64(ctx, d);
} }
return v; return __JS_NewFloat64(ctx, d);
} }
static inline JS_BOOL JS_IsNumber(JSValueConst v) static inline JS_BOOL JS_IsNumber(JSValueConst v)
@ -575,19 +614,7 @@ static inline JS_BOOL JS_IsNumber(JSValueConst v)
static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
{ {
int tag = JS_VALUE_GET_TAG(v); int tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG_BIG_INT; return tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT;
}
static inline JS_BOOL JS_IsBigFloat(JSValueConst v)
{
int tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG_BIG_FLOAT;
}
static inline JS_BOOL JS_IsBigDecimal(JSValueConst v)
{
int tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG_BIG_DECIMAL;
} }
static inline JS_BOOL JS_IsBool(JSValueConst v) static inline JS_BOOL JS_IsBool(JSValueConst v)
@ -617,7 +644,8 @@ static inline JS_BOOL JS_IsUninitialized(JSValueConst v)
static inline JS_BOOL JS_IsString(JSValueConst v) static inline JS_BOOL JS_IsString(JSValueConst v)
{ {
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING; return JS_VALUE_GET_TAG(v) == JS_TAG_STRING ||
JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE;
} }
static inline JS_BOOL JS_IsSymbol(JSValueConst v) static inline JS_BOOL JS_IsSymbol(JSValueConst v)
@ -632,7 +660,9 @@ static inline JS_BOOL JS_IsObject(JSValueConst v)
JSValue JS_Throw(JSContext *ctx, JSValue obj); JSValue JS_Throw(JSContext *ctx, JSValue obj);
JSValue JS_GetException(JSContext *ctx); JSValue JS_GetException(JSContext *ctx);
JS_BOOL JS_HasException(JSContext *ctx);
JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, JS_BOOL flag);
void JS_ResetUncatchableError(JSContext *ctx); void JS_ResetUncatchableError(JSContext *ctx);
JSValue JS_NewError(JSContext *ctx); JSValue JS_NewError(JSContext *ctx);
JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
@ -681,6 +711,10 @@ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
return (JSValue)v; return (JSValue)v;
} }
JS_BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2);
JS_BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2);
JS_BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
@ -696,7 +730,10 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val); int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val);
JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1); JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1);
JSValue JS_NewString(JSContext *ctx, const char *str); static inline JSValue JS_NewString(JSContext *ctx, const char *str)
{
return JS_NewStringLen(ctx, str, strlen(str));
}
JSValue JS_NewAtomString(JSContext *ctx, const char *str); JSValue JS_NewAtomString(JSContext *ctx, const char *str);
JSValue JS_ToString(JSContext *ctx, JSValueConst val); JSValue JS_ToString(JSContext *ctx, JSValueConst val);
JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val); JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val);
@ -723,6 +760,8 @@ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val)
JSValue JS_NewArray(JSContext *ctx); JSValue JS_NewArray(JSContext *ctx);
int JS_IsArray(JSContext *ctx, JSValueConst val); int JS_IsArray(JSContext *ctx, JSValueConst val);
JSValue JS_NewDate(JSContext *ctx, double epoch_ms);
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
JSAtom prop, JSValueConst receiver, JSAtom prop, JSValueConst receiver,
JS_BOOL throw_ref_error); JS_BOOL throw_ref_error);
@ -804,6 +843,7 @@ int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
void JS_SetOpaque(JSValue obj, void *opaque); void JS_SetOpaque(JSValue obj, void *opaque);
void *JS_GetOpaque(JSValueConst obj, JSClassID class_id); void *JS_GetOpaque(JSValueConst obj, JSClassID class_id);
void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id); void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id);
void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id);
/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */ /* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
@ -821,6 +861,23 @@ JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len);
void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj);
uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj);
typedef enum JSTypedArrayEnum {
JS_TYPED_ARRAY_UINT8C = 0,
JS_TYPED_ARRAY_INT8,
JS_TYPED_ARRAY_UINT8,
JS_TYPED_ARRAY_INT16,
JS_TYPED_ARRAY_UINT16,
JS_TYPED_ARRAY_INT32,
JS_TYPED_ARRAY_UINT32,
JS_TYPED_ARRAY_BIG_INT64,
JS_TYPED_ARRAY_BIG_UINT64,
JS_TYPED_ARRAY_FLOAT32,
JS_TYPED_ARRAY_FLOAT64,
} JSTypedArrayEnum;
JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv,
JSTypedArrayEnum array_type);
JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
size_t *pbyte_offset, size_t *pbyte_offset,
size_t *pbyte_length, size_t *pbyte_length,
@ -855,6 +912,12 @@ typedef int JSInterruptHandler(JSRuntime *rt, void *opaque);
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque); void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
/* if can_block is TRUE, Atomics.wait() can be used */ /* if can_block is TRUE, Atomics.wait() can be used */
void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block); void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
/* select which debug info is stripped from the compiled code */
#define JS_STRIP_SOURCE (1 << 0) /* strip source code */
#define JS_STRIP_DEBUG (1 << 1) /* strip all debug info including source code */
void JS_SetStripInfo(JSRuntime *rt, int flags);
int JS_GetStripInfo(JSRuntime *rt);
/* set the [IsHTMLDDA] internal slot */ /* set the [IsHTMLDDA] internal slot */
void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj); void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj);
@ -876,6 +939,7 @@ void JS_SetModuleLoaderFunc(JSRuntime *rt,
/* return the import.meta object of a module */ /* return the import.meta object of a module */
JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);
JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);
JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m);
/* JS Job support */ /* JS Job support */

21
readme-cosmo.txt Normal file
View File

@ -0,0 +1,21 @@
The executables included in this archive run on Linux, Mac, Windows,
FreeBSD, OpenBSD and NetBSD for both the ARM64 and x86_64
architectures.
Platform Notes:
- if you get errors on Linux, you should disable the binfmt_misc
module which automatically invokes wine with Windows executable:
sudo sh -c 'echo -1 > /proc/sys/fs/binfmt_misc/cli' # remove Ubuntu's MZ interpreter
sudo sh -c 'echo -1 > /proc/sys/fs/binfmt_misc/status' # remove ALL binfmt_misc entries
- Under Windows, you can rename the executables with a .exe extension.
- Use the --assimilate option to build a platform specific binary for
better startup time:
./qjs --assimilate
- See https://github.com/jart/cosmopolitan for more information about
platform specific issues.

View File

@ -8,12 +8,12 @@ version=`cat VERSION`
if [ "$1" = "-h" ] ; then if [ "$1" = "-h" ] ; then
echo "release.sh [release_list]" echo "release.sh [release_list]"
echo "" echo ""
echo "release_list: extras binary win_binary quickjs" echo "release_list: extras binary win_binary cosmo_binary quickjs"
exit 1 exit 1
fi fi
release_list="extras binary win_binary quickjs" release_list="extras binary win_binary cosmo_binary quickjs"
if [ "$1" != "" ] ; then if [ "$1" != "" ] ; then
release_list="$1" release_list="$1"
@ -53,7 +53,10 @@ outdir="/tmp/${d}"
rm -rf $outdir rm -rf $outdir
mkdir -p $outdir mkdir -p $outdir
make CONFIG_WIN32=y qjs.exe make clean
make CONFIG_WIN32=y clean
make CONFIG_WIN32=y CONFIG_LTO=y qjs.exe
cp qjs.exe $outdir cp qjs.exe $outdir
${cross_prefix}strip $outdir/qjs.exe ${cross_prefix}strip $outdir/qjs.exe
cp $dlldir/libwinpthread-1.dll $outdir cp $dlldir/libwinpthread-1.dll $outdir
@ -75,7 +78,7 @@ mkdir -p $outdir
make clean make clean
make CONFIG_WIN32=y clean make CONFIG_WIN32=y clean
make CONFIG_WIN32=y CONFIG_M32=y qjs.exe make CONFIG_WIN32=y CONFIG_M32=y CONFIG_LTO=y qjs.exe
cp qjs.exe $outdir cp qjs.exe $outdir
${cross_prefix}strip $outdir/qjs.exe ${cross_prefix}strip $outdir/qjs.exe
cp $dlldir/libwinpthread-1.dll $outdir cp $dlldir/libwinpthread-1.dll $outdir
@ -91,9 +94,8 @@ if echo $release_list | grep -w -q binary ; then
make clean make clean
make CONFIG_WIN32=y clean make CONFIG_WIN32=y clean
make -j4 qjs run-test262 make -j4 CONFIG_LTO=y qjs run-test262
make -j4 CONFIG_M32=y qjs32 run-test262-32 strip qjs run-test262
strip qjs run-test262 qjs32 run-test262-32
d="quickjs-linux-x86_64-${version}" d="quickjs-linux-x86_64-${version}"
outdir="/tmp/${d}" outdir="/tmp/${d}"
@ -105,14 +107,39 @@ cp qjs run-test262 $outdir
( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) ( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . )
make clean
make -j4 CONFIG_LTO=y CONFIG_M32=y qjs run-test262
strip qjs run-test262
d="quickjs-linux-i686-${version}" d="quickjs-linux-i686-${version}"
outdir="/tmp/${d}" outdir="/tmp/${d}"
rm -rf $outdir rm -rf $outdir
mkdir -p $outdir mkdir -p $outdir
cp qjs32 $outdir/qjs cp qjs run-test262 $outdir
cp run-test262-32 $outdir/run-test262
( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . )
fi
#################################################"
# Cosmopolitan binary release
if echo $release_list | grep -w -q cosmo_binary ; then
export PATH=$PATH:$HOME/cosmocc/bin
d="quickjs-cosmo-${version}"
outdir="/tmp/${d}"
rm -rf $outdir
mkdir -p $outdir
make clean
make CONFIG_COSMO=y -j4 qjs run-test262
cp qjs run-test262 $outdir
cp readme-cosmo.txt $outdir/readme.txt
( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) ( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . )
@ -133,13 +160,13 @@ mkdir -p $outdir $outdir/doc $outdir/tests $outdir/examples
cp Makefile VERSION TODO Changelog readme.txt LICENSE \ cp Makefile VERSION TODO Changelog readme.txt LICENSE \
release.sh unicode_download.sh \ release.sh unicode_download.sh \
qjs.c qjsc.c qjscalc.js repl.js \ qjs.c qjsc.c repl.js \
quickjs.c quickjs.h quickjs-atom.h \ quickjs.c quickjs.h quickjs-atom.h \
quickjs-libc.c quickjs-libc.h quickjs-opcode.h \ quickjs-libc.c quickjs-libc.h quickjs-opcode.h \
cutils.c cutils.h list.h \ cutils.c cutils.h list.h \
libregexp.c libregexp.h libregexp-opcode.h \ libregexp.c libregexp.h libregexp-opcode.h \
libunicode.c libunicode.h libunicode-table.h \ libunicode.c libunicode.h libunicode-table.h \
libbf.c libbf.h \ dtoa.c dtoa.h \
unicode_gen.c unicode_gen_def.h \ unicode_gen.c unicode_gen_def.h \
run-test262.c test262o.conf test262.conf \ run-test262.c test262o.conf test262.conf \
test262o_errors.txt test262_errors.txt \ test262o_errors.txt test262_errors.txt \
@ -150,7 +177,6 @@ cp tests/*.js tests/*.patch tests/bjson.c $outdir/tests
cp examples/*.js examples/*.c $outdir/examples cp examples/*.js examples/*.c $outdir/examples
cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \ cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \
doc/jsbignum.texi doc/jsbignum.html doc/jsbignum.pdf \
$outdir/doc $outdir/doc
( cd /tmp && tar Jcvf /tmp/${d}.tar.xz ${d} ) ( cd /tmp && tar Jcvf /tmp/${d}.tar.xz ${d} )

261
repl.js
View File

@ -22,8 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
"use strip";
import * as std from "std"; import * as std from "std";
import * as os from "os"; import * as os from "os";
@ -41,11 +39,6 @@ import * as os from "os";
var isFinite = g.isFinite; var isFinite = g.isFinite;
var parseFloat = g.parseFloat; var parseFloat = g.parseFloat;
/* XXX: use preprocessor ? */
var config_numcalc = (typeof os.open === "undefined");
var has_jscalc = (typeof Fraction === "function");
var has_bignum = (typeof BigFloat === "function");
var colors = { var colors = {
none: "\x1b[0m", none: "\x1b[0m",
black: "\x1b[30m", black: "\x1b[30m",
@ -67,38 +60,20 @@ import * as os from "os";
bright_white: "\x1b[37;1m", bright_white: "\x1b[37;1m",
}; };
var styles; var styles = {
if (config_numcalc) { 'default': 'bright_green',
styles = { 'comment': 'white',
'default': 'black', 'string': 'bright_cyan',
'comment': 'white', 'regex': 'cyan',
'string': 'green', 'number': 'green',
'regex': 'cyan', 'keyword': 'bright_white',
'number': 'green', 'function': 'bright_yellow',
'keyword': 'blue', 'type': 'bright_magenta',
'function': 'gray', 'identifier': 'bright_green',
'type': 'bright_magenta', 'error': 'red',
'identifier': 'yellow', 'result': 'bright_white',
'error': 'bright_red', 'error_msg': 'bright_red',
'result': 'black', };
'error_msg': 'bright_red',
};
} else {
styles = {
'default': 'bright_green',
'comment': 'white',
'string': 'bright_cyan',
'regex': 'cyan',
'number': 'green',
'keyword': 'bright_white',
'function': 'bright_yellow',
'type': 'bright_magenta',
'identifier': 'bright_green',
'error': 'red',
'result': 'bright_white',
'error_msg': 'bright_red',
};
}
var history = []; var history = [];
var clip_board = ""; var clip_board = "";
@ -109,11 +84,7 @@ import * as os from "os";
var pstate = ""; var pstate = "";
var prompt = ""; var prompt = "";
var plen = 0; var plen = 0;
var ps1; var ps1 = "qjs > ";
if (config_numcalc)
ps1 = "> ";
else
ps1 = "qjs > ";
var ps2 = " ... "; var ps2 = " ... ";
var utf8 = true; var utf8 = true;
var show_time = false; var show_time = false;
@ -613,6 +584,9 @@ import * as os from "os";
base = get_context_word(line, pos); base = get_context_word(line, pos);
if (["true", "false", "null", "this"].includes(base) || !isNaN(+base)) if (["true", "false", "null", "this"].includes(base) || !isNaN(+base))
return eval(base); return eval(base);
// Check if `base` is a set of regexp flags
if (pos - base.length >= 3 && line[pos - base.length - 1] === '/')
return new RegExp('', base);
obj = get_context_object(line, pos - base.length); obj = get_context_object(line, pos - base.length);
if (obj === null || obj === void 0) if (obj === null || obj === void 0)
return obj; return obj;
@ -932,48 +906,6 @@ import * as os from "os";
} }
} }
function bigfloat_to_string(a, radix) {
var s;
if (!BigFloat.isFinite(a)) {
/* NaN, Infinite */
if (eval_mode !== "math") {
return "BigFloat(" + a.toString() + ")";
} else {
return a.toString();
}
} else {
if (a == 0) {
if (1 / a < 0)
s = "-0";
else
s = "0";
} else {
if (radix == 16) {
var s;
if (a < 0) {
a = -a;
s = "-";
} else {
s = "";
}
s += "0x" + a.toString(16);
} else {
s = a.toString();
}
}
if (typeof a === "bigfloat" && eval_mode !== "math") {
s += "l";
} else if (eval_mode !== "std" && s.indexOf(".") < 0 &&
((radix == 16 && s.indexOf("p") < 0) ||
(radix == 10 && s.indexOf("e") < 0))) {
/* add a decimal point so that the floating point type
is visible */
s += ".0";
}
return s;
}
}
function bigint_to_string(a, radix) { function bigint_to_string(a, radix) {
var s; var s;
if (radix == 16) { if (radix == 16) {
@ -1005,14 +937,8 @@ import * as os from "os";
std.puts(a); std.puts(a);
} else if (stack.indexOf(a) >= 0) { } else if (stack.indexOf(a) >= 0) {
std.puts("[circular]"); std.puts("[circular]");
} else if (has_jscalc && (a instanceof Fraction || } else if (a instanceof Date) {
a instanceof Complex || std.puts("Date " + a.toGMTString().__quote());
a instanceof Mod ||
a instanceof Polynomial ||
a instanceof PolyMod ||
a instanceof RationalFunction ||
a instanceof Series)) {
std.puts(a.toString());
} else { } else {
stack.push(a); stack.push(a);
if (Array.isArray(a)) { if (Array.isArray(a)) {
@ -1058,10 +984,6 @@ import * as os from "os";
std.puts(number_to_string(a, hex_mode ? 16 : 10)); std.puts(number_to_string(a, hex_mode ? 16 : 10));
} else if (type === "bigint") { } else if (type === "bigint") {
std.puts(bigint_to_string(a, hex_mode ? 16 : 10)); std.puts(bigint_to_string(a, hex_mode ? 16 : 10));
} else if (type === "bigfloat") {
std.puts(bigfloat_to_string(a, hex_mode ? 16 : 10));
} else if (type === "bigdecimal") {
std.puts(a.toString() + "m");
} else if (type === "symbol") { } else if (type === "symbol") {
std.puts(String(a)); std.puts(String(a));
} else if (type === "function") { } else if (type === "function") {
@ -1102,75 +1024,10 @@ import * as os from "os";
hex_mode = false; hex_mode = false;
} else if (cmd === "t") { } else if (cmd === "t") {
show_time = !show_time; show_time = !show_time;
} else if (has_bignum && cmd === "p") {
param = expr.substring(cmd.length + 1).trim().split(" ");
if (param.length === 1 && param[0] === "") {
std.puts("BigFloat precision=" + prec + " bits (~" +
Math.floor(prec / log2_10) +
" digits), exponent size=" + expBits + " bits\n");
} else if (param[0] === "f16") {
prec = 11;
expBits = 5;
} else if (param[0] === "f32") {
prec = 24;
expBits = 8;
} else if (param[0] === "f64") {
prec = 53;
expBits = 11;
} else if (param[0] === "f128") {
prec = 113;
expBits = 15;
} else {
prec1 = parseInt(param[0]);
if (param.length >= 2)
expBits1 = parseInt(param[1]);
else
expBits1 = BigFloatEnv.expBitsMax;
if (Number.isNaN(prec1) ||
prec1 < BigFloatEnv.precMin ||
prec1 > BigFloatEnv.precMax) {
std.puts("Invalid precision\n");
return false;
}
if (Number.isNaN(expBits1) ||
expBits1 < BigFloatEnv.expBitsMin ||
expBits1 > BigFloatEnv.expBitsMax) {
std.puts("Invalid exponent bits\n");
return false;
}
prec = prec1;
expBits = expBits1;
}
return false;
} else if (has_bignum && cmd === "digits") {
param = expr.substring(cmd.length + 1).trim();
prec1 = Math.ceil(parseFloat(param) * log2_10);
if (prec1 < BigFloatEnv.precMin ||
prec1 > BigFloatEnv.precMax) {
std.puts("Invalid precision\n");
return false;
}
prec = prec1;
expBits = BigFloatEnv.expBitsMax;
return false;
} else if (has_bignum && cmd === "mode") {
param = expr.substring(cmd.length + 1).trim();
if (param === "") {
std.puts("Running mode=" + eval_mode + "\n");
} else if (param === "std" || param === "math") {
eval_mode = param;
} else {
std.puts("Invalid mode\n");
}
return false;
} else if (cmd === "clear") { } else if (cmd === "clear") {
std.puts("\x1b[H\x1b[J"); std.puts("\x1b[H\x1b[J");
} else if (cmd === "q") { } else if (cmd === "q") {
std.exit(0); std.exit(0);
} else if (has_jscalc && cmd === "a") {
algebraicMode = true;
} else if (has_jscalc && cmd === "n") {
algebraicMode = false;
} else { } else {
std.puts("Unknown directive: " + cmd + "\n"); std.puts("Unknown directive: " + cmd + "\n");
return false; return false;
@ -1178,26 +1035,6 @@ import * as os from "os";
return true; return true;
} }
if (config_numcalc) {
/* called by the GUI */
g.execCmd = function (cmd) {
switch(cmd) {
case "dec":
hex_mode = false;
break;
case "hex":
hex_mode = true;
break;
case "num":
algebraicMode = false;
break;
case "alg":
algebraicMode = true;
break;
}
}
}
function help() { function help() {
function sel(n) { function sel(n) {
return n ? "*": " "; return n ? "*": " ";
@ -1206,40 +1043,12 @@ import * as os from "os";
"\\x " + sel(hex_mode) + "hexadecimal number display\n" + "\\x " + sel(hex_mode) + "hexadecimal number display\n" +
"\\d " + sel(!hex_mode) + "decimal number display\n" + "\\d " + sel(!hex_mode) + "decimal number display\n" +
"\\t " + sel(show_time) + "toggle timing display\n" + "\\t " + sel(show_time) + "toggle timing display\n" +
"\\clear clear the terminal\n"); "\\clear clear the terminal\n" +
if (has_jscalc) { "\\q exit\n");
std.puts("\\a " + sel(algebraicMode) + "algebraic mode\n" +
"\\n " + sel(!algebraicMode) + "numeric mode\n");
}
if (has_bignum) {
std.puts("\\p [m [e]] set the BigFloat precision to 'm' bits\n" +
"\\digits n set the BigFloat precision to 'ceil(n*log2(10))' bits\n");
if (!has_jscalc) {
std.puts("\\mode [std|math] change the running mode (current = " + eval_mode + ")\n");
}
}
if (!config_numcalc) {
std.puts("\\q exit\n");
}
} }
function cmd_start() { function cmd_start() {
if (!config_numcalc) { std.puts('QuickJS - Type "\\h" for help\n');
if (has_jscalc)
std.puts('QJSCalc - Type "\\h" for help\n');
else
std.puts('QuickJS - Type "\\h" for help\n');
}
if (has_bignum) {
log2_10 = Math.log(10) / Math.log(2);
prec = 113;
expBits = 15;
if (has_jscalc) {
eval_mode = "math";
/* XXX: numeric mode should always be the default ? */
g.algebraicMode = config_numcalc;
}
}
cmd_readline_start(); cmd_readline_start();
} }
@ -1287,37 +1096,27 @@ import * as os from "os";
} }
mexpr = ""; mexpr = "";
if (has_bignum) { eval_and_print_start(expr);
/* XXX: async is not supported in this case */
BigFloatEnv.setPrec(eval_and_print_start.bind(null, expr, false),
prec, expBits);
} else {
eval_and_print_start(expr, true);
}
return true; return true;
} }
function eval_and_print_start(expr, is_async) { function eval_and_print_start(expr) {
var result; var result;
try { try {
if (eval_mode === "math")
expr = '"use math"; void 0;' + expr;
eval_start_time = os.now(); eval_start_time = os.now();
/* eval as a script */ /* eval as a script */
result = std.evalScript(expr, { backtrace_barrier: true, async: is_async }); result = std.evalScript(expr, { backtrace_barrier: true, async: true });
if (is_async) { /* result is a promise */
/* result is a promise */ result.then(print_eval_result, print_eval_error);
result.then(print_eval_result, print_eval_error);
} else {
print_eval_result(result);
}
} catch (error) { } catch (error) {
print_eval_error(error); print_eval_error(error);
} }
} }
function print_eval_result(result) { function print_eval_result(result) {
result = result.value;
eval_time = os.now() - eval_start_time; eval_time = os.now() - eval_start_time;
std.puts(colors[styles.result]); std.puts(colors[styles.result]);
print(result); print(result);

View File

@ -63,6 +63,8 @@ enum test_mode_t {
TEST_STRICT, /* run tests as strict, skip nostrict tests */ TEST_STRICT, /* run tests as strict, skip nostrict tests */
TEST_ALL, /* run tests in both strict and nostrict, unless restricted by spec */ TEST_ALL, /* run tests in both strict and nostrict, unless restricted by spec */
} test_mode = TEST_DEFAULT_NOSTRICT; } test_mode = TEST_DEFAULT_NOSTRICT;
int compact;
int show_timings;
int skip_async; int skip_async;
int skip_module; int skip_module;
int new_style; int new_style;
@ -530,6 +532,7 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
{ {
const char *script; const char *script;
Test262Agent *agent; Test262Agent *agent;
pthread_attr_t attr;
if (JS_GetContextOpaque(ctx) != NULL) if (JS_GetContextOpaque(ctx) != NULL)
return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); return JS_ThrowTypeError(ctx, "cannot be called inside an agent");
@ -544,7 +547,12 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
agent->script = strdup(script); agent->script = strdup(script);
JS_FreeCString(ctx, script); JS_FreeCString(ctx, script);
list_add_tail(&agent->link, &agent_list); list_add_tail(&agent->link, &agent_list);
pthread_create(&agent->tid, NULL, agent_start, agent); pthread_attr_init(&attr);
// musl libc gives threads 80 kb stacks, much smaller than
// JS_DEFAULT_STACK_SIZE (256 kb)
pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default
pthread_create(&agent->tid, &attr, agent_start, agent);
pthread_attr_destroy(&attr);
return JS_UNDEFINED; return JS_UNDEFINED;
} }
@ -749,6 +757,13 @@ static JSValue js_IsHTMLDDA(JSContext *ctx, JSValue this_val,
return JS_NULL; return JS_NULL;
} }
static JSValue js_gc(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JS_RunGC(JS_GetRuntime(ctx));
return JS_UNDEFINED;
}
static JSValue add_helpers1(JSContext *ctx) static JSValue add_helpers1(JSContext *ctx)
{ {
JSValue global_obj; JSValue global_obj;
@ -782,6 +797,8 @@ static JSValue add_helpers1(JSContext *ctx)
obj = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0); obj = JS_NewCFunction(ctx, js_IsHTMLDDA, "IsHTMLDDA", 0);
JS_SetIsHTMLDDA(ctx, obj); JS_SetIsHTMLDDA(ctx, obj);
JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj); JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj);
JS_SetPropertyStr(ctx, obj262, "gc",
JS_NewCFunction(ctx, js_gc, "gc", 0));
JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262)); JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262));
@ -813,6 +830,19 @@ static JSModuleDef *js_module_loader_test(JSContext *ctx,
uint8_t *buf; uint8_t *buf;
JSModuleDef *m; JSModuleDef *m;
JSValue func_val; JSValue func_val;
char *filename, *slash, path[1024];
// interpret import("bar.js") from path/to/foo.js as
// import("path/to/bar.js") but leave import("./bar.js") untouched
filename = opaque;
if (!strchr(module_name, '/')) {
slash = strrchr(filename, '/');
if (slash) {
snprintf(path, sizeof(path), "%.*s/%s",
(int)(slash - filename), filename, module_name);
module_name = path;
}
}
buf = js_load_file(ctx, &buf_len, module_name); buf = js_load_file(ctx, &buf_len, module_name);
if (!buf) { if (!buf) {
@ -910,7 +940,7 @@ void update_exclude_dirs(void)
lp->count = count; lp->count = count;
} }
void load_config(const char *filename) void load_config(const char *filename, const char *ignore)
{ {
char buf[1024]; char buf[1024];
FILE *f; FILE *f;
@ -965,6 +995,10 @@ void load_config(const char *filename)
printf("%s:%d: syntax error\n", filename, lineno); printf("%s:%d: syntax error\n", filename, lineno);
continue; continue;
} }
if (strstr(ignore, p)) {
printf("%s:%d: ignoring %s=%s\n", filename, lineno, p, q);
continue;
}
if (str_equal(p, "style")) { if (str_equal(p, "style")) {
new_style = str_equal(q, "new"); new_style = str_equal(q, "new");
continue; continue;
@ -1540,13 +1574,13 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
JS_SetCanBlock(rt, can_block); JS_SetCanBlock(rt, can_block);
/* loader for ES6 modules */ /* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL); JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, (void *)filename);
add_helpers(ctx); add_helpers(ctx);
for (i = 0; i < ip->count; i++) { for (i = 0; i < ip->count; i++) {
if (eval_file(ctx, harness, ip->array[i], if (eval_file(ctx, harness, ip->array[i],
JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRIP)) { JS_EVAL_TYPE_GLOBAL)) {
fatal(1, "error including %s for %s", ip->array[i], filename); fatal(1, "error including %s for %s", ip->array[i], filename);
} }
} }
@ -1656,7 +1690,7 @@ int run_test(const char *filename, int index)
/* XXX: should extract the phase */ /* XXX: should extract the phase */
char *q = find_tag(p, "type:", &state); char *q = find_tag(p, "type:", &state);
if (q) { if (q) {
while (isspace(*q)) while (isspace((unsigned char)*q))
q++; q++;
error_type = strdup_len(q, strcspn(q, " \n")); error_type = strdup_len(q, strcspn(q, " \n"));
} }
@ -1841,7 +1875,7 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
JS_SetCanBlock(rt, can_block); JS_SetCanBlock(rt, can_block);
/* loader for ES6 modules */ /* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL); JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, (void *)filename);
add_helpers(ctx); add_helpers(ctx);
@ -1900,9 +1934,27 @@ void show_progress(int force) {
clock_t t = clock(); clock_t t = clock();
if (force || !last_clock || (t - last_clock) > CLOCKS_PER_SEC / 20) { if (force || !last_clock || (t - last_clock) > CLOCKS_PER_SEC / 20) {
last_clock = t; last_clock = t;
/* output progress indicator: erase end of line and return to col 0 */ if (compact) {
fprintf(stderr, "%d/%d/%d\033[K\r", static int last_test_skipped;
test_failed, test_count, test_skipped); static int last_test_failed;
static int dots;
char c = '.';
if (test_skipped > last_test_skipped)
c = '-';
if (test_failed > last_test_failed)
c = '!';
last_test_skipped = test_skipped;
last_test_failed = test_failed;
fputc(c, stderr);
if (force || ++dots % 60 == 0) {
fprintf(stderr, " %d/%d/%d\n",
test_failed, test_count, test_skipped);
}
} else {
/* output progress indicator: erase end of line and return to col 0 */
fprintf(stderr, "%d/%d/%d\033[K\r",
test_failed, test_count, test_skipped);
}
fflush(stderr); fflush(stderr);
} }
} }
@ -1953,6 +2005,8 @@ void help(void)
"-N run test prepared by test262-harness+eshost\n" "-N run test prepared by test262-harness+eshost\n"
"-s run tests in strict mode, skip @nostrict tests\n" "-s run tests in strict mode, skip @nostrict tests\n"
"-E only run tests from the error file\n" "-E only run tests from the error file\n"
"-C use compact progress indicator\n"
"-t show timings\n"
"-u update error file\n" "-u update error file\n"
"-v verbose: output error messages\n" "-v verbose: output error messages\n"
"-T duration display tests taking more than 'duration' ms\n" "-T duration display tests taking more than 'duration' ms\n"
@ -1979,14 +2033,29 @@ int main(int argc, char **argv)
BOOL is_dir_list; BOOL is_dir_list;
BOOL only_check_errors = FALSE; BOOL only_check_errors = FALSE;
const char *filename; const char *filename;
const char *ignore = "";
BOOL is_test262_harness = FALSE; BOOL is_test262_harness = FALSE;
BOOL is_module = FALSE; BOOL is_module = FALSE;
clock_t clocks;
#if !defined(_WIN32) #if !defined(_WIN32)
compact = !isatty(STDERR_FILENO);
/* Date tests assume California local time */ /* Date tests assume California local time */
setenv("TZ", "America/Los_Angeles", 1); setenv("TZ", "America/Los_Angeles", 1);
#endif #endif
optind = 1;
while (optind < argc) {
char *arg = argv[optind];
if (*arg != '-')
break;
optind++;
if (strstr("-c -d -e -x -f -r -E -T", arg))
optind++;
if (strstr("-d -f", arg))
ignore = "testdir"; // run only the tests from -d or -f
}
/* cannot use getopt because we want to pass the command line to /* cannot use getopt because we want to pass the command line to
the script */ the script */
optind = 1; optind = 1;
@ -2006,12 +2075,16 @@ int main(int argc, char **argv)
test_mode = TEST_STRICT; test_mode = TEST_STRICT;
} else if (str_equal(arg, "-a")) { } else if (str_equal(arg, "-a")) {
test_mode = TEST_ALL; test_mode = TEST_ALL;
} else if (str_equal(arg, "-t")) {
show_timings++;
} else if (str_equal(arg, "-u")) { } else if (str_equal(arg, "-u")) {
update_errors++; update_errors++;
} else if (str_equal(arg, "-v")) { } else if (str_equal(arg, "-v")) {
verbose++; verbose++;
} else if (str_equal(arg, "-C")) {
compact = 1;
} else if (str_equal(arg, "-c")) { } else if (str_equal(arg, "-c")) {
load_config(get_opt_arg(arg, argv[optind++])); load_config(get_opt_arg(arg, argv[optind++]), ignore);
} else if (str_equal(arg, "-d")) { } else if (str_equal(arg, "-d")) {
enumerate_tests(get_opt_arg(arg, argv[optind++])); enumerate_tests(get_opt_arg(arg, argv[optind++]));
} else if (str_equal(arg, "-e")) { } else if (str_equal(arg, "-e")) {
@ -2062,8 +2135,10 @@ int main(int argc, char **argv)
update_exclude_dirs(); update_exclude_dirs();
clocks = clock();
if (is_dir_list) { if (is_dir_list) {
if (optind < argc && !isdigit(argv[optind][0])) { if (optind < argc && !isdigit((unsigned char)argv[optind][0])) {
filename = argv[optind++]; filename = argv[optind++];
namelist_load(&test_list, filename); namelist_load(&test_list, filename);
} }
@ -2098,6 +2173,8 @@ int main(int argc, char **argv)
} }
} }
clocks = clock() - clocks;
if (dump_memory) { if (dump_memory) {
if (dump_memory > 1 && stats_count > 1) { if (dump_memory > 1 && stats_count > 1) {
printf("\nMininum memory statistics for %s:\n\n", stats_min_filename); printf("\nMininum memory statistics for %s:\n\n", stats_min_filename);
@ -2126,6 +2203,8 @@ int main(int argc, char **argv)
fprintf(stderr, ", %d fixed", fixed_errors); fprintf(stderr, ", %d fixed", fixed_errors);
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
if (show_timings)
fprintf(stderr, "Total time: %.3fs\n", (double)clocks / CLOCKS_PER_SEC);
} }
if (error_out && error_out != stdout) { if (error_out && error_out != stdout) {
@ -2141,5 +2220,6 @@ int main(int argc, char **argv)
free(harness_exclude); free(harness_exclude);
free(error_file); free(error_file);
return 0; /* Signal that the error file is out of date. */
return new_errors || changed_errors || fixed_errors;
} }

View File

@ -61,7 +61,6 @@ Array.fromAsync=skip
Array.prototype.at Array.prototype.at
Array.prototype.flat Array.prototype.flat
Array.prototype.flatMap Array.prototype.flatMap
Array.prototype.flatten
Array.prototype.includes Array.prototype.includes
Array.prototype.values Array.prototype.values
ArrayBuffer ArrayBuffer
@ -70,9 +69,11 @@ arrow-function
async-functions async-functions
async-iteration async-iteration
Atomics Atomics
Atomics.pause=skip
Atomics.waitAsync=skip Atomics.waitAsync=skip
BigInt BigInt
caller caller
canonical-tz=skip
change-array-by-copy change-array-by-copy
class class
class-fields-private class-fields-private
@ -83,7 +84,6 @@ class-static-block
class-static-fields-private class-static-fields-private
class-static-fields-public class-static-fields-public
class-static-methods-private class-static-methods-private
cleanupSome=skip
coalesce-expression coalesce-expression
computed-property-names computed-property-names
const const
@ -103,11 +103,12 @@ destructuring-assignment
destructuring-binding destructuring-binding
dynamic-import dynamic-import
error-cause error-cause
Error.isError=skip
explicit-resource-management=skip
exponentiation exponentiation
export-star-as-namespace-from-module export-star-as-namespace-from-module
FinalizationGroup=skip FinalizationRegistry
FinalizationRegistry.prototype.cleanupSome=skip Float16Array=skip
FinalizationRegistry=skip
Float32Array Float32Array
Float64Array Float64Array
for-in-order for-in-order
@ -118,12 +119,31 @@ hashbang
host-gc-required=skip host-gc-required=skip
import-assertions=skip import-assertions=skip
import-attributes=skip import-attributes=skip
import-defer=skip
import.meta import.meta
Int16Array Int16Array
Int32Array Int32Array
Int8Array Int8Array
Intl-enumeration=skip
intl-normative-optional=skip
Intl.DateTimeFormat-datetimestyle=skip
Intl.DateTimeFormat-dayPeriod=skip
Intl.DateTimeFormat-extend-timezonename=skip
Intl.DateTimeFormat-formatRange=skip
Intl.DateTimeFormat-fractionalSecondDigits=skip
Intl.DisplayNames-v2=skip
Intl.DisplayNames=skip
Intl.DurationFormat=skip
Intl.ListFormat=skip
Intl.Locale-info=skip
Intl.Locale=skip
Intl.NumberFormat-unified=skip
Intl.NumberFormat-v3=skip
Intl.RelativeTimeFormat=skip
Intl.Segmenter=skip
IsHTMLDDA IsHTMLDDA
iterator-helpers=skip iterator-helpers=skip
iterator-sequencing=skip
json-modules=skip json-modules=skip
json-parse-with-source=skip json-parse-with-source=skip
json-superset json-superset
@ -131,6 +151,7 @@ legacy-regexp=skip
let let
logical-assignment-operators logical-assignment-operators
Map Map
Math.sumPrecise=skip
new.target new.target
numeric-separator-literal numeric-separator-literal
object-rest object-rest
@ -141,6 +162,7 @@ Object.is
optional-catch-binding optional-catch-binding
optional-chaining optional-chaining
Promise Promise
promise-try=skip
promise-with-resolvers promise-with-resolvers
Promise.allSettled Promise.allSettled
Promise.any Promise.any
@ -155,15 +177,19 @@ regexp-dotall
regexp-duplicate-named-groups=skip regexp-duplicate-named-groups=skip
regexp-lookbehind regexp-lookbehind
regexp-match-indices regexp-match-indices
regexp-modifiers=skip
regexp-named-groups regexp-named-groups
regexp-unicode-property-escapes regexp-unicode-property-escapes
regexp-v-flag=skip regexp-v-flag=skip
RegExp.escape=skip
resizable-arraybuffer=skip resizable-arraybuffer=skip
rest-parameters rest-parameters
Set Set
set-methods=skip set-methods=skip
ShadowRealm=skip ShadowRealm=skip
SharedArrayBuffer SharedArrayBuffer
source-phase-imports-module-source=skip
source-phase-imports=skip
string-trimming string-trimming
String.fromCodePoint String.fromCodePoint
String.prototype.at String.prototype.at
@ -191,7 +217,7 @@ Symbol.split
Symbol.toPrimitive Symbol.toPrimitive
Symbol.toStringTag Symbol.toStringTag
Symbol.unscopables Symbol.unscopables
symbols-as-weakmap-keys=skip symbols-as-weakmap-keys
tail-call-optimization=skip tail-call-optimization=skip
template template
Temporal=skip Temporal=skip
@ -202,9 +228,10 @@ u180e
Uint16Array Uint16Array
Uint32Array Uint32Array
Uint8Array Uint8Array
uint8array-base64=skip
Uint8ClampedArray Uint8ClampedArray
WeakMap WeakMap
WeakRef=skip WeakRef
WeakSet WeakSet
well-formed-json-stringify well-formed-json-stringify
@ -223,5 +250,116 @@ test262/test/built-ins/ThrowTypeError/unique-per-realm-function-proto.js
#test262/test/built-ins/RegExp/CharacterClassEscapes/ #test262/test/built-ins/RegExp/CharacterClassEscapes/
#test262/test/built-ins/RegExp/property-escapes/ #test262/test/built-ins/RegExp/property-escapes/
# feature regexp-v-flag is missing in the tests
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-digit-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-digit-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-whitespace-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-non-word-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-whitespace-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-negative-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-positive-cases.js
test262/test/built-ins/RegExp/CharacterClassEscapes/character-class-word-class-escape-positive-cases.js
# not yet in official specification
test262/test/built-ins/String/prototype/match/cstm-matcher-on-bigint-primitive.js
test262/test/built-ins/String/prototype/match/cstm-matcher-on-bigint-primitive.js
test262/test/built-ins/String/prototype/match/cstm-matcher-on-boolean-primitive.js
test262/test/built-ins/String/prototype/match/cstm-matcher-on-boolean-primitive.js
test262/test/built-ins/String/prototype/match/cstm-matcher-on-number-primitive.js
test262/test/built-ins/String/prototype/match/cstm-matcher-on-number-primitive.js
test262/test/built-ins/String/prototype/match/cstm-matcher-on-string-primitive.js
test262/test/built-ins/String/prototype/match/cstm-matcher-on-string-primitive.js
test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-bigint-primitive.js
test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-bigint-primitive.js
test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-number-primitive.js
test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-number-primitive.js
test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-string-primitive.js
test262/test/built-ins/String/prototype/matchAll/cstm-matchall-on-string-primitive.js
test262/test/built-ins/String/prototype/replace/cstm-replace-on-bigint-primitive.js
test262/test/built-ins/String/prototype/replace/cstm-replace-on-bigint-primitive.js
test262/test/built-ins/String/prototype/replace/cstm-replace-on-boolean-primitive.js
test262/test/built-ins/String/prototype/replace/cstm-replace-on-boolean-primitive.js
test262/test/built-ins/String/prototype/replace/cstm-replace-on-number-primitive.js
test262/test/built-ins/String/prototype/replace/cstm-replace-on-number-primitive.js
test262/test/built-ins/String/prototype/replace/cstm-replace-on-string-primitive.js
test262/test/built-ins/String/prototype/replace/cstm-replace-on-string-primitive.js
test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-bigint-primitive.js
test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-bigint-primitive.js
test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-boolean-primitive.js
test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-boolean-primitive.js
test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-number-primitive.js
test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-number-primitive.js
test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-string-primitive.js
test262/test/built-ins/String/prototype/replaceAll/cstm-replaceall-on-string-primitive.js
test262/test/built-ins/String/prototype/search/cstm-search-on-bigint-primitive.js
test262/test/built-ins/String/prototype/search/cstm-search-on-bigint-primitive.js
test262/test/built-ins/String/prototype/search/cstm-search-on-boolean-primitive.js
test262/test/built-ins/String/prototype/search/cstm-search-on-boolean-primitive.js
test262/test/built-ins/String/prototype/search/cstm-search-on-number-primitive.js
test262/test/built-ins/String/prototype/search/cstm-search-on-number-primitive.js
test262/test/built-ins/String/prototype/search/cstm-search-on-string-primitive.js
test262/test/built-ins/String/prototype/search/cstm-search-on-string-primitive.js
test262/test/built-ins/String/prototype/split/cstm-split-on-bigint-primitive.js
test262/test/built-ins/String/prototype/split/cstm-split-on-bigint-primitive.js
test262/test/built-ins/String/prototype/split/cstm-split-on-boolean-primitive.js
test262/test/built-ins/String/prototype/split/cstm-split-on-boolean-primitive.js
test262/test/built-ins/String/prototype/split/cstm-split-on-number-primitive.js
test262/test/built-ins/String/prototype/split/cstm-split-on-number-primitive.js
test262/test/built-ins/String/prototype/split/cstm-split-on-string-primitive.js
test262/test/built-ins/String/prototype/split/cstm-split-on-string-primitive.js
####################################
# staging tests
# sort() does not modify the array and we don't update it (XXX: the
# spec updates it in this case)
test262/test/staging/sm/Array/frozen-dense-array.js
# not supported
test262/test/staging/sm/Set/difference.js
test262/test/staging/sm/Set/intersection.js
test262/test/staging/sm/Set/is-disjoint-from.js
test262/test/staging/sm/Set/is-subset-of.js
test262/test/staging/sm/Set/is-superset-of.js
test262/test/staging/sm/Set/symmetric-difference.js
test262/test/staging/sm/Set/union.js
test262/test/staging/sm/extensions/censor-strict-caller.js
test262/test/staging/sm/JSON/parse-with-source.js
test262/test/staging/sm/RegExp/flags.js
test262/test/staging/sm/RegExp/prototype.js
# no f16
test262/test/staging/sm/Math/f16round.js
test262/test/staging/sm/TypedArray/sort_small.js
test262/test/staging/sm/extensions/dataview.js
test262/test/staging/sm/TypedArray/toString.js
# not standard
test262/test/staging/sm/Function/builtin-no-construct.js
test262/test/staging/sm/Function/function-caller-restrictions.js
test262/test/staging/sm/Function/function-toString-builtin-name.js
test262/test/staging/sm/extensions/arguments-property-access-in-function.js
test262/test/staging/sm/extensions/function-caller-skips-eval-frames.js
test262/test/staging/sm/extensions/function-properties.js
# RegExp toSource not fully compliant
test262/test/staging/sm/RegExp/toString.js
test262/test/staging/sm/RegExp/source.js
[tests] [tests]
# list test files or use config.testdir # list test files or use config.testdir

View File

@ -1,8 +1,72 @@
test262/test/annexB/language/eval-code/direct/script-decl-lex-collision-in-sloppy-mode.js:13: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all test262/test/language/module-code/top-level-await/module-graphs-does-not-hang.js:10: TypeError: $DONE() not called
test262/test/language/expressions/assignment/target-member-computed-reference-null.js:32: Test262Error: Expected a DummyError but got a TypeError test262/test/staging/sm/Date/UTC-convert-all-arguments.js:75: Test262Error: index 1: expected 42, got Error: didn't throw Expected SameValue(«Error: didn't throw», «42») to be true
test262/test/language/expressions/assignment/target-member-computed-reference-null.js:32: strict mode: Test262Error: Expected a DummyError but got a TypeError test262/test/staging/sm/Date/constructor-convert-all-arguments.js:75: Test262Error: index undefined: expected 42, got Error: didn't throw Expected SameValue(«Error: didn't throw», «42») to be true
test262/test/language/expressions/assignment/target-member-computed-reference-undefined.js:32: Test262Error: Expected a DummyError but got a TypeError test262/test/staging/sm/Date/non-iso.js:76: Test262Error: Expected SameValue(«NaN», «-40071559730000») to be true
test262/test/language/expressions/assignment/target-member-computed-reference-undefined.js:32: strict mode: Test262Error: Expected a DummyError but got a TypeError test262/test/staging/sm/Date/two-digit-years.js:76: Test262Error: Expected SameValue(«915177600000», «NaN») to be true
test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: unexpected error type: Test262: This statement should not be evaluated. test262/test/staging/sm/Function/arguments-parameter-shadowing.js:15: Test262Error: Expected SameValue(«true», «false») to be true
test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: strict mode: unexpected error type: Test262: This statement should not be evaluated. test262/test/staging/sm/Function/constructor-binding.js:12: Test262Error: Expected SameValue(«"function"», «"undefined"») to be true
test262/test/language/global-code/script-decl-lex-var-declared-via-eval-sloppy.js:13: Test262Error: variable Expected a SyntaxError to be thrown but no exception was thrown at all test262/test/staging/sm/Function/function-bind.js:14: Test262Error: Expected SameValue(«false», «true») to be true
test262/test/staging/sm/Function/function-name-for.js:12: Test262Error: Expected SameValue(«""», «"forInHead"») to be true
test262/test/staging/sm/Function/function-toString-builtin.js:14: Test262Error: Expected match to '/^\s*function\s*(get|set)?\s*(\w+|(?:'[^']*')|(?:"[^"]*")|\d+|(?:\[[^\]]+\]))?\s*\(\s*\)\s*\{\s*\[native code\]\s*\}\s*$/', Actual value 'function bound fn() {
[native code]
}' Expected SameValue(«null», «null») to be false
test262/test/staging/sm/Function/implicit-this-in-parameter-expression.js:13: Test262Error: Expected SameValue(«[object Object]», «undefined») to be true
test262/test/staging/sm/Function/invalid-parameter-list.js:35: Error: Assertion failed: expected exception SyntaxError, no exception thrown
test262/test/staging/sm/JSON/parse-number-syntax.js:39: Test262Error: parsing string <1.> threw a non-SyntaxError exception: Test262Error: string <1.> shouldn't have parsed as JSON Expected SameValue(«false», «true») to be true Expected SameValue(«true», «false») to be true
test262/test/staging/sm/JSON/parse-syntax-errors-02.js:51: Test262Error: parsing string <["Illegal backslash escape: \x15"]> threw a non-SyntaxError exception: Test262Error: string <["Illegal backslash escape: \x15"]> shouldn't have parsed as JSON Expected SameValue(«false», «true») to be true Expected SameValue(«true», «false») to be true
test262/test/staging/sm/Math/cbrt-approx.js:26: Error: got 1.39561242508609, expected a number near 1.3956124250860895 (relative error: 2)
test262/test/staging/sm/RegExp/constructor-ordering-2.js:15: Test262Error: Expected SameValue(«false», «true») to be true
test262/test/staging/sm/RegExp/escape.js:13: Test262Error: Expected SameValue(«"\\\n"», «"\\n"») to be true
test262/test/staging/sm/RegExp/match-trace.js:13: Test262Error: Expected SameValue(«"get:flags,get:unicode,set:lastIndex,get:exec,call:exec,get:result[0],get:exec,call:exec,get:result[0],get:exec,call:exec,"», «"get:flags,set:lastIndex,get:exec,call:exec,get:result[0],get:exec,call:exec,get:result[0],get:exec,call:exec,"») to be true
test262/test/staging/sm/RegExp/regress-613820-1.js:13: Test262Error: Expected SameValue(«"aaa"», «"aa"») to be true
test262/test/staging/sm/RegExp/regress-613820-2.js:13: Test262Error: Expected SameValue(«"f"», «undefined») to be true
test262/test/staging/sm/RegExp/regress-613820-3.js:13: Test262Error: Expected SameValue(«"aab"», «"aa"») to be true
test262/test/staging/sm/RegExp/replace-trace.js:13: Test262Error: Expected SameValue(«"get:flags,get:unicode,set:lastIndex,get:exec,call:exec,get:result[0],get:exec,call:exec,get:result[length],get:result[0],get:result[index],get:result[groups],"», «"get:flags,set:lastIndex,get:exec,call:exec,get:result[0],get:exec,call:exec,get:result[length],get:result[0],get:result[index],get:result[groups],"») to be true
test262/test/staging/sm/RegExp/unicode-ignoreCase-escape.js:22: Test262Error: Actual argument shouldn't be nullish.
test262/test/staging/sm/RegExp/unicode-ignoreCase-word-boundary.js:13: Test262Error: Expected SameValue(«false», «true») to be true
test262/test/staging/sm/String/match-defines-match-elements.js:52: Test262Error: Expected SameValue(«true», «false») to be true
test262/test/staging/sm/TypedArray/constructor-buffer-sequence.js:73: Error: Assertion failed: expected exception ExpectedError, got Error: Poisoned Value
test262/test/staging/sm/TypedArray/prototype-constructor-identity.js:17: Test262Error: Expected SameValue(«2», «6») to be true
test262/test/staging/sm/TypedArray/set-detached-bigint.js:27: Error: Assertion failed: expected exception SyntaxError, got RangeError: invalid array length
test262/test/staging/sm/TypedArray/set-detached.js:112: RangeError: invalid array length
test262/test/staging/sm/TypedArray/sort-negative-nan.js:102: TypeError: cannot read property 'name' of undefined
test262/test/staging/sm/TypedArray/sort_modifications.js:12: Test262Error: Int8Array at index 0 for size 4 Expected SameValue(«0», «1») to be true
test262/test/staging/sm/TypedArray/subarray.js:15: Test262Error: Expected SameValue(«0», «1») to be true
test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:45: Error: Assertion failed: expected exception SyntaxError, no exception thrown
test262/test/staging/sm/async-functions/await-error.js:12: Test262Error: Expected SameValue(«false», «true») to be true
test262/test/staging/sm/async-functions/await-in-arrow-parameters.js:33: Error: Assertion failed: expected exception SyntaxError, no exception thrown - AsyncFunction:(a = (b = await/r/g) => {}) => {}
test262/test/staging/sm/class/boundFunctionSubclassing.js:12: Test262Error: Expected SameValue(«false», «true») to be true
test262/test/staging/sm/class/compPropNames.js:26: Error: Expected syntax error: ({[1, 2]: 3})
test262/test/staging/sm/class/methDefn.js:26: Error: Expected syntax error: b = {a() => 0}
test262/test/staging/sm/class/strictExecution.js:32: Error: Assertion failed: expected exception TypeError, no exception thrown
test262/test/staging/sm/class/superPropOrdering.js:83: Error: Assertion failed: expected exception TypeError, no exception thrown
test262/test/staging/sm/expressions/optional-chain.js:25: Error: Assertion failed: expected exception SyntaxError, no exception thrown
test262/test/staging/sm/expressions/short-circuit-compound-assignment-const.js:97: TypeError: 'a' is read-only
test262/test/staging/sm/expressions/short-circuit-compound-assignment-tdz.js:23: Error: Assertion failed: expected exception ReferenceError, got TypeError: 'a' is read-only
test262/test/staging/sm/extensions/TypedArray-set-object-funky-length-detaches.js:55: RangeError: invalid array length
test262/test/staging/sm/extensions/regress-469625-01.js:16: Test262Error: TM: Array prototype and expression closures Expected SameValue(«"TypeError: [].__proto__ is not a function"», «"TypeError: not a function"») to be true
test262/test/staging/sm/generators/syntax.js:30: Error: Assertion failed: expected SyntaxError, but no exception thrown - function* g() { (function* yield() {}); }
test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-arguments.js:14: Test262Error: Expected SameValue(«"object"», «"function"») to be true
test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-eval.js:12: Test262Error: Expected SameValue(«"outer-gouter-geval-gtruefalseq"», «"outer-geval-gwith-gtruefalseq"») to be true
test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-if.js:20: TypeError: not a function
test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-notapplicable.js:15: Test262Error: Expected SameValue(«function x() {2}», «function x() {1}») to be true
test262/test/staging/sm/lexical-environment/block-scoped-functions-deprecated-redecl.js:23: Test262Error: Expected SameValue(«3», «4») to be true
test262/test/staging/sm/lexical-environment/unscopables-proto.js:15: Test262Error: Expected SameValue(«true», «false») to be true
test262/test/staging/sm/lexical-environment/var-in-catch-body-annex-b-eval.js:17: Test262Error: Expected SameValue(«"g"», «"global-x"») to be true
test262/test/staging/sm/module/module-export-name-star.js:15: SyntaxError: identifier expected
test262/test/staging/sm/object/defineProperties-order.js:14: Test262Error: Expected SameValue(«"ownKeys,getOwnPropertyDescriptor,getOwnPropertyDescriptor,get,get"», «"ownKeys,getOwnPropertyDescriptor,get,getOwnPropertyDescriptor,get"») to be true
test262/test/staging/sm/object/defineProperty-proxy.js:32: Test262Error: Expected ["has configurable", "get configurable", "has writable", "get writable", "has enumerable", "get enumerable", "has value", "get value", "has get", "has set"] to be structurally equal to ["has enumerable", "get enumerable", "has configurable", "get configurable", "has value", "get value", "has writable", "get writable", "has get", "has set"].
test262/test/staging/sm/regress/regress-577648-1.js:21: Test262Error: 1 Expected SameValue(«true», «false») to be true
test262/test/staging/sm/regress/regress-577648-2.js:14: Test262Error: Expected SameValue(«true», «false») to be true
test262/test/staging/sm/regress/regress-584355.js:12: Test262Error: Expected SameValue(«"function f () { ff (); }"», «"undefined"») to be true
test262/test/staging/sm/regress/regress-586482-1.js:19: Test262Error: ok Expected SameValue(«true», «false») to be true
test262/test/staging/sm/regress/regress-586482-2.js:19: Test262Error: ok Expected SameValue(«true», «false») to be true
test262/test/staging/sm/regress/regress-586482-3.js:18: Test262Error: ok Expected SameValue(«true», «false») to be true
test262/test/staging/sm/regress/regress-586482-4.js:14: Test262Error: ok Expected SameValue(«function() { this.f(); }», «undefined») to be true
test262/test/staging/sm/regress/regress-602621.js:14: Test262Error: function sub-statement must override arguments Expected SameValue(«"function"», «"object"») to be true
test262/test/staging/sm/regress/regress-699682.js:15: Test262Error: Expected SameValue(«false», «true») to be true
test262/test/staging/sm/regress/regress-1383630.js:30: Error: Assertion failed: expected exception TypeError, no exception thrown
test262/test/staging/sm/statements/arrow-function-in-for-statement-head.js:15: Test262Error: expected syntax error, got Error: didn't throw Expected SameValue(«false», «true») to be true
test262/test/staging/sm/statements/regress-642975.js:14: Test262Error: Expected SameValue(«undefined», «"y"») to be true
test262/test/staging/sm/statements/try-completion.js:17: Test262Error: Expected SameValue(«"try"», «undefined») to be true
test262/test/staging/sm/syntax/syntax-parsed-arrow-then-directive.js:77: Test262Error: stack should contain 'http://example.com/foo.js': block, semi Expected SameValue(«false», «true») to be true

View File

@ -406,5 +406,8 @@ test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-2.js
test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-3.js test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-3.js
test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-4.js test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-4.js
# String.prototype.localeCompare special cases
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.9/15.5.4.9_CE.js
[tests] [tests]
# list test files or use config.testdir # list test files or use config.testdir

49
tests/assert.js Normal file
View File

@ -0,0 +1,49 @@
export function assert(actual, expected, message) {
if (arguments.length === 1)
expected = true;
if (typeof actual === typeof expected) {
if (actual === expected) {
if (actual !== 0 || (1 / actual) === (1 / expected))
return;
}
if (typeof actual === 'number') {
if (isNaN(actual) && isNaN(expected))
return;
}
if (typeof actual === 'object') {
if (actual !== null && expected !== null
&& actual.constructor === expected.constructor
&& actual.toString() === expected.toString())
return;
}
}
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
export function assertThrows(err, func)
{
var ex;
ex = false;
try {
func();
} catch(e) {
ex = true;
assert(e instanceof err);
}
assert(ex, true, "exception expected");
}
export function assertArrayEquals(a, b)
{
if (!Array.isArray(a) || !Array.isArray(b))
return assert(false);
assert(a.length, b.length);
a.forEach((value, idx) => {
assert(b[idx], value);
});
}

View File

@ -0,0 +1,2 @@
import * as a from "./test_cyclic_import.js"
export function f(x) { return 2 * a.g(x) }

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js
index 9c1217351e..3c24755558 100644 index 9828b15..4a5919d 100644
--- a/harness/atomicsHelper.js --- a/harness/atomicsHelper.js
+++ b/harness/atomicsHelper.js +++ b/harness/atomicsHelper.js
@@ -227,10 +227,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) { @@ -272,10 +272,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) {
* } * }
*/ */
$262.agent.timeouts = { $262.agent.timeouts = {
@ -22,11 +22,11 @@ index 9c1217351e..3c24755558 100644
/** /**
diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js
index be7039fda0..7b38abf8df 100644 index b397be0..c197ddc 100644
--- a/harness/regExpUtils.js --- a/harness/regExpUtils.js
+++ b/harness/regExpUtils.js +++ b/harness/regExpUtils.js
@@ -6,24 +6,27 @@ description: | @@ -6,27 +6,30 @@ description: |
defines: [buildString, testPropertyEscapes, matchValidator] defines: [buildString, testPropertyEscapes, testPropertyOfStrings, testExtendedCharacterClass, matchValidator]
---*/ ---*/
+if ($262 && typeof $262.codePointRange === "function") { +if ($262 && typeof $262.codePointRange === "function") {
@ -44,28 +44,62 @@ index be7039fda0..7b38abf8df 100644
+ } + }
+} +}
+ +
function buildString({ loneCodePoints, ranges }) { function buildString(args) {
// Use member expressions rather than destructuring `args` for improved
// compatibility with engines that only implement assignment patterns
// partially or not at all.
const loneCodePoints = args.loneCodePoints;
const ranges = args.ranges;
- const CHUNK_SIZE = 10000; - const CHUNK_SIZE = 10000;
- let result = Reflect.apply(String.fromCodePoint, null, loneCodePoints); let result = String.fromCodePoint.apply(null, loneCodePoints);
- for (let i = 0; i < ranges.length; i++) { - for (let i = 0; i < ranges.length; i++) {
- const range = ranges[i]; - let range = ranges[i];
- const start = range[0]; - let start = range[0];
- const end = range[1]; - let end = range[1];
- const codePoints = []; - let codePoints = [];
- for (let length = 0, codePoint = start; codePoint <= end; codePoint++) { - for (let length = 0, codePoint = start; codePoint <= end; codePoint++) {
- codePoints[length++] = codePoint; - codePoints[length++] = codePoint;
- if (length === CHUNK_SIZE) { - if (length === CHUNK_SIZE) {
- result += Reflect.apply(String.fromCodePoint, null, codePoints); - result += String.fromCodePoint.apply(null, codePoints);
- codePoints.length = length = 0; - codePoints.length = length = 0;
- } - }
+ let result = String.fromCodePoint.apply(null, loneCodePoints); - }
+ for (const [start, end] of ranges) { - result += String.fromCodePoint.apply(null, codePoints);
+ result += codePointRange(start, end + 1); + for (const [start, end] of ranges) {
} + result += codePointRange(start, end + 1);
- result += Reflect.apply(String.fromCodePoint, null, codePoints); }
- } return result;
- return result; }
+ return result; diff --git a/harness/sm/non262.js b/harness/sm/non262.js
index c1829e3..3a3ee27 100644
--- a/harness/sm/non262.js
+++ b/harness/sm/non262.js
@@ -41,8 +41,6 @@ globalThis.createNewGlobal = function() {
return $262.createRealm().global
} }
function testPropertyEscapes(regex, string, expression) { -function print(...args) {
-}
function assertEq(...args) {
assert.sameValue(...args)
}
@@ -71,4 +69,4 @@ if (globalThis.createExternalArrayBuffer === undefined) {
if (globalThis.enableGeckoProfilingWithSlowAssertions === undefined) {
globalThis.enableGeckoProfilingWithSlowAssertions = globalThis.enableGeckoProfiling =
globalThis.disableGeckoProfiling = () => {}
-}
\ No newline at end of file
+}
diff --git a/test/staging/sm/misc/new-with-non-constructor.js b/test/staging/sm/misc/new-with-non-constructor.js
index 18c2f0c..f9aa209 100644
--- a/test/staging/sm/misc/new-with-non-constructor.js
+++ b/test/staging/sm/misc/new-with-non-constructor.js
@@ -16,7 +16,7 @@ function checkConstruct(thing) {
new thing();
assert.sameValue(0, 1, "not reached " + thing);
} catch (e) {
- assert.sameValue(e.message.includes(" is not a constructor") ||
+ assert.sameValue(e.message.includes("not a constructor") ||
e.message === "Function.prototype.toString called on incompatible object", true);
}
}

249
tests/test_bigint.js Normal file
View File

@ -0,0 +1,249 @@
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
function assertThrows(err, func)
{
var ex;
ex = false;
try {
func();
} catch(e) {
ex = true;
assert(e instanceof err);
}
assert(ex, true, "exception expected");
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function bigint_pow(a, n)
{
var r, i;
r = 1n;
for(i = 0n; i < n; i++)
r *= a;
return r;
}
/* a must be < b */
function test_less(a, b)
{
assert(a < b);
assert(!(b < a));
assert(a <= b);
assert(!(b <= a));
assert(b > a);
assert(!(a > b));
assert(b >= a);
assert(!(a >= b));
assert(a != b);
assert(!(a == b));
}
/* a must be numerically equal to b */
function test_eq(a, b)
{
assert(a == b);
assert(b == a);
assert(!(a != b));
assert(!(b != a));
assert(a <= b);
assert(b <= a);
assert(!(a < b));
assert(a >= b);
assert(b >= a);
assert(!(a > b));
}
function test_bigint1()
{
var a, r;
test_less(2n, 3n);
test_eq(3n, 3n);
test_less(2, 3n);
test_eq(3, 3n);
test_less(2.1, 3n);
test_eq(Math.sqrt(4), 2n);
a = bigint_pow(3n, 100n);
assert((a - 1n) != a);
assert(a == 515377520732011331036461129765621272702107522001n);
assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1n);
r = 1n << 31n;
assert(r, 2147483648n, "1 << 31n === 2147483648n");
r = 1n << 32n;
assert(r, 4294967296n, "1 << 32n === 4294967296n");
}
function test_bigint2()
{
assert(BigInt(""), 0n);
assert(BigInt(" 123"), 123n);
assert(BigInt(" 123 "), 123n);
assertThrows(SyntaxError, () => { BigInt("+") } );
assertThrows(SyntaxError, () => { BigInt("-") } );
assertThrows(SyntaxError, () => { BigInt("\x00a") } );
assertThrows(SyntaxError, () => { BigInt(" 123 r") } );
}
function test_bigint3()
{
assert(Number(0xffffffffffffffffn), 18446744073709552000);
assert(Number(-0xffffffffffffffffn), -18446744073709552000);
assert(100000000000000000000n == 1e20, true);
assert(100000000000000000001n == 1e20, false);
assert((1n << 100n).toString(10), "1267650600228229401496703205376");
assert((-1n << 100n).toString(36), "-3ewfdnca0n6ld1ggvfgg");
assert((1n << 100n).toString(8), "2000000000000000000000000000000000");
assert(0x5a4653ca673768565b41f775n << 78n, 8443945299673273647701379149826607537748959488376832n);
assert(-0x5a4653ca673768565b41f775n << 78n, -8443945299673273647701379149826607537748959488376832n);
assert(0x5a4653ca673768565b41f775n >> 78n, 92441n);
assert(-0x5a4653ca673768565b41f775n >> 78n, -92442n);
assert(~0x5a653ca6n, -1516584103n);
assert(0x5a463ca6n | 0x67376856n, 2138537206n);
assert(0x5a463ca6n & 0x67376856n, 1107699718n);
assert(0x5a463ca6n ^ 0x67376856n, 1030837488n);
assert(3213213213213213432453243n / 123434343439n, 26031760073331n);
assert(-3213213213213213432453243n / 123434343439n, -26031760073331n);
assert(-3213213213213213432453243n % -123434343439n, -26953727934n);
assert(3213213213213213432453243n % 123434343439n, 26953727934n);
assert((-2n) ** 127n, -170141183460469231731687303715884105728n);
assert((2n) ** 127n, 170141183460469231731687303715884105728n);
assert((-256n) ** 11n, -309485009821345068724781056n);
assert((7n) ** 20n, 79792266297612001n);
}
/* pi computation */
/* return floor(log2(a)) for a > 0 and 0 for a = 0 */
function floor_log2(a)
{
var k_max, a1, k, i;
k_max = 0n;
while ((a >> (2n ** k_max)) != 0n) {
k_max++;
}
k = 0n;
a1 = a;
for(i = k_max - 1n; i >= 0n; i--) {
a1 = a >> (2n ** i);
if (a1 != 0n) {
a = a1;
k |= (1n << i);
}
}
return k;
}
/* return ceil(log2(a)) for a > 0 */
function ceil_log2(a)
{
return floor_log2(a - 1n) + 1n;
}
/* return floor(sqrt(a)) (not efficient but simple) */
function int_sqrt(a)
{
var l, u, s;
if (a == 0n)
return a;
l = ceil_log2(a);
u = 1n << ((l + 1n) / 2n);
/* u >= floor(sqrt(a)) */
for(;;) {
s = u;
u = ((a / s) + s) / 2n;
if (u >= s)
break;
}
return s;
}
/* return pi * 2**prec */
function calc_pi(prec) {
const CHUD_A = 13591409n;
const CHUD_B = 545140134n;
const CHUD_C = 640320n;
const CHUD_C3 = 10939058860032000n; /* C^3/24 */
const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
/* return [P, Q, G] */
function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
if (a == (b - 1n)) {
G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
P = G * (CHUD_B * b + CHUD_A);
if (b & 1n)
P = -P;
Q = b * b * b * CHUD_C3;
} else {
c = (a + b) >> 1n;
[P1, Q1, G1] = chud_bs(a, c, true);
[P2, Q2, G2] = chud_bs(c, b, need_G);
P = P1 * Q2 + P2 * G1;
Q = Q1 * Q2;
if (need_G)
G = G1 * G2;
else
G = 0n;
}
return [P, Q, G];
}
var n, P, Q, G;
/* number of serie terms */
n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n;
[P, Q, G] = chud_bs(0n, n, false);
Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A);
G = int_sqrt(CHUD_C << (2n * prec));
return (Q * G) >> prec;
}
function compute_pi(n_digits) {
var r, n_digits, n_bits, out;
/* we add more bits to reduce the probability of bad rounding for
the last digits */
n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n;
r = calc_pi(n_bits);
r = ((10n ** BigInt(n_digits)) * r) >> n_bits;
out = r.toString();
return out[0] + "." + out.slice(1);
}
function test_pi()
{
assert(compute_pi(2000), "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151557485724245415069595082953311686172785588907509838175463746493931925506040092770167113900984882401285836160356370766010471018194295559619894676783744944825537977472684710404753464620804668425906949129331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992458631503028618297455570674983850549458858692699569092721079750930295532116534498720275596023648066549911988183479775356636980742654252786255181841757467289097777279380008164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179049460165346680498862723279178608578438382796797668145410095388378636095068006422512520511739298489608412848862694560424196528502221066118630674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009");
}
test_bigint1();
test_bigint2();
test_bigint3();
test_pi();

View File

@ -1,326 +0,0 @@
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
function assertThrows(err, func)
{
var ex;
ex = false;
try {
func();
} catch(e) {
ex = true;
assert(e instanceof err);
}
assert(ex, true, "exception expected");
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function bigint_pow(a, n)
{
var r, i;
r = 1n;
for(i = 0n; i < n; i++)
r *= a;
return r;
}
/* a must be < b */
function test_less(a, b)
{
assert(a < b);
assert(!(b < a));
assert(a <= b);
assert(!(b <= a));
assert(b > a);
assert(!(a > b));
assert(b >= a);
assert(!(a >= b));
assert(a != b);
assert(!(a == b));
}
/* a must be numerically equal to b */
function test_eq(a, b)
{
assert(a == b);
assert(b == a);
assert(!(a != b));
assert(!(b != a));
assert(a <= b);
assert(b <= a);
assert(!(a < b));
assert(a >= b);
assert(b >= a);
assert(!(a > b));
}
function test_bigint1()
{
var a, r;
test_less(2n, 3n);
test_eq(3n, 3n);
test_less(2, 3n);
test_eq(3, 3n);
test_less(2.1, 3n);
test_eq(Math.sqrt(4), 2n);
a = bigint_pow(3n, 100n);
assert((a - 1n) != a);
assert(a == 515377520732011331036461129765621272702107522001n);
assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1n);
r = 1n << 31n;
assert(r, 2147483648n, "1 << 31n === 2147483648n");
r = 1n << 32n;
assert(r, 4294967296n, "1 << 32n === 4294967296n");
}
function test_bigint2()
{
assert(BigInt(""), 0n);
assert(BigInt(" 123"), 123n);
assert(BigInt(" 123 "), 123n);
assertThrows(SyntaxError, () => { BigInt("+") } );
assertThrows(SyntaxError, () => { BigInt("-") } );
assertThrows(SyntaxError, () => { BigInt("\x00a") } );
assertThrows(SyntaxError, () => { BigInt(" 123 r") } );
}
function test_divrem(div1, a, b, q)
{
var div, divrem, t;
div = BigInt[div1];
divrem = BigInt[div1 + "rem"];
assert(div(a, b) == q);
t = divrem(a, b);
assert(t[0] == q);
assert(a == b * q + t[1]);
}
function test_idiv1(div, a, b, r)
{
test_divrem(div, a, b, r[0]);
test_divrem(div, -a, b, r[1]);
test_divrem(div, a, -b, r[2]);
test_divrem(div, -a, -b, r[3]);
}
/* QuickJS BigInt extensions */
function test_bigint_ext()
{
var r;
assert(BigInt.floorLog2(0n) === -1n);
assert(BigInt.floorLog2(7n) === 2n);
assert(BigInt.sqrt(0xffffffc000000000000000n) === 17592185913343n);
r = BigInt.sqrtrem(0xffffffc000000000000000n);
assert(r[0] === 17592185913343n);
assert(r[1] === 35167191957503n);
test_idiv1("tdiv", 3n, 2n, [1n, -1n, -1n, 1n]);
test_idiv1("fdiv", 3n, 2n, [1n, -2n, -2n, 1n]);
test_idiv1("cdiv", 3n, 2n, [2n, -1n, -1n, 2n]);
test_idiv1("ediv", 3n, 2n, [1n, -2n, -1n, 2n]);
}
function test_bigfloat()
{
var e, a, b, sqrt2;
assert(typeof 1n === "bigint");
assert(typeof 1l === "bigfloat");
assert(1 == 1.0l);
assert(1 !== 1.0l);
test_less(2l, 3l);
test_eq(3l, 3l);
test_less(2, 3l);
test_eq(3, 3l);
test_less(2.1, 3l);
test_eq(Math.sqrt(9), 3l);
test_less(2n, 3l);
test_eq(3n, 3l);
e = new BigFloatEnv(128);
assert(e.prec == 128);
a = BigFloat.sqrt(2l, e);
assert(a === BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e));
assert(e.inexact === true);
assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcc908b2fb1366ea95l);
b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128);
assert(a === b);
assert(BigFloat.isNaN(BigFloat(NaN)));
assert(BigFloat.isFinite(1l));
assert(!BigFloat.isFinite(1l/0l));
assert(BigFloat.abs(-3l) === 3l);
assert(BigFloat.sign(-3l) === -1l);
assert(BigFloat.exp(0.2l) === 1.2214027581601698339210719946396742l);
assert(BigFloat.log(3l) === 1.0986122886681096913952452369225256l);
assert(BigFloat.pow(2.1l, 1.6l) === 3.277561666451861947162828744873745l);
assert(BigFloat.sin(-1l) === -0.841470984807896506652502321630299l);
assert(BigFloat.cos(1l) === 0.5403023058681397174009366074429766l);
assert(BigFloat.tan(0.1l) === 0.10033467208545054505808004578111154l);
assert(BigFloat.asin(0.3l) === 0.30469265401539750797200296122752915l);
assert(BigFloat.acos(0.4l) === 1.1592794807274085998465837940224159l);
assert(BigFloat.atan(0.7l) === 0.610725964389208616543758876490236l);
assert(BigFloat.atan2(7.1l, -5.1l) === 2.1937053809751415549388104628759813l);
assert(BigFloat.floor(2.5l) === 2l);
assert(BigFloat.ceil(2.5l) === 3l);
assert(BigFloat.trunc(-2.5l) === -2l);
assert(BigFloat.round(2.5l) === 3l);
assert(BigFloat.fmod(3l,2l) === 1l);
assert(BigFloat.remainder(3l,2l) === -1l);
/* string conversion */
assert((1234.125l).toString(), "1234.125");
assert((1234.125l).toFixed(2), "1234.13");
assert((1234.125l).toFixed(2, "down"), "1234.12");
assert((1234.125l).toExponential(), "1.234125e+3");
assert((1234.125l).toExponential(5), "1.23413e+3");
assert((1234.125l).toExponential(5, BigFloatEnv.RNDZ), "1.23412e+3");
assert((1234.125l).toPrecision(6), "1234.13");
assert((1234.125l).toPrecision(6, BigFloatEnv.RNDZ), "1234.12");
/* string conversion with binary base */
assert((0x123.438l).toString(16), "123.438");
assert((0x323.438l).toString(16), "323.438");
assert((0x723.438l).toString(16), "723.438");
assert((0xf23.438l).toString(16), "f23.438");
assert((0x123.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "123.44");
assert((0x323.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "323.44");
assert((0x723.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "723.44");
assert((0xf23.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "f23.44");
assert((0x0.0000438l).toFixed(6, BigFloatEnv.RNDNA, 16), "0.000044");
assert((0x1230000000l).toFixed(1, BigFloatEnv.RNDNA, 16), "1230000000.0");
assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "123.44");
assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDZ, 16), "123.43");
assert((0x323.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "323.44");
assert((0x723.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "723.44");
assert((-0xf23.438l).toPrecision(5, BigFloatEnv.RNDD, 16), "-f23.44");
assert((0x123.438l).toExponential(4, BigFloatEnv.RNDNA, 16), "1.2344p+8");
}
function test_bigdecimal()
{
assert(1m === 1m);
assert(1m !== 2m);
test_less(1m, 2m);
test_eq(2m, 2m);
test_less(1, 2m);
test_eq(2, 2m);
test_less(1.1, 2m);
test_eq(Math.sqrt(4), 2m);
test_less(2n, 3m);
test_eq(3n, 3m);
assert(BigDecimal("1234.1") === 1234.1m);
assert(BigDecimal(" 1234.1") === 1234.1m);
assert(BigDecimal(" 1234.1 ") === 1234.1m);
assert(BigDecimal(0.1) === 0.1m);
assert(BigDecimal(123) === 123m);
assert(BigDecimal(true) === 1m);
assert(123m + 1m === 124m);
assert(123m - 1m === 122m);
assert(3.2m * 3m === 9.6m);
assert(10m / 2m === 5m);
assertThrows(RangeError, () => { 10m / 3m } );
assert(10m % 3m === 1m);
assert(-10m % 3m === -1m);
assert(1234.5m ** 3m === 1881365963.625m);
assertThrows(RangeError, () => { 2m ** 3.1m } );
assertThrows(RangeError, () => { 2m ** -3m } );
assert(BigDecimal.sqrt(2m,
{ roundingMode: "half-even",
maximumSignificantDigits: 4 }) === 1.414m);
assert(BigDecimal.sqrt(101m,
{ roundingMode: "half-even",
maximumFractionDigits: 3 }) === 10.050m);
assert(BigDecimal.sqrt(0.002m,
{ roundingMode: "half-even",
maximumFractionDigits: 3 }) === 0.045m);
assert(BigDecimal.round(3.14159m,
{ roundingMode: "half-even",
maximumFractionDigits: 3 }) === 3.142m);
assert(BigDecimal.add(3.14159m, 0.31212m,
{ roundingMode: "half-even",
maximumFractionDigits: 2 }) === 3.45m);
assert(BigDecimal.sub(3.14159m, 0.31212m,
{ roundingMode: "down",
maximumFractionDigits: 2 }) === 2.82m);
assert(BigDecimal.mul(3.14159m, 0.31212m,
{ roundingMode: "half-even",
maximumFractionDigits: 3 }) === 0.981m);
assert(BigDecimal.mod(3.14159m, 0.31211m,
{ roundingMode: "half-even",
maximumFractionDigits: 4 }) === 0.0205m);
assert(BigDecimal.div(20m, 3m,
{ roundingMode: "half-even",
maximumSignificantDigits: 3 }) === 6.67m);
assert(BigDecimal.div(20m, 3m,
{ roundingMode: "half-even",
maximumFractionDigits: 50 }) ===
6.66666666666666666666666666666666666666666666666667m);
/* string conversion */
assert((1234.125m).toString(), "1234.125");
assert((1234.125m).toFixed(2), "1234.13");
assert((1234.125m).toFixed(2, "down"), "1234.12");
assert((1234.125m).toExponential(), "1.234125e+3");
assert((1234.125m).toExponential(5), "1.23413e+3");
assert((1234.125m).toExponential(5, "down"), "1.23412e+3");
assert((1234.125m).toPrecision(6), "1234.13");
assert((1234.125m).toPrecision(6, "down"), "1234.12");
assert((-1234.125m).toPrecision(6, "floor"), "-1234.13");
}
test_bigint1();
test_bigint2();
test_bigint_ext();
test_bigfloat();
test_bigdecimal();

View File

@ -87,7 +87,6 @@ function toStr(a)
case "string": case "string":
return a.__quote(); return a.__quote();
case "number": case "number":
case "bigfloat":
if (a == 0 && 1 / a < 0) if (a == 0 && 1 / a < 0)
return "-0"; return "-0";
else else
@ -155,21 +154,6 @@ function bjson_test_all()
bjson_test([BigInt("1"), -BigInt("0x123456789"), bjson_test([BigInt("1"), -BigInt("0x123456789"),
BigInt("0x123456789abcdef123456789abcdef")]); BigInt("0x123456789abcdef123456789abcdef")]);
} }
if (typeof BigFloat !== "undefined") {
BigFloatEnv.setPrec(function () {
bjson_test([BigFloat("0.1"), BigFloat("-1e30"), BigFloat("0"),
BigFloat("-0"), BigFloat("Infinity"), BigFloat("-Infinity"),
0.0 / BigFloat("0"), BigFloat.MAX_VALUE,
BigFloat.MIN_VALUE]);
}, 113, 15);
}
if (typeof BigDecimal !== "undefined") {
bjson_test([BigDecimal("0"),
BigDecimal("0.8"), BigDecimal("123321312321321e100"),
BigDecimal("-1233213123213214332333223332e100"),
BigDecimal("1.233e-1000")]);
}
bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]); bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]);
bjson_test(new Int32Array([123123, 222111, -32222])); bjson_test(new Int32Array([123123, 222111, -32222]));

View File

@ -1,19 +1,51 @@
"use strict"; "use strict";
var status = 0;
var throw_errors = true;
function throw_error(msg) {
if (throw_errors)
throw Error(msg);
console.log(msg);
status = 1;
}
function assert(actual, expected, message) { function assert(actual, expected, message) {
function get_full_type(o) {
var type = typeof(o);
if (type === 'object') {
if (o === null)
return 'null';
if (o.constructor && o.constructor.name)
return o.constructor.name;
}
return type;
}
if (arguments.length == 1) if (arguments.length == 1)
expected = true; expected = true;
if (actual === expected) if (typeof actual === typeof expected) {
return; if (actual === expected) {
if (actual !== 0 || (1 / actual) === (1 / expected))
if (actual !== null && expected !== null return;
&& typeof actual == 'object' && typeof expected == 'object' }
&& actual.toString() === expected.toString()) if (typeof actual === 'number') {
return; if (isNaN(actual) && isNaN(expected))
return true;
throw Error("assertion failed: got |" + actual + "|" + }
", expected |" + expected + "|" + if (typeof actual === 'object') {
if (actual !== null && expected !== null
&& actual.constructor === expected.constructor
&& actual.toString() === expected.toString())
return;
}
}
// Should output the source file and line number and extract
// the expression from the assert call
throw_error("assertion failed: got " +
get_full_type(actual) + ":|" + actual + "|, expected " +
get_full_type(expected) + ":|" + expected + "|" +
(message ? " (" + message + ")" : "")); (message ? " (" + message + ")" : ""));
} }
@ -25,11 +57,16 @@ function assert_throws(expected_error, func)
} catch(e) { } catch(e) {
err = true; err = true;
if (!(e instanceof expected_error)) { if (!(e instanceof expected_error)) {
throw Error("unexpected exception type"); // Should output the source file and line number and extract
// the expression from the assert_throws() call
throw_error("unexpected exception type");
return;
} }
} }
if (!err) { if (!err) {
throw Error("expected exception"); // Should output the source file and line number and extract
// the expression from the assert_throws() call
throw_error("expected exception");
} }
} }
@ -311,10 +348,14 @@ function test_math()
assert(Math.floor(a), 1); assert(Math.floor(a), 1);
assert(Math.ceil(a), 2); assert(Math.ceil(a), 2);
assert(Math.imul(0x12345678, 123), -1088058456); assert(Math.imul(0x12345678, 123), -1088058456);
assert(Math.imul(0xB505, 0xB504), 2147441940);
assert(Math.imul(0xB505, 0xB505), -2147479015);
assert(Math.imul((-2)**31, (-2)**31), 0);
assert(Math.imul(2**31-1, 2**31-1), 1);
assert(Math.fround(0.1), 0.10000000149011612); assert(Math.fround(0.1), 0.10000000149011612);
assert(Math.hypot() == 0); assert(Math.hypot(), 0);
assert(Math.hypot(-2) == 2); assert(Math.hypot(-2), 2);
assert(Math.hypot(3, 4) == 5); assert(Math.hypot(3, 4), 5);
assert(Math.abs(Math.hypot(3, 4, 5) - 7.0710678118654755) <= 1e-15); assert(Math.abs(Math.hypot(3, 4, 5) - 7.0710678118654755) <= 1e-15);
} }
@ -327,6 +368,10 @@ function test_number()
assert(+" 123 ", 123); assert(+" 123 ", 123);
assert(+"0b111", 7); assert(+"0b111", 7);
assert(+"0o123", 83); assert(+"0o123", 83);
assert(parseFloat("2147483647"), 2147483647);
assert(parseFloat("2147483648"), 2147483648);
assert(parseFloat("-2147483647"), -2147483647);
assert(parseFloat("-2147483648"), -2147483648);
assert(parseFloat("0x1234"), 0); assert(parseFloat("0x1234"), 0);
assert(parseFloat("Infinity"), Infinity); assert(parseFloat("Infinity"), Infinity);
assert(parseFloat("-Infinity"), -Infinity); assert(parseFloat("-Infinity"), -Infinity);
@ -336,12 +381,22 @@ function test_number()
assert(Number.isNaN(Number("-"))); assert(Number.isNaN(Number("-")));
assert(Number.isNaN(Number("\x00a"))); assert(Number.isNaN(Number("\x00a")));
assert((1-2**-53).toString(12), "0.bbbbbbbbbbbbbba");
assert((1000000000000000128).toString(), "1000000000000000100");
assert((1000000000000000128).toFixed(0), "1000000000000000128");
assert((25).toExponential(0), "3e+1"); assert((25).toExponential(0), "3e+1");
assert((-25).toExponential(0), "-3e+1"); assert((-25).toExponential(0), "-3e+1");
assert((2.5).toPrecision(1), "3"); assert((2.5).toPrecision(1), "3");
assert((-2.5).toPrecision(1), "-3"); assert((-2.5).toPrecision(1), "-3");
assert((25).toPrecision(1) === "3e+1");
assert((1.125).toFixed(2), "1.13"); assert((1.125).toFixed(2), "1.13");
assert((-1.125).toFixed(2), "-1.13"); assert((-1.125).toFixed(2), "-1.13");
assert((0.5).toFixed(0), "1");
assert((-0.5).toFixed(0), "-1");
assert((-1e-10).toFixed(0), "-0");
assert((1.3).toString(7), "1.2046204620462046205");
assert((1.3).toString(35), "1.ahhhhhhhhhm");
} }
function test_eval2() function test_eval2()
@ -454,6 +509,66 @@ function test_typed_array()
assert(a.toString(), "1,2,10,11"); assert(a.toString(), "1,2,10,11");
} }
/* return [s, line_num, col_num] where line_num and col_num are the
position of the '@' character in 'str'. 's' is str without the '@'
character */
function get_string_pos(str)
{
var p, line_num, col_num, s, q, r;
p = str.indexOf('@');
assert(p >= 0, true);
q = 0;
line_num = 1;
for(;;) {
r = str.indexOf('\n', q);
if (r < 0 || r >= p)
break;
q = r + 1;
line_num++;
}
col_num = p - q + 1;
s = str.slice(0, p) + str.slice(p + 1);
return [s, line_num, col_num];
}
function check_error_pos(e, expected_error, line_num, col_num, level)
{
var expected_pos, tab, line;
level |= 0;
expected_pos = ":" + line_num + ":" + col_num;
tab = e.stack.split("\n");
line = tab[level];
if (line.slice(-1) == ')')
line = line.slice(0, -1);
if (line.indexOf(expected_pos) < 0) {
throw_error("unexpected line or column number. error=" + e.message +
".got |" + line + "|, expected |" + expected_pos + "|");
}
}
function assert_json_error(str, line_num, col_num)
{
var err = false;
var expected_pos, tab;
tab = get_string_pos(str);
try {
JSON.parse(tab[0]);
} catch(e) {
err = true;
if (!(e instanceof SyntaxError)) {
throw_error("unexpected exception type");
return;
}
/* XXX: the way quickjs returns JSON errors is not similar to Node or spiderMonkey */
check_error_pos(e, SyntaxError, tab[1], tab[2]);
}
if (!err) {
throw_error("expected exception");
}
}
function test_json() function test_json()
{ {
var a, s; var a, s;
@ -477,30 +592,112 @@ function test_json()
3 3
] ]
]`); ]`);
assert_json_error('\n" @\\x"');
assert_json_error('\n{ "a": @x }"');
} }
function test_date() function test_date()
{ {
var d = new Date(1506098258091), a, s; // Date Time String format is YYYY-MM-DDTHH:mm:ss.sssZ
// accepted date formats are: YYYY, YYYY-MM and YYYY-MM-DD
// accepted time formats are: THH:mm, THH:mm:ss, THH:mm:ss.sss
// expanded years are represented with 6 digits prefixed by + or -
// -000000 is invalid.
// A string containing out-of-bounds or nonconforming elements
// is not a valid instance of this format.
// Hence the fractional part after . should have 3 digits and how
// a different number of digits is handled is implementation defined.
assert(Date.parse(""), NaN);
assert(Date.parse("2000"), 946684800000);
assert(Date.parse("2000-01"), 946684800000);
assert(Date.parse("2000-01-01"), 946684800000);
//assert(Date.parse("2000-01-01T"), NaN);
//assert(Date.parse("2000-01-01T00Z"), NaN);
assert(Date.parse("2000-01-01T00:00Z"), 946684800000);
assert(Date.parse("2000-01-01T00:00:00Z"), 946684800000);
assert(Date.parse("2000-01-01T00:00:00.1Z"), 946684800100);
assert(Date.parse("2000-01-01T00:00:00.10Z"), 946684800100);
assert(Date.parse("2000-01-01T00:00:00.100Z"), 946684800100);
assert(Date.parse("2000-01-01T00:00:00.1000Z"), 946684800100);
assert(Date.parse("2000-01-01T00:00:00+00:00"), 946684800000);
//assert(Date.parse("2000-01-01T00:00:00+00:30"), 946686600000);
var d = new Date("2000T00:00"); // Jan 1st 2000, 0:00:00 local time
assert(typeof d === 'object' && d.toString() != 'Invalid Date');
assert((new Date('Jan 1 2000')).toISOString(),
d.toISOString());
assert((new Date('Jan 1 2000 00:00')).toISOString(),
d.toISOString());
assert((new Date('Jan 1 2000 00:00:00')).toISOString(),
d.toISOString());
assert((new Date('Jan 1 2000 00:00:00 GMT+0100')).toISOString(),
'1999-12-31T23:00:00.000Z');
assert((new Date('Jan 1 2000 00:00:00 GMT+0200')).toISOString(),
'1999-12-31T22:00:00.000Z');
assert((new Date('Sat Jan 1 2000')).toISOString(),
d.toISOString());
assert((new Date('Sat Jan 1 2000 00:00')).toISOString(),
d.toISOString());
assert((new Date('Sat Jan 1 2000 00:00:00')).toISOString(),
d.toISOString());
assert((new Date('Sat Jan 1 2000 00:00:00 GMT+0100')).toISOString(),
'1999-12-31T23:00:00.000Z');
assert((new Date('Sat Jan 1 2000 00:00:00 GMT+0200')).toISOString(),
'1999-12-31T22:00:00.000Z');
var d = new Date(1506098258091);
assert(d.toISOString(), "2017-09-22T16:37:38.091Z"); assert(d.toISOString(), "2017-09-22T16:37:38.091Z");
d.setUTCHours(18, 10, 11); d.setUTCHours(18, 10, 11);
assert(d.toISOString(), "2017-09-22T18:10:11.091Z"); assert(d.toISOString(), "2017-09-22T18:10:11.091Z");
a = Date.parse(d.toISOString()); var a = Date.parse(d.toISOString());
assert((new Date(a)).toISOString(), d.toISOString()); assert((new Date(a)).toISOString(), d.toISOString());
s = new Date("2020-01-01T01:01:01.1Z").toISOString();
assert(s == "2020-01-01T01:01:01.100Z"); assert((new Date("2020-01-01T01:01:01.123Z")).toISOString(),
s = new Date("2020-01-01T01:01:01.12Z").toISOString(); "2020-01-01T01:01:01.123Z");
assert(s == "2020-01-01T01:01:01.120Z"); /* implementation defined behavior */
s = new Date("2020-01-01T01:01:01.123Z").toISOString(); assert((new Date("2020-01-01T01:01:01.1Z")).toISOString(),
assert(s == "2020-01-01T01:01:01.123Z"); "2020-01-01T01:01:01.100Z");
s = new Date("2020-01-01T01:01:01.1234Z").toISOString(); assert((new Date("2020-01-01T01:01:01.12Z")).toISOString(),
assert(s == "2020-01-01T01:01:01.123Z"); "2020-01-01T01:01:01.120Z");
s = new Date("2020-01-01T01:01:01.12345Z").toISOString(); assert((new Date("2020-01-01T01:01:01.1234Z")).toISOString(),
assert(s == "2020-01-01T01:01:01.123Z"); "2020-01-01T01:01:01.123Z");
s = new Date("2020-01-01T01:01:01.1235Z").toISOString(); assert((new Date("2020-01-01T01:01:01.12345Z")).toISOString(),
assert(s == "2020-01-01T01:01:01.124Z"); "2020-01-01T01:01:01.123Z");
s = new Date("2020-01-01T01:01:01.9999Z").toISOString(); assert((new Date("2020-01-01T01:01:01.1235Z")).toISOString(),
assert(s == "2020-01-01T01:01:02.000Z"); "2020-01-01T01:01:01.123Z");
assert((new Date("2020-01-01T01:01:01.9999Z")).toISOString(),
"2020-01-01T01:01:01.999Z");
assert(Date.UTC(2017), 1483228800000);
assert(Date.UTC(2017, 9), 1506816000000);
assert(Date.UTC(2017, 9, 22), 1508630400000);
assert(Date.UTC(2017, 9, 22, 18), 1508695200000);
assert(Date.UTC(2017, 9, 22, 18, 10), 1508695800000);
assert(Date.UTC(2017, 9, 22, 18, 10, 11), 1508695811000);
assert(Date.UTC(2017, 9, 22, 18, 10, 11, 91), 1508695811091);
assert(Date.UTC(NaN), NaN);
assert(Date.UTC(2017, NaN), NaN);
assert(Date.UTC(2017, 9, NaN), NaN);
assert(Date.UTC(2017, 9, 22, NaN), NaN);
assert(Date.UTC(2017, 9, 22, 18, NaN), NaN);
assert(Date.UTC(2017, 9, 22, 18, 10, NaN), NaN);
assert(Date.UTC(2017, 9, 22, 18, 10, 11, NaN), NaN);
assert(Date.UTC(2017, 9, 22, 18, 10, 11, 91, NaN), 1508695811091);
// TODO: Fix rounding errors on Windows/Cygwin.
if (!(typeof os !== 'undefined' && ['win32', 'cygwin'].includes(os.platform))) {
// from test262/test/built-ins/Date/UTC/fp-evaluation-order.js
assert(Date.UTC(1970, 0, 1, 80063993375, 29, 1, -288230376151711740), 29312,
'order of operations / precision in MakeTime');
assert(Date.UTC(1970, 0, 213503982336, 0, 0, 0, -18446744073709552000), 34447360,
'precision in MakeDate');
}
//assert(Date.UTC(2017 - 1e9, 9 + 12e9), 1506816000000); // node fails this
assert(Date.UTC(2017, 9, 22 - 1e10, 18 + 24e10), 1508695200000);
assert(Date.UTC(2017, 9, 22, 18 - 1e10, 10 + 60e10), 1508695800000);
assert(Date.UTC(2017, 9, 22, 18, 10 - 1e10, 11 + 60e10), 1508695811000);
assert(Date.UTC(2017, 9, 22, 18, 10, 11 - 1e12, 91 + 1000e12), 1508695811091);
} }
function test_regexp() function test_regexp()
@ -549,6 +746,8 @@ function test_regexp()
assert(a, ["a", undefined]); assert(a, ["a", undefined]);
a = /(?:|[\w])+([0-9])/.exec("123a23"); a = /(?:|[\w])+([0-9])/.exec("123a23");
assert(a, ["123a23", "3"]); assert(a, ["123a23", "3"]);
a = /()*?a/.exec(",");
assert(a, null);
} }
function test_symbol() function test_symbol()
@ -583,15 +782,26 @@ function test_symbol()
assert(b.toString(), "Symbol(aaa)"); assert(b.toString(), "Symbol(aaa)");
} }
function test_map() function test_map1(key_type, n)
{ {
var a, i, n, tab, o, v; var a, i, tab, o, v;
n = 1000;
a = new Map(); a = new Map();
tab = []; tab = [];
for(i = 0; i < n; i++) { for(i = 0; i < n; i++) {
v = { }; v = { };
o = { id: i }; switch(key_type) {
case "small_bigint":
o = BigInt(i);
break;
case "bigint":
o = BigInt(i) + (1n << 128n);
break;
case "object":
o = { id: i };
break;
default:
assert(false);
}
tab[i] = [o, v]; tab[i] = [o, v];
a.set(o, v); a.set(o, v);
} }
@ -612,6 +822,29 @@ function test_map()
assert(a.size, 0); assert(a.size, 0);
} }
function test_map()
{
var a, i, n, tab, o, v;
n = 1000;
a = new Map();
for (var i = 0; i < n; i++) {
a.set(i, i);
}
a.set(-2147483648, 1);
assert(a.get(-2147483648), 1);
assert(a.get(-2147483647 - 1), 1);
assert(a.get(-2147483647.5 - 0.5), 1);
a.set(1n, 1n);
assert(a.get(1n), 1n);
assert(a.get(2n**1000n - (2n**1000n - 1n)), 1n);
test_map1("object", n);
test_map1("small_bigint", n);
test_map1("bigint", n);
}
function test_weak_map() function test_weak_map()
{ {
var a, i, n, tab, o, v, n2; var a, i, n, tab, o, v, n2;
@ -620,22 +853,97 @@ function test_weak_map()
tab = []; tab = [];
for(i = 0; i < n; i++) { for(i = 0; i < n; i++) {
v = { }; v = { };
o = { id: i }; if (i & 1)
o = Symbol("x" + i);
else
o = { id: i };
tab[i] = [o, v]; tab[i] = [o, v];
a.set(o, v); a.set(o, v);
} }
o = null; o = null;
n2 = n >> 1; n2 = 5;
for(i = 0; i < n2; i++) { for(i = 0; i < n2; i++) {
a.delete(tab[i][0]); a.delete(tab[i][0]);
} }
for(i = n2; i < n; i++) { for(i = n2; i < n; i++) {
tab[i][0] = null; /* should remove the object from the WeakMap too */ tab[i][0] = null; /* should remove the object from the WeakMap too */
} }
std.gc();
/* the WeakMap should be empty here */ /* the WeakMap should be empty here */
} }
function test_weak_map_cycles()
{
const weak1 = new WeakMap();
const weak2 = new WeakMap();
function createCyclicKey() {
const parent = {};
const child = {parent};
parent.child = child;
return child;
}
function testWeakMap() {
const cyclicKey = createCyclicKey();
const valueOfCyclicKey = {};
weak1.set(cyclicKey, valueOfCyclicKey);
weak2.set(valueOfCyclicKey, 1);
}
testWeakMap();
// Force to free cyclicKey.
std.gc();
// Here will cause sigsegv because [cyclicKey] and [valueOfCyclicKey] in [weak1] was free,
// but weak2's map record was not removed, and it's key refers [valueOfCyclicKey] which is free.
weak2.get({});
std.gc();
}
function test_weak_ref()
{
var w1, w2, o, i;
for(i = 0; i < 2; i++) {
if (i == 0)
o = { };
else
o = Symbol("x");
w1 = new WeakRef(o);
assert(w1.deref(), o);
w2 = new WeakRef(o);
assert(w2.deref(), o);
o = null;
assert(w1.deref(), undefined);
assert(w2.deref(), undefined);
std.gc();
assert(w1.deref(), undefined);
assert(w2.deref(), undefined);
}
}
function test_finalization_registry()
{
{
let expected = {};
let actual;
let finrec = new FinalizationRegistry(v => { actual = v });
finrec.register({}, expected);
os.setTimeout(() => {
assert(actual, expected);
}, 0);
}
{
let expected = 42;
let actual;
let finrec = new FinalizationRegistry(v => { actual = v });
finrec.register({}, expected);
os.setTimeout(() => {
assert(actual, expected);
}, 0);
}
std.gc();
}
function test_generator() function test_generator()
{ {
function *f() { function *f() {
@ -695,6 +1003,116 @@ function test_generator()
assert(v.value === 6 && v.done === true); assert(v.value === 6 && v.done === true);
} }
function rope_concat(n, dir)
{
var i, s;
s = "";
if (dir > 0) {
for(i = 0; i < n; i++)
s += String.fromCharCode(i & 0xffff);
} else {
for(i = n - 1; i >= 0; i--)
s = String.fromCharCode(i & 0xffff) + s;
}
for(i = 0; i < n; i++) {
/* test before the assert to go faster */
if (s.charCodeAt(i) != (i & 0xffff)) {
assert(s.charCodeAt(i), i & 0xffff);
}
}
}
function test_rope()
{
rope_concat(100000, 1);
rope_concat(100000, -1);
}
function eval_error(eval_str, expected_error, level)
{
var err = false;
var expected_pos, tab;
tab = get_string_pos(eval_str);
try {
eval(tab[0]);
} catch(e) {
err = true;
if (!(e instanceof expected_error)) {
throw_error("unexpected exception type");
return;
}
check_error_pos(e, expected_error, tab[1], tab[2], level);
}
if (!err) {
throw_error("expected exception");
}
}
var poisoned_number = {
valueOf: function() { throw Error("poisoned number") },
};
function test_line_column_numbers()
{
var f, e, tab;
/* The '@' character provides the expected position of the
error. It is removed before evaluating the string. */
/* parsing */
eval_error("\n 123 @a ", SyntaxError);
eval_error("\n @/* ", SyntaxError);
eval_error("function f @a", SyntaxError);
/* currently regexp syntax errors point to the start of the regexp */
eval_error("\n @/aaa]/u", SyntaxError);
/* function definitions */
tab = get_string_pos("\n @function f() { }; f;");
e = eval(tab[0]);
assert(e.lineNumber, tab[1]);
assert(e.columnNumber, tab[2]);
/* errors */
tab = get_string_pos('\n Error@("hello");');
e = eval(tab[0]);
check_error_pos(e, Error, tab[1], tab[2]);
eval_error('\n throw Error@("hello");', Error);
/* operators */
eval_error('\n 1 + 2 @* poisoned_number;', Error, 1);
eval_error('\n 1 + "café" @* poisoned_number;', Error, 1);
eval_error('\n 1 + 2 @** poisoned_number;', Error, 1);
eval_error('\n 2 * @+ poisoned_number;', Error, 1);
eval_error('\n 2 * @- poisoned_number;', Error, 1);
eval_error('\n 2 * @~ poisoned_number;', Error, 1);
eval_error('\n 2 * @++ poisoned_number;', Error, 1);
eval_error('\n 2 * @-- poisoned_number;', Error, 1);
eval_error('\n 2 * poisoned_number @++;', Error, 1);
eval_error('\n 2 * poisoned_number @--;', Error, 1);
/* accessors */
eval_error('\n 1 + null@[0];', TypeError);
eval_error('\n 1 + null @. abcd;', TypeError);
eval_error('\n 1 + null @( 1234 );', TypeError);
eval_error('var obj = { get a() { throw Error("test"); } }\n 1 + obj @. a;',
Error, 1);
eval_error('var obj = { set a(b) { throw Error("test"); } }\n obj @. a = 1;',
Error, 1);
/* variables reference */
eval_error('\n 1 + @not_def', ReferenceError, 0);
/* assignments */
eval_error('1 + (@not_def = 1)', ReferenceError, 0);
eval_error('1 + (@not_def += 2)', ReferenceError, 0);
eval_error('var a;\n 1 + (a @+= poisoned_number);', Error, 1);
}
test(); test();
test_function(); test_function();
test_enum(); test_enum();
@ -710,4 +1128,9 @@ test_regexp();
test_symbol(); test_symbol();
test_map(); test_map();
test_weak_map(); test_weak_map();
test_weak_map_cycles();
test_weak_ref();
test_finalization_registry();
test_generator(); test_generator();
test_rope();
test_line_column_numbers();

View File

@ -0,0 +1,12 @@
/*---
negative:
phase: resolution
type: SyntaxError
---*/
// FIXME(bnoordhuis) shouldn't throw SyntaxError but that's still better
// than segfaulting, see https://github.com/quickjs-ng/quickjs/issues/567
import {assert} from "./assert.js"
import {f} from "./fixture_cyclic_import.js"
export {f}
export function g(x) { return x + 1 }
assert(f(1), 4)

View File

@ -335,6 +335,13 @@ function test_class()
assert(S.x === 42); assert(S.x === 42);
assert(S.y === 42); assert(S.y === 42);
assert(S.z === 42); assert(S.z === 42);
class P {
get = () => "123";
static() { return 42; }
}
assert(new P().get() === "123");
assert(new P().static() === 42);
}; };
function test_template() function test_template()
@ -362,8 +369,9 @@ function test_template_skip()
function test_object_literal() function test_object_literal()
{ {
var x = 0, get = 1, set = 2; async = 3; var x = 0, get = 1, set = 2; async = 3;
a = { get: 2, set: 3, async: 4 }; a = { get: 2, set: 3, async: 4, get a(){ return this.get} };
assert(JSON.stringify(a), '{"get":2,"set":3,"async":4}'); assert(JSON.stringify(a), '{"get":2,"set":3,"async":4,"a":2}');
assert(a.a === 2);
a = { x, get, set, async }; a = { x, get, set, async };
assert(JSON.stringify(a), '{"x":0,"get":1,"set":2,"async":3}'); assert(JSON.stringify(a), '{"x":0,"get":1,"set":2,"async":3}');
@ -390,6 +398,24 @@ function test_labels()
while (0) x: { break x; }; while (0) x: { break x; };
} }
function test_labels2()
{
while (1) label: break
var i = 0
while (i < 3) label: {
if (i > 0)
break
i++
}
assert(i, 1)
for (;;) label: break
for (i = 0; i < 3; i++) label: {
if (i > 0)
break
}
assert(i, 1)
}
function test_destructuring() function test_destructuring()
{ {
function * g () { return 0; }; function * g () { return 0; };
@ -420,8 +446,12 @@ function test_argument_scope()
var f; var f;
var c = "global"; var c = "global";
f = function(a = eval("var arguments")) {}; (function() {
assert_throws(SyntaxError, f); "use strict";
// XXX: node only throws in strict mode
f = function(a = eval("var arguments")) {};
assert_throws(SyntaxError, f);
})();
f = function(a = eval("1"), b = arguments[0]) { return b; }; f = function(a = eval("1"), b = arguments[0]) { return b; };
assert(f(12), 12); assert(f(12), 12);
@ -558,6 +588,15 @@ function test_parse_semicolon()
} }
} }
function test_parse_arrow_function()
{
assert(typeof eval("() => {}\n() => {}"), "function");
assert(eval("() => {}\n+1"), 1);
assert(typeof eval("x => {}\n() => {}"), "function");
assert(typeof eval("async () => {}\n() => {}"), "function");
assert(typeof eval("async x => {}\n() => {}"), "function");
}
/* optional chaining tests not present in test262 */ /* optional chaining tests not present in test262 */
function test_optional_chaining() function test_optional_chaining()
{ {
@ -583,6 +622,12 @@ function test_optional_chaining()
assert((a?.["b"])().c, 42); assert((a?.["b"])().c, 42);
} }
function test_unicode_ident()
{
var õ = 3;
assert(typeof õ, "undefined");
}
test_op1(); test_op1();
test_cvt(); test_cvt();
test_eq(); test_eq();
@ -597,6 +642,7 @@ test_template_skip();
test_object_literal(); test_object_literal();
test_regexp_skip(); test_regexp_skip();
test_labels(); test_labels();
test_labels2();
test_destructuring(); test_destructuring();
test_spread(); test_spread();
test_function_length(); test_function_length();
@ -604,3 +650,5 @@ test_argument_scope();
test_function_expr_name(); test_function_expr_name();
test_parse_semicolon(); test_parse_semicolon();
test_optional_chaining(); test_optional_chaining();
test_parse_arrow_function();
test_unicode_ident();

View File

@ -149,7 +149,7 @@ function test_for_in()
function test_for_in2() function test_for_in2()
{ {
var i; var i, tab;
tab = []; tab = [];
for(i in {x:1, y: 2, z:3}) { for(i in {x:1, y: 2, z:3}) {
if (i === "y") if (i === "y")
@ -371,6 +371,16 @@ function test_try_catch8()
assert(s === "xafyaf"); assert(s === "xafyaf");
} }
function test_cyclic_labels()
{
/* just check that it compiles without a crash */
for (;;) {
l: break l;
l: break l;
l: break l;
}
}
test_while(); test_while();
test_while_break(); test_while_break();
test_do_while(); test_do_while();

View File

@ -1,207 +0,0 @@
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
/* operators overloading with Operators.create() */
function test_operators_create() {
class Vec2
{
constructor(x, y) {
this.x = x;
this.y = y;
}
static mul_scalar(p1, a) {
var r = new Vec2();
r.x = p1.x * a;
r.y = p1.y * a;
return r;
}
toString() {
return "Vec2(" + this.x + "," + this.y + ")";
}
}
Vec2.prototype[Symbol.operatorSet] = Operators.create(
{
"+"(p1, p2) {
var r = new Vec2();
r.x = p1.x + p2.x;
r.y = p1.y + p2.y;
return r;
},
"-"(p1, p2) {
var r = new Vec2();
r.x = p1.x - p2.x;
r.y = p1.y - p2.y;
return r;
},
"=="(a, b) {
return a.x == b.x && a.y == b.y;
},
"<"(a, b) {
var r;
/* lexicographic order */
if (a.x == b.x)
r = (a.y < b.y);
else
r = (a.x < b.x);
return r;
},
"++"(a) {
var r = new Vec2();
r.x = a.x + 1;
r.y = a.y + 1;
return r;
}
},
{
left: Number,
"*"(a, b) {
return Vec2.mul_scalar(b, a);
}
},
{
right: Number,
"*"(a, b) {
return Vec2.mul_scalar(a, b);
}
});
var a = new Vec2(1, 2);
var b = new Vec2(3, 4);
var r;
r = a * 2 + 3 * b;
assert(r.x === 11 && r.y === 16);
assert(a == a, true);
assert(a == b, false);
assert(a != a, false);
assert(a < b, true);
assert(a <= b, true);
assert(b < a, false);
assert(b <= a, false);
assert(a <= a, true);
assert(a >= a, true);
a++;
assert(a.x === 2 && a.y === 3);
r = ++a;
assert(a.x === 3 && a.y === 4);
assert(r === a);
}
/* operators overloading thru inheritance */
function test_operators()
{
var Vec2;
function mul_scalar(p1, a) {
var r = new Vec2();
r.x = p1.x * a;
r.y = p1.y * a;
return r;
}
var vec2_ops = Operators({
"+"(p1, p2) {
var r = new Vec2();
r.x = p1.x + p2.x;
r.y = p1.y + p2.y;
return r;
},
"-"(p1, p2) {
var r = new Vec2();
r.x = p1.x - p2.x;
r.y = p1.y - p2.y;
return r;
},
"=="(a, b) {
return a.x == b.x && a.y == b.y;
},
"<"(a, b) {
var r;
/* lexicographic order */
if (a.x == b.x)
r = (a.y < b.y);
else
r = (a.x < b.x);
return r;
},
"++"(a) {
var r = new Vec2();
r.x = a.x + 1;
r.y = a.y + 1;
return r;
}
},
{
left: Number,
"*"(a, b) {
return mul_scalar(b, a);
}
},
{
right: Number,
"*"(a, b) {
return mul_scalar(a, b);
}
});
Vec2 = class Vec2 extends vec2_ops
{
constructor(x, y) {
super();
this.x = x;
this.y = y;
}
toString() {
return "Vec2(" + this.x + "," + this.y + ")";
}
}
var a = new Vec2(1, 2);
var b = new Vec2(3, 4);
var r;
r = a * 2 + 3 * b;
assert(r.x === 11 && r.y === 16);
assert(a == a, true);
assert(a == b, false);
assert(a != a, false);
assert(a < b, true);
assert(a <= b, true);
assert(b < a, false);
assert(b <= a, false);
assert(a <= a, true);
assert(a >= a, true);
a++;
assert(a.x === 2 && a.y === 3);
r = ++a;
assert(a.x === 3 && a.y === 4);
assert(r === a);
}
function test_default_op()
{
assert(Object(1) + 2, 3);
assert(Object(1) + true, 2);
assert(-Object(1), -1);
}
test_operators_create();
test_operators();
test_default_op();

View File

@ -1,256 +0,0 @@
"use math";
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
function assertThrows(err, func)
{
var ex;
ex = false;
try {
func();
} catch(e) {
ex = true;
assert(e instanceof err);
}
assert(ex, true, "exception expected");
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function pow(a, n)
{
var r, i;
r = 1;
for(i = 0; i < n; i++)
r *= a;
return r;
}
function test_integer()
{
var a, r;
a = pow(3, 100);
assert((a - 1) != a);
assert(a == 515377520732011331036461129765621272702107522001);
assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1);
assert(Integer.isInteger(1) === true);
assert(Integer.isInteger(1.0) === false);
assert(Integer.floorLog2(0) === -1);
assert(Integer.floorLog2(7) === 2);
r = 1 << 31;
assert(r, 2147483648, "1 << 31 === 2147483648");
r = 1 << 32;
assert(r, 4294967296, "1 << 32 === 4294967296");
r = (1 << 31) < 0;
assert(r, false, "(1 << 31) < 0 === false");
assert(typeof 1 === "number");
assert(typeof 9007199254740991 === "number");
assert(typeof 9007199254740992 === "bigint");
}
function test_float()
{
assert(typeof 1.0 === "bigfloat");
assert(1 == 1.0);
assert(1 !== 1.0);
}
/* jscalc tests */
function test_modulo()
{
var i, p, a, b;
/* Euclidian modulo operator */
assert((-3) % 2 == 1);
assert(3 % (-2) == 1);
p = 101;
for(i = 1; i < p; i++) {
a = Integer.invmod(i, p);
assert(a >= 0 && a < p);
assert((i * a) % p == 1);
}
assert(Integer.isPrime(2^107-1));
assert(!Integer.isPrime((2^107-1) * (2^89-1)));
a = Integer.factor((2^89-1)*2^3*11*13^2*1009);
assert(a == [ 2,2,2,11,13,13,1009,618970019642690137449562111 ]);
}
function test_fraction()
{
assert((1/3 + 1).toString(), "4/3")
assert((2/3)^30, 1073741824/205891132094649);
assert(1/3 < 2/3);
assert(1/3 < 1);
assert(1/3 == 1.0/3);
assert(1.0/3 < 2/3);
}
function test_mod()
{
var a, b, p;
a = Mod(3, 101);
b = Mod(-1, 101);
assert((a + b) == Mod(2, 101));
assert(a ^ 100 == Mod(1, 101));
p = 2 ^ 607 - 1; /* mersenne prime */
a = Mod(3, p) ^ (p - 1);
assert(a == Mod(1, p));
}
function test_polynomial()
{
var a, b, q, r, t, i;
a = (1 + X) ^ 4;
assert(a == X^4+4*X^3+6*X^2+4*X+1);
r = (1 + X);
q = (1+X+X^2);
b = (1 - X^2);
a = q * b + r;
t = Polynomial.divrem(a, b);
assert(t[0] == q);
assert(t[1] == r);
a = 1 + 2*X + 3*X^2;
assert(a.apply(0.1) == 1.23);
a = 1-2*X^2+2*X^3;
assert(deriv(a) == (6*X^2-4*X));
assert(deriv(integ(a)) == a);
a = (X-1)*(X-2)*(X-3)*(X-4)*(X-0.1);
r = polroots(a);
for(i = 0; i < r.length; i++) {
b = abs(a.apply(r[i]));
assert(b <= 1e-13);
}
}
function test_poly_mod()
{
var a, p;
/* modulo using polynomials */
p = X^2 + X + 1;
a = PolyMod(3+X, p) ^ 10;
assert(a == PolyMod(-3725*X-18357, p));
a = PolyMod(1/X, 1+X^2);
assert(a == PolyMod(-X, X^2+1));
}
function test_rfunc()
{
var a;
a = (X+1)/((X+1)*(X-1));
assert(a == 1/(X-1));
a = (X + 2) / (X - 2);
assert(a.apply(1/3) == -7/5);
assert(deriv((X^2-X+1)/(X-1)) == (X^2-2*X)/(X^2-2*X+1));
}
function test_series()
{
var a, b;
a = 1+X+O(X^5);
b = a.inverse();
assert(b == 1-X+X^2-X^3+X^4+O(X^5));
assert(deriv(b) == -1+2*X-3*X^2+4*X^3+O(X^4));
assert(deriv(integ(b)) == b);
a = Series(1/(1-X), 5);
assert(a == 1+X+X^2+X^3+X^4+O(X^5));
b = a.apply(0.1);
assert(b == 1.1111);
assert(exp(3*X^2+O(X^10)) == 1+3*X^2+9/2*X^4+9/2*X^6+27/8*X^8+O(X^10));
assert(sin(X+O(X^6)) == X-1/6*X^3+1/120*X^5+O(X^6));
assert(cos(X+O(X^6)) == 1-1/2*X^2+1/24*X^4+O(X^6));
assert(tan(X+O(X^8)) == X+1/3*X^3+2/15*X^5+17/315*X^7+O(X^8));
assert((1+X+O(X^6))^(2+X) == 1+2*X+2*X^2+3/2*X^3+5/6*X^4+5/12*X^5+O(X^6));
}
function test_matrix()
{
var a, b, r;
a = [[1, 2],[3, 4]];
b = [3, 4];
r = a * b;
assert(r == [11, 25]);
r = (a^-1) * 2;
assert(r == [[-4, 2],[3, -1]]);
assert(norm2([1,2,3]) == 14);
assert(diag([1,2,3]) == [ [ 1, 0, 0 ], [ 0, 2, 0 ], [ 0, 0, 3 ] ]);
assert(trans(a) == [ [ 1, 3 ], [ 2, 4 ] ]);
assert(trans([1,2,3]) == [[1,2,3]]);
assert(trace(a) == 5);
assert(charpoly(Matrix.hilbert(4)) == X^4-176/105*X^3+3341/12600*X^2-41/23625*X+1/6048000);
assert(det(Matrix.hilbert(4)) == 1/6048000);
a = [[1,2,1],[-2,-3,1],[3,5,0]];
assert(rank(a) == 2);
assert(ker(a) == [ [ 5 ], [ -3 ], [ 1 ] ]);
assert(dp([1, 2, 3], [3, -4, -7]) === -26);
assert(cp([1, 2, 3], [3, -4, -7]) == [ -2, 16, -10 ]);
}
function assert_eq(a, ref)
{
assert(abs(a / ref - 1.0) <= 1e-15);
}
function test_trig()
{
assert_eq(sin(1/2), 0.479425538604203);
assert_eq(sin(2+3*I), 9.154499146911428-4.168906959966565*I);
assert_eq(cos(2+3*I), -4.189625690968807-9.109227893755337*I);
assert_eq((2+0.5*I)^(1.1-0.5*I), 2.494363021357619-0.23076804554558092*I);
assert_eq(sqrt(2*I), 1 + I);
}
test_integer();
test_float();
test_modulo();
test_fraction();
test_mod();
test_polynomial();
test_poly_mod();
test_rfunc();
test_series();
test_matrix();
test_trig();

View File

@ -144,7 +144,9 @@ function test_os()
{ {
var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path; var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path;
assert(os.isatty(0)); const stdinIsTTY = !os.exec(["/bin/sh", "-c", "test -t 0"], { usePath: false });
assert(os.isatty(0), stdinIsTTY, `isatty(STDIN)`);
fdir = "test_tmp_dir"; fdir = "test_tmp_dir";
fname = "tmp_file.txt"; fname = "tmp_file.txt";
@ -253,10 +255,11 @@ function test_os_exec()
pid = os.exec(["cat"], { block: false } ); pid = os.exec(["cat"], { block: false } );
assert(pid >= 0); assert(pid >= 0);
os.kill(pid, os.SIGQUIT); os.kill(pid, os.SIGTERM);
[ret, status] = os.waitpid(pid, 0); [ret, status] = os.waitpid(pid, 0);
assert(ret, pid); assert(ret, pid);
assert(status & 0x7f, os.SIGQUIT); assert(status !== 0, true, `expect nonzero exit code (got ${status})`);
assert(status & 0x7f, os.SIGTERM);
} }
function test_timer() function test_timer()

View File

@ -10,7 +10,7 @@ function handle_msg(e) {
switch(ev.type) { switch(ev.type) {
case "abort": case "abort":
parent.postMessage({ type: "done" }); parent.postMessage({ type: "done" });
parent.onMessage = null; /* terminate the worker */ parent.onmessage = null; /* terminate the worker */
break; break;
case "sab": case "sab":
/* modify the SharedArrayBuffer */ /* modify the SharedArrayBuffer */

View File

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
set -e set -e
url="ftp://ftp.unicode.org/Public/15.0.0/ucd" url="ftp://ftp.unicode.org/Public/16.0.0/ucd"
emoji_url="${url}/emoji/emoji-data.txt" emoji_url="${url}/emoji/emoji-data.txt"
files="CaseFolding.txt DerivedNormalizationProps.txt PropList.txt \ files="CaseFolding.txt DerivedNormalizationProps.txt PropList.txt \

View File

@ -33,6 +33,11 @@
#include "cutils.h" #include "cutils.h"
uint32_t total_tables;
uint32_t total_table_bytes;
uint32_t total_index;
uint32_t total_index_bytes;
/* define it to be able to test unicode.c */ /* define it to be able to test unicode.c */
//#define USE_TEST //#define USE_TEST
/* profile tests */ /* profile tests */
@ -268,7 +273,7 @@ int find_name(const char **tab, int tab_len, const char *name)
return -1; return -1;
} }
static int get_prop(uint32_t c, int prop_idx) static BOOL get_prop(uint32_t c, int prop_idx)
{ {
return (unicode_db[c].prop_bitmap_tab[prop_idx >> 5] >> (prop_idx & 0x1f)) & 1; return (unicode_db[c].prop_bitmap_tab[prop_idx >> 5] >> (prop_idx & 0x1f)) & 1;
} }
@ -620,7 +625,7 @@ void parse_derived_core_properties(const char *filename)
p++; p++;
p += strspn(p, " \t"); p += strspn(p, " \t");
q = buf; q = buf;
while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t') { while (*p != '\0' && *p != ' ' && *p != '#' && *p != '\t' && *p != ';') {
if ((q - buf) < sizeof(buf) - 1) if ((q - buf) < sizeof(buf) - 1)
*q++ = *p; *q++ = *p;
p++; p++;
@ -1112,6 +1117,24 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
te->ext_data[1] = ci->u_data[1]; te->ext_data[1] = ci->u_data[1];
te->ext_data[2] = ci->u_data[2]; te->ext_data[2] = ci->u_data[2];
te->ext_len = 3; te->ext_len = 3;
} else if (ci->u_len == 2 && ci->l_len == 0 && ci->f_len == 1) {
// U+FB05 LATIN SMALL LIGATURE LONG S T
assert(code == 0xFB05);
te->len = 1;
te->type = RUN_TYPE_UF_EXT2;
te->ext_data[0] = ci->u_data[0];
te->ext_data[1] = ci->u_data[1];
te->ext_len = 2;
} else if (ci->u_len == 3 && ci->l_len == 0 && ci->f_len == 1) {
// U+1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA or
// U+1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
assert(code == 0x1FD3 || code == 0x1FE3);
te->len = 1;
te->type = RUN_TYPE_UF_EXT3;
te->ext_data[0] = ci->u_data[0];
te->ext_data[1] = ci->u_data[1];
te->ext_data[2] = ci->u_data[2];
te->ext_len = 3;
} else { } else {
printf("unsupported encoding case:\n"); printf("unsupported encoding case:\n");
dump_cc_info(ci, code); dump_cc_info(ci, code);
@ -1328,7 +1351,9 @@ void dump_case_conv_table(FILE *f)
uint32_t v; uint32_t v;
const TableEntry *te; const TableEntry *te;
fprintf(f, "static const uint32_t case_conv_table1[%u] = {", conv_table_len); total_tables++;
total_table_bytes += conv_table_len * sizeof(uint32_t);
fprintf(f, "static const uint32_t case_conv_table1[%d] = {", conv_table_len);
for(i = 0; i < conv_table_len; i++) { for(i = 0; i < conv_table_len; i++) {
if (i % 4 == 0) if (i % 4 == 0)
fprintf(f, "\n "); fprintf(f, "\n ");
@ -1341,7 +1366,9 @@ void dump_case_conv_table(FILE *f)
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
fprintf(f, "static const uint8_t case_conv_table2[%u] = {", conv_table_len); total_tables++;
total_table_bytes += conv_table_len;
fprintf(f, "static const uint8_t case_conv_table2[%d] = {", conv_table_len);
for(i = 0; i < conv_table_len; i++) { for(i = 0; i < conv_table_len; i++) {
if (i % 8 == 0) if (i % 8 == 0)
fprintf(f, "\n "); fprintf(f, "\n ");
@ -1350,7 +1377,9 @@ void dump_case_conv_table(FILE *f)
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
fprintf(f, "static const uint16_t case_conv_ext[%u] = {", ext_data_len); total_tables++;
total_table_bytes += ext_data_len * sizeof(uint16_t);
fprintf(f, "static const uint16_t case_conv_ext[%d] = {", ext_data_len);
for(i = 0; i < ext_data_len; i++) { for(i = 0; i < ext_data_len; i++) {
if (i % 8 == 0) if (i % 8 == 0)
fprintf(f, "\n "); fprintf(f, "\n ");
@ -1470,6 +1499,9 @@ void compute_internal_props(void)
void dump_byte_table(FILE *f, const char *cname, const uint8_t *tab, int len) void dump_byte_table(FILE *f, const char *cname, const uint8_t *tab, int len)
{ {
int i; int i;
total_tables++;
total_table_bytes += len;
fprintf(f, "static const uint8_t %s[%d] = {", cname, len); fprintf(f, "static const uint8_t %s[%d] = {", cname, len);
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
if (i % 8 == 0) if (i % 8 == 0)
@ -1479,9 +1511,26 @@ void dump_byte_table(FILE *f, const char *cname, const uint8_t *tab, int len)
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
} }
void dump_index_table(FILE *f, const char *cname, const uint8_t *tab, int len)
{
int i, code, offset;
total_index++;
total_index_bytes += len;
fprintf(f, "static const uint8_t %s[%d] = {\n", cname, len);
for(i = 0; i < len; i += 3) {
code = tab[i] + (tab[i+1] << 8) + ((tab[i+2] & 0x1f) << 16);
offset = ((i / 3) + 1) * 32 + (tab[i+2] >> 5);
fprintf(f, " 0x%02x, 0x%02x, 0x%02x,", tab[i], tab[i+1], tab[i+2]);
fprintf(f, " // %6.5X at %d%s\n", code, offset,
i == len - 3 ? " (upper bound)" : "");
}
fprintf(f, "};\n\n");
}
#define PROP_BLOCK_LEN 32 #define PROP_BLOCK_LEN 32
void build_prop_table(FILE *f, int prop_index, BOOL add_index) void build_prop_table(FILE *f, const char *name, int prop_index, BOOL add_index)
{ {
int i, j, n, v, offset, code; int i, j, n, v, offset, code;
DynBuf dbuf_s, *dbuf = &dbuf_s; DynBuf dbuf_s, *dbuf = &dbuf_s;
@ -1533,6 +1582,14 @@ void build_prop_table(FILE *f, int prop_index, BOOL add_index)
block_end_pos += PROP_BLOCK_LEN; block_end_pos += PROP_BLOCK_LEN;
} }
/* Compressed byte encoding:
00..3F: 2 packed lengths: 3-bit + 3-bit
40..5F: 5-bits plus extra byte for length
60..7F: 5-bits plus 2 extra bytes for length
80..FF: 7-bit length
lengths must be incremented to get character count
Ranges alternate between false and true return value.
*/
v = buf[i]; v = buf[i];
code += v + 1; code += v + 1;
bit ^= 1; bit ^= 1;
@ -1573,7 +1630,7 @@ void build_prop_table(FILE *f, int prop_index, BOOL add_index)
dump_byte_table(f, cname, dbuf->buf, dbuf->size); dump_byte_table(f, cname, dbuf->buf, dbuf->size);
if (add_index) { if (add_index) {
snprintf(cname, sizeof(cname), "unicode_prop_%s_index", unicode_prop_name[prop_index]); snprintf(cname, sizeof(cname), "unicode_prop_%s_index", unicode_prop_name[prop_index]);
dump_byte_table(f, cname, dbuf2->buf, dbuf2->size); dump_index_table(f, cname, dbuf2->buf, dbuf2->size);
} }
dbuf_free(dbuf); dbuf_free(dbuf);
@ -1583,10 +1640,10 @@ void build_prop_table(FILE *f, int prop_index, BOOL add_index)
void build_flags_tables(FILE *f) void build_flags_tables(FILE *f)
{ {
build_prop_table(f, PROP_Cased1, TRUE); build_prop_table(f, "Cased1", PROP_Cased1, TRUE);
build_prop_table(f, PROP_Case_Ignorable, TRUE); build_prop_table(f, "Case_Ignorable", PROP_Case_Ignorable, TRUE);
build_prop_table(f, PROP_ID_Start, TRUE); build_prop_table(f, "ID_Start", PROP_ID_Start, TRUE);
build_prop_table(f, PROP_ID_Continue1, TRUE); build_prop_table(f, "ID_Continue1", PROP_ID_Continue1, TRUE);
} }
void dump_name_table(FILE *f, const char *cname, const char **tab_name, int len, void dump_name_table(FILE *f, const char *cname, const char **tab_name, int len,
@ -1621,7 +1678,9 @@ void build_general_category_table(FILE *f)
{ {
int i, v, j, n, n1; int i, v, j, n, n1;
DynBuf dbuf_s, *dbuf = &dbuf_s; DynBuf dbuf_s, *dbuf = &dbuf_s;
#ifdef DUMP_TABLE_SIZE
int cw_count, cw_len_count[4], cw_start; int cw_count, cw_len_count[4], cw_start;
#endif
fprintf(f, "typedef enum {\n"); fprintf(f, "typedef enum {\n");
for(i = 0; i < GCAT_COUNT; i++) for(i = 0; i < GCAT_COUNT; i++)
@ -1635,9 +1694,11 @@ void build_general_category_table(FILE *f)
dbuf_init(dbuf); dbuf_init(dbuf);
#ifdef DUMP_TABLE_SIZE
cw_count = 0; cw_count = 0;
for(i = 0; i < 4; i++) for(i = 0; i < 4; i++)
cw_len_count[i] = 0; cw_len_count[i] = 0;
#endif
for(i = 0; i <= CHARCODE_MAX;) { for(i = 0; i <= CHARCODE_MAX;) {
v = unicode_db[i].general_category; v = unicode_db[i].general_category;
j = i + 1; j = i + 1;
@ -1656,9 +1717,11 @@ void build_general_category_table(FILE *f)
} }
} }
// printf("%05x %05x %d\n", i, n, v); // printf("%05x %05x %d\n", i, n, v);
cw_count++;
n--; n--;
#ifdef DUMP_TABLE_SIZE
cw_count++;
cw_start = dbuf->size; cw_start = dbuf->size;
#endif
if (n < 7) { if (n < 7) {
dbuf_putc(dbuf, (n << 5) | v); dbuf_putc(dbuf, (n << 5) | v);
} else if (n < 7 + 128) { } else if (n < 7 + 128) {
@ -1680,12 +1743,13 @@ void build_general_category_table(FILE *f)
dbuf_putc(dbuf, n1 >> 8); dbuf_putc(dbuf, n1 >> 8);
dbuf_putc(dbuf, n1); dbuf_putc(dbuf, n1);
} }
#ifdef DUMP_TABLE_SIZE
cw_len_count[dbuf->size - cw_start - 1]++; cw_len_count[dbuf->size - cw_start - 1]++;
#endif
i += n + 1; i += n + 1;
} }
#ifdef DUMP_TABLE_SIZE #ifdef DUMP_TABLE_SIZE
printf("general category: %d entries [", printf("general category: %d entries [", cw_count);
cw_count);
for(i = 0; i < 4; i++) for(i = 0; i < 4; i++)
printf(" %d", cw_len_count[i]); printf(" %d", cw_len_count[i]);
printf(" ], length=%d bytes\n", (int)dbuf->size); printf(" ], length=%d bytes\n", (int)dbuf->size);
@ -1700,7 +1764,9 @@ void build_script_table(FILE *f)
{ {
int i, v, j, n, n1, type; int i, v, j, n, n1, type;
DynBuf dbuf_s, *dbuf = &dbuf_s; DynBuf dbuf_s, *dbuf = &dbuf_s;
#ifdef DUMP_TABLE_SIZE
int cw_count, cw_len_count[4], cw_start; int cw_count, cw_len_count[4], cw_start;
#endif
fprintf(f, "typedef enum {\n"); fprintf(f, "typedef enum {\n");
for(i = 0; i < SCRIPT_COUNT; i++) for(i = 0; i < SCRIPT_COUNT; i++)
@ -1714,9 +1780,11 @@ void build_script_table(FILE *f)
unicode_script_short_name + i); unicode_script_short_name + i);
dbuf_init(dbuf); dbuf_init(dbuf);
#ifdef DUMP_TABLE_SIZE
cw_count = 0; cw_count = 0;
for(i = 0; i < 4; i++) for(i = 0; i < 4; i++)
cw_len_count[i] = 0; cw_len_count[i] = 0;
#endif
for(i = 0; i <= CHARCODE_MAX;) { for(i = 0; i <= CHARCODE_MAX;) {
v = unicode_db[i].script; v = unicode_db[i].script;
j = i + 1; j = i + 1;
@ -1726,9 +1794,11 @@ void build_script_table(FILE *f)
if (v == 0 && j == (CHARCODE_MAX + 1)) if (v == 0 && j == (CHARCODE_MAX + 1))
break; break;
// printf("%05x %05x %d\n", i, n, v); // printf("%05x %05x %d\n", i, n, v);
cw_count++;
n--; n--;
#ifdef DUMP_TABLE_SIZE
cw_count++;
cw_start = dbuf->size; cw_start = dbuf->size;
#endif
if (v == 0) if (v == 0)
type = 0; type = 0;
else else
@ -1750,12 +1820,13 @@ void build_script_table(FILE *f)
if (type != 0) if (type != 0)
dbuf_putc(dbuf, v); dbuf_putc(dbuf, v);
#ifdef DUMP_TABLE_SIZE
cw_len_count[dbuf->size - cw_start - 1]++; cw_len_count[dbuf->size - cw_start - 1]++;
#endif
i += n + 1; i += n + 1;
} }
#if defined(DUMP_TABLE_SIZE) #ifdef DUMP_TABLE_SIZE
printf("script: %d entries [", printf("script: %d entries [", cw_count);
cw_count);
for(i = 0; i < 4; i++) for(i = 0; i < 4; i++)
printf(" %d", cw_len_count[i]); printf(" %d", cw_len_count[i]);
printf(" ], length=%d bytes\n", (int)dbuf->size); printf(" ], length=%d bytes\n", (int)dbuf->size);
@ -1770,10 +1841,11 @@ void build_script_ext_table(FILE *f)
{ {
int i, j, n, n1, script_ext_len; int i, j, n, n1, script_ext_len;
DynBuf dbuf_s, *dbuf = &dbuf_s; DynBuf dbuf_s, *dbuf = &dbuf_s;
int cw_count; #if defined(DUMP_TABLE_SIZE)
int cw_count = 0;
#endif
dbuf_init(dbuf); dbuf_init(dbuf);
cw_count = 0;
for(i = 0; i <= CHARCODE_MAX;) { for(i = 0; i <= CHARCODE_MAX;) {
script_ext_len = unicode_db[i].script_ext_len; script_ext_len = unicode_db[i].script_ext_len;
j = i + 1; j = i + 1;
@ -1784,7 +1856,9 @@ void build_script_ext_table(FILE *f)
j++; j++;
} }
n = j - i; n = j - i;
#if defined(DUMP_TABLE_SIZE)
cw_count++; cw_count++;
#endif
n--; n--;
if (n < 128) { if (n < 128) {
dbuf_putc(dbuf, n); dbuf_putc(dbuf, n);
@ -1806,8 +1880,7 @@ void build_script_ext_table(FILE *f)
i += n + 1; i += n + 1;
} }
#ifdef DUMP_TABLE_SIZE #ifdef DUMP_TABLE_SIZE
printf("script_ext: %d entries", printf("script_ext: %d entries", cw_count);
cw_count);
printf(", length=%d bytes\n", (int)dbuf->size); printf(", length=%d bytes\n", (int)dbuf->size);
#endif #endif
@ -1829,7 +1902,7 @@ void build_prop_list_table(FILE *f)
i == PROP_ID_Continue1) { i == PROP_ID_Continue1) {
/* already generated */ /* already generated */
} else { } else {
build_prop_table(f, i, FALSE); build_prop_table(f, unicode_prop_name[i], i, FALSE);
} }
} }
@ -1926,7 +1999,7 @@ void check_flags(void)
BOOL flag_ref, flag; BOOL flag_ref, flag;
for(c = 0; c <= CHARCODE_MAX; c++) { for(c = 0; c <= CHARCODE_MAX; c++) {
flag_ref = get_prop(c, PROP_Cased); flag_ref = get_prop(c, PROP_Cased);
flag = lre_is_cased(c); flag = !!lre_is_cased(c);
if (flag != flag_ref) { if (flag != flag_ref) {
printf("ERROR: c=%05x cased=%d ref=%d\n", printf("ERROR: c=%05x cased=%d ref=%d\n",
c, flag, flag_ref); c, flag, flag_ref);
@ -1934,7 +2007,7 @@ void check_flags(void)
} }
flag_ref = get_prop(c, PROP_Case_Ignorable); flag_ref = get_prop(c, PROP_Case_Ignorable);
flag = lre_is_case_ignorable(c); flag = !!lre_is_case_ignorable(c);
if (flag != flag_ref) { if (flag != flag_ref) {
printf("ERROR: c=%05x case_ignorable=%d ref=%d\n", printf("ERROR: c=%05x case_ignorable=%d ref=%d\n",
c, flag, flag_ref); c, flag, flag_ref);
@ -1942,7 +2015,7 @@ void check_flags(void)
} }
flag_ref = get_prop(c, PROP_ID_Start); flag_ref = get_prop(c, PROP_ID_Start);
flag = lre_is_id_start(c); flag = !!lre_is_id_start(c);
if (flag != flag_ref) { if (flag != flag_ref) {
printf("ERROR: c=%05x id_start=%d ref=%d\n", printf("ERROR: c=%05x id_start=%d ref=%d\n",
c, flag, flag_ref); c, flag, flag_ref);
@ -1950,7 +2023,7 @@ void check_flags(void)
} }
flag_ref = get_prop(c, PROP_ID_Continue); flag_ref = get_prop(c, PROP_ID_Continue);
flag = lre_is_id_continue(c); flag = !!lre_is_id_continue(c);
if (flag != flag_ref) { if (flag != flag_ref) {
printf("ERROR: c=%05x id_cont=%d ref=%d\n", printf("ERROR: c=%05x id_cont=%d ref=%d\n",
c, flag, flag_ref); c, flag, flag_ref);
@ -1964,7 +2037,7 @@ void check_flags(void)
count = 0; count = 0;
for(c = 0x20; c <= 0xffff; c++) { for(c = 0x20; c <= 0xffff; c++) {
flag_ref = get_prop(c, PROP_ID_Start); flag_ref = get_prop(c, PROP_ID_Start);
flag = lre_is_id_start(c); flag = !!lre_is_id_start(c);
assert(flag == flag_ref); assert(flag == flag_ref);
count++; count++;
} }
@ -1981,17 +2054,23 @@ void check_flags(void)
void build_cc_table(FILE *f) void build_cc_table(FILE *f)
{ {
int i, cc, n, cc_table_len, type, n1; // Compress combining class table
// see: https://www.unicode.org/reports/tr44/#Canonical_Combining_Class_Values
int i, cc, n, type, n1, block_end_pos;
DynBuf dbuf_s, *dbuf = &dbuf_s; DynBuf dbuf_s, *dbuf = &dbuf_s;
DynBuf dbuf1_s, *dbuf1 = &dbuf1_s; DynBuf dbuf1_s, *dbuf1 = &dbuf1_s;
int cw_len_tab[3], cw_start, block_end_pos; #if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
int cw_len_tab[3], cw_start, cc_table_len;
#endif
uint32_t v; uint32_t v;
dbuf_init(dbuf); dbuf_init(dbuf);
dbuf_init(dbuf1); dbuf_init(dbuf1);
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
cc_table_len = 0; cc_table_len = 0;
for(i = 0; i < countof(cw_len_tab); i++) for(i = 0; i < countof(cw_len_tab); i++)
cw_len_tab[i] = 0; cw_len_tab[i] = 0;
#endif
block_end_pos = CC_BLOCK_LEN; block_end_pos = CC_BLOCK_LEN;
for(i = 0; i <= CHARCODE_MAX;) { for(i = 0; i <= CHARCODE_MAX;) {
cc = unicode_db[i].combining_class; cc = unicode_db[i].combining_class;
@ -2032,7 +2111,16 @@ void build_cc_table(FILE *f)
dbuf_putc(dbuf1, v >> 16); dbuf_putc(dbuf1, v >> 16);
block_end_pos += CC_BLOCK_LEN; block_end_pos += CC_BLOCK_LEN;
} }
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
cw_start = dbuf->size; cw_start = dbuf->size;
#endif
/* Compressed run length encoding:
- 2 high order bits are combining class type
- 0:0, 1:230, 2:extra byte linear progression, 3:extra byte
- 00..2F: range length (add 1)
- 30..37: 3-bit range-length + 1 extra byte
- 38..3F: 3-bit range-length + 2 extra byte
*/
if (n1 < 48) { if (n1 < 48) {
dbuf_putc(dbuf, n1 | (type << 6)); dbuf_putc(dbuf, n1 | (type << 6));
} else if (n1 < 48 + (1 << 11)) { } else if (n1 < 48 + (1 << 11)) {
@ -2046,10 +2134,12 @@ void build_cc_table(FILE *f)
dbuf_putc(dbuf, n1 >> 8); dbuf_putc(dbuf, n1 >> 8);
dbuf_putc(dbuf, n1); dbuf_putc(dbuf, n1);
} }
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
cw_len_tab[dbuf->size - cw_start - 1]++; cw_len_tab[dbuf->size - cw_start - 1]++;
cc_table_len++;
#endif
if (type == 0 || type == 1) if (type == 0 || type == 1)
dbuf_putc(dbuf, cc); dbuf_putc(dbuf, cc);
cc_table_len++;
i += n; i += n;
} }
@ -2060,7 +2150,7 @@ void build_cc_table(FILE *f)
dbuf_putc(dbuf1, v >> 16); dbuf_putc(dbuf1, v >> 16);
dump_byte_table(f, "unicode_cc_table", dbuf->buf, dbuf->size); dump_byte_table(f, "unicode_cc_table", dbuf->buf, dbuf->size);
dump_byte_table(f, "unicode_cc_index", dbuf1->buf, dbuf1->size); dump_index_table(f, "unicode_cc_index", dbuf1->buf, dbuf1->size);
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) #if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
printf("CC table: size=%d (%d entries) [", printf("CC table: size=%d (%d entries) [",
@ -2741,8 +2831,9 @@ void build_decompose_table(FILE *f)
} }
#endif #endif
fprintf(f, "static const uint32_t unicode_decomp_table1[%u] = {", total_tables++;
array_len); total_table_bytes += array_len * sizeof(uint32_t);
fprintf(f, "static const uint32_t unicode_decomp_table1[%d] = {", array_len);
count = 0; count = 0;
for(i = 0; i <= code_max; i++) { for(i = 0; i <= code_max; i++) {
de = &tab_de[i]; de = &tab_de[i];
@ -2760,8 +2851,9 @@ void build_decompose_table(FILE *f)
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
fprintf(f, "static const uint16_t unicode_decomp_table2[%u] = {", total_tables++;
array_len); total_table_bytes += array_len * sizeof(uint16_t);
fprintf(f, "static const uint16_t unicode_decomp_table2[%d] = {", array_len);
count = 0; count = 0;
for(i = 0; i <= code_max; i++) { for(i = 0; i <= code_max; i++) {
de = &tab_de[i]; de = &tab_de[i];
@ -2774,8 +2866,9 @@ void build_decompose_table(FILE *f)
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
fprintf(f, "static const uint8_t unicode_decomp_data[%u] = {", total_tables++;
data_len); total_table_bytes += data_len;
fprintf(f, "static const uint8_t unicode_decomp_data[%d] = {", data_len);
for(i = 0; i < data_len; i++) { for(i = 0; i < data_len; i++) {
if (i % 8 == 0) if (i % 8 == 0)
fprintf(f, "\n "); fprintf(f, "\n ");
@ -2866,8 +2959,9 @@ void build_compose_table(FILE *f, const DecompEntry *tab_de)
} }
#endif #endif
fprintf(f, "static const uint16_t unicode_comp_table[%u] = {", total_tables++;
tab_ce_len); total_table_bytes += tab_ce_len * sizeof(uint16_t);
fprintf(f, "static const uint16_t unicode_comp_table[%u] = {", tab_ce_len);
for(i = 0; i < tab_ce_len; i++) { for(i = 0; i < tab_ce_len; i++) {
if (i % 8 == 0) if (i % 8 == 0)
fprintf(f, "\n "); fprintf(f, "\n ");
@ -3042,22 +3136,24 @@ void normalization_test(const char *filename)
} }
#endif #endif
int main(int argc, char **argv) int main(int argc, char *argv[])
{ {
const char *unicode_db_path, *outfilename; const char *unicode_db_path, *outfilename;
char filename[1024]; char filename[1024];
int arg = 1;
if (argc < 2) { if (arg >= argc || (!strcmp(argv[arg], "-h") || !strcmp(argv[arg], "--help"))) {
printf("usage: %s unicode_db_path [output_file]\n" printf("usage: %s PATH [OUTPUT]\n"
"\n" " PATH path to the Unicode database directory\n"
"If no output_file is given, a self test is done using the current unicode library\n", " OUTPUT name of the output file. If omitted, a self test is performed\n"
argv[0]); " using the files from the Unicode library\n"
exit(1); , argv[0]);
return 1;
} }
unicode_db_path = argv[1]; unicode_db_path = argv[arg++];
outfilename = NULL; outfilename = NULL;
if (argc >= 3) if (arg < argc)
outfilename = argv[2]; outfilename = argv[arg++];
unicode_db = mallocz(sizeof(unicode_db[0]) * (CHARCODE_MAX + 1)); unicode_db = mallocz(sizeof(unicode_db[0]) * (CHARCODE_MAX + 1));
@ -3139,6 +3235,8 @@ int main(int argc, char **argv)
build_script_ext_table(fo); build_script_ext_table(fo);
build_prop_list_table(fo); build_prop_list_table(fo);
fprintf(fo, "#endif /* CONFIG_ALL_UNICODE */\n"); fprintf(fo, "#endif /* CONFIG_ALL_UNICODE */\n");
fprintf(fo, "/* %u tables / %u bytes, %u index / %u bytes */\n",
total_tables, total_table_bytes, total_index, total_index_bytes);
fclose(fo); fclose(fo);
} }
return 0; return 0;

View File

@ -82,6 +82,7 @@ DEF(Egyptian_Hieroglyphs, "Egyp")
DEF(Elbasan, "Elba") DEF(Elbasan, "Elba")
DEF(Elymaic, "Elym") DEF(Elymaic, "Elym")
DEF(Ethiopic, "Ethi") DEF(Ethiopic, "Ethi")
DEF(Garay, "Gara")
DEF(Georgian, "Geor") DEF(Georgian, "Geor")
DEF(Glagolitic, "Glag") DEF(Glagolitic, "Glag")
DEF(Gothic, "Goth") DEF(Gothic, "Goth")
@ -90,6 +91,7 @@ DEF(Greek, "Grek")
DEF(Gujarati, "Gujr") DEF(Gujarati, "Gujr")
DEF(Gunjala_Gondi, "Gong") DEF(Gunjala_Gondi, "Gong")
DEF(Gurmukhi, "Guru") DEF(Gurmukhi, "Guru")
DEF(Gurung_Khema, "Gukh")
DEF(Han, "Hani") DEF(Han, "Hani")
DEF(Hangul, "Hang") DEF(Hangul, "Hang")
DEF(Hanifi_Rohingya, "Rohg") DEF(Hanifi_Rohingya, "Rohg")
@ -112,6 +114,7 @@ DEF(Khmer, "Khmr")
DEF(Khojki, "Khoj") DEF(Khojki, "Khoj")
DEF(Khitan_Small_Script, "Kits") DEF(Khitan_Small_Script, "Kits")
DEF(Khudawadi, "Sind") DEF(Khudawadi, "Sind")
DEF(Kirat_Rai, "Krai")
DEF(Lao, "Laoo") DEF(Lao, "Laoo")
DEF(Latin, "Latn") DEF(Latin, "Latn")
DEF(Lepcha, "Lepc") DEF(Lepcha, "Lepc")
@ -149,6 +152,7 @@ DEF(Nushu, "Nshu")
DEF(Nyiakeng_Puachue_Hmong, "Hmnp") DEF(Nyiakeng_Puachue_Hmong, "Hmnp")
DEF(Ogham, "Ogam") DEF(Ogham, "Ogam")
DEF(Ol_Chiki, "Olck") DEF(Ol_Chiki, "Olck")
DEF(Ol_Onal, "Onao")
DEF(Old_Hungarian, "Hung") DEF(Old_Hungarian, "Hung")
DEF(Old_Italic, "Ital") DEF(Old_Italic, "Ital")
DEF(Old_North_Arabian, "Narb") DEF(Old_North_Arabian, "Narb")
@ -180,6 +184,7 @@ DEF(Sogdian, "Sogd")
DEF(Sora_Sompeng, "Sora") DEF(Sora_Sompeng, "Sora")
DEF(Soyombo, "Soyo") DEF(Soyombo, "Soyo")
DEF(Sundanese, "Sund") DEF(Sundanese, "Sund")
DEF(Sunuwar, "Sunu")
DEF(Syloti_Nagri, "Sylo") DEF(Syloti_Nagri, "Sylo")
DEF(Syriac, "Syrc") DEF(Syriac, "Syrc")
DEF(Tagalog, "Tglg") DEF(Tagalog, "Tglg")
@ -197,7 +202,9 @@ DEF(Tibetan, "Tibt")
DEF(Tifinagh, "Tfng") DEF(Tifinagh, "Tfng")
DEF(Tirhuta, "Tirh") DEF(Tirhuta, "Tirh")
DEF(Tangsa, "Tnsa") DEF(Tangsa, "Tnsa")
DEF(Todhri, "Todr")
DEF(Toto, "Toto") DEF(Toto, "Toto")
DEF(Tulu_Tigalari, "Tutg")
DEF(Ugaritic, "Ugar") DEF(Ugaritic, "Ugar")
DEF(Vai, "Vaii") DEF(Vai, "Vaii")
DEF(Vithkuqi, "Vith") DEF(Vithkuqi, "Vith")
@ -236,11 +243,13 @@ DEF(Deprecated, "Dep")
DEF(Diacritic, "Dia") DEF(Diacritic, "Dia")
DEF(Extender, "Ext") DEF(Extender, "Ext")
DEF(Hex_Digit, "Hex") DEF(Hex_Digit, "Hex")
DEF(IDS_Unary_Operator, "IDSU")
DEF(IDS_Binary_Operator, "IDSB") DEF(IDS_Binary_Operator, "IDSB")
DEF(IDS_Trinary_Operator, "IDST") DEF(IDS_Trinary_Operator, "IDST")
DEF(Ideographic, "Ideo") DEF(Ideographic, "Ideo")
DEF(Join_Control, "Join_C") DEF(Join_Control, "Join_C")
DEF(Logical_Order_Exception, "LOE") DEF(Logical_Order_Exception, "LOE")
DEF(Modifier_Combining_Mark, "MCM")
DEF(Noncharacter_Code_Point, "NChar") DEF(Noncharacter_Code_Point, "NChar")
DEF(Pattern_Syntax, "Pat_Syn") DEF(Pattern_Syntax, "Pat_Syn")
DEF(Pattern_White_Space, "Pat_WS") DEF(Pattern_White_Space, "Pat_WS")
@ -279,6 +288,9 @@ DEF(Changes_When_Uppercased, "CWU")
DEF(Grapheme_Base, "Gr_Base") DEF(Grapheme_Base, "Gr_Base")
DEF(Grapheme_Extend, "Gr_Ext") DEF(Grapheme_Extend, "Gr_Ext")
DEF(ID_Continue, "IDC") DEF(ID_Continue, "IDC")
DEF(ID_Compat_Math_Start, "")
DEF(ID_Compat_Math_Continue, "")
DEF(InCB, "")
DEF(Lowercase, "Lower") DEF(Lowercase, "Lower")
DEF(Math, "") DEF(Math, "")
DEF(Uppercase, "Upper") DEF(Uppercase, "Upper")