Compare commits

..

1 commit

Author SHA1 Message Date
bellard
e02472dabc
Revert "Add JS_HasException() (#265)"
This reverts commit db9dbd0a2b.
2024-05-30 14:28:49 +02:00
63 changed files with 4330 additions and 5085 deletions

8
.gitignore vendored
View file

@ -1,15 +1,17 @@
*.a *.a
.obj/ .obj/
tests/bjson.so tests/bjson.so
examples/test_fib
test_fib.c
examples/*.so examples/*.so
examples/hello
examples/hello_module
hello.c
microbench*.txt microbench*.txt
qjs qjs
qjsc qjsc
qjscalc qjscalc
qjscalc.c qjscalc.c
hello.c
hello
hello_module
repl.c repl.c
run-test262 run-test262
test262 test262

View file

@ -13,7 +13,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

View file

@ -1,6 +1,5 @@
QuickJS Javascript Engine (DoneJS Edition) QuickJS Javascript Engine
Copyright (c) 2024 Sneed Group
Copyright (c) 2017-2021 Fabrice Bellard Copyright (c) 2017-2021 Fabrice Bellard
Copyright (c) 2017-2021 Charlie Gordon Copyright (c) 2017-2021 Charlie Gordon

161
Makefile
View file

@ -1,6 +1,6 @@
# #
# QuickJS Javascript Engine # QuickJS Javascript Engine
# #
# Copyright (c) 2017-2021 Fabrice Bellard # Copyright (c) 2017-2021 Fabrice Bellard
# Copyright (c) 2017-2021 Charlie Gordon # Copyright (c) 2017-2021 Charlie Gordon
# #
@ -25,10 +25,13 @@
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
#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 for some utilities
@ -43,16 +46,37 @@ 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_MSAN=y
# use UB sanitizer
#CONFIG_UBSAN=y
# include the code for BigFloat/BigDecimal and math mode
CONFIG_BIGNUM=y CONFIG_BIGNUM=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
@ -87,6 +111,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
@ -106,7 +131,7 @@ else
AR=$(CROSS_PREFIX)ar AR=$(CROSS_PREFIX)ar
endif endif
endif endif
STRIP=$(CROSS_PREFIX)strip STRIP?=$(CROSS_PREFIX)strip
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
@ -118,6 +143,11 @@ 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 +172,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
@ -176,18 +214,21 @@ endif
# examples # examples
ifeq ($(CROSS_PREFIX),) ifeq ($(CROSS_PREFIX),)
PROGS+=examples/hello
ifndef CONFIG_ASAN ifndef CONFIG_ASAN
PROGS+=examples/hello_module ifndef CONFIG_MSAN
endif ifndef CONFIG_UBSAN
PROGS+=examples/hello examples/hello_module examples/test_fib
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)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o $(OBJDIR)/libbf.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 ifdef CONFIG_BIGNUM
@ -213,6 +254,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 \
@ -254,6 +306,9 @@ libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^ $(AR) rcs $@ $^
endif # CONFIG_LTO endif # CONFIG_LTO
libquickjs.fuzz.a: $(patsubst %.o, %.fuzz.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^
repl.c: $(QJSC) repl.js repl.c: $(QJSC) repl.js
$(QJSC) -c -o $@ -m repl.js $(QJSC) -c -o $@ -m repl.js
@ -282,6 +337,9 @@ run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)
$(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 $@ $<
@ -300,6 +358,9 @@ $(OBJDIR)/%.m32s.o: %.c | $(OBJDIR)
$(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 $@ $<
@ -311,17 +372,18 @@ unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c u
clean: clean:
rm -f repl.c qjscalc.c out.c rm -f repl.c qjscalc.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
rm -rf run-test262-debug run-test262-32 rm -rf run-test262-debug run-test262-32
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" ln -sf qjs$(EXE) "$(DESTDIR)$(PREFIX)/bin/qjscalc$(EXE)"
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
@ -375,11 +437,11 @@ 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 doc/jsbignum.pdf doc/jsbignum.html
build_doc: $(DOCS) build_doc: $(DOCS)
clean_doc: clean_doc:
rm -f $(DOCS) rm -f $(DOCS)
doc/%.pdf: doc/%.texi doc/%.pdf: doc/%.texi
@ -404,8 +466,9 @@ endif
test: qjs test: qjs
./qjs tests/test_closure.js ./qjs tests/test_closure.js
./qjs tests/test_language.js ./qjs tests/test_language.js
./qjs tests/test_builtin.js ./qjs --std tests/test_builtin.js
./qjs tests/test_loop.js ./qjs tests/test_loop.js
./qjs tests/test_bignum.js
./qjs tests/test_std.js ./qjs tests/test_std.js
./qjs tests/test_worker.js ./qjs tests/test_worker.js
ifdef CONFIG_SHARED_LIBS ifdef CONFIG_SHARED_LIBS
@ -418,19 +481,20 @@ endif
endif endif
ifdef CONFIG_BIGNUM ifdef CONFIG_BIGNUM
./qjs --bignum tests/test_op_overloading.js ./qjs --bignum tests/test_op_overloading.js
./qjs --bignum tests/test_bignum.js ./qjs --bignum tests/test_bigfloat.js
./qjs --qjscalc tests/test_qjscalc.js ./qjs --qjscalc tests/test_qjscalc.js
endif endif
ifdef CONFIG_M32 ifdef CONFIG_M32
./qjs32 tests/test_closure.js ./qjs32 tests/test_closure.js
./qjs32 tests/test_language.js ./qjs32 tests/test_language.js
./qjs32 tests/test_builtin.js ./qjs32 --std tests/test_builtin.js
./qjs32 tests/test_loop.js ./qjs32 tests/test_loop.js
./qjs32 tests/test_bignum.js
./qjs32 tests/test_std.js ./qjs32 tests/test_std.js
./qjs32 tests/test_worker.js ./qjs32 tests/test_worker.js
ifdef CONFIG_BIGNUM ifdef CONFIG_BIGNUM
./qjs32 --bignum tests/test_op_overloading.js ./qjs32 --bignum tests/test_op_overloading.js
./qjs32 --bignum tests/test_bignum.js ./qjs32 --bignum tests/test_bigfloat.js
./qjs32 --qjscalc tests/test_qjscalc.js ./qjs32 --qjscalc tests/test_qjscalc.js
endif endif
endif endif
@ -440,36 +504,46 @@ stats: qjs qjs32
./qjs32 -qd ./qjs32 -qd
microbench: qjs microbench: qjs
./qjs tests/microbench.js ./qjs --std tests/microbench.js
microbench-32: qjs32 microbench-32: qjs32
./qjs32 tests/microbench.js ./qjs32 --std tests/microbench.js
ifeq ($(wildcard test262o/tests.txt),)
test2o test2o-32 test2o-update:
@echo test262o tests not installed
else
# 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 test2o-32: run-test262-32
time ./run-test262-32 -m -c test262o.conf time ./run-test262-32 -t -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 test262o/tests.txt),)
test2 test2-32 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 test2-32: run-test262-32
time ./run-test262-32 -m -c test262.conf -a time ./run-test262-32 -t -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
@ -477,11 +551,40 @@ testall-32: all test-32 microbench-32 test2o-32 test2-32
testall-complete: testall testall-32 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_bignum.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)

View file

@ -1,14 +0,0 @@
# QuickJS (DoneJS edition)
An *unofficial* fork of QuickJS that contains polyfills and modifications to the source code to make it more like something such as NodeJS or De*no*.
# FAQ
## Documentation Location
The main documentation is in "doc/quickjs.pdf" for the PDF version or "doc/quickjs.html" for the HTML one.
## Location of Polyfills
The polyfills are located in the "qjs.c" file.

2
TODO
View file

@ -63,5 +63,5 @@ 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: 8/76947 errors, 1497 excluded, 8117 skipped
Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb

View file

@ -1 +1 @@
2024-01-13 2024-02-14

View file

@ -1,6 +1,6 @@
/* /*
* C utilities * C utilities
* *
* Copyright (c) 2017 Fabrice Bellard * Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon * Copyright (c) 2018 Charlie Gordon
* *
@ -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;
} }
@ -172,7 +172,7 @@ int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
va_list ap; va_list ap;
char buf[128]; char buf[128];
int len; int len;
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);

View file

@ -1,6 +1,6 @@
/* /*
* C utilities * C utilities
* *
* Copyright (c) 2017 Fabrice Bellard * Copyright (c) 2017 Fabrice Bellard
* Copyright (c) 2018 Charlie Gordon * Copyright (c) 2018 Charlie Gordon
* *
@ -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,28 +221,34 @@ 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)) |
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
((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')

View file

@ -1,734 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<!-- Created by GNU Texinfo 6.1, http://www.gnu.org/software/texinfo/ -->
<head>
<title>Javascript Bignum Extensions</title>
<meta name="description" content="Javascript Bignum Extensions">
<meta name="keywords" content="Javascript Bignum Extensions">
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta name="Generator" content="makeinfo">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href="#SEC_Contents" rel="contents" title="Table of Contents">
<style type="text/css">
<!--
a.summary-letter {text-decoration: none}
blockquote.indentedblock {margin-right: 0em}
blockquote.smallindentedblock {margin-right: 0em; font-size: smaller}
blockquote.smallquotation {font-size: smaller}
div.display {margin-left: 3.2em}
div.example {margin-left: 3.2em}
div.lisp {margin-left: 3.2em}
div.smalldisplay {margin-left: 3.2em}
div.smallexample {margin-left: 3.2em}
div.smalllisp {margin-left: 3.2em}
kbd {font-style: oblique}
pre.display {font-family: inherit}
pre.format {font-family: inherit}
pre.menu-comment {font-family: serif}
pre.menu-preformatted {font-family: serif}
pre.smalldisplay {font-family: inherit; font-size: smaller}
pre.smallexample {font-size: smaller}
pre.smallformat {font-family: inherit; font-size: smaller}
pre.smalllisp {font-size: smaller}
span.nolinebreak {white-space: nowrap}
span.roman {font-family: initial; font-weight: normal}
span.sansserif {font-family: sans-serif; font-weight: normal}
ul.no-bullet {list-style: none}
-->
</style>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body lang="en">
<h1 class="settitle" align="center">Javascript Bignum Extensions</h1>
<a name="SEC_Contents"></a>
<h2 class="contents-heading">Table of Contents</h2>
<div class="contents">
<ul class="no-bullet">
<li><a name="toc-Introduction" href="#Introduction">1 Introduction</a></li>
<li><a name="toc-Operator-overloading" href="#Operator-overloading">2 Operator overloading</a></li>
<li><a name="toc-BigInt-extensions" href="#BigInt-extensions">3 BigInt extensions</a></li>
<li><a name="toc-BigFloat" href="#BigFloat">4 BigFloat</a>
<ul class="no-bullet">
<li><a name="toc-Introduction-1" href="#Introduction-1">4.1 Introduction</a></li>
<li><a name="toc-Floating-point-rounding" href="#Floating-point-rounding">4.2 Floating point rounding</a></li>
<li><a name="toc-Operators" href="#Operators">4.3 Operators</a></li>
<li><a name="toc-BigFloat-literals" href="#BigFloat-literals">4.4 BigFloat literals</a></li>
<li><a name="toc-Builtin-Object-changes" href="#Builtin-Object-changes">4.5 Builtin Object changes</a>
<ul class="no-bullet">
<li><a name="toc-BigFloat-function" href="#BigFloat-function">4.5.1 <code>BigFloat</code> function</a></li>
<li><a name="toc-BigFloat_002eprototype" href="#BigFloat_002eprototype">4.5.2 <code>BigFloat.prototype</code></a></li>
<li><a name="toc-BigFloatEnv-constructor" href="#BigFloatEnv-constructor">4.5.3 <code>BigFloatEnv</code> constructor</a></li>
</ul></li>
</ul></li>
<li><a name="toc-BigDecimal" href="#BigDecimal">5 BigDecimal</a>
<ul class="no-bullet">
<li><a name="toc-Operators-1" href="#Operators-1">5.1 Operators</a></li>
<li><a name="toc-BigDecimal-literals" href="#BigDecimal-literals">5.2 BigDecimal literals</a></li>
<li><a name="toc-Builtin-Object-changes-1" href="#Builtin-Object-changes-1">5.3 Builtin Object changes</a>
<ul class="no-bullet">
<li><a name="toc-The-BigDecimal-function_002e" href="#The-BigDecimal-function_002e">5.3.1 The <code>BigDecimal</code> function.</a></li>
<li><a name="toc-Properties-of-the-BigDecimal-object" href="#Properties-of-the-BigDecimal-object">5.3.2 Properties of the <code>BigDecimal</code> object</a></li>
<li><a name="toc-Properties-of-the-BigDecimal_002eprototype-object" href="#Properties-of-the-BigDecimal_002eprototype-object">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</a></li>
</ul></li>
</ul></li>
<li><a name="toc-Math-mode" href="#Math-mode">6 Math mode</a></li>
</ul>
</div>
<a name="Introduction"></a>
<h2 class="chapter">1 Introduction</h2>
<p>The Bignum extensions add the following features to the Javascript
language while being 100% backward compatible:
</p>
<ul>
<li> Operator overloading with a dispatch logic inspired from the proposal available at <a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>.
</li><li> Arbitrarily large floating point numbers (<code>BigFloat</code>) in base 2 using the IEEE 754 semantics.
</li><li> Arbitrarily large floating point numbers (<code>BigDecimal</code>) in base 10 based on the proposal available at
<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>.
</li><li> <code>math</code> 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>%</code>) is defined as the Euclidian
remainder. <code>^</code> is an alias to the power operator
(<code>**</code>). <code>^^</code> is used as the exclusive or operator.
</li></ul>
<p>The extensions are independent from each other except the <code>math</code>
mode which relies on BigFloat and operator overloading.
</p>
<a name="Operator-overloading"></a>
<h2 class="chapter">2 Operator overloading</h2>
<p>Operator overloading is inspired from the proposal available at
<a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>. It
implements the same dispatch logic but finds the operator sets by
looking at the <code>Symbol.operatorSet</code> property in the objects. The
changes were done in order to simplify the implementation.
</p>
<p>More precisely, the following modifications were made:
</p>
<ul>
<li> <code>with operators from</code> is not supported. Operator overloading is always enabled.
</li><li> The dispatch is not based on a static <code>[[OperatorSet]]</code> field in all instances. Instead, a dynamic lookup of the <code>Symbol.operatorSet</code> property is done. This property is typically added in the prototype of each object.
</li><li> <code>Operators.create(...dictionaries)</code> is used to create a new OperatorSet object. The <code>Operators</code> function is supported as an helper to be closer to the TC39 proposal.
</li><li> <code>[]</code> cannot be overloaded.
</li><li> In math mode, the BigInt division and power operators can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>.
</li></ul>
<a name="BigInt-extensions"></a>
<h2 class="chapter">3 BigInt extensions</h2>
<p>A few properties are added to the BigInt object:
</p>
<dl compact="compact">
<dt><code>tdiv(a, b)</code></dt>
<dd><p>Return <em>trunc(a/b)</em>. <code>b = 0</code> raises a RangeError
exception.
</p>
</dd>
<dt><code>fdiv(a, b)</code></dt>
<dd><p>Return <em>\lfloor a/b \rfloor</em>. <code>b = 0</code> raises a RangeError
exception.
</p>
</dd>
<dt><code>cdiv(a, b)</code></dt>
<dd><p>Return <em>\lceil a/b \rceil</em>. <code>b = 0</code> raises a RangeError
exception.
</p>
</dd>
<dt><code>ediv(a, b)</code></dt>
<dd><p>Return <em>sgn(b) \lfloor a/{|b|} \rfloor</em> (Euclidian
division). <code>b = 0</code> raises a RangeError exception.
</p>
</dd>
<dt><code>tdivrem(a, b)</code></dt>
<dt><code>fdivrem(a, b)</code></dt>
<dt><code>cdivrem(a, b)</code></dt>
<dt><code>edivrem(a, b)</code></dt>
<dd><p>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.
</p>
</dd>
<dt><code>sqrt(a)</code></dt>
<dd><p>Return <em>\lfloor \sqrt(a) \rfloor</em>. A RangeError exception is
raised if <em>a &lt; 0</em>.
</p>
</dd>
<dt><code>sqrtrem(a)</code></dt>
<dd><p>Return an array of two elements. The first element is <em>\lfloor
\sqrt{a} \rfloor</em>. The second element is <em>a-\lfloor \sqrt{a}
\rfloor^2</em>. A RangeError exception is raised if <em>a &lt; 0</em>.
</p>
</dd>
<dt><code>floorLog2(a)</code></dt>
<dd><p>Return -1 if <em>a \leq 0</em> otherwise return <em>\lfloor \log2(a) \rfloor</em>.
</p>
</dd>
<dt><code>ctz(a)</code></dt>
<dd><p>Return the number of trailing zeros in the two&rsquo;s complement binary representation of a. Return -1 if <em>a=0</em>.
</p>
</dd>
</dl>
<a name="BigFloat"></a>
<h2 class="chapter">4 BigFloat</h2>
<a name="Introduction-1"></a>
<h3 class="section">4.1 Introduction</h3>
<p>This extension adds the <code>BigFloat</code> primitive type. The
<code>BigFloat</code> 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>, <code>+/-Infinity</code>, <code>+0</code> and <code>-0</code>
are supported. The mantissa and exponent can have any bit length with
an implementation specific minimum and maximum.
</p>
<a name="Floating-point-rounding"></a>
<h3 class="section">4.2 Floating point rounding</h3>
<p>Each floating point operation operates with infinite precision and
then rounds the result according to the specified floating point
environment (<code>BigFloatEnv</code> object). The status flags of the
environment are also set according to the result of the operation.
</p>
<p>If no floating point environment is provided, the global floating
point environment is used.
</p>
<p>The rounding mode of the global floating point environment is always
<code>RNDN</code> (&ldquo;round to nearest with ties to even&rdquo;)<a name="DOCF1" href="#FOOT1"><sup>1</sup></a>. The status flags of the global environment cannot be
read<a name="DOCF2" href="#FOOT2"><sup>2</sup></a>. The precision of the global environment is
<code>BigFloatEnv.prec</code>. The number of exponent bits of the global
environment is <code>BigFloatEnv.expBits</code>. The global environment
subnormal flag is set to <code>true</code>.
</p>
<p>For example, <code>prec = 53</code> and <code> expBits = 11</code> exactly give
the same precision as the IEEE 754 64 bit floating point format. The
default precision is <code>prec = 113</code> and <code> expBits = 15</code> (IEEE
754 128 bit floating point format).
</p>
<p>The global floating point environment can only be modified temporarily
when calling a function (see <code>BigFloatEnv.setPrec</code>). Hence a
function can change the global floating point environment for its
callees but not for its caller.
</p>
<a name="Operators"></a>
<h3 class="section">4.3 Operators</h3>
<p>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.
</p>
<p><code>typeof</code> applied on a <code>BigFloat</code> returns <code>bigfloat</code>.
</p>
<p>BigFloat can be compared with all the other numeric types and the
result follows the expected mathematical relations.
</p>
<p>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</code> is false).
</p>
<a name="BigFloat-literals"></a>
<h3 class="section">4.4 BigFloat literals</h3>
<p>BigFloat literals are floating point numbers with a trailing <code>l</code>
suffix. BigFloat literals have an infinite precision. They are rounded
according to the global floating point environment when they are
evaluated.<a name="DOCF3" href="#FOOT3"><sup>3</sup></a>
</p>
<a name="Builtin-Object-changes"></a>
<h3 class="section">4.5 Builtin Object changes</h3>
<a name="BigFloat-function"></a>
<h4 class="subsection">4.5.1 <code>BigFloat</code> function</h4>
<p>The <code>BigFloat</code> 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.
</p>
<p><code>BigFloat</code> properties:
</p>
<dl compact="compact">
<dt><code>LN2</code></dt>
<dt><code>PI</code></dt>
<dd><p>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.
</p>
</dd>
<dt><code>MIN_VALUE</code></dt>
<dt><code>MAX_VALUE</code></dt>
<dt><code>EPSILON</code></dt>
<dd><p>Getter. Return the minimum, maximum and epsilon <code>BigFloat</code> values
(same definition as the corresponding <code>Number</code> constants).
</p>
</dd>
<dt><code>fpRound(a[, e])</code></dt>
<dd><p>Round the floating point number <code>a</code> according to the floating
point environment <code>e</code> or the global environment if <code>e</code> is
undefined.
</p>
</dd>
<dt><code>parseFloat(a[, radix[, e]])</code></dt>
<dd><p>Parse the string <code>a</code> as a floating point number in radix
<code>radix</code>. 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</code>
or the global environment if <code>e</code> is undefined.
</p>
</dd>
<dt><code>isFinite(a)</code></dt>
<dd><p>Return true if <code>a</code> is a finite bigfloat.
</p>
</dd>
<dt><code>isNaN(a)</code></dt>
<dd><p>Return true if <code>a</code> is a NaN bigfloat.
</p>
</dd>
<dt><code>add(a, b[, e])</code></dt>
<dt><code>sub(a, b[, e])</code></dt>
<dt><code>mul(a, b[, e])</code></dt>
<dt><code>div(a, b[, e])</code></dt>
<dd><p>Perform the specified floating point operation and round the floating
point number <code>a</code> according to the floating point environment
<code>e</code> or the global environment if <code>e</code> is undefined. If
<code>e</code> is specified, the floating point status flags are updated.
</p>
</dd>
<dt><code>floor(x)</code></dt>
<dt><code>ceil(x)</code></dt>
<dt><code>round(x)</code></dt>
<dt><code>trunc(x)</code></dt>
<dd><p>Round to an integer. No additional rounding is performed.
</p>
</dd>
<dt><code>abs(x)</code></dt>
<dd><p>Return the absolute value of x. No additional rounding is performed.
</p>
</dd>
<dt><code>fmod(x, y[, e])</code></dt>
<dt><code>remainder(x, y[, e])</code></dt>
<dd><p>Floating point remainder. The quotient is truncated to zero (fmod) or
to the nearest integer with ties to even (remainder). <code>e</code> is an
optional floating point environment.
</p>
</dd>
<dt><code>sqrt(x[, e])</code></dt>
<dd><p>Square root. Return a rounded floating point number. <code>e</code> is an
optional floating point environment.
</p>
</dd>
<dt><code>sin(x[, e])</code></dt>
<dt><code>cos(x[, e])</code></dt>
<dt><code>tan(x[, e])</code></dt>
<dt><code>asin(x[, e])</code></dt>
<dt><code>acos(x[, e])</code></dt>
<dt><code>atan(x[, e])</code></dt>
<dt><code>atan2(x, y[, e])</code></dt>
<dt><code>exp(x[, e])</code></dt>
<dt><code>log(x[, e])</code></dt>
<dt><code>pow(x, y[, e])</code></dt>
<dd><p>Transcendental operations. Return a rounded floating point
number. <code>e</code> is an optional floating point environment.
</p>
</dd>
</dl>
<a name="BigFloat_002eprototype"></a>
<h4 class="subsection">4.5.2 <code>BigFloat.prototype</code></h4>
<p>The following properties are modified:
</p>
<dl compact="compact">
<dt><code>valueOf()</code></dt>
<dd><p>Return the bigfloat primitive value corresponding to <code>this</code>.
</p>
</dd>
<dt><code>toString(radix)</code></dt>
<dd>
<p>For floating point numbers:
</p>
<ul>
<li> If the radix is a power of two, the conversion is done with infinite
precision.
</li><li> 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.
</li></ul>
<p>The exponent letter is <code>e</code> for base 10, <code>p</code> for bases 2, 8,
16 with a binary exponent and <code>@</code> for the other bases.
</p>
</dd>
<dt><code>toPrecision(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt>
<dt><code>toFixed(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt>
<dt><code>toExponential(p, rnd_mode = BigFloatEnv.RNDNA, radix = 10)</code></dt>
<dd><p>Same semantics as the corresponding <code>Number</code> functions with
BigFloats. There is no limit on the accepted precision <code>p</code>. The
rounding mode and radix can be optionally specified. The radix must be
between 2 and 36.
</p>
</dd>
</dl>
<a name="BigFloatEnv-constructor"></a>
<h4 class="subsection">4.5.3 <code>BigFloatEnv</code> constructor</h4>
<p>The <code>BigFloatEnv([p, [,rndMode]]</code> constructor cannot be invoked as a
function. The floating point environment contains:
</p>
<ul>
<li> the mantissa precision in bits
</li><li> the exponent size in bits assuming an IEEE 754 representation;
</li><li> the subnormal flag (if true, subnormal floating point numbers can
be generated by the floating point operations).
</li><li> the rounding mode
</li><li> 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()</code> or with the various status flag setters.
</li></ul>
<p><code>new BigFloatEnv([p, [,rndMode]]</code> 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</code>, the number of exponent bits is set to <code>expBitsMax</code> and the
subnormal flags is set to <code>false</code>. If <code>rndMode</code> is
<code>undefined</code>, the rounding mode is set to <code>RNDN</code>.
</p>
<p><code>BigFloatEnv</code> properties:
</p>
<dl compact="compact">
<dt><code>prec</code></dt>
<dd><p>Getter. Return the precision in bits of the global floating point
environment. The initial value is <code>113</code>.
</p>
</dd>
<dt><code>expBits</code></dt>
<dd><p>Getter. Return the exponent size in bits of the global floating point
environment assuming an IEEE 754 representation. The initial value is
<code>15</code>.
</p>
</dd>
<dt><code>setPrec(f, p[, e])</code></dt>
<dd><p>Set the precision of the global floating point environment to <code>p</code>
and the exponent size to <code>e</code> then call the function
<code>f</code>. Then the Float precision and exponent size are reset to
their precious value and the return value of <code>f</code> is returned (or
an exception is raised if <code>f</code> raised an exception). If <code>e</code>
is <code>undefined</code> it is set to <code>BigFloatEnv.expBitsMax</code>.
</p>
</dd>
<dt><code>precMin</code></dt>
<dd><p>Read-only integer. Return the minimum allowed precision. Must be at least 2.
</p>
</dd>
<dt><code>precMax</code></dt>
<dd><p>Read-only integer. Return the maximum allowed precision. Must be at least 113.
</p>
</dd>
<dt><code>expBitsMin</code></dt>
<dd><p>Read-only integer. Return the minimum allowed exponent size in
bits. Must be at least 3.
</p>
</dd>
<dt><code>expBitsMax</code></dt>
<dd><p>Read-only integer. Return the maximum allowed exponent size in
bits. Must be at least 15.
</p>
</dd>
<dt><code>RNDN</code></dt>
<dd><p>Read-only integer. Round to nearest, with ties to even rounding mode.
</p>
</dd>
<dt><code>RNDZ</code></dt>
<dd><p>Read-only integer. Round to zero rounding mode.
</p>
</dd>
<dt><code>RNDD</code></dt>
<dd><p>Read-only integer. Round to -Infinity rounding mode.
</p>
</dd>
<dt><code>RNDU</code></dt>
<dd><p>Read-only integer. Round to +Infinity rounding mode.
</p>
</dd>
<dt><code>RNDNA</code></dt>
<dd><p>Read-only integer. Round to nearest, with ties away from zero rounding mode.
</p>
</dd>
<dt><code>RNDA</code></dt>
<dd><p>Read-only integer. Round away from zero rounding mode.
</p>
</dd>
<dt><code>RNDF<a name="DOCF4" href="#FOOT4"><sup>4</sup></a></code></dt>
<dd><p>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.
</p>
</dd>
</dl>
<p><code>BigFloatEnv.prototype</code> properties:
</p>
<dl compact="compact">
<dt><code>prec</code></dt>
<dd><p>Getter and setter (Integer). Return or set the precision in bits.
</p>
</dd>
<dt><code>expBits</code></dt>
<dd><p>Getter and setter (Integer). Return or set the exponent size in bits
assuming an IEEE 754 representation.
</p>
</dd>
<dt><code>rndMode</code></dt>
<dd><p>Getter and setter (Integer). Return or set the rounding mode.
</p>
</dd>
<dt><code>subnormal</code></dt>
<dd><p>Getter and setter (Boolean). subnormal flag. It is false when
<code>expBits = expBitsMax</code>.
</p>
</dd>
<dt><code>clearStatus()</code></dt>
<dd><p>Clear the status flags.
</p>
</dd>
<dt><code>invalidOperation</code></dt>
<dt><code>divideByZero</code></dt>
<dt><code>overflow</code></dt>
<dt><code>underflow</code></dt>
<dt><code>inexact</code></dt>
<dd><p>Getter and setter (Boolean). Status flags.
</p>
</dd>
</dl>
<a name="BigDecimal"></a>
<h2 class="chapter">5 BigDecimal</h2>
<p>This extension adds the <code>BigDecimal</code> primitive type. The
<code>BigDecimal</code> type represents floating point numbers in base
10. It is inspired from the proposal available at
<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>.
</p>
<p>The <code>BigDecimal</code> floating point numbers are always normalized and
finite. There is no concept of <code>-0</code>, <code>Infinity</code> or
<code>NaN</code>. By default, all the computations are done with infinite
precision.
</p>
<a name="Operators-1"></a>
<h3 class="section">5.1 Operators</h3>
<p>The following builtin operators support BigDecimal:
</p>
<dl compact="compact">
<dt><code>+</code></dt>
<dt><code>-</code></dt>
<dt><code>*</code></dt>
<dd><p>Both operands must be BigDecimal. The result is computed with infinite
precision.
</p></dd>
<dt><code>%</code></dt>
<dd><p>Both operands must be BigDecimal. The result is computed with infinite
precision. A range error is throws in case of division by zero.
</p>
</dd>
<dt><code>/</code></dt>
<dd><p>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</code> to specify the rounding).
</p>
</dd>
<dt><code>**</code></dt>
<dd><p>Both operands must be BigDecimal. The exponent must be a positive
integer. The result is computed with infinite precision.
</p>
</dd>
<dt><code>===</code></dt>
<dd><p>When one of the operand is a BigDecimal, return true if both operands
are a BigDecimal and if they are equal.
</p>
</dd>
<dt><code>==</code></dt>
<dt><code>!=</code></dt>
<dt><code>&lt;=</code></dt>
<dt><code>&gt;=</code></dt>
<dt><code>&lt;</code></dt>
<dt><code>&gt;</code></dt>
<dd>
<p>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.
</p>
</dd>
</dl>
<a name="BigDecimal-literals"></a>
<h3 class="section">5.2 BigDecimal literals</h3>
<p>BigDecimal literals are decimal floating point numbers with a trailing
<code>m</code> suffix.
</p>
<a name="Builtin-Object-changes-1"></a>
<h3 class="section">5.3 Builtin Object changes</h3>
<a name="The-BigDecimal-function_002e"></a>
<h4 class="subsection">5.3.1 The <code>BigDecimal</code> function.</h4>
<p>It returns <code>0m</code> 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.
</p>
<a name="Properties-of-the-BigDecimal-object"></a>
<h4 class="subsection">5.3.2 Properties of the <code>BigDecimal</code> object</h4>
<dl compact="compact">
<dt><code>add(a, b[, e])</code></dt>
<dt><code>sub(a, b[, e])</code></dt>
<dt><code>mul(a, b[, e])</code></dt>
<dt><code>div(a, b[, e])</code></dt>
<dt><code>mod(a, b[, e])</code></dt>
<dt><code>sqrt(a, e)</code></dt>
<dt><code>round(a, e)</code></dt>
<dd><p>Perform the specified floating point operation and round the floating
point result according to the rounding object <code>e</code>. If the
rounding object is not present, the operation is executed with
infinite precision.
</p>
<p>For <code>div</code>, a <code>RangeError</code> 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.
</p>
<p>For <code>sqrt</code>, a range error is thrown if <code>a</code> is less than
zero.
</p>
<p>The rounding object must contain the following properties:
<code>roundingMode</code> is a string specifying the rounding mode
(<code>&quot;floor&quot;</code>, <code>&quot;ceiling&quot;</code>, <code>&quot;down&quot;</code>, <code>&quot;up&quot;</code>,
<code>&quot;half-even&quot;</code>, <code>&quot;half-up&quot;</code>). Either
<code>maximumSignificantDigits</code> or <code>maximumFractionDigits</code> must
be present to specify respectively the number of significant digits
(must be &gt;= 1) or the number of digits after the decimal point (must
be &gt;= 0).
</p>
</dd>
</dl>
<a name="Properties-of-the-BigDecimal_002eprototype-object"></a>
<h4 class="subsection">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</h4>
<dl compact="compact">
<dt><code>valueOf()</code></dt>
<dd><p>Return the bigdecimal primitive value corresponding to <code>this</code>.
</p>
</dd>
<dt><code>toString()</code></dt>
<dd><p>Convert <code>this</code> to a string with infinite precision in base 10.
</p>
</dd>
<dt><code>toPrecision(p, rnd_mode = &quot;half-up&quot;)</code></dt>
<dt><code>toFixed(p, rnd_mode = &quot;half-up&quot;)</code></dt>
<dt><code>toExponential(p, rnd_mode = &quot;half-up&quot;)</code></dt>
<dd><p>Convert the BigDecimal <code>this</code> to string with the specified
precision <code>p</code>. There is no limit on the accepted precision
<code>p</code>. The rounding mode can be optionally
specified. <code>toPrecision</code> outputs either in decimal fixed notation
or in decimal exponential notation with a <code>p</code> digits of
precision. <code>toExponential</code> outputs in decimal exponential
notation with <code>p</code> digits after the decimal point. <code>toFixed</code>
outputs in decimal notation with <code>p</code> digits after the decimal
point.
</p>
</dd>
</dl>
<a name="Math-mode"></a>
<h2 class="chapter">6 Math mode</h2>
<p>A new <em>math mode</em> is enabled with the <code>&quot;use math&quot;</code>
directive. It propagates the same way as the <em>strict mode</em>. 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.
</p>
<p>The following changes are made to the Javascript semantics:
</p>
<ul>
<li> Floating point literals (i.e. number with a decimal point or an exponent) are <code>BigFloat</code> by default (i.e. a <code>l</code> suffix is implied). Hence <code>typeof 1.0 === &quot;bigfloat&quot;</code>.
</li><li> Integer literals (i.e. numbers without a decimal point or an exponent) with or without the <code>n</code> suffix are <code>BigInt</code> 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</code>. Hence <code>typeof 1 === &quot;number &quot;</code>, <code>typeof 1n === &quot;number&quot;</code> but <code>typeof 9007199254740992 === &quot;bigint&quot; </code>.
</li><li> 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.
</li><li> 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.
</li><li> The <code>^</code> operator is an alias to the power operator (<code>**</code>).
</li><li> The power operator (both <code>^</code> and <code>**</code>) grammar is modified so that <code>-2^2</code> is allowed and yields <code>-4</code>.
</li><li> The logical xor operator is still available with the <code>^^</code> operator.
</li><li> The modulo operator (<code>%</code>) returns the Euclidian remainder (always positive) instead of the truncated remainder.
</li><li> The integer division operator can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>.
</li><li> The integer power operator with a non zero negative exponent can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>.
</li></ul>
<div class="footnote">
<hr>
<h4 class="footnotes-heading">Footnotes</h4>
<h3><a name="FOOT1" href="#DOCF1">(1)</a></h3>
<p>The
rationale is that the rounding mode changes must always be
explicit.</p>
<h3><a name="FOOT2" href="#DOCF2">(2)</a></h3>
<p>The rationale is to avoid side effects for the built-in
operators.</p>
<h3><a name="FOOT3" href="#DOCF3">(3)</a></h3>
<p>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.</p>
<h3><a name="FOOT4" href="#DOCF4">(4)</a></h3>
<p>Could be removed in case a deterministic behavior for floating point operations is required.</p>
</div>
<hr>
</body>
</html>

Binary file not shown.

View file

@ -289,7 +289,7 @@ precision.
Otherwise, the number is rounded to nearest with ties to even using Otherwise, the number is rounded to nearest with ties to even using
the global precision. It is then converted to string using the minimum 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 number of digits so that its conversion back to a floating point using
the global precision and round to nearest gives the same number. the global precision and round to nearest gives the same number.
@end itemize @end itemize

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -160,7 +160,7 @@ Options are:
@table @code @table @code
@item -c @item -c
Only output bytecode in a C file. The default is to output an executable file. Only output bytecode in a C file. The default is to output an executable file.
@item -e @item -e
Output @code{main()} and bytecode in a C file. The default is to output an Output @code{main()} and bytecode in a C file. The default is to output an
executable file. executable file.
@item -o output @item -o output
@ -379,7 +379,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)
@ -487,7 +489,7 @@ optional properties:
to be UTF-8 encoded. to be UTF-8 encoded.
@item full @item full
Boolean (default = false). If true, return the an object contains Boolean (default = false). If true, return the an object contains
the properties @code{response} (response content), the properties @code{response} (response content),
@code{responseHeaders} (headers separated by CRLF), @code{status} @code{responseHeaders} (headers separated by CRLF), @code{status}
@ -594,7 +596,7 @@ Available exports:
Open a file. Return a handle or < 0 if error. Open a file. Return a handle or < 0 if error.
@item O_RDONLY @item O_RDONLY
@item O_WRONLY @item O_WRONLY
@item O_RDWR @item O_RDWR
@item O_APPEND @item O_APPEND
@item O_CREAT @item O_CREAT
@ -732,7 +734,7 @@ object containing optional parameters:
terminated. In this case, @code{exec} return the exit code if positive terminated. In this case, @code{exec} return the exit code if positive
or the negated signal number if the process was interrupted by a or the negated signal number if the process was interrupted by a
signal. If false, do not block and return the process id of the child. signal. If false, do not block and return the process id of the child.
@item usePath @item usePath
Boolean (default = true). If true, the file is searched in the Boolean (default = true). If true, the file is searched in the
@code{PATH} environment variable. @code{PATH} environment variable.
@ -756,7 +758,7 @@ object containing optional parameters:
@item uid @item uid
Integer. If present, the process uid with @code{setuid}. Integer. If present, the process uid with @code{setuid}.
@item gid @item gid
Integer. If present, the process gid with @code{setgid}. Integer. If present, the process gid with @code{setgid}.
@end table @end table
@ -827,7 +829,7 @@ The worker instances have the following properties:
@table @code @table @code
@item postMessage(msg) @item postMessage(msg)
Send a message to the corresponding worker. @code{msg} is cloned in Send a message to the corresponding worker. @code{msg} is cloned in
the destination worker using an algorithm similar to the @code{HTML} the destination worker using an algorithm similar to the @code{HTML}
structured clone algorithm. @code{SharedArrayBuffer} are shared structured clone algorithm. @code{SharedArrayBuffer} are shared
@ -970,7 +972,7 @@ The compiler generates bytecode directly with no intermediate
representation such as a parse tree, hence it is very fast. Several representation such as a parse tree, hence it is very fast. Several
optimizations passes are done over the generated bytecode. optimizations passes are done over the generated bytecode.
A stack-based bytecode was chosen because it is simple and generates A stack-based bytecode was chosen because it is simple and generates
compact code. compact code.
For each function, the maximum stack size is computed at compile time so that For each function, the maximum stack size is computed at compile time so that

View file

@ -1,6 +1,6 @@
/* /*
* QuickJS: Example of C module * QuickJS: Example of C module
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy

View file

@ -1,9 +0,0 @@
function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}

View file

@ -1,6 +0,0 @@
/* example of JS module */
const fibModule = require("./fib_require.js");
console.log("Hello World");
console.log("fib(10)=", fib(10));

View file

@ -11,7 +11,7 @@ function calc_pi(prec) {
const CHUD_C = 640320m; const CHUD_C = 640320m;
const CHUD_C3 = 10939058860032000m; /* C^3/24 */ const CHUD_C3 = 10939058860032000m; /* C^3/24 */
const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(C/12)*3 */ const CHUD_DIGITS_PER_TERM = 14.18164746272548; /* log10(C/12)*3 */
/* return [P, Q, G] */ /* return [P, Q, G] */
function chud_bs(a, b, need_G) { function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1; var c, P, Q, G, P1, Q1, G1, P2, Q2, G2, b1;

View file

@ -11,7 +11,7 @@ function calc_pi() {
const CHUD_C = 640320n; const CHUD_C = 640320n;
const CHUD_C3 = 10939058860032000n; /* C^3/24 */ const CHUD_C3 = 10939058860032000n; /* C^3/24 */
const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */ const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
/* return [P, Q, G] */ /* return [P, Q, G] */
function chud_bs(a, b, need_G) { function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2; var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;

View file

@ -54,7 +54,7 @@ function calc_pi(prec) {
const CHUD_C = 640320n; const CHUD_C = 640320n;
const CHUD_C3 = 10939058860032000n; /* C^3/24 */ const CHUD_C3 = 10939058860032000n; /* C^3/24 */
const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */ const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
/* return [P, Q, G] */ /* return [P, Q, G] */
function chud_bs(a, b, need_G) { function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2; var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;

View file

@ -1,6 +1,6 @@
/* /*
* QuickJS: Example of C module with a class * QuickJS: Example of C module with a class
* *
* Copyright (c) 2019 Fabrice Bellard * Copyright (c) 2019 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -49,7 +49,7 @@ static JSValue js_point_ctor(JSContext *ctx,
JSPointData *s; JSPointData *s;
JSValue obj = JS_UNDEFINED; JSValue obj = JS_UNDEFINED;
JSValue proto; JSValue proto;
s = js_mallocz(ctx, sizeof(*s)); s = js_mallocz(ctx, sizeof(*s));
if (!s) if (!s)
return JS_EXCEPTION; return JS_EXCEPTION;
@ -112,7 +112,7 @@ static JSValue js_point_norm(JSContext *ctx, JSValueConst this_val,
static JSClassDef js_point_class = { static JSClassDef js_point_class = {
"Point", "Point",
.finalizer = js_point_finalizer, .finalizer = js_point_finalizer,
}; };
static const JSCFunctionListEntry js_point_proto_funcs[] = { static const JSCFunctionListEntry js_point_proto_funcs[] = {
JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0), JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0),
@ -123,19 +123,19 @@ static const JSCFunctionListEntry js_point_proto_funcs[] = {
static int js_point_init(JSContext *ctx, JSModuleDef *m) static int js_point_init(JSContext *ctx, JSModuleDef *m)
{ {
JSValue point_proto, point_class; JSValue point_proto, point_class;
/* create the Point class */ /* create the Point class */
JS_NewClassID(&js_point_class_id); JS_NewClassID(&js_point_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class); JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class);
point_proto = JS_NewObject(ctx); point_proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, point_proto, js_point_proto_funcs, countof(js_point_proto_funcs)); JS_SetPropertyFunctionList(ctx, point_proto, js_point_proto_funcs, countof(js_point_proto_funcs));
point_class = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0); point_class = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0);
/* set proto.constructor and ctor.prototype */ /* set proto.constructor and ctor.prototype */
JS_SetConstructor(ctx, point_class, point_proto); JS_SetConstructor(ctx, point_class, point_proto);
JS_SetClassProto(ctx, js_point_class_id, point_proto); JS_SetClassProto(ctx, js_point_class_id, point_proto);
JS_SetModuleExport(ctx, m, "Point", point_class); JS_SetModuleExport(ctx, m, "Point", point_class);
return 0; return 0;
} }

542
libbf.c

File diff suppressed because it is too large Load diff

20
libbf.h
View file

@ -1,6 +1,6 @@
/* /*
* Tiny arbitrary precision floating point library * Tiny arbitrary precision floating point library
* *
* Copyright (c) 2017-2021 Fabrice Bellard * Copyright (c) 2017-2021 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -171,7 +171,7 @@ static inline bf_flags_t bf_set_exp_bits(int n)
#define BF_ST_UNDERFLOW (1 << 3) #define BF_ST_UNDERFLOW (1 << 3)
#define BF_ST_INEXACT (1 << 4) #define BF_ST_INEXACT (1 << 4)
/* indicate that a memory allocation error occured. NaN is returned */ /* indicate that a memory allocation error occured. NaN is returned */
#define BF_ST_MEM_ERROR (1 << 5) #define BF_ST_MEM_ERROR (1 << 5)
#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ #define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */
@ -284,7 +284,7 @@ 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_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(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_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, int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec,
bf_flags_t flags); bf_flags_t flags);
int bf_mul_2exp(bf_t *r, slimb_t e, 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); int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags);
@ -341,12 +341,12 @@ int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix,
/* fractional format: prec digits after the decimal point rounded with /* fractional format: prec digits after the decimal point rounded with
(flags & BF_RND_MASK) */ (flags & BF_RND_MASK) */
#define BF_FTOA_FORMAT_FRAC (1 << 16) #define BF_FTOA_FORMAT_FRAC (1 << 16)
/* free format: /* free format:
For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum
number of digits to represent 'a'. The precision and the rounding number of digits to represent 'a'. The precision and the rounding
mode are ignored. mode are ignored.
For the non binary radices with bf_ftoa(): use as many digits as For the non binary radices with bf_ftoa(): use as many digits as
necessary so that bf_atof() return the same number when using necessary so that bf_atof() return the same number when using
precision 'prec', rounding to nearest and the subnormal precision 'prec', rounding to nearest and the subnormal
@ -373,7 +373,7 @@ char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec,
bf_flags_t flags); bf_flags_t flags);
/* modulo 2^n instead of saturation. NaN and infinity return 0 */ /* modulo 2^n instead of saturation. NaN and infinity return 0 */
#define BF_GET_INT_MOD (1 << 0) #define BF_GET_INT_MOD (1 << 0)
int bf_get_int32(int *pres, const bf_t *a, int flags); 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_int64(int64_t *pres, const bf_t *a, int flags);
int bf_get_uint64(uint64_t *pres, const bf_t *a); int bf_get_uint64(uint64_t *pres, const bf_t *a);
@ -387,10 +387,10 @@ 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); 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, slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
int is_ceil1); int is_ceil1);
int mp_mul(bf_context_t *s, limb_t *result, int mp_mul(bf_context_t *s, limb_t *result,
const limb_t *op1, limb_t op1_size, const limb_t *op1, limb_t op1_size,
const limb_t *op2, limb_t op2_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 mp_add(limb_t *res, const limb_t *op1, const limb_t *op2,
limb_t n, limb_t carry); limb_t n, limb_t carry);
limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); 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_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n);

View file

@ -1,6 +1,6 @@
/* /*
* Regular Expression Engine * Regular Expression Engine
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy

View file

@ -1,6 +1,6 @@
/* /*
* Regular Expression Engine * Regular Expression Engine
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -30,6 +30,7 @@
#include "cutils.h" #include "cutils.h"
#include "libregexp.h" #include "libregexp.h"
#include "libunicode.h"
/* /*
TODO: TODO:
@ -66,7 +67,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 +101,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 +142,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 +161,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,
@ -196,7 +172,7 @@ static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c)
BOOL invert; BOOL invert;
const uint16_t *c_pt; const uint16_t *c_pt;
int len, i; int len, i;
invert = c & 1; invert = c & 1;
c_pt = char_range_table[c >> 1]; c_pt = char_range_table[c >> 1];
len = *c_pt++; len = *c_pt++;
@ -221,19 +197,19 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
{ {
int pos, len, opcode, bc_len, re_flags, i; int pos, len, opcode, bc_len, re_flags, i;
uint32_t val; uint32_t val;
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);
@ -392,7 +368,7 @@ static int parse_digits(const uint8_t **pp, BOOL allow_overflow)
const uint8_t *p; const uint8_t *p;
uint64_t v; uint64_t v;
int c; int c;
p = *pp; p = *pp;
v = 0; v = 0;
for(;;) { for(;;) {
@ -464,7 +440,7 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
{ {
int h, n, i; int h, n, i;
uint32_t c1; uint32_t c1;
if (*p == '{' && allow_utf16) { if (*p == '{' && allow_utf16) {
p++; p++;
c = 0; c = 0;
@ -494,7 +470,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 +481,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);
} }
} }
} }
@ -658,7 +634,7 @@ static int get_class_atom(REParseState *s, CharRange *cr,
const uint8_t *p; const uint8_t *p;
uint32_t c; uint32_t c;
int ret; int ret;
p = *pp; p = *pp;
c = *p; c = *p;
@ -696,10 +672,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' */
@ -710,7 +686,7 @@ static int get_class_atom(REParseState *s, CharRange *cr,
#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 +696,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 +725,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");
} }
@ -766,7 +742,7 @@ static int re_emit_range(REParseState *s, const CharRange *cr)
{ {
int len, i; int len, i;
uint32_t high; uint32_t high;
len = (unsigned)cr->len / 2; len = (unsigned)cr->len / 2;
if (len >= 65535) if (len >= 65535)
return re_parse_error(s, "too many ranges"); return re_parse_error(s, "too many ranges");
@ -807,15 +783,17 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
CharRange cr_s, *cr = &cr_s; CharRange cr_s, *cr = &cr_s;
CharRange cr1_s, *cr1 = &cr1_s; CharRange cr1_s, *cr1 = &cr1_s;
BOOL invert; BOOL invert;
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 +803,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 +815,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 +844,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) {
@ -895,7 +873,7 @@ static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len)
int pos, opcode, len; int pos, opcode, len;
uint32_t val; uint32_t val;
BOOL ret; BOOL ret;
ret = TRUE; ret = TRUE;
pos = 0; pos = 0;
while (pos < bc_buf_len) { while (pos < bc_buf_len) {
@ -934,7 +912,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;
@ -948,7 +926,7 @@ static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len)
{ {
int pos, opcode, len, count; int pos, opcode, len, count;
uint32_t val; uint32_t val;
count = 0; count = 0;
pos = 0; pos = 0;
while (pos < bc_buf_len) { while (pos < bc_buf_len) {
@ -1003,10 +981,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;
} }
} }
@ -1113,10 +1091,11 @@ static int find_group_name(REParseState *s, const char *name)
const char *p, *buf_end; const char *p, *buf_end;
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);
@ -1136,7 +1115,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
int c, last_atom_start, quant_min, quant_max, last_capture_count; int c, last_atom_start, quant_min, quant_max, last_capture_count;
BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead; BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead;
CharRange cr_s, *cr = &cr_s; CharRange cr_s, *cr = &cr_s;
last_atom_start = -1; last_atom_start = -1;
last_capture_count = 0; last_capture_count = 0;
p = s->buf_ptr; p = s->buf_ptr;
@ -1161,7 +1140,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 +1192,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;
} }
@ -1259,15 +1238,15 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
capture_index = s->capture_count++; capture_index = s->capture_count++;
re_emit_op_u8(s, REOP_save_start + is_backward_dir, re_emit_op_u8(s, REOP_save_start + is_backward_dir,
capture_index); capture_index);
s->buf_ptr = p; s->buf_ptr = p;
if (re_parse_disjunction(s, is_backward_dir)) if (re_parse_disjunction(s, is_backward_dir))
return -1; return -1;
p = s->buf_ptr; p = s->buf_ptr;
re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir, re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir,
capture_index); capture_index);
if (re_parse_expect(s, &p, ')')) if (re_parse_expect(s, &p, ')'))
return -1; return -1;
} }
@ -1283,13 +1262,13 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
{ {
const uint8_t *p1; const uint8_t *p1;
int dummy_res; int dummy_res;
p1 = p; p1 = p;
if (p1[2] != '<') { if (p1[2] != '<') {
/* 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 +1276,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 +1287,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 +1299,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");
} }
@ -1336,13 +1315,13 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
goto normal_char; goto normal_char;
case '1': case '2': case '3': case '4': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '5': case '6': case '7': case '8':
case '9': case '9':
{ {
const uint8_t *q = ++p; const uint8_t *q = ++p;
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 +1363,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 +1385,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 +1421,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 +1440,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;
@ -1480,7 +1459,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
} }
if (greedy) { if (greedy) {
int len, pos; int len, pos;
if (quant_max > 0) { if (quant_max > 0) {
/* specific optimization for simple quantifiers */ /* specific optimization for simple quantifiers */
if (dbuf_error(&s->byte_code)) if (dbuf_error(&s->byte_code))
@ -1489,7 +1468,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
s->byte_code.size - last_atom_start); s->byte_code.size - last_atom_start);
if (len > 0) { if (len > 0) {
re_emit_op(s, REOP_match); re_emit_op(s, REOP_match);
if (dbuf_insert(&s->byte_code, last_atom_start, 17)) if (dbuf_insert(&s->byte_code, last_atom_start, 17))
goto out_of_memory; goto out_of_memory;
pos = last_atom_start; pos = last_atom_start;
@ -1506,7 +1485,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
goto done; goto done;
} }
} }
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 /* the spec tells that if there is no advance when
@ -1518,7 +1497,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
} else { } else {
add_zero_advance_check = FALSE; add_zero_advance_check = FALSE;
} }
{ {
int len, pos; int len, pos;
len = s->byte_code.size - last_atom_start; len = s->byte_code.size - last_atom_start;
@ -1544,7 +1523,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
len + 5 * has_goto + add_zero_advance_check * 2); len + 5 * has_goto + add_zero_advance_check * 2);
if (add_zero_advance_check) { if (add_zero_advance_check) {
s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos; s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos;
re_emit_op(s, REOP_check_advance); re_emit_op(s, REOP_check_advance);
} }
if (has_goto) if (has_goto)
re_emit_goto(s, REOP_goto, last_atom_start); re_emit_goto(s, REOP_goto, last_atom_start);
@ -1560,7 +1539,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
pos += 4; pos += 4;
if (add_zero_advance_check) { if (add_zero_advance_check) {
s->byte_code.buf[pos++] = REOP_push_char_pos; s->byte_code.buf[pos++] = REOP_push_char_pos;
re_emit_op(s, REOP_check_advance); re_emit_op(s, REOP_check_advance);
} }
re_emit_goto(s, REOP_loop, last_atom_start + 5); re_emit_goto(s, REOP_loop, last_atom_start + 5);
re_emit_op(s, REOP_drop); re_emit_op(s, REOP_drop);
@ -1655,14 +1634,14 @@ static int re_parse_alternative(REParseState *s, BOOL is_backward_dir)
} }
return 0; return 0;
} }
static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)
{ {
int start, len, pos; int start, len, pos;
if (lre_check_stack_overflow(s->opaque, 0)) if (lre_check_stack_overflow(s->opaque, 0))
return re_parse_error(s, "stack overflow"); return re_parse_error(s, "stack overflow");
start = s->byte_code.size; start = s->byte_code.size;
if (re_parse_alternative(s, is_backward_dir)) if (re_parse_alternative(s, is_backward_dir))
return -1; return -1;
@ -1682,7 +1661,7 @@ static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)
if (re_parse_alternative(s, is_backward_dir)) if (re_parse_alternative(s, is_backward_dir))
return -1; return -1;
/* patch the goto */ /* patch the goto */
len = s->byte_code.size - (pos + 4); len = s->byte_code.size - (pos + 4);
put_u32(s->byte_code.buf + pos, len); put_u32(s->byte_code.buf + pos, len);
@ -1695,7 +1674,7 @@ static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len)
{ {
int stack_size, stack_size_max, pos, opcode, len; int stack_size, stack_size_max, pos, opcode, len;
uint32_t val; uint32_t val;
stack_size = 0; stack_size = 0;
stack_size_max = 0; stack_size_max = 0;
bc_buf += RE_HEADER_LEN; bc_buf += RE_HEADER_LEN;
@ -1746,21 +1725,21 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
REParseState s_s, *s = &s_s; REParseState s_s, *s = &s_s;
int stack_size; int stack_size;
BOOL is_sticky; BOOL is_sticky;
memset(s, 0, sizeof(*s)); memset(s, 0, sizeof(*s));
s->opaque = opaque; s->opaque = opaque;
s->buf_ptr = (const uint8_t *)buf; s->buf_ptr = (const uint8_t *)buf;
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);
s->capture_count = 1; s->capture_count = 1;
s->total_capture_count = -1; s->total_capture_count = -1;
s->has_named_captures = -1; s->has_named_captures = -1;
dbuf_init2(&s->byte_code, opaque, lre_realloc); dbuf_init2(&s->byte_code, opaque, lre_realloc);
dbuf_init2(&s->group_names, opaque, lre_realloc); dbuf_init2(&s->group_names, opaque, lre_realloc);
@ -1768,7 +1747,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */ dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */
dbuf_putc(&s->byte_code, 0); /* stack size */ dbuf_putc(&s->byte_code, 0); /* stack size */
dbuf_put_u32(&s->byte_code, 0); /* bytecode length */ dbuf_put_u32(&s->byte_code, 0); /* bytecode length */
if (!is_sticky) { if (!is_sticky) {
/* iterate thru all positions (about the same as .*?( ... ) ) /* iterate thru all positions (about the same as .*?( ... ) )
. We do it without an explicit loop so that lock step . We do it without an explicit loop so that lock step
@ -1790,7 +1769,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
} }
re_emit_op_u8(s, REOP_save_end, 0); re_emit_op_u8(s, REOP_save_end, 0);
re_emit_op(s, REOP_match); re_emit_op(s, REOP_match);
if (*s->buf_ptr != '\0') { if (*s->buf_ptr != '\0') {
@ -1802,16 +1781,17 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
re_parse_out_of_memory(s); re_parse_out_of_memory(s);
goto error; goto error;
} }
stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size); stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size);
if (stack_size < 0) { if (stack_size < 0) {
re_parse_error(s, "too many imbricated quantifiers"); re_parse_error(s, "too many imbricated quantifiers");
goto error; goto error;
} }
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)) {
@ -1819,11 +1799,11 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
s->byte_code.buf[RE_HEADER_FLAGS] |= LRE_FLAG_NAMED_GROUPS; s->byte_code.buf[RE_HEADER_FLAGS] |= LRE_FLAG_NAMED_GROUPS;
} }
dbuf_free(&s->group_names); dbuf_free(&s->group_names);
#ifdef DUMP_REOP #ifdef DUMP_REOP
lre_dump_bytecode(s->byte_code.buf, s->byte_code.size); lre_dump_bytecode(s->byte_code.buf, s->byte_code.size);
#endif #endif
error_msg[0] = '\0'; error_msg[0] = '\0';
*plen = s->byte_code.size; *plen = s->byte_code.size;
return s->byte_code.buf; return s->byte_code.buf;
@ -1842,93 +1822,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)
@ -1954,12 +1927,12 @@ typedef struct {
const uint8_t *cbuf; const uint8_t *cbuf;
const uint8_t *cbuf_end; const uint8_t *cbuf_end;
/* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */ /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */
int cbuf_type; int cbuf_type;
int capture_count; int capture_count;
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;
void *opaque; /* used for stack overflow check */ void *opaque; /* used for stack overflow check */
size_t state_size; size_t state_size;
@ -2016,7 +1989,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
int cbuf_type; int cbuf_type;
uint32_t val, c; uint32_t val, c;
const uint8_t *cbuf_end; const uint8_t *cbuf_end;
cbuf_type = s->cbuf_type; cbuf_type = s->cbuf_type;
cbuf_end = s->cbuf_end; cbuf_end = s->cbuf_end;
@ -2068,7 +2041,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 +2076,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;
@ -2114,7 +2087,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
case REOP_split_next_first: case REOP_split_next_first:
{ {
const uint8_t *pc1; const uint8_t *pc1;
val = get_u32(pc); val = get_u32(pc);
pc += 4; pc += 4;
if (opcode == REOP_split_next_first) { if (opcode == REOP_split_next_first) {
@ -2140,7 +2113,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
if (ret < 0) if (ret < 0)
return -1; return -1;
break; break;
case REOP_goto: case REOP_goto:
val = get_u32(pc); val = get_u32(pc);
pc += 4 + (int)val; pc += 4 + (int)val;
@ -2150,7 +2123,7 @@ 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_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 +2132,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:
@ -2225,14 +2198,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))
@ -2244,7 +2217,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
{ {
const uint8_t *cptr1, *cptr1_end, *cptr1_start; const uint8_t *cptr1, *cptr1_end, *cptr1_start;
uint32_t c1, c2; uint32_t c1, c2;
val = *pc++; val = *pc++;
if (val >= s->capture_count) if (val >= s->capture_count)
goto no_match; goto no_match;
@ -2257,11 +2230,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 +2244,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;
@ -2287,14 +2260,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
{ {
int n; int n;
uint32_t low, high, idx_min, idx_max, idx; uint32_t low, high, idx_min, idx_max, idx;
n = get_u16(pc); /* n must be >= 1 */ n = get_u16(pc); /* n must be >= 1 */
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);
@ -2327,14 +2300,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
{ {
int n; int n;
uint32_t low, high, idx_min, idx_max, idx; uint32_t low, high, idx_min, idx_max, idx;
n = get_u16(pc); /* n must be >= 1 */ n = get_u16(pc); /* n must be >= 1 */
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 +2337,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:
{ {
@ -2372,14 +2345,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
size_t q; size_t q;
intptr_t res; intptr_t res;
const uint8_t *pc1; const uint8_t *pc1;
next_pos = get_u32(pc); next_pos = get_u32(pc);
quant_min = get_u32(pc + 4); quant_min = get_u32(pc + 4);
quant_max = get_u32(pc + 8); quant_max = get_u32(pc + 8);
pc += 16; pc += 16;
pc1 = pc; pc1 = pc;
pc += (int)next_pos; pc += (int)next_pos;
q = 0; q = 0;
for(;;) { for(;;) {
res = lre_exec_backtrack(s, capture, stack, stack_len, res = lre_exec_backtrack(s, capture, stack, stack_len,
@ -2422,17 +2395,17 @@ int lre_exec(uint8_t **capture,
REExecContext s_s, *s = &s_s; REExecContext s_s, *s = &s_s;
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->opaque = opaque; s->opaque = opaque;
@ -2442,7 +2415,7 @@ int lre_exec(uint8_t **capture,
s->state_stack = NULL; s->state_stack = NULL;
s->state_stack_len = 0; s->state_stack_len = 0;
s->state_stack_size = 0; s->state_stack_size = 0;
for(i = 0; i < s->capture_count * 2; i++) for(i = 0; i < s->capture_count * 2; i++)
capture[i] = NULL; capture[i] = NULL;
alloca_size = s->stack_size_max * sizeof(stack_buf[0]); alloca_size = s->stack_size_max * sizeof(stack_buf[0]);
@ -2470,8 +2443,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,27 +2461,28 @@ 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);
printf("ret=%d\n", ret); printf("ret=%d\n", ret);
if (ret == 1) { if (ret == 1) {

View file

@ -1,6 +1,6 @@
/* /*
* Regular Expression Engine * Regular Expression Engine
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -25,19 +25,15 @@
#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 */
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,
@ -51,43 +47,9 @@ 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);
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 */

View file

@ -189,9 +189,13 @@ static const uint8_t unicode_prop_Cased1_table[196] = {
}; };
static const uint8_t unicode_prop_Cased1_index[21] = { static const uint8_t unicode_prop_Cased1_index[21] = {
0xb9, 0x02, 0xe0, 0xc0, 0x1d, 0x20, 0xe5, 0x2c, 0xb9, 0x02, 0xe0, // 002B9 at 39
0x20, 0xb1, 0x07, 0x21, 0xc1, 0xd6, 0x21, 0x4a, 0xc0, 0x1d, 0x20, // 01DC0 at 65
0xf1, 0x01, 0x8a, 0xf1, 0x01, 0xe5, 0x2c, 0x20, // 02CE5 at 97
0xb1, 0x07, 0x21, // 107B1 at 129
0xc1, 0xd6, 0x21, // 1D6C1 at 161
0x4a, 0xf1, 0x01, // 1F14A at 192
0x8a, 0xf1, 0x01, // 1F18A at 224 (upper bound)
}; };
static const uint8_t unicode_prop_Case_Ignorable_table[737] = { static const uint8_t unicode_prop_Case_Ignorable_table[737] = {
@ -291,15 +295,29 @@ static const uint8_t unicode_prop_Case_Ignorable_table[737] = {
}; };
static const uint8_t unicode_prop_Case_Ignorable_index[69] = { static const uint8_t unicode_prop_Case_Ignorable_index[69] = {
0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, 0xbe, 0x05, 0x00, // 005BE at 32
0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f, 0xfe, 0x07, 0x00, // 007FE at 64
0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20, 0x52, 0x0a, 0xa0, // 00A52 at 101
0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0, 0xc1, 0x0b, 0x00, // 00BC1 at 128
0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56, 0x82, 0x0d, 0x00, // 00D82 at 160
0xfe, 0x20, 0xb1, 0x07, 0x01, 0x75, 0x10, 0x01, 0x3f, 0x10, 0x80, // 0103F at 196
0xeb, 0x12, 0x21, 0x41, 0x16, 0x01, 0x5c, 0x1a, 0xd4, 0x17, 0x40, // 017D4 at 226
0x01, 0x43, 0x1f, 0x01, 0x2e, 0xcf, 0x41, 0x25, 0xcf, 0x1a, 0x20, // 01ACF at 257
0xe0, 0x01, 0xf0, 0x01, 0x0e, 0xf5, 0x1c, 0x00, // 01CF5 at 288
0x80, 0x20, 0x00, // 02080 at 320
0x16, 0xa0, 0x00, // 0A016 at 352
0xc6, 0xa8, 0x00, // 0A8C6 at 384
0xc2, 0xaa, 0x60, // 0AAC2 at 419
0x56, 0xfe, 0x20, // 0FE56 at 449
0xb1, 0x07, 0x01, // 107B1 at 480
0x75, 0x10, 0x01, // 11075 at 512
0xeb, 0x12, 0x21, // 112EB at 545
0x41, 0x16, 0x01, // 11641 at 576
0x5c, 0x1a, 0x01, // 11A5C at 608
0x43, 0x1f, 0x01, // 11F43 at 640
0x2e, 0xcf, 0x41, // 1CF2E at 674
0x25, 0xe0, 0x01, // 1E025 at 704
0xf0, 0x01, 0x0e, // E01F0 at 736 (upper bound)
}; };
static const uint8_t unicode_prop_ID_Start_table[1100] = { static const uint8_t unicode_prop_ID_Start_table[1100] = {
@ -444,20 +462,41 @@ static const uint8_t unicode_prop_ID_Start_table[1100] = {
}; };
static const uint8_t unicode_prop_ID_Start_index[105] = { static const uint8_t unicode_prop_ID_Start_index[105] = {
0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09, 0xf6, 0x03, 0x20, // 003F6 at 33
0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b, 0xa6, 0x07, 0x00, // 007A6 at 64
0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00, 0xa9, 0x09, 0x20, // 009A9 at 97
0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d, 0xb1, 0x0a, 0x00, // 00AB1 at 128
0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00, 0xba, 0x0b, 0x20, // 00BBA at 161
0x32, 0x00, 0xda, 0xa7, 0x00, 0x4c, 0xaa, 0x20, 0x3b, 0x0d, 0x20, // 00D3B at 193
0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02, 0xc7, 0x0e, 0x20, // 00EC7 at 225
0x21, 0x96, 0x05, 0x01, 0xf3, 0x08, 0x01, 0xb3, 0x49, 0x12, 0x00, // 01249 at 256
0x0c, 0x21, 0x73, 0x11, 0x61, 0x34, 0x13, 0x01, 0x9b, 0x16, 0x00, // 0169B at 288
0x1b, 0x17, 0x21, 0x8a, 0x1a, 0x01, 0x34, 0x1f, 0xac, 0x19, 0x00, // 019AC at 320
0x21, 0xbf, 0x6a, 0x01, 0x23, 0xb1, 0xa1, 0xad, 0xc0, 0x1d, 0x80, // 01DC0 at 356
0xd4, 0x01, 0x6f, 0xd7, 0x01, 0xff, 0xe7, 0x61, 0x80, 0x20, 0x20, // 02080 at 385
0x5e, 0xee, 0x01, 0xe1, 0xeb, 0x22, 0xb0, 0x23, 0x70, 0x2d, 0x00, // 02D70 at 416
0x03, 0x00, 0x32, 0x00, // 03200 at 448
0xda, 0xa7, 0x00, // 0A7DA at 480
0x4c, 0xaa, 0x20, // 0AA4C at 513
0xc7, 0xd7, 0x20, // 0D7C7 at 545
0xfc, 0xfd, 0x20, // 0FDFC at 577
0x9d, 0x02, 0x21, // 1029D at 609
0x96, 0x05, 0x01, // 10596 at 640
0xf3, 0x08, 0x01, // 108F3 at 672
0xb3, 0x0c, 0x21, // 10CB3 at 705
0x73, 0x11, 0x61, // 11173 at 739
0x34, 0x13, 0x01, // 11334 at 768
0x1b, 0x17, 0x21, // 1171B at 801
0x8a, 0x1a, 0x01, // 11A8A at 832
0x34, 0x1f, 0x21, // 11F34 at 865
0xbf, 0x6a, 0x01, // 16ABF at 896
0x23, 0xb1, 0xa1, // 1B123 at 933
0xad, 0xd4, 0x01, // 1D4AD at 960
0x6f, 0xd7, 0x01, // 1D76F at 992
0xff, 0xe7, 0x61, // 1E7FF at 1027
0x5e, 0xee, 0x01, // 1EE5E at 1056
0xe1, 0xeb, 0x22, // 2EBE1 at 1089
0xb0, 0x23, 0x03, // 323B0 at 1120 (upper bound)
}; };
static const uint8_t unicode_prop_ID_Continue1_table[660] = { static const uint8_t unicode_prop_ID_Continue1_table[660] = {
@ -547,14 +586,27 @@ static const uint8_t unicode_prop_ID_Continue1_table[660] = {
}; };
static const uint8_t unicode_prop_ID_Continue1_index[63] = { static const uint8_t unicode_prop_ID_Continue1_index[63] = {
0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a, 0xfa, 0x06, 0x00, // 006FA at 32
0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7, 0x70, 0x09, 0x00, // 00970 at 64
0x0f, 0x20, 0xea, 0x17, 0x40, 0x05, 0x1b, 0x00, 0xf0, 0x0a, 0x40, // 00AF0 at 98
0x41, 0x20, 0x00, 0x0c, 0xa8, 0x80, 0x37, 0xaa, 0x57, 0x0c, 0x00, // 00C57 at 128
0x20, 0x50, 0xfe, 0x20, 0x3a, 0x0d, 0x21, 0x74, 0xf0, 0x0d, 0x60, // 00DF0 at 163
0x11, 0x01, 0x5a, 0x14, 0x21, 0x44, 0x19, 0x81, 0xc7, 0x0f, 0x20, // 00FC7 at 193
0x5a, 0x1d, 0xa1, 0xf5, 0x6a, 0x21, 0x45, 0xd2, 0xea, 0x17, 0x40, // 017EA at 226
0x41, 0xaf, 0xe2, 0x21, 0xf0, 0x01, 0x0e, 0x05, 0x1b, 0x00, // 01B05 at 256
0x41, 0x20, 0x00, // 02041 at 288
0x0c, 0xa8, 0x80, // 0A80C at 324
0x37, 0xaa, 0x20, // 0AA37 at 353
0x50, 0xfe, 0x20, // 0FE50 at 385
0x3a, 0x0d, 0x21, // 10D3A at 417
0x74, 0x11, 0x01, // 11174 at 448
0x5a, 0x14, 0x21, // 1145A at 481
0x44, 0x19, 0x81, // 11944 at 516
0x5a, 0x1d, 0xa1, // 11D5A at 549
0xf5, 0x6a, 0x21, // 16AF5 at 577
0x45, 0xd2, 0x41, // 1D245 at 610
0xaf, 0xe2, 0x21, // 1E2AF at 641
0xf0, 0x01, 0x0e, // E01F0 at 672 (upper bound)
}; };
#ifdef CONFIG_ALL_UNICODE #ifdef CONFIG_ALL_UNICODE
@ -676,17 +728,35 @@ static const uint8_t unicode_cc_table[899] = {
}; };
static const uint8_t unicode_cc_index[87] = { static const uint8_t unicode_cc_index[87] = {
0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, 0x4d, 0x03, 0x00, // 0034D at 32
0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c, 0x97, 0x05, 0x20, // 00597 at 65
0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00, 0xc6, 0x05, 0x00, // 005C6 at 96
0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10, 0xe7, 0x06, 0x00, // 006E7 at 128
0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3, 0x45, 0x07, 0x00, // 00745 at 160
0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00, 0x9c, 0x08, 0x00, // 0089C at 192
0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab, 0x4d, 0x09, 0x00, // 0094D at 224
0x00, 0x39, 0x0a, 0x01, 0x51, 0x0f, 0x01, 0x73, 0x3c, 0x0b, 0x00, // 00B3C at 256
0x11, 0x01, 0x75, 0x13, 0x01, 0x2b, 0x17, 0x21, 0x3d, 0x0d, 0x00, // 00D3D at 288
0x3f, 0x1c, 0x21, 0x9e, 0xbc, 0x21, 0x08, 0xe0, 0x36, 0x0f, 0x00, // 00F36 at 320
0x01, 0x44, 0xe9, 0x01, 0x4b, 0xe9, 0x01, 0x38, 0x10, 0x20, // 01038 at 353
0x3a, 0x19, 0x00, // 0193A at 384
0xcb, 0x1a, 0x20, // 01ACB at 417
0xd3, 0x1c, 0x00, // 01CD3 at 448
0xcf, 0x1d, 0x00, // 01DCF at 480
0xe2, 0x20, 0x00, // 020E2 at 512
0x2e, 0x30, 0x20, // 0302E at 545
0x2b, 0xa9, 0x20, // 0A92B at 577
0xed, 0xab, 0x00, // 0ABED at 608
0x39, 0x0a, 0x01, // 10A39 at 640
0x51, 0x0f, 0x01, // 10F51 at 672
0x73, 0x11, 0x01, // 11173 at 704
0x75, 0x13, 0x01, // 11375 at 736
0x2b, 0x17, 0x21, // 1172B at 769
0x3f, 0x1c, 0x21, // 11C3F at 801
0x9e, 0xbc, 0x21, // 1BC9E at 833
0x08, 0xe0, 0x01, // 1E008 at 864
0x44, 0xe9, 0x01, // 1E944 at 896
0x4b, 0xe9, 0x01, // 1E94B at 928 (upper bound)
}; };
static const uint32_t unicode_decomp_table1[699] = { static const uint32_t unicode_decomp_table1[699] = {
@ -4484,3 +4554,4 @@ static const uint16_t unicode_prop_len_table[] = {
}; };
#endif /* CONFIG_ALL_UNICODE */ #endif /* CONFIG_ALL_UNICODE */
/* 62 tables / 32261 bytes, 5 index / 345 bytes */

View file

@ -1,6 +1,6 @@
/* /*
* Unicode utilities * Unicode utilities
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -149,9 +149,9 @@ static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_
} }
/* conv_type: /* conv_type:
0 = to upper 0 = to upper
1 = to lower 1 = to lower
2 = case folding (= to lower with modifications) 2 = case folding (= to lower with modifications)
*/ */
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
{ {
@ -168,7 +168,7 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
} else { } else {
uint32_t v, code, len; uint32_t v, code, len;
int idx, idx_min, idx_max; int idx, idx_min, idx_max;
idx_min = 0; idx_min = 0;
idx_max = countof(case_conv_table1) - 1; idx_max = countof(case_conv_table1) - 1;
while (idx_min <= idx_max) { while (idx_min <= idx_max) {
@ -240,7 +240,7 @@ int lre_canonicalize(uint32_t c, BOOL is_unicode)
} else { } else {
uint32_t v, code, len; uint32_t v, code, len;
int idx, idx_min, idx_max; int idx, idx_min, idx_max;
idx_min = 0; idx_min = 0;
idx_max = countof(case_conv_table1) - 1; idx_max = countof(case_conv_table1) - 1;
while (idx_min <= idx_max) { while (idx_min <= idx_max) {
@ -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
@ -311,12 +307,20 @@ static BOOL lre_is_in_table(uint32_t c, const uint8_t *table,
uint32_t code, b, bit; uint32_t code, b, bit;
int pos; int pos;
const uint8_t *p; const uint8_t *p;
pos = get_index_pos(&code, c, index_table, index_table_len); pos = get_index_pos(&code, c, index_table, index_table_len);
if (pos < 0) if (pos < 0)
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) {
@ -344,7 +348,7 @@ BOOL lre_is_cased(uint32_t c)
{ {
uint32_t v, code, len; uint32_t v, code, len;
int idx, idx_min, idx_max; int idx, idx_min, idx_max;
idx_min = 0; idx_min = 0;
idx_max = countof(case_conv_table1) - 1; idx_max = countof(case_conv_table1) - 1;
while (idx_min <= idx_max) { while (idx_min <= idx_max) {
@ -403,7 +407,7 @@ int cr_realloc(CharRange *cr, int size)
{ {
int new_size; int new_size;
uint32_t *new_buf; uint32_t *new_buf;
if (size > cr->size) { if (size > cr->size) {
new_size = max_int(size, cr->size * 3 / 2); new_size = max_int(size, cr->size * 3 / 2);
new_buf = cr->realloc_func(cr->mem_opaque, cr->points, new_buf = cr->realloc_func(cr->mem_opaque, cr->points,
@ -430,7 +434,7 @@ static void cr_compress(CharRange *cr)
{ {
int i, j, k, len; int i, j, k, len;
uint32_t *pt; uint32_t *pt;
pt = cr->points; pt = cr->points;
len = cr->len; len = cr->len;
i = 0; i = 0;
@ -460,7 +464,7 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
{ {
int a_idx, b_idx, is_in; int a_idx, b_idx, is_in;
uint32_t v; uint32_t v;
a_idx = 0; a_idx = 0;
b_idx = 0; b_idx = 0;
for(;;) { for(;;) {
@ -761,7 +765,7 @@ static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1)
{ {
uint32_t v, type, is_compat, code, len; uint32_t v, type, is_compat, code, len;
int idx_min, idx_max, idx; int idx_min, idx_max, idx;
idx_min = 0; idx_min = 0;
idx_max = countof(unicode_decomp_table1) - 1; idx_max = countof(unicode_decomp_table1) - 1;
while (idx_min <= idx_max) { while (idx_min <= idx_max) {
@ -791,7 +795,7 @@ static int unicode_compose_pair(uint32_t c0, uint32_t c1)
uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; uint32_t code, len, type, v, idx1, d_idx, d_offset, ch;
int idx_min, idx_max, idx, d; int idx_min, idx_max, idx, d;
uint32_t pair[2]; uint32_t pair[2];
idx_min = 0; idx_min = 0;
idx_max = countof(unicode_comp_table) - 1; idx_max = countof(unicode_comp_table) - 1;
while (idx_min <= idx_max) { while (idx_min <= idx_max) {
@ -827,12 +831,19 @@ static int unicode_get_cc(uint32_t c)
uint32_t code, n, type, cc, c1, b; uint32_t code, n, type, cc, c1, b;
int pos; int pos;
const uint8_t *p; const uint8_t *p;
pos = get_index_pos(&code, c, pos = get_index_pos(&code, c,
unicode_cc_index, sizeof(unicode_cc_index) / 3); unicode_cc_index, sizeof(unicode_cc_index) / 3);
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;
@ -876,7 +887,7 @@ static int unicode_get_cc(uint32_t c)
static void sort_cc(int *buf, int len) static void sort_cc(int *buf, int len)
{ {
int i, j, k, cc, cc1, start, ch1; int i, j, k, cc, cc1, start, ch1;
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
cc = unicode_get_cc(buf[i]); cc = unicode_get_cc(buf[i]);
if (cc != 0) { if (cc != 0) {
@ -915,7 +926,7 @@ static void to_nfd_rec(DynBuf *dbuf,
uint32_t c, v; uint32_t c, v;
int i, l; int i, l;
uint32_t res[UNICODE_DECOMP_LEN_MAX]; uint32_t res[UNICODE_DECOMP_LEN_MAX];
for(i = 0; i < src_len; i++) { for(i = 0; i < src_len; i++) {
c = src[i]; c = src[i];
if (c >= 0xac00 && c < 0xd7a4) { if (c >= 0xac00 && c < 0xd7a4) {
@ -960,7 +971,7 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len;
BOOL is_compat; BOOL is_compat;
DynBuf dbuf_s, *dbuf = &dbuf_s; DynBuf dbuf_s, *dbuf = &dbuf_s;
is_compat = n_type >> 1; is_compat = n_type >> 1;
dbuf_init2(dbuf, opaque, realloc_func); dbuf_init2(dbuf, opaque, realloc_func);
@ -988,15 +999,15 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
} }
buf = (int *)dbuf->buf; buf = (int *)dbuf->buf;
buf_len = dbuf->size / sizeof(int); buf_len = dbuf->size / sizeof(int);
sort_cc(buf, buf_len); sort_cc(buf, buf_len);
if (buf_len <= 1 || (n_type & 1) != 0) { if (buf_len <= 1 || (n_type & 1) != 0) {
/* NFD / NFKD */ /* NFD / NFKD */
*pdst = (uint32_t *)buf; *pdst = (uint32_t *)buf;
return buf_len; return buf_len;
} }
i = 1; i = 1;
out_len = 1; out_len = 1;
while (i < buf_len) { while (i < buf_len) {
@ -1033,7 +1044,7 @@ static int unicode_find_name(const char *name_table, const char *name)
const char *p, *r; const char *p, *r;
int pos; int pos;
size_t name_len, len; size_t name_len, len;
p = name_table; p = name_table;
pos = 0; pos = 0;
name_len = strlen(name); name_len = strlen(name);
@ -1066,13 +1077,13 @@ int unicode_script(CharRange *cr,
CharRange cr1_s, *cr1; CharRange cr1_s, *cr1;
CharRange cr2_s, *cr2 = &cr2_s; CharRange cr2_s, *cr2 = &cr2_s;
BOOL is_common; BOOL is_common;
script_idx = unicode_find_name(unicode_script_name_table, script_name); script_idx = unicode_find_name(unicode_script_name_table, script_name);
if (script_idx < 0) if (script_idx < 0)
return -2; return -2;
/* Note: we remove the "Unknown" Script */ /* Note: we remove the "Unknown" Script */
script_idx += UNICODE_SCRIPT_Unknown + 1; script_idx += UNICODE_SCRIPT_Unknown + 1;
is_common = (script_idx == UNICODE_SCRIPT_Common || is_common = (script_idx == UNICODE_SCRIPT_Common ||
script_idx == UNICODE_SCRIPT_Inherited); script_idx == UNICODE_SCRIPT_Inherited);
if (is_ext) { if (is_ext) {
@ -1185,6 +1196,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 +1258,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++;
@ -1350,7 +1378,7 @@ static int point_cmp(const void *p1, const void *p2, void *arg)
static void cr_sort_and_remove_overlap(CharRange *cr) static void cr_sort_and_remove_overlap(CharRange *cr)
{ {
uint32_t start, end, start1, end1, i, j; uint32_t start, end, start1, end1, i, j;
/* the resulting ranges are not necessarily sorted and may overlap */ /* the resulting ranges are not necessarily sorted and may overlap */
rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL); rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL);
j = 0; j = 0;
@ -1389,7 +1417,7 @@ int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode)
{ {
CharRange cr_inter, cr_mask, cr_result, cr_sub; CharRange cr_inter, cr_mask, cr_result, cr_sub;
uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d; 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_mask, cr->mem_opaque, cr->realloc_func);
cr_init(&cr_inter, 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_result, cr->mem_opaque, cr->realloc_func);
@ -1404,7 +1432,7 @@ int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode)
goto fail; goto fail;
if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER))
goto fail; goto fail;
/* cr_inter = cr & cr_mask */ /* cr_inter = cr & cr_mask */
/* cr_sub = cr & ~cr_mask */ /* cr_sub = cr & ~cr_mask */
@ -1488,7 +1516,7 @@ static int unicode_prop_ops(CharRange *cr, ...)
CharRange stack[POP_STACK_LEN_MAX]; CharRange stack[POP_STACK_LEN_MAX];
int stack_len, op, ret, i; int stack_len, op, ret, i;
uint32_t a; uint32_t a;
va_start(ap, cr); va_start(ap, cr);
stack_len = 0; stack_len = 0;
for(;;) { for(;;) {
@ -1574,7 +1602,7 @@ int unicode_general_category(CharRange *cr, const char *gc_name)
{ {
int gc_idx; int gc_idx;
uint32_t gc_mask; uint32_t gc_mask;
gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); gc_idx = unicode_find_name(unicode_gc_name_table, gc_name);
if (gc_idx < 0) if (gc_idx < 0)
return -2; return -2;
@ -1592,7 +1620,7 @@ 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)
{ {
int prop_idx, ret; int prop_idx, ret;
prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); prop_idx = unicode_find_name(unicode_prop_name_table, prop_name);
if (prop_idx < 0) if (prop_idx < 0)
return -2; return -2;
@ -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

@ -1,6 +1,6 @@
/* /*
* Unicode utilities * Unicode utilities
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -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 */

4
list.h
View file

@ -1,6 +1,6 @@
/* /*
* Linux klist like system * Linux klist like system
* *
* Copyright (c) 2016-2017 Fabrice Bellard * Copyright (c) 2016-2017 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -45,7 +45,7 @@ static inline void init_list_head(struct list_head *head)
} }
/* insert 'el' between 'prev' and 'next' */ /* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el, static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next) struct list_head *prev, struct list_head *next)
{ {
prev->next = el; prev->next = el;

90
qjs.c
View file

@ -1,7 +1,6 @@
/* /*
* QuickJS stand alone interpreter * QuickJS stand alone interpreter
* *
* Copyright (c) 2024 Sneed Group
* Copyright (c) 2017-2021 Fabrice Bellard * Copyright (c) 2017-2021 Fabrice Bellard
* Copyright (c) 2017-2021 Charlie Gordon * Copyright (c) 2017-2021 Charlie Gordon
* *
@ -37,6 +36,8 @@
#include <malloc/malloc.h> #include <malloc/malloc.h>
#elif defined(__linux__) #elif defined(__linux__)
#include <malloc.h> #include <malloc.h>
#elif defined(__FreeBSD__)
#include <malloc_np.h>
#endif #endif
#include "cutils.h" #include "cutils.h"
@ -65,6 +66,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);
} }
@ -83,7 +85,7 @@ static int eval_file(JSContext *ctx, const char *filename, int module)
uint8_t *buf; uint8_t *buf;
int ret, eval_flags; int ret, eval_flags;
size_t buf_len; size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename); buf = js_load_file(ctx, &buf_len, filename);
if (!buf) { if (!buf) {
perror(filename); perror(filename);
@ -98,34 +100,6 @@ static int eval_file(JSContext *ctx, const char *filename, int module)
eval_flags = JS_EVAL_TYPE_MODULE; eval_flags = JS_EVAL_TYPE_MODULE;
else else
eval_flags = JS_EVAL_TYPE_GLOBAL; eval_flags = JS_EVAL_TYPE_GLOBAL;
//POLYFILLS FOR QJS FILES BEGIN
const char *pf = "globalThis.global = globalThis;\n"
"global.console.error = console.log\n"
"global.console.warn = console.log\n"
"globalThis.breakFunction = () => { throw new Error('Function Break'); };\n"
"\n"
"if (typeof os !== 'undefined') {\n"
" globalThis.sleep = os.sleep;\n"
" async function setTimeout2(func, ms) {globalThis.clearTimeout = false; await sleep(ms); if (!clearTimeout) { func(); } }\n"
" globalThis.setTimeout = setTimeout2\n"
"} else {\n"
" console.error('os is not defined.');\n"
"}\n"
"\n"
"if (typeof std !== 'undefined') {\n"
" globalThis.urlGet = std.urlGet;\n"
" globalThis.loadFile = std.loadFile;\n"
" globalThis.printf = console.log;\n"
" globalThis.evalFile = std.loadScript;\n"
" globalThis.require = std.loadScript;\n"
" globalThis.getURL = std.urlGet;\n"
"} else {\n"
" console.error('std is not defined.');\n"
"}\n";
eval_buf(ctx, pf, strlen(pf), "<input>", JS_EVAL_TYPE_MODULE);
ret = eval_buf(ctx, buf, buf_len, filename, eval_flags); ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
js_free(ctx, buf); js_free(ctx, buf);
return ret; return ret;
@ -334,7 +308,7 @@ int main(int argc, char **argv)
int trace_memory = 0; int trace_memory = 0;
int empty_run = 0; int empty_run = 0;
int module = -1; int module = -1;
//int load_std = 0; //we don't need this anymore, we ignore this and always load std libs int load_std = 0;
int dump_unhandled_promise_rejection = 0; int dump_unhandled_promise_rejection = 0;
size_t memory_limit = 0; size_t memory_limit = 0;
char *include_list[32]; char *include_list[32];
@ -343,7 +317,7 @@ int main(int argc, char **argv)
int load_jscalc; int load_jscalc;
#endif #endif
size_t stack_size = 0; size_t stack_size = 0;
#ifdef CONFIG_BIGNUM #ifdef CONFIG_BIGNUM
/* load jscalc runtime if invoked as 'qjscalc' */ /* load jscalc runtime if invoked as 'qjscalc' */
{ {
@ -355,7 +329,7 @@ int main(int argc, char **argv)
load_jscalc = !strcmp(exename, "qjscalc"); load_jscalc = !strcmp(exename, "qjscalc");
} }
#endif #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;
@ -425,10 +399,10 @@ int main(int argc, char **argv)
trace_memory++; trace_memory++;
continue; continue;
} }
//if (!strcmp(longopt, "std")) { if (!strcmp(longopt, "std")) {
//load_std = 1; load_std = 1;
//continue; continue;
//} }
if (!strcmp(longopt, "unhandled-rejection")) { if (!strcmp(longopt, "unhandled-rejection")) {
dump_unhandled_promise_rejection = 1; dump_unhandled_promise_rejection = 1;
continue; continue;
@ -506,7 +480,7 @@ int main(int argc, char **argv)
JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker,
NULL); NULL);
} }
if (!empty_run) { if (!empty_run) {
#ifdef CONFIG_BIGNUM #ifdef CONFIG_BIGNUM
if (load_jscalc) { if (load_jscalc) {
@ -515,15 +489,14 @@ int main(int argc, char **argv)
#endif #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 all code */ /* make 'std' and 'os' visible to non module code */
//if (load_std) { if (load_std) {
const char *str = "import * as std from 'std';\n" const char *str = "import * as std from 'std';\n"
"import * as os from 'os';\n" "import * as os from 'os';\n"
"globalThis.std = std;\n" "globalThis.std = std;\n"
"globalThis.os = os;\n"; "globalThis.os = os;\n";
eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE); eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
//} }
for(i = 0; i < include_count; i++) { for(i = 0; i < include_count; i++) {
if (eval_file(ctx, include_list[i], module)) if (eval_file(ctx, include_list[i], module))
@ -546,38 +519,9 @@ int main(int argc, char **argv)
if (interactive) { if (interactive) {
js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0); js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
} }
//POLYFILLS FOR QJS REPL BEGIN
const char *pf = "globalThis.global = globalThis;\n"
"global.console.error = console.log\n"
"global.console.warn = console.log\n"
"globalThis.breakFunction = () => { throw new Error('Function Break'); };\n"
"\n"
"if (typeof os !== 'undefined') {\n"
" globalThis.sleep = os.sleep;\n"
" async function setTimeout2(func, ms) {globalThis.clearTimeout = false; await sleep(ms); if (!clearTimeout) { func(); } }\n"
" globalThis.setTimeout = setTimeout2\n"
"} else {\n"
" console.error('os is not defined.');\n"
"}\n"
"\n"
"if (typeof std !== 'undefined') {\n"
" globalThis.urlGet = std.urlGet;\n"
" globalThis.loadFile = std.loadFile;\n"
" globalThis.printf = console.log;\n"
" globalThis.evalFile = std.loadScript;\n"
" globalThis.require = std.loadScript;\n"
" globalThis.getURL = std.urlGet;\n"
"} else {\n"
" console.error('std is not defined.');\n"
"}\n";
eval_buf(ctx, pf, strlen(pf), "<input>", JS_EVAL_TYPE_MODULE);
js_std_loop(ctx); js_std_loop(ctx);
} }
if (dump_memory) { if (dump_memory) {
JSMemoryUsage stats; JSMemoryUsage stats;
JS_ComputeMemoryUsage(rt, &stats); JS_ComputeMemoryUsage(rt, &stats);

52
qjsc.c
View file

@ -1,6 +1,6 @@
/* /*
* QuickJS command line compiler * QuickJS command line compiler
* *
* Copyright (c) 2018-2021 Fabrice Bellard * Copyright (c) 2018-2021 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -129,7 +129,7 @@ static void get_c_name(char *buf, size_t buf_size, const char *file)
size_t len, i; size_t len, i;
int c; int c;
char *q; char *q;
p = strrchr(file, '/'); p = strrchr(file, '/');
if (!p) if (!p)
p = file; p = file;
@ -187,8 +187,8 @@ static void output_object_code(JSContext *ctx,
} }
namelist_add(&cname_list, c_name, NULL, load_only); namelist_add(&cname_list, c_name, NULL, load_only);
fprintf(fo, "const uint32_t %s_size = %u;\n\n", fprintf(fo, "const uint32_t %s_size = %u;\n\n",
c_name, (unsigned int)out_buf_len); c_name, (unsigned int)out_buf_len);
fprintf(fo, "const uint8_t %s[%u] = {\n", fprintf(fo, "const uint8_t %s[%u] = {\n",
c_name, (unsigned int)out_buf_len); c_name, (unsigned int)out_buf_len);
@ -251,14 +251,14 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
uint8_t *buf; uint8_t *buf;
JSValue func_val; JSValue func_val;
char cname[1024]; char cname[1024];
buf = js_load_file(ctx, &buf_len, module_name); buf = js_load_file(ctx, &buf_len, module_name);
if (!buf) { if (!buf) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s'", JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
module_name); module_name);
return NULL; return NULL;
} }
/* compile the module */ /* compile the module */
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
@ -270,7 +270,7 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
find_unique_cname(cname, sizeof(cname)); find_unique_cname(cname, sizeof(cname));
} }
output_object_code(ctx, outfile, func_val, cname, TRUE); output_object_code(ctx, outfile, func_val, cname, TRUE);
/* the module is already referenced, so we must free it */ /* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val); m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val); JS_FreeValue(ctx, func_val);
@ -288,7 +288,7 @@ static void compile_file(JSContext *ctx, FILE *fo,
int eval_flags; int eval_flags;
JSValue obj; JSValue obj;
size_t buf_len; size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename); buf = js_load_file(ctx, &buf_len, filename);
if (!buf) { if (!buf) {
fprintf(stderr, "Could not load '%s'\n", filename); fprintf(stderr, "Could not load '%s'\n", filename);
@ -383,7 +383,7 @@ int exec_cmd(char **argv)
if (pid == 0) { if (pid == 0) {
execvp(argv[0], argv); execvp(argv[0], argv);
exit(1); exit(1);
} }
for(;;) { for(;;) {
ret = waitpid(pid, &status, 0); ret = waitpid(pid, &status, 0);
@ -401,7 +401,7 @@ static int output_executable(const char *out_filename, const char *cfilename,
char libjsname[1024]; char libjsname[1024];
char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p; char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p;
int ret; int ret;
/* get the directory of the executable */ /* get the directory of the executable */
pstrcpy(exe_dir, sizeof(exe_dir), exename); pstrcpy(exe_dir, sizeof(exe_dir), exename);
p = strrchr(exe_dir, '/'); p = strrchr(exe_dir, '/');
@ -421,10 +421,10 @@ static int output_executable(const char *out_filename, const char *cfilename,
snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX); snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX);
snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX); snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX);
} }
lto_suffix = ""; lto_suffix = "";
bn_suffix = ""; bn_suffix = "";
arg = argv; arg = argv;
*arg++ = CONFIG_CC; *arg++ = CONFIG_CC;
*arg++ = "-O2"; *arg++ = "-O2";
@ -452,13 +452,13 @@ static int output_executable(const char *out_filename, const char *cfilename,
*arg++ = "-ldl"; *arg++ = "-ldl";
*arg++ = "-lpthread"; *arg++ = "-lpthread";
*arg = NULL; *arg = NULL;
if (verbose) { if (verbose) {
for(arg = argv; *arg != NULL; arg++) for(arg = argv; *arg != NULL; arg++)
printf("%s ", *arg); printf("%s ", *arg);
printf("\n"); printf("\n");
} }
ret = exec_cmd((char **)argv); ret = exec_cmd((char **)argv);
unlink(cfilename); unlink(cfilename);
return ret; return ret;
@ -496,7 +496,7 @@ int main(int argc, char **argv)
BOOL bignum_ext = FALSE; BOOL bignum_ext = FALSE;
#endif #endif
namelist_t dynamic_module_list; namelist_t dynamic_module_list;
out_filename = NULL; out_filename = NULL;
output_type = OUTPUT_EXECUTABLE; output_type = OUTPUT_EXECUTABLE;
cname = NULL; cname = NULL;
@ -507,7 +507,7 @@ int main(int argc, char **argv)
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));
/* add system modules */ /* add system modules */
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);
@ -620,14 +620,14 @@ int main(int argc, char **argv)
} else { } else {
pstrcpy(cfilename, sizeof(cfilename), out_filename); pstrcpy(cfilename, sizeof(cfilename), out_filename);
} }
fo = fopen(cfilename, "w"); fo = fopen(cfilename, "w");
if (!fo) { if (!fo) {
perror(cfilename); perror(cfilename);
exit(1); exit(1);
} }
outfile = fo; outfile = fo;
rt = JS_NewRuntime(); rt = JS_NewRuntime();
ctx = JS_NewContext(rt); ctx = JS_NewContext(rt);
#ifdef CONFIG_BIGNUM #ifdef CONFIG_BIGNUM
@ -638,14 +638,14 @@ int main(int argc, char **argv)
JS_EnableBignumExt(ctx, TRUE); JS_EnableBignumExt(ctx, TRUE);
} }
#endif #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);
fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n" fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
"\n" "\n"
); );
if (output_type != OUTPUT_C) { if (output_type != OUTPUT_C) {
fprintf(fo, "#include \"quickjs-libc.h\"\n" fprintf(fo, "#include \"quickjs-libc.h\"\n"
"\n" "\n"
@ -669,7 +669,7 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
} }
if (output_type != OUTPUT_C) { if (output_type != OUTPUT_C) {
fprintf(fo, fprintf(fo,
"static JSContext *JS_NewCustomContext(JSRuntime *rt)\n" "static JSContext *JS_NewCustomContext(JSRuntime *rt)\n"
@ -700,7 +700,7 @@ int main(int argc, char **argv)
for(i = 0; i < init_module_list.count; i++) { for(i = 0; i < init_module_list.count; i++) {
namelist_entry_t *e = &init_module_list.array[i]; namelist_entry_t *e = &init_module_list.array[i];
/* initialize the static C modules */ /* initialize the static C modules */
fprintf(fo, fprintf(fo,
" {\n" " {\n"
" extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n" " extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n"
@ -718,19 +718,19 @@ int main(int argc, char **argv)
fprintf(fo, fprintf(fo,
" return ctx;\n" " return ctx;\n"
"}\n\n"); "}\n\n");
fputs(main_c_template1, fo); fputs(main_c_template1, fo);
if (stack_size != 0) { if (stack_size != 0) {
fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n", fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n",
(unsigned int)stack_size); (unsigned int)stack_size);
} }
/* add the module loader if necessary */ /* add the module loader if necessary */
if (feature_bitmap & (1 << FE_MODULE_LOADER)) { if (feature_bitmap & (1 << FE_MODULE_LOADER)) {
fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n"); fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n");
} }
fprintf(fo, fprintf(fo,
" ctx = JS_NewCustomContext(rt);\n" " ctx = JS_NewCustomContext(rt);\n"
" js_std_add_helpers(ctx, argc, argv);\n"); " js_std_add_helpers(ctx, argc, argv);\n");
@ -744,7 +744,7 @@ int main(int argc, char **argv)
} }
fputs(main_c_template2, fo); fputs(main_c_template2, fo);
} }
JS_FreeContext(ctx); JS_FreeContext(ctx);
JS_FreeRuntime(rt); JS_FreeRuntime(rt);

View file

@ -1,6 +1,6 @@
/* /*
* QuickJS Javascript Calculator * QuickJS Javascript Calculator
* *
* Copyright (c) 2017-2020 Fabrice Bellard * Copyright (c) 2017-2020 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -30,7 +30,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
global.Integer = global.BigInt; global.Integer = global.BigInt;
global.Float = global.BigFloat; global.Float = global.BigFloat;
global.algebraicMode = true; global.algebraicMode = true;
/* add non enumerable properties */ /* add non enumerable properties */
function add_props(obj, props) { function add_props(obj, props) {
var i, val, prop, tab, desc; var i, val, prop, tab, desc;
@ -85,9 +85,9 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
} }
} }
proto[Symbol.operatorSet] = proto[Symbol.operatorSet] =
Operators.create.call(null, ...new_op_list); Operators.create.call(null, ...new_op_list);
} }
/* Integer */ /* Integer */
function generic_pow(a, b) { function generic_pow(a, b) {
@ -121,7 +121,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
} }
return r; return r;
} }
var small_primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499 ]; var small_primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499 ];
function miller_rabin_test(n, t) { function miller_rabin_test(n, t) {
@ -184,7 +184,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
} }
} }
}); });
add_props(Integer, { add_props(Integer, {
isInteger(a) { isInteger(a) {
/* integers are represented either as bigint or as number */ /* integers are represented either as bigint or as number */
@ -305,7 +305,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
r.push(-1); r.push(-1);
n = -n; n = -n;
} }
while ((n % 2) == 0) { while ((n % 2) == 0) {
n >>= 1; n >>= 1;
r.push(2); r.push(2);
@ -394,7 +394,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
a = Integer.tdiv(a, d); a = Integer.tdiv(a, d);
b = Integer.tdiv(b, d); b = Integer.tdiv(b, d);
} }
/* the fractions are normalized with den > 0 */ /* the fractions are normalized with den > 0 */
if (b < 0) { if (b < 0) {
a = -a; a = -a;
@ -476,7 +476,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
else else
return a < b; return a < b;
} }
operators_set(Fraction.prototype, operators_set(Fraction.prototype,
{ {
"+": fraction_add, "+": fraction_add,
@ -518,7 +518,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
"==": float_eq, "==": float_eq,
"<": float_lt, "<": float_lt,
}); });
add_props(Fraction, { add_props(Fraction, {
/* (internal use) simplify 'a' to an integer when possible */ /* (internal use) simplify 'a' to an integer when possible */
toFraction(a, b) { toFraction(a, b) {
@ -602,11 +602,11 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
} }
}, },
}); });
/* Float */ /* Float */
var const_tab = []; var const_tab = [];
/* we cache the constants for small precisions */ /* we cache the constants for small precisions */
function get_const(n) { function get_const(n) {
var t, c, p; var t, c, p;
@ -631,7 +631,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
return c; return c;
} }
} }
add_props(Float, { add_props(Float, {
isFloat(a) { isFloat(a) {
return typeof a === "number" || typeof a === "bigfloat"; return typeof a === "number" || typeof a === "bigfloat";
@ -700,9 +700,9 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
} }
}, },
}); });
/* Complex */ /* Complex */
Complex = function Complex(re, im) Complex = function Complex(re, im)
{ {
var obj; var obj;
@ -719,7 +719,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
return obj; return obj;
} }
function complex_add(a, b) { function complex_add(a, b) {
a = Complex(a); a = Complex(a);
b = Complex(b); b = Complex(b);
@ -746,7 +746,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
b = Complex(b); b = Complex(b);
return a.re == b.re && a.im == b.im; return a.re == b.re && a.im == b.im;
} }
operators_set(Complex.prototype, operators_set(Complex.prototype,
{ {
"+": complex_add, "+": complex_add,
@ -772,7 +772,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
"**": generic_pow, "**": generic_pow,
"==": complex_eq, "==": complex_eq,
}); });
add_props(Complex, { add_props(Complex, {
/* simplify to real number when possible */ /* simplify to real number when possible */
toComplex(re, im) { toComplex(re, im) {
@ -851,7 +851,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
obj.mod = m; obj.mod = m;
return obj; return obj;
}; };
function mod_add(a, b) { function mod_add(a, b) {
if (!(a instanceof Mod)) { if (!(a instanceof Mod)) {
return Mod(a + b.res, b.mod); return Mod(a + b.res, b.mod);
@ -947,7 +947,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
return true; return true;
return false; return false;
} }
Polynomial = function Polynomial(a) Polynomial = function Polynomial(a)
{ {
if (new.target) if (new.target)
@ -1017,7 +1017,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
/* trivial zero */ /* trivial zero */
if (p[0] == 0) if (p[0] == 0)
return 0.0; return 0.0;
p1 = p.deriv(); p1 = p.deriv();
p2 = p1.deriv(); p2 = p1.deriv();
el = 0.0; el = 0.0;
@ -1041,7 +1041,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
} }
el = e; el = e;
zl = z; zl = z;
z1 = p1.apply(z); z1 = p1.apply(z);
z2 = p2.apply(z); z2 = p2.apply(z);
t0 = (d - 1) * z1; t0 = (d - 1) * z1;
@ -1052,7 +1052,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
d2 = z1 - t0; d2 = z1 - t0;
if (norm2(d2) > norm2(d1)) if (norm2(d2) > norm2(d1))
d1 = d2; d1 = d2;
if (d1 == 0) if (d1 == 0)
return null; return null;
z = z - d * z0 / d1; z = z - d * z0 / d1;
} }
@ -1274,7 +1274,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
"/": polynomial_div_scalar, "/": polynomial_div_scalar,
"**": generic_pow, /* XXX: only for integer */ "**": generic_pow, /* XXX: only for integer */
}); });
add_props(Polynomial, { add_props(Polynomial, {
divrem(a, b) { divrem(a, b) {
var n1, n2, i, j, q, r, n, c; var n1, n2, i, j, q, r, n, c;
@ -1437,7 +1437,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
}); });
/* Rational function */ /* Rational function */
RationalFunction = function RationalFunction(a, b) RationalFunction = function RationalFunction(a, b)
{ {
var t, r, d, obj; var t, r, d, obj;
@ -1538,7 +1538,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
"/": ratfunc_div, "/": ratfunc_div,
"**": generic_pow, /* should only be used with integers */ "**": generic_pow, /* should only be used with integers */
}); });
add_props(RationalFunction, { add_props(RationalFunction, {
/* This function always return a RationalFunction object even /* This function always return a RationalFunction object even
if it could simplified to a polynomial, so it is not if it could simplified to a polynomial, so it is not
@ -1555,7 +1555,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
} }
}, },
}); });
/* Power series */ /* Power series */
/* 'a' is an array */ /* 'a' is an array */
@ -1574,11 +1574,11 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
return polynomial_is_scalar(a) || return polynomial_is_scalar(a) ||
(a instanceof Polynomial); (a instanceof Polynomial);
} }
/* n is the maximum number of terms if 'a' is not a serie */ /* n is the maximum number of terms if 'a' is not a serie */
Series = function Series(a, n) { Series = function Series(a, n) {
var emin, r, i; var emin, r, i;
if (a instanceof Series) { if (a instanceof Series) {
return a; return a;
} else if (series_is_scalar_or_polynomial(a)) { } else if (series_is_scalar_or_polynomial(a)) {
@ -1897,7 +1897,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
return Series.zero(0, n); return Series.zero(0, n);
}, },
}); });
/* Array (Matrix) */ /* Array (Matrix) */
Matrix = function Matrix(h, w) { Matrix = function Matrix(h, w) {
@ -2002,7 +2002,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
}, },
det(a) { det(a) {
var n, i, j, k, s, src, v, c; var n, i, j, k, s, src, v, c;
n = Matrix.check_square(a); n = Matrix.check_square(a);
s = 1; s = 1;
src = a.dup(); src = a.dup();
@ -2061,7 +2061,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
src[i][k] *= c; src[i][k] *= c;
dst[i][k] *= c; dst[i][k] *= c;
} }
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
if (j != i) { if (j != i) {
c = src[j][i]; c = src[j][i];
@ -2078,7 +2078,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
}, },
rank(a) { rank(a) {
var src, i, j, k, w, h, l, c; var src, i, j, k, w, h, l, c;
if (!Array.isArray(a) || if (!Array.isArray(a) ||
!Array.isArray(a[0])) !Array.isArray(a[0]))
throw TypeError("matrix expected"); throw TypeError("matrix expected");
@ -2101,12 +2101,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
src[l][k] = v; src[l][k] = v;
} }
} }
c = src[l][i].inverse(); c = src[l][i].inverse();
for(k = 0; k < w; k++) { for(k = 0; k < w; k++) {
src[l][k] *= c; src[l][k] *= c;
} }
for(j = l + 1; j < h; j++) { for(j = l + 1; j < h; j++) {
c = src[j][i]; c = src[j][i];
for(k = i; k < w; k++) { for(k = i; k < w; k++) {
@ -2119,7 +2119,7 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
}, },
ker(a) { ker(a) {
var src, i, j, k, w, h, l, m, r, im_cols, ker_dim, c; var src, i, j, k, w, h, l, m, r, im_cols, ker_dim, c;
if (!Array.isArray(a) || if (!Array.isArray(a) ||
!Array.isArray(a[0])) !Array.isArray(a[0]))
throw TypeError("matrix expected"); throw TypeError("matrix expected");
@ -2145,12 +2145,12 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio
src[l][k] = v; src[l][k] = v;
} }
} }
c = src[l][i].inverse(); c = src[l][i].inverse();
for(k = 0; k < w; k++) { for(k = 0; k < w; k++) {
src[l][k] *= c; src[l][k] *= c;
} }
for(j = 0; j < h; j++) { for(j = 0; j < h; j++) {
if (j != l) { if (j != l) {
c = src[j][i]; c = src[j][i];

View file

@ -1,6 +1,6 @@
/* /*
* QuickJS atom definitions * QuickJS atom definitions
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon * Copyright (c) 2017-2018 Charlie Gordon
* *
@ -204,7 +204,7 @@ DEF(RegExp, "RegExp")
DEF(ArrayBuffer, "ArrayBuffer") DEF(ArrayBuffer, "ArrayBuffer")
DEF(SharedArrayBuffer, "SharedArrayBuffer") DEF(SharedArrayBuffer, "SharedArrayBuffer")
/* must keep same order as class IDs for typed arrays */ /* must keep same order as class IDs for typed arrays */
DEF(Uint8ClampedArray, "Uint8ClampedArray") DEF(Uint8ClampedArray, "Uint8ClampedArray")
DEF(Int8Array, "Int8Array") DEF(Int8Array, "Int8Array")
DEF(Uint8Array, "Uint8Array") DEF(Uint8Array, "Uint8Array")
DEF(Int16Array, "Int16Array") DEF(Int16Array, "Int16Array")
@ -269,5 +269,5 @@ DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
#ifdef CONFIG_BIGNUM #ifdef CONFIG_BIGNUM
DEF(Symbol_operatorSet, "Symbol.operatorSet") DEF(Symbol_operatorSet, "Symbol.operatorSet")
#endif #endif
#endif /* DEF */ #endif /* DEF */

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/* /*
* QuickJS C library * QuickJS C library
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -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);
@ -51,7 +52,7 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason, JSValueConst reason,
JS_BOOL is_handled, void *opaque); JS_BOOL is_handled, void *opaque);
void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt));
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" { */ } /* extern "C" { */
#endif #endif

View file

@ -1,6 +1,6 @@
/* /*
* QuickJS opcode definitions * QuickJS opcode definitions
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon * Copyright (c) 2017-2018 Charlie Gordon
* *
@ -165,7 +165,7 @@ DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
DEF( get_arg, 3, 0, 1, arg) DEF( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
DEF( get_var_ref, 3, 0, 1, var_ref) DEF( get_var_ref, 3, 0, 1, var_ref)
DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
DEF(set_loc_uninitialized, 3, 0, 0, loc) DEF(set_loc_uninitialized, 3, 0, 0, loc)
@ -173,7 +173,7 @@ DEF( get_loc_check, 3, 0, 1, loc)
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc) DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_loc_checkthis, 3, 0, 1, loc) DEF(get_loc_checkthis, 3, 0, 1, loc)
DEF(get_var_ref_check, 3, 0, 1, var_ref) DEF(get_var_ref_check, 3, 0, 1, var_ref)
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
DEF(put_var_ref_check_init, 3, 1, 0, var_ref) DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
DEF( close_loc, 3, 0, 0, loc) DEF( close_loc, 3, 0, 0, loc)
@ -263,7 +263,7 @@ DEF( mul_pow10, 1, 2, 1, none)
DEF( math_mod, 1, 2, 1, none) DEF( math_mod, 1, 2, 1, none)
#endif #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)
/* temporary opcodes: never emitted in the final bytecode */ /* temporary opcodes: never emitted in the final bytecode */
@ -289,7 +289,7 @@ def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1,
def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */
def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */ def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */ def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
#if SHORT_OPCODES #if SHORT_OPCODES

2681
quickjs.c

File diff suppressed because it is too large Load diff

View file

@ -126,7 +126,7 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
{ {
return 0; return 0;
} }
#elif defined(JS_NAN_BOXING) #elif defined(JS_NAN_BOXING)
typedef uint64_t JSValue; typedef uint64_t JSValue;
@ -191,7 +191,7 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
tag = JS_VALUE_GET_TAG(v); tag = JS_VALUE_GET_TAG(v);
return tag == (JS_NAN >> 32); return tag == (JS_NAN >> 32);
} }
#else /* !JS_NAN_BOXING */ #else /* !JS_NAN_BOXING */
typedef union JSValueUnion { typedef union JSValueUnion {
@ -309,7 +309,7 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) #define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
/* allow top-level await in normal script. JS_Eval() returns a /* allow top-level await in normal script. JS_Eval() returns a
promise. Only allowed with JS_EVAL_TYPE_GLOBAL */ promise. Only allowed with JS_EVAL_TYPE_GLOBAL */
#define JS_EVAL_FLAG_ASYNC (1 << 7) #define JS_EVAL_FLAG_ASYNC (1 << 7)
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
@ -499,7 +499,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 +550,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)
@ -633,6 +634,7 @@ 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_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 +683,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)
@ -723,6 +729,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);
@ -821,6 +829,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,
@ -876,6 +901,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 */
@ -968,7 +994,7 @@ static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *fun
{ {
return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic);
} }
void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
JSValueConst proto); JSValueConst proto);
/* C property definition */ /* C property definition */

1
readme.txt Normal file
View file

@ -0,0 +1 @@
The main documentation is in doc/quickjs.pdf or doc/quickjs.html.

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 cosmo_binary quickjs" echo "release_list: extras binary win_binary quickjs"
exit 1 exit 1
fi fi
release_list="extras binary win_binary cosmo_binary quickjs" release_list="extras binary win_binary quickjs"
if [ "$1" != "" ] ; then if [ "$1" != "" ] ; then
release_list="$1" release_list="$1"
@ -29,7 +29,7 @@ name="quickjs-extras-${version}"
outdir="/tmp/${d}" outdir="/tmp/${d}"
rm -rf $outdir rm -rf $outdir
mkdir -p $outdir $outdir/unicode $outdir/tests mkdir -p $outdir $outdir/unicode $outdir/tests
cp unicode/* $outdir/unicode cp unicode/* $outdir/unicode
cp -a tests/bench-v8 $outdir/tests cp -a tests/bench-v8 $outdir/tests
@ -82,28 +82,6 @@ cp $dlldir/libwinpthread-1.dll $outdir
( cd /tmp/$d && rm -f ../${d}.zip && zip -r ../${d}.zip . ) ( 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 . )
fi fi
#################################################" #################################################"
@ -173,7 +151,7 @@ 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 \ 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} )

146
repl.js
View file

@ -1,7 +1,6 @@
/* /*
* QuickJS Read Eval Print Loop * QuickJS Read Eval Print Loop
* *
* Copyright (c) 2024 Sneed Group
* Copyright (c) 2017-2020 Fabrice Bellard * Copyright (c) 2017-2020 Fabrice Bellard
* Copyright (c) 2017-2020 Charlie Gordon * Copyright (c) 2017-2020 Charlie Gordon
* *
@ -32,7 +31,7 @@ import * as os from "os";
/* add 'os' and 'std' bindings */ /* add 'os' and 'std' bindings */
g.os = os; g.os = os;
g.std = std; g.std = std;
/* close global objects */ /* close global objects */
var Object = g.Object; var Object = g.Object;
var String = g.String; var String = g.String;
@ -46,7 +45,7 @@ import * as os from "os";
var config_numcalc = (typeof os.open === "undefined"); var config_numcalc = (typeof os.open === "undefined");
var has_jscalc = (typeof Fraction === "function"); var has_jscalc = (typeof Fraction === "function");
var has_bignum = (typeof BigFloat === "function"); var has_bignum = (typeof BigFloat === "function");
var colors = { var colors = {
none: "\x1b[0m", none: "\x1b[0m",
black: "\x1b[30m", black: "\x1b[30m",
@ -68,60 +67,38 @@ 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 = "";
var prec; var prec;
var expBits; var expBits;
var log2_10; var log2_10;
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 = "donejs > ";
var ps2 = " ... "; var ps2 = " ... ";
var utf8 = true; var utf8 = true;
var show_time = false; var show_time = false;
var show_colors = true; var show_colors = true;
var eval_start_time; var eval_start_time;
var eval_time = 0; var eval_time = 0;
var mexpr = ""; var mexpr = "";
var level = 0; var level = 0;
var cmd = ""; var cmd = "";
@ -139,12 +116,12 @@ import * as os from "os";
var term_read_buf; var term_read_buf;
var term_width; var term_width;
/* current X position of the cursor in the terminal */ /* current X position of the cursor in the terminal */
var term_cursor_x = 0; var term_cursor_x = 0;
function termInit() { function termInit() {
var tab; var tab;
term_fd = std.in.fileno(); term_fd = std.in.fileno();
/* get the terminal size */ /* get the terminal size */
term_width = 80; term_width = 80;
if (os.isatty(term_fd)) { if (os.isatty(term_fd)) {
@ -171,14 +148,14 @@ import * as os from "os";
/* send Ctrl-C to readline */ /* send Ctrl-C to readline */
handle_byte(3); handle_byte(3);
} }
function term_read_handler() { function term_read_handler() {
var l, i; var l, i;
l = os.read(term_fd, term_read_buf.buffer, 0, term_read_buf.length); l = os.read(term_fd, term_read_buf.buffer, 0, term_read_buf.length);
for(i = 0; i < l; i++) for(i = 0; i < l; i++)
handle_byte(term_read_buf[i]); handle_byte(term_read_buf[i]);
} }
function handle_byte(c) { function handle_byte(c) {
if (!utf8) { if (!utf8) {
handle_char(c); handle_char(c);
@ -196,12 +173,12 @@ import * as os from "os";
handle_char(c); handle_char(c);
} }
} }
function is_alpha(c) { function is_alpha(c) {
return typeof c === "string" && return typeof c === "string" &&
((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
} }
function is_digit(c) { function is_digit(c) {
return typeof c === "string" && (c >= '0' && c <= '9'); return typeof c === "string" && (c >= '0' && c <= '9');
} }
@ -233,7 +210,7 @@ import * as os from "os";
d = c.codePointAt(0); /* can be NaN if empty string */ d = c.codePointAt(0); /* can be NaN if empty string */
return d >= 0xdc00 && d < 0xe000; return d >= 0xdc00 && d < 0xe000;
} }
function is_balanced(a, b) { function is_balanced(a, b) {
switch (a + b) { switch (a + b) {
case "()": case "()":
@ -272,7 +249,7 @@ import * as os from "os";
} else { } else {
l = Math.min(term_width - 1 - term_cursor_x, delta); l = Math.min(term_width - 1 - term_cursor_x, delta);
print_csi(l, "C"); /* right */ print_csi(l, "C"); /* right */
delta -= l; delta -= l;
term_cursor_x += l; term_cursor_x += l;
} }
} }
@ -400,7 +377,7 @@ import * as os from "os";
function backward_word() { function backward_word() {
cursor_pos = skip_word_backward(cursor_pos); cursor_pos = skip_word_backward(cursor_pos);
} }
function accept_line() { function accept_line() {
std.puts("\n"); std.puts("\n");
@ -578,7 +555,7 @@ import * as os from "os";
readline_print_prompt(); readline_print_prompt();
} }
} }
function reset() { function reset() {
cmd = ""; cmd = "";
cursor_pos = 0; cursor_pos = 0;
@ -614,6 +591,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;
@ -732,7 +712,7 @@ import * as os from "os";
readline_print_prompt(); readline_print_prompt();
} }
} }
var commands = { /* command table */ var commands = { /* command table */
"\x01": beginning_of_line, /* ^A - bol */ "\x01": beginning_of_line, /* ^A - bol */
"\x02": backward_char, /* ^B - backward-char */ "\x02": backward_char, /* ^B - backward-char */
@ -808,9 +788,9 @@ import * as os from "os";
cursor_pos = cmd.length; cursor_pos = cmd.length;
history_index = history.length; history_index = history.length;
readline_cb = cb; readline_cb = cb;
prompt = pstate; prompt = pstate;
if (mexpr) { if (mexpr) {
prompt += dupstr(" ", plen - prompt.length); prompt += dupstr(" ", plen - prompt.length);
prompt += ps2; prompt += ps2;
@ -895,7 +875,7 @@ import * as os from "os";
} else { } else {
alert(); /* beep! */ alert(); /* beep! */
} }
cursor_pos = (cursor_pos < 0) ? 0 : cursor_pos = (cursor_pos < 0) ? 0 :
(cursor_pos > cmd.length) ? cmd.length : cursor_pos; (cursor_pos > cmd.length) ? cmd.length : cursor_pos;
update(); update();
@ -993,19 +973,21 @@ import * as os from "os";
s += "n"; s += "n";
return s; return s;
} }
function print(a) { function print(a) {
var stack = []; var stack = [];
function print_rec(a) { function print_rec(a) {
var n, i, keys, key, type, s; var n, i, keys, key, type, s;
type = typeof(a); type = typeof(a);
if (type === "object") { if (type === "object") {
if (a === null) { if (a === null) {
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 (a instanceof Date) {
std.puts("Date " + a.toGMTString().__quote());
} else if (has_jscalc && (a instanceof Fraction || } else if (has_jscalc && (a instanceof Fraction ||
a instanceof Complex || a instanceof Complex ||
a instanceof Mod || a instanceof Mod ||
@ -1073,7 +1055,7 @@ import * as os from "os";
} }
print_rec(a); print_rec(a);
} }
function extract_directive(a) { function extract_directive(a) {
var pos; var pos;
if (a[0] !== '\\') if (a[0] !== '\\')
@ -1088,7 +1070,7 @@ import * as os from "os";
/* return true if the string after cmd can be evaluted as JS */ /* return true if the string after cmd can be evaluted as JS */
function handle_directive(cmd, expr) { function handle_directive(cmd, expr) {
var param, prec1, expBits1; var param, prec1, expBits1;
if (cmd === "h" || cmd === "?" || cmd == "help") { if (cmd === "h" || cmd === "?" || cmd == "help") {
help(); help();
} else if (cmd === "load") { } else if (cmd === "load") {
@ -1180,6 +1162,23 @@ import * as os from "os";
} }
if (config_numcalc) { if (config_numcalc) {
styles = {
'default': 'black',
'comment': 'white',
'string': 'green',
'regex': 'cyan',
'number': 'green',
'keyword': 'blue',
'function': 'gray',
'type': 'bright_magenta',
'identifier': 'yellow',
'error': 'bright_red',
'result': 'black',
'error_msg': 'bright_red',
};
ps1 = "> ";
/* called by the GUI */ /* called by the GUI */
g.execCmd = function (cmd) { g.execCmd = function (cmd) {
switch(cmd) { switch(cmd) {
@ -1198,7 +1197,7 @@ import * as os from "os";
} }
} }
} }
function help() { function help() {
function sel(n) { function sel(n) {
return n ? "*": " "; return n ? "*": " ";
@ -1227,9 +1226,9 @@ import * as os from "os";
function cmd_start() { function cmd_start() {
if (!config_numcalc) { if (!config_numcalc) {
if (has_jscalc) if (has_jscalc)
std.puts('QJSCalc (DoneJS Edition) - Type "\\h" for help\n'); std.puts('QJSCalc - Type "\\h" for help\n');
else else
std.puts('QuickJS (DoneJS Edition) - Type "\\h" for help\n'); std.puts('QuickJS - Type "\\h" for help\n');
} }
if (has_bignum) { if (has_bignum) {
log2_10 = Math.log(10) / Math.log(2); log2_10 = Math.log(10) / Math.log(2);
@ -1248,7 +1247,7 @@ import * as os from "os";
function cmd_readline_start() { function cmd_readline_start() {
readline_start(dupstr(" ", level), readline_handle_cmd); readline_start(dupstr(" ", level), readline_handle_cmd);
} }
function readline_handle_cmd(expr) { function readline_handle_cmd(expr) {
if (!handle_cmd(expr)) { if (!handle_cmd(expr)) {
cmd_readline_start(); cmd_readline_start();
@ -1258,7 +1257,7 @@ import * as os from "os";
/* return true if async termination */ /* return true if async termination */
function handle_cmd(expr) { function handle_cmd(expr) {
var colorstate, cmd; var colorstate, cmd;
if (expr === null) { if (expr === null) {
expr = ""; expr = "";
return false; return false;
@ -1276,7 +1275,7 @@ import * as os from "os";
} }
if (expr === "") if (expr === "")
return false; return false;
if (mexpr) if (mexpr)
expr = mexpr + '\n' + expr; expr = mexpr + '\n' + expr;
colorstate = colorize_js(expr); colorstate = colorize_js(expr);
@ -1287,7 +1286,7 @@ import * as os from "os";
return false; return false;
} }
mexpr = ""; mexpr = "";
if (has_bignum) { if (has_bignum) {
/* XXX: async is not supported in this case */ /* XXX: async is not supported in this case */
BigFloatEnv.setPrec(eval_and_print_start.bind(null, expr, false), BigFloatEnv.setPrec(eval_and_print_start.bind(null, expr, false),
@ -1300,7 +1299,7 @@ import * as os from "os";
function eval_and_print_start(expr, is_async) { function eval_and_print_start(expr, is_async) {
var result; var result;
try { try {
if (eval_mode === "math") if (eval_mode === "math")
expr = '"use math"; void 0;' + expr; expr = '"use math"; void 0;' + expr;
@ -1311,7 +1310,7 @@ import * as os from "os";
/* 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 { } else {
print_eval_result(result); print_eval_result({ value: result });
} }
} catch (error) { } catch (error) {
print_eval_error(error); print_eval_error(error);
@ -1319,6 +1318,7 @@ import * as os from "os";
} }
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);
@ -1342,7 +1342,7 @@ import * as os from "os";
console.log(error); console.log(error);
} }
std.puts(colors.none); std.puts(colors.none);
handle_cmd_end(); handle_cmd_end();
} }
@ -1585,7 +1585,7 @@ import * as os from "os";
} }
termInit(); termInit();
cmd_start(); cmd_start();
})(globalThis); })(globalThis);

View file

@ -1,6 +1,6 @@
/* /*
* ECMA Test 262 Runner for QuickJS * ECMA Test 262 Runner for QuickJS
* *
* Copyright (c) 2017-2021 Fabrice Bellard * Copyright (c) 2017-2021 Fabrice Bellard
* Copyright (c) 2017-2021 Charlie Gordon * Copyright (c) 2017-2021 Charlie Gordon
* *
@ -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;
@ -321,7 +323,7 @@ void namelist_load(namelist_t *lp, const char *filename)
char *p = str_strip(buf); char *p = str_strip(buf);
if (*p == '#' || *p == ';' || *p == '\0') if (*p == '#' || *p == ';' || *p == '\0')
continue; /* line comment */ continue; /* line comment */
namelist_add(lp, base_name, p); namelist_add(lp, base_name, p);
} }
free(base_name); free(base_name);
@ -457,11 +459,11 @@ static void *agent_start(void *arg)
JSContext *ctx; JSContext *ctx;
JSValue ret_val; JSValue ret_val;
int ret; int ret;
rt = JS_NewRuntime(); rt = JS_NewRuntime();
if (rt == NULL) { if (rt == NULL) {
fatal(1, "JS_NewRuntime failure"); fatal(1, "JS_NewRuntime failure");
} }
ctx = JS_NewContext(rt); ctx = JS_NewContext(rt);
if (ctx == NULL) { if (ctx == NULL) {
JS_FreeRuntime(rt); JS_FreeRuntime(rt);
@ -470,7 +472,7 @@ static void *agent_start(void *arg)
JS_SetContextOpaque(ctx, agent); JS_SetContextOpaque(ctx, agent);
JS_SetRuntimeInfo(rt, "agent"); JS_SetRuntimeInfo(rt, "agent");
JS_SetCanBlock(rt, TRUE); JS_SetCanBlock(rt, TRUE);
add_helpers(ctx); add_helpers(ctx);
ret_val = JS_Eval(ctx, agent->script, strlen(agent->script), ret_val = JS_Eval(ctx, agent->script, strlen(agent->script),
"<evalScript>", JS_EVAL_TYPE_GLOBAL); "<evalScript>", JS_EVAL_TYPE_GLOBAL);
@ -479,7 +481,7 @@ static void *agent_start(void *arg)
if (JS_IsException(ret_val)) if (JS_IsException(ret_val))
js_std_dump_error(ctx); js_std_dump_error(ctx);
JS_FreeValue(ctx, ret_val); JS_FreeValue(ctx, ret_val);
for(;;) { for(;;) {
JSContext *ctx1; JSContext *ctx1;
ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
@ -491,12 +493,12 @@ static void *agent_start(void *arg)
break; break;
} else { } else {
JSValue args[2]; JSValue args[2];
pthread_mutex_lock(&agent_mutex); pthread_mutex_lock(&agent_mutex);
while (!agent->broadcast_pending) { while (!agent->broadcast_pending) {
pthread_cond_wait(&agent_cond, &agent_mutex); pthread_cond_wait(&agent_cond, &agent_mutex);
} }
agent->broadcast_pending = FALSE; agent->broadcast_pending = FALSE;
pthread_cond_signal(&agent_cond); pthread_cond_signal(&agent_cond);
@ -530,10 +532,11 @@ 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");
script = JS_ToCString(ctx, argv[0]); script = JS_ToCString(ctx, argv[0]);
if (!script) if (!script)
return JS_EXCEPTION; return JS_EXCEPTION;
@ -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;
} }
@ -552,7 +560,7 @@ static void js_agent_free(JSContext *ctx)
{ {
struct list_head *el, *el1; struct list_head *el, *el1;
Test262Agent *agent; Test262Agent *agent;
list_for_each_safe(el, el1, &agent_list) { list_for_each_safe(el, el1, &agent_list) {
agent = list_entry(el, Test262Agent, link); agent = list_entry(el, Test262Agent, link);
pthread_join(agent->tid, NULL); pthread_join(agent->tid, NULL);
@ -561,7 +569,7 @@ static void js_agent_free(JSContext *ctx)
free(agent); free(agent);
} }
} }
static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val, static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv) int argc, JSValue *argv)
{ {
@ -593,16 +601,16 @@ static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val,
uint8_t *buf; uint8_t *buf;
size_t buf_size; size_t buf_size;
int32_t val; int32_t val;
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");
buf = JS_GetArrayBuffer(ctx, &buf_size, sab); buf = JS_GetArrayBuffer(ctx, &buf_size, sab);
if (!buf) if (!buf)
return JS_EXCEPTION; return JS_EXCEPTION;
if (JS_ToInt32(ctx, &val, argv[1])) if (JS_ToInt32(ctx, &val, argv[1]))
return JS_EXCEPTION; return JS_EXCEPTION;
/* broadcast the values and wait until all agents have started /* broadcast the values and wait until all agents have started
calling their callbacks */ calling their callbacks */
pthread_mutex_lock(&agent_mutex); pthread_mutex_lock(&agent_mutex);
@ -697,7 +705,7 @@ static JSValue js_agent_report(JSContext *ctx, JSValue this_val,
rep = malloc(sizeof(*rep)); rep = malloc(sizeof(*rep));
rep->str = strdup(str); rep->str = strdup(str);
JS_FreeCString(ctx, str); JS_FreeCString(ctx, str);
pthread_mutex_lock(&report_mutex); pthread_mutex_lock(&report_mutex);
list_add_tail(&rep->link, &report_list); list_add_tail(&rep->link, &report_list);
pthread_mutex_unlock(&report_mutex); pthread_mutex_unlock(&report_mutex);
@ -717,7 +725,7 @@ static const JSCFunctionListEntry js_agent_funcs[] = {
JS_CFUNC_DEF("sleep", 1, js_agent_sleep ), JS_CFUNC_DEF("sleep", 1, js_agent_sleep ),
JS_CFUNC_DEF("monotonicNow", 0, js_agent_monotonicNow ), JS_CFUNC_DEF("monotonicNow", 0, js_agent_monotonicNow ),
}; };
static JSValue js_new_agent(JSContext *ctx) static JSValue js_new_agent(JSContext *ctx)
{ {
JSValue agent; JSValue agent;
@ -733,7 +741,7 @@ static JSValue js_createRealm(JSContext *ctx, JSValue this_val,
{ {
JSContext *ctx1; JSContext *ctx1;
JSValue ret; JSValue ret;
ctx1 = JS_NewContext(JS_GetRuntime(ctx)); ctx1 = JS_NewContext(JS_GetRuntime(ctx));
if (!ctx1) if (!ctx1)
return JS_ThrowOutOfMemory(ctx); return JS_ThrowOutOfMemory(ctx);
@ -753,7 +761,7 @@ static JSValue add_helpers1(JSContext *ctx)
{ {
JSValue global_obj; JSValue global_obj;
JSValue obj262, obj; JSValue obj262, obj;
global_obj = JS_GetGlobalObject(ctx); global_obj = JS_GetGlobalObject(ctx);
JS_SetPropertyStr(ctx, global_obj, "print", JS_SetPropertyStr(ctx, global_obj, "print",
@ -784,7 +792,7 @@ static JSValue add_helpers1(JSContext *ctx)
JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj); JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj);
JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262)); JS_SetPropertyStr(ctx, global_obj, "$262", JS_DupValue(ctx, obj262));
JS_FreeValue(ctx, global_obj); JS_FreeValue(ctx, global_obj);
return obj262; return obj262;
} }
@ -813,14 +821,27 @@ 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) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s'", JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
module_name); module_name);
return NULL; return NULL;
} }
/* compile the module */ /* compile the module */
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
@ -910,7 +931,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;
@ -929,14 +950,14 @@ void load_config(const char *filename)
perror_exit(1, filename); perror_exit(1, filename);
} }
base_name = get_basename(filename); base_name = get_basename(filename);
while (fgets(buf, sizeof(buf), f) != NULL) { while (fgets(buf, sizeof(buf), f) != NULL) {
char *p, *q; char *p, *q;
lineno++; lineno++;
p = str_strip(buf); p = str_strip(buf);
if (*p == '#' || *p == ';' || *p == '\0') if (*p == '#' || *p == ';' || *p == '\0')
continue; /* line comment */ continue; /* line comment */
if (*p == "[]"[0]) { if (*p == "[]"[0]) {
/* new section */ /* new section */
p++; p++;
@ -965,6 +986,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;
@ -1002,7 +1027,7 @@ void load_config(const char *filename)
test_mode = TEST_STRICT; test_mode = TEST_STRICT;
else if (str_equal(q, "all") || str_equal(q, "both")) else if (str_equal(q, "all") || str_equal(q, "both"))
test_mode = TEST_ALL; test_mode = TEST_ALL;
else else
fatal(2, "unknown test mode: %s", q); fatal(2, "unknown test mode: %s", q);
continue; continue;
} }
@ -1143,7 +1168,7 @@ int longest_match(const char *str, const char *find, int pos, int *ppos, int lin
int len, maxlen; int len, maxlen;
maxlen = 0; maxlen = 0;
if (*find) { if (*find) {
const char *p; const char *p;
for (p = str + pos; *p; p++) { for (p = str + pos; *p; p++) {
@ -1176,7 +1201,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
int ret, error_line, pos, pos_line; int ret, error_line, pos, pos_line;
BOOL is_error, has_error_line, ret_promise; BOOL is_error, has_error_line, ret_promise;
const char *error_name; const char *error_name;
pos = skip_comments(buf, 1, &pos_line); pos = skip_comments(buf, 1, &pos_line);
error_line = pos_line; error_line = pos_line;
has_error_line = FALSE; has_error_line = FALSE;
@ -1186,7 +1211,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
/* a module evaluation returns a promise */ /* a module evaluation returns a promise */
ret_promise = ((eval_flags & JS_EVAL_TYPE_MODULE) != 0); ret_promise = ((eval_flags & JS_EVAL_TYPE_MODULE) != 0);
async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */ async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */
res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
if ((is_async || ret_promise) && !JS_IsException(res_val)) { if ((is_async || ret_promise) && !JS_IsException(res_val)) {
@ -1239,7 +1264,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
if (is_error) { if (is_error) {
JSValue name, stack; JSValue name, stack;
const char *stack_str; const char *stack_str;
name = JS_GetPropertyStr(ctx, exception_val, "name"); name = JS_GetPropertyStr(ctx, exception_val, "name");
error_name = JS_ToCString(ctx, name); error_name = JS_ToCString(ctx, name);
stack = JS_GetPropertyStr(ctx, exception_val, "stack"); stack = JS_GetPropertyStr(ctx, exception_val, "stack");
@ -1248,10 +1273,10 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
if (stack_str) { if (stack_str) {
const char *p; const char *p;
int len; int len;
if (outfile) if (outfile)
fprintf(outfile, "%s", stack_str); fprintf(outfile, "%s", stack_str);
len = strlen(filename); len = strlen(filename);
p = strstr(stack_str, filename); p = strstr(stack_str, filename);
if (p != NULL && p[len] == ':') { if (p != NULL && p[len] == ':') {
@ -1269,7 +1294,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
if (error_type) { if (error_type) {
char *error_class; char *error_class;
const char *msg; const char *msg;
msg = JS_ToCString(ctx, exception_val); msg = JS_ToCString(ctx, exception_val);
error_class = strdup_len(msg, strcspn(msg, ":")); error_class = strdup_len(msg, strcspn(msg, ":"));
if (!str_equal(error_class, error_type)) if (!str_equal(error_class, error_type))
@ -1393,7 +1418,7 @@ char *extract_desc(const char *buf, char style)
const char *p, *desc_start; const char *p, *desc_start;
char *desc; char *desc;
int len; int len;
p = buf; p = buf;
while (*p != '\0') { while (*p != '\0') {
if (p[0] == '/' && p[1] == '*' && p[2] == style && p[3] != '/') { if (p[0] == '/' && p[1] == '*' && p[2] == style && p[3] != '/') {
@ -1525,11 +1550,11 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
JSRuntime *rt; JSRuntime *rt;
JSContext *ctx; JSContext *ctx;
int i, ret; int i, ret;
rt = JS_NewRuntime(); rt = JS_NewRuntime();
if (rt == NULL) { if (rt == NULL) {
fatal(1, "JS_NewRuntime failure"); fatal(1, "JS_NewRuntime failure");
} }
ctx = JS_NewContext(rt); ctx = JS_NewContext(rt);
if (ctx == NULL) { if (ctx == NULL) {
JS_FreeRuntime(rt); JS_FreeRuntime(rt);
@ -1538,10 +1563,10 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
JS_SetRuntimeInfo(rt, filename); JS_SetRuntimeInfo(rt, filename);
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++) {
@ -1554,7 +1579,7 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
ret = eval_buf(ctx, buf, buf_len, filename, TRUE, is_negative, ret = eval_buf(ctx, buf, buf_len, filename, TRUE, is_negative,
error_type, outfile, eval_flags, is_async); error_type, outfile, eval_flags, is_async);
ret = (ret != 0); ret = (ret != 0);
if (dump_memory) { if (dump_memory) {
update_stats(rt, filename); update_stats(rt, filename);
} }
@ -1587,7 +1612,7 @@ int run_test(const char *filename, int index)
BOOL is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip; BOOL is_negative, is_nostrict, is_onlystrict, is_async, is_module, skip;
BOOL can_block; BOOL can_block;
namelist_t include_list = { 0 }, *ip = &include_list; namelist_t include_list = { 0 }, *ip = &include_list;
is_nostrict = is_onlystrict = is_negative = is_async = is_module = skip = FALSE; is_nostrict = is_onlystrict = is_negative = is_async = is_module = skip = FALSE;
can_block = TRUE; can_block = TRUE;
error_type = NULL; error_type = NULL;
@ -1656,7 +1681,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"));
} }
@ -1823,13 +1848,13 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
int eval_flags, ret_code, ret; int eval_flags, ret_code, ret;
JSValue res_val; JSValue res_val;
BOOL can_block; BOOL can_block;
outfile = stdout; /* for js_print */ outfile = stdout; /* for js_print */
rt = JS_NewRuntime(); rt = JS_NewRuntime();
if (rt == NULL) { if (rt == NULL) {
fatal(1, "JS_NewRuntime failure"); fatal(1, "JS_NewRuntime failure");
} }
ctx = JS_NewContext(rt); ctx = JS_NewContext(rt);
if (ctx == NULL) { if (ctx == NULL) {
JS_FreeRuntime(rt); JS_FreeRuntime(rt);
@ -1839,10 +1864,10 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
can_block = TRUE; can_block = TRUE;
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);
buf = load_file(filename, &buf_len); buf = load_file(filename, &buf_len);
@ -1900,9 +1925,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 +1996,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 +2024,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 +2066,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")) {
@ -2035,14 +2099,14 @@ int main(int argc, char **argv)
break; break;
} }
} }
if (optind >= argc && !test_list.count) if (optind >= argc && !test_list.count)
help(); help();
if (is_test262_harness) { if (is_test262_harness) {
return run_test262_harness_test(argv[optind], is_module); return run_test262_harness_test(argv[optind], is_module);
} }
error_out = stdout; error_out = stdout;
if (error_filename) { if (error_filename) {
error_file = load_file(error_filename, NULL); error_file = load_file(error_filename, NULL);
@ -2062,8 +2126,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 +2164,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 +2194,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 +2211,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

@ -1,5 +0,0 @@
function test() {
breakFunction();
console.warn("lol it works!");
}
test()

View file

@ -15,7 +15,7 @@ mode=default
# handle tests flagged as [async]: yes, no, skip # handle tests flagged as [async]: yes, no, skip
# for these, load 'harness/doneprintHandle.js' prior to test # for these, load 'harness/doneprintHandle.js' prior to test
# and expect `print('Test262:AsyncTestComplete')` to be called for # and expect `print('Test262:AsyncTestComplete')` to be called for
# successful termination # successful termination
async=yes async=yes

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

View file

@ -1,6 +1,6 @@
/* /*
* QuickJS: binary JSON module (test only) * QuickJS: binary JSON module (test only)
* *
* Copyright (c) 2017-2019 Fabrice Bellard * Copyright (c) 2017-2019 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
@ -32,7 +32,7 @@ static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val,
JSValue obj; JSValue obj;
size_t size; size_t size;
int flags; int flags;
if (JS_ToIndex(ctx, &pos, argv[1])) if (JS_ToIndex(ctx, &pos, argv[1]))
return JS_EXCEPTION; return JS_EXCEPTION;
if (JS_ToIndex(ctx, &len, argv[2])) if (JS_ToIndex(ctx, &len, argv[2]))
@ -56,7 +56,7 @@ static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val,
uint8_t *buf; uint8_t *buf;
JSValue array; JSValue array;
int flags; int flags;
flags = 0; flags = 0;
if (JS_ToBool(ctx, argv[1])) if (JS_ToBool(ctx, argv[1]))
flags |= JS_WRITE_OBJ_REFERENCE; flags |= JS_WRITE_OBJ_REFERENCE;

View file

@ -22,8 +22,10 @@
* 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.
*/ */
import * as std from "std";
import * as os from "os"; if (typeof require !== 'undefined') {
var fs = require('fs');
}
function pad(str, n) { function pad(str, n) {
str += ""; str += "";
@ -61,21 +63,17 @@ function toPrec(n, prec) {
s = s.substring(0, i) + "." + s.substring(i); s = s.substring(0, i) + "." + s.substring(i);
return s; return s;
} }
var ref_data; var ref_data;
var log_data; var log_data;
var heads = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (%)" ]; var heads = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (1000)" ];
var widths = [ 22, 10, 9, 9, 9 ]; var widths = [ 22, 10, 9, 9, 9 ];
var precs = [ 0, 0, 2, 2, 2 ]; var precs = [ 0, 0, 2, 2, 0 ];
var total = [ 0, 0, 0, 0, 0 ]; var total = [ 0, 0, 0, 0, 0 ];
var total_score = 0; var total_score = 0;
var total_scale = 0; var total_scale = 0;
if (typeof console == "undefined") {
var console = { log: print };
}
function log_line() { function log_line() {
var i, n, s, a; var i, n, s, a;
s = ""; s = "";
@ -83,7 +81,7 @@ function log_line() {
if (i > 0) if (i > 0)
s += " "; s += " ";
a = arguments[i]; a = arguments[i];
if (typeof a == "number") { if (typeof a === "number") {
total[i] += a; total[i] += a;
a = toPrec(a, precs[i]); a = toPrec(a, precs[i]);
s += pad_left(a, widths[i]); s += pad_left(a, widths[i]);
@ -95,11 +93,28 @@ function log_line() {
} }
var clocks_per_sec = 1000; var clocks_per_sec = 1000;
var max_iterations = 10; var max_iterations = 100;
var clock_threshold = 100; /* favoring short measuring spans */ var clock_threshold = 2; /* favoring short measuring spans */
var min_n_argument = 1; var min_n_argument = 1;
//var get_clock = Date.now; var get_clock;
var get_clock = os.now; if (typeof performance !== "undefined") {
// use more precise clock on NodeJS
// need a method call on performance object
get_clock = () => performance.now();
} else
if (typeof os !== "undefined") {
// use more precise clock on QuickJS
get_clock = os.now;
} else {
// use Date.now and round up to the next millisecond
get_clock = () => {
var t0 = Date.now();
var t;
while ((t = Date.now()) == t0)
continue;
return t;
}
}
function log_one(text, n, ti) { function log_one(text, n, ti) {
var ref; var ref;
@ -112,7 +127,7 @@ function log_one(text, n, ti) {
ti = Math.round(ti * 100) / 100; ti = Math.round(ti * 100) / 100;
log_data[text] = ti; log_data[text] = ti;
if (typeof ref === "number") { if (typeof ref === "number") {
log_line(text, n, ti, ref, ti * 100 / ref); log_line(text, n, ti, ref, Math.round(ref * 1000 / ti));
total_score += ti * 100 / ref; total_score += ti * 100 / ref;
total_scale += 100; total_scale += 100;
} else { } else {
@ -124,28 +139,27 @@ function log_one(text, n, ti) {
function bench(f, text) function bench(f, text)
{ {
var i, j, n, t, t1, ti, nb_its, ref, ti_n, ti_n1, min_ti; var i, j, n, t, ti, nb_its, ref, ti_n, ti_n1;
nb_its = n = 1; nb_its = n = 1;
if (f.bench) { if (f.bench) {
ti_n = f(text); ti_n = f(text);
} else { } else {
// measure ti_n: the shortest time for an individual operation
ti_n = 1000000000; ti_n = 1000000000;
min_ti = clock_threshold / 10;
for(i = 0; i < 30; i++) { for(i = 0; i < 30; i++) {
// measure ti: the shortest time for max_iterations iterations
ti = 1000000000; ti = 1000000000;
for (j = 0; j < max_iterations; j++) { for (j = 0; j < max_iterations; j++) {
t = get_clock(); t = get_clock();
while ((t1 = get_clock()) == t)
continue;
nb_its = f(n); nb_its = f(n);
t = get_clock() - t;
if (nb_its < 0) if (nb_its < 0)
return; // test failure return; // test failure
t1 = get_clock() - t1; if (ti > t)
if (ti > t1) ti = t;
ti = t1;
} }
if (ti >= min_ti) { if (ti >= clock_threshold / 10) {
ti_n1 = ti / nb_its; ti_n1 = ti / nb_its;
if (ti_n > ti_n1) if (ti_n > ti_n1)
ti_n = ti_n1; ti_n = ti_n1;
@ -171,6 +185,26 @@ function empty_loop(n) {
return n; return n;
} }
function empty_down_loop(n) {
var j;
for(j = n; j > 0; j--) {
}
return n;
}
function empty_down_loop2(n) {
var j;
for(j = n; j --> 0;) {
}
return n;
}
function empty_do_loop(n) {
var j = n;
do { } while (--j > 0);
return n;
}
function date_now(n) { function date_now(n) {
var j; var j;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
@ -179,6 +213,32 @@ function date_now(n) {
return n; return n;
} }
function date_parse(n) {
var x0 = 0, dx = 0;
var j;
for(j = 0; j < n; j++) {
var x1 = x0 - x0 % 1000;
var x2 = -x0;
var x3 = -x1;
var d0 = new Date(x0);
var d1 = new Date(x1);
var d2 = new Date(x2);
var d3 = new Date(x3);
if (Date.parse(d0.toISOString()) != x0
|| Date.parse(d1.toGMTString()) != x1
|| Date.parse(d1.toString()) != x1
|| Date.parse(d2.toISOString()) != x2
|| Date.parse(d3.toGMTString()) != x3
|| Date.parse(d3.toString()) != x3) {
console.log("Date.parse error for " + x0);
return -1;
}
dx = (dx * 1.1 + 1) >> 0;
x0 = (x0 + dx) % 8.64e15;
}
return n * 6;
}
function prop_read(n) function prop_read(n)
{ {
var obj, sum, j; var obj, sum, j;
@ -207,30 +267,78 @@ function prop_write(n)
return n * 4; return n * 4;
} }
function prop_create(n) function prop_update(n)
{ {
var obj, j; var obj, j;
obj = {a: 1, b: 2, c:3, d:4 };
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
obj = new Object(); obj.a += j;
obj.a = 1; obj.b += j;
obj.b = 2; obj.c += j;
obj.c = 3; obj.d += j;
obj.d = 4;
} }
return n * 4; return n * 4;
} }
function prop_create(n)
{
var obj, i, j;
for(j = 0; j < n; j++) {
obj = {};
obj.a = 1;
obj.b = 2;
obj.c = 3;
obj.d = 4;
obj.e = 5;
obj.f = 6;
obj.g = 7;
obj.h = 8;
obj.i = 9;
obj.j = 10;
for(i = 0; i < 10; i++) {
obj[i] = i;
}
}
return n * 20;
}
function prop_clone(n)
{
var ref, obj, j, k;
ref = { a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10 };
for(k = 0; k < 10; k++) {
ref[k] = k;
}
for (j = 0; j < n; j++) {
global_res = { ...ref };
}
return n * 20;
}
function prop_delete(n) function prop_delete(n)
{ {
var obj, j; var ref, obj, j, k;
obj = {}; ref = { a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10 };
for(j = 0; j < n; j++) { for(k = 0; k < 10; k++) {
obj[j] = 1; ref[k] = k;
} }
for(j = 0; j < n; j++) { for (j = 0; j < n; j++) {
delete obj[j]; obj = { ...ref };
delete obj.a;
delete obj.b;
delete obj.c;
delete obj.d;
delete obj.e;
delete obj.f;
delete obj.g;
delete obj.h;
delete obj.i;
delete obj.j;
for(k = 0; k < 10; k++) {
delete obj[k];
}
} }
return n; return n * 20;
} }
function array_read(n) function array_read(n)
@ -291,15 +399,32 @@ function array_prop_create(n)
return len * n; return len * n;
} }
function array_slice(n)
{
var ref, a, i, j, len;
len = 1000;
ref = [];
for(i = 0; i < len; i++)
ref[i] = i;
for(j = 0; j < n; j++) {
ref[0] = j;
a = ref.slice();
a[0] = 0;
global_res = a;
}
return len * n;
}
function array_length_decr(n) function array_length_decr(n)
{ {
var tab, i, j, len; var tab, ref, i, j, len;
len = 1000; len = 1000;
tab = []; ref = [];
for(i = 0; i < len; i++) for(i = 0; i < len; i++)
tab[i] = i; ref[i] = i;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
for(i = len - 1; i >= 0; i--) tab = ref.slice();
for(i = len; i --> 0;)
tab.length = i; tab.length = i;
} }
return len * n; return len * n;
@ -307,15 +432,16 @@ function array_length_decr(n)
function array_hole_length_decr(n) function array_hole_length_decr(n)
{ {
var tab, i, j, len; var tab, ref, i, j, len;
len = 1000; len = 1000;
tab = []; ref = [];
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
if (i != 3) if (i % 10 == 9)
tab[i] = i; ref[i] = i;
} }
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
for(i = len - 1; i >= 0; i--) tab = ref.slice();
for(i = len; i --> 0;)
tab.length = i; tab.length = i;
} }
return len * n; return len * n;
@ -335,12 +461,13 @@ function array_push(n)
function array_pop(n) function array_pop(n)
{ {
var tab, i, j, len, sum; var tab, ref, i, j, len, sum;
len = 500; len = 500;
ref = [];
for(i = 0; i < len; i++)
ref[i] = i;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
tab = []; tab = ref.slice();
for(i = 0; i < len; i++)
tab[i] = i;
sum = 0; sum = 0;
for(i = 0; i < len; i++) for(i = 0; i < len; i++)
sum += tab.pop(); sum += tab.pop();
@ -412,6 +539,7 @@ function global_read(n)
return n * 4; return n * 4;
} }
// non strict version
var global_write = var global_write =
(1, eval)(`(function global_write(n) (1, eval)(`(function global_write(n)
{ {
@ -454,6 +582,7 @@ function local_destruct(n)
var global_v1, global_v2, global_v3, global_v4; var global_v1, global_v2, global_v3, global_v4;
var global_a, global_b, global_c, global_d; var global_a, global_b, global_c, global_d;
// non strict version
var global_destruct = var global_destruct =
(1, eval)(`(function global_destruct(n) (1, eval)(`(function global_destruct(n)
{ {
@ -481,6 +610,25 @@ function global_destruct_strict(n)
return n * 8; return n * 8;
} }
function g(a)
{
return 1;
}
function global_func_call(n)
{
var j, sum;
sum = 0;
for(j = 0; j < n; j++) {
sum += g(j);
sum += g(j);
sum += g(j);
sum += g(j);
}
global_res = sum;
return n * 4;
}
function func_call(n) function func_call(n)
{ {
function f(a) function f(a)
@ -500,7 +648,7 @@ function func_call(n)
return n * 4; return n * 4;
} }
function closure_var(n) function func_closure_call(n)
{ {
function f(a) function f(a)
{ {
@ -605,8 +753,8 @@ function bigint256_arith(n)
function set_collection_add(n) function set_collection_add(n)
{ {
var s, i, j, len = 100; var s, i, j, len = 100;
s = new Set();
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
s = new Set();
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
s.add(String(i), i); s.add(String(i), i);
} }
@ -620,25 +768,25 @@ function set_collection_add(n)
function array_for(n) function array_for(n)
{ {
var r, i, j, sum; var r, i, j, sum, len = 100;
r = []; r = [];
for(i = 0; i < 100; i++) for(i = 0; i < len; i++)
r[i] = i; r[i] = i;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
sum = 0; sum = 0;
for(i = 0; i < 100; i++) { for(i = 0; i < len; i++) {
sum += r[i]; sum += r[i];
} }
global_res = sum; global_res = sum;
} }
return n * 100; return n * len;
} }
function array_for_in(n) function array_for_in(n)
{ {
var r, i, j, sum; var r, i, j, sum, len = 100;
r = []; r = [];
for(i = 0; i < 100; i++) for(i = 0; i < len; i++)
r[i] = i; r[i] = i;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
sum = 0; sum = 0;
@ -647,14 +795,14 @@ function array_for_in(n)
} }
global_res = sum; global_res = sum;
} }
return n * 100; return n * len;
} }
function array_for_of(n) function array_for_of(n)
{ {
var r, i, j, sum; var r, i, j, sum, len = 100;
r = []; r = [];
for(i = 0; i < 100; i++) for(i = 0; i < len; i++)
r[i] = i; r[i] = i;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
sum = 0; sum = 0;
@ -663,7 +811,7 @@ function array_for_of(n)
} }
global_res = sum; global_res = sum;
} }
return n * 100; return n * len;
} }
function math_min(n) function math_min(n)
@ -678,58 +826,108 @@ function math_min(n)
return n * 1000; return n * 1000;
} }
function regexp_ascii(n)
{
var i, j, r, s;
s = "the quick brown fox jumped over the lazy dog"
for(j = 0; j < n; j++) {
for(i = 0; i < 1000; i++)
r = /the quick brown fox/.exec(s)
global_res = r;
}
return n * 1000;
}
function regexp_utf16(n)
{
var i, j, r, s;
s = "the quick brown ᶠᵒˣ jumped over the lazy ᵈᵒᵍ"
for(j = 0; j < n; j++) {
for(i = 0; i < 1000; i++)
r = /the quick brown ᶠᵒˣ/.exec(s)
global_res = r;
}
return n * 1000;
}
/* incremental string contruction as local var */ /* incremental string contruction as local var */
function string_build1(n) function string_build1(n)
{ {
var i, j, r; var i, j, r;
r = "";
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
for(i = 0; i < 100; i++) r = "";
for(i = 0; i < 1000; i++)
r += "x"; r += "x";
global_res = r; global_res = r;
} }
return n * 100; return n * 1000;
}
/* incremental string contruction using + */
function string_build1x(n)
{
var i, j, r;
for(j = 0; j < n; j++) {
r = "";
for(i = 0; i < 1000; i++)
r = r + "x";
global_res = r;
}
return n * 1000;
}
/* incremental string contruction using +2c */
function string_build2c(n)
{
var i, j;
for(j = 0; j < n; j++) {
var r = "";
for(i = 0; i < 1000; i++)
r += "xy";
global_res = r;
}
return n * 1000;
} }
/* incremental string contruction as arg */ /* incremental string contruction as arg */
function string_build2(n, r) function string_build2(n, r)
{ {
var i, j; var i, j;
r = "";
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
for(i = 0; i < 100; i++) r = "";
for(i = 0; i < 1000; i++)
r += "x"; r += "x";
global_res = r; global_res = r;
} }
return n * 100; return n * 1000;
} }
/* incremental string contruction by prepending */ /* incremental string contruction by prepending */
function string_build3(n, r) function string_build3(n)
{ {
var i, j; var i, j, r;
r = "";
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
for(i = 0; i < 100; i++) r = "";
for(i = 0; i < 1000; i++)
r = "x" + r; r = "x" + r;
global_res = r; global_res = r;
} }
return n * 100; return n * 1000;
} }
/* incremental string contruction with multiple reference */ /* incremental string contruction with multiple reference */
function string_build4(n) function string_build4(n)
{ {
var i, j, r, s; var i, j, r, s;
r = "";
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
for(i = 0; i < 100; i++) { r = "";
for(i = 0; i < 1000; i++) {
s = r; s = r;
r += "x"; r += "x";
} }
global_res = r; global_res = r;
} }
return n * 100; return n * 1000;
} }
/* sort bench */ /* sort bench */
@ -862,32 +1060,33 @@ function sort_bench(text) {
": " + arr[i - 1] + " > " + arr[i]); ": " + arr[i - 1] + " > " + arr[i]);
} }
if (sort_bench.verbose) if (sort_bench.verbose)
log_one("sort_" + f.name, n, ti, n * 100); log_one("sort_" + f.name, 1, ti / 100);
} }
total_score = save_total_score; total_score = save_total_score;
total_scale = save_total_scale; total_scale = save_total_scale;
return total / n / 1000; return total / n / 100;
} }
sort_bench.bench = true; sort_bench.bench = true;
sort_bench.verbose = false; sort_bench.verbose = false;
function int_to_string(n) function int_to_string(n)
{ {
var s, r, j; var s, j;
r = 0;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
s = (j + 1).toString(); s = (j % 1000).toString();
s = (1234000 + j % 1000).toString();
} }
return n; global_res = s;
return n * 2;
} }
function float_to_string(n) function float_to_string(n)
{ {
var s, r, j; var s, j;
r = 0;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
s = (j + 0.1).toString(); s = (j + 0.1).toString();
} }
global_res = s;
return n; return n;
} }
@ -896,7 +1095,6 @@ function string_to_int(n)
var s, r, j; var s, r, j;
r = 0; r = 0;
s = "12345"; s = "12345";
r = 0;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
r += (s | 0); r += (s | 0);
} }
@ -909,7 +1107,6 @@ function string_to_float(n)
var s, r, j; var s, r, j;
r = 0; r = 0;
s = "12345.6"; s = "12345.6";
r = 0;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
r -= s; r -= s;
} }
@ -919,41 +1116,92 @@ function string_to_float(n)
function load_result(filename) function load_result(filename)
{ {
var f, str, res; var has_filename = filename;
if (typeof std === "undefined") var has_error = false;
var str, res;
if (!filename)
filename = "microbench.txt";
if (typeof fs !== "undefined") {
// read the file in Node.js
try {
str = fs.readFileSync(filename, { encoding: "utf8" });
} catch {
has_error = true;
}
} else
if (typeof std !== "undefined") {
// read the file in QuickJS
var f = std.open(filename, "r");
if (f) {
str = f.readAsString();
f.close();
} else {
has_error = true;
}
} else {
return null; return null;
f = std.open(filename, "r"); }
if (!f) if (has_error) {
if (has_filename) {
// Should throw exception?
console.log("cannot load " + filename);
}
return null; return null;
str = f.readAsString(); }
res = JSON.parse(str); res = JSON.parse(str);
f.close();
return res; return res;
} }
function save_result(filename, obj) function save_result(filename, obj)
{ {
var f; var str = JSON.stringify(obj, null, 2) + "\n";
if (typeof std === "undefined") var has_error = false;
if (typeof fs !== "undefined") {
// save the file in Node.js
try {
str = fs.writeFileSync(filename, str, { encoding: "utf8" });
} catch {
has_error = true;
}
} else
if (typeof std !== "undefined") {
// save the file in QuickJS
var f = std.open(filename, "w");
if (f) {
f.puts(str);
f.close();
} else {
has_error = 'true';
}
} else {
return; return;
f = std.open(filename, "w"); }
f.puts(JSON.stringify(obj, null, 2)); if (has_error)
f.puts("\n"); console.log("cannot save " + filename);
f.close();
} }
function main(argc, argv, g) function main(argc, argv, g)
{ {
var test_list = [ var test_list = [
empty_loop, empty_loop,
empty_down_loop,
empty_down_loop2,
empty_do_loop,
date_now, date_now,
date_parse,
prop_read, prop_read,
prop_write, prop_write,
prop_update,
prop_create, prop_create,
prop_clone,
prop_delete, prop_delete,
array_read, array_read,
array_write, array_write,
array_prop_create, array_prop_create,
array_slice,
array_length_decr, array_length_decr,
array_hole_length_decr, array_hole_length_decr,
array_push, array_push,
@ -966,8 +1214,9 @@ function main(argc, argv, g)
local_destruct, local_destruct,
global_destruct, global_destruct,
global_destruct_strict, global_destruct_strict,
global_func_call,
func_call, func_call,
closure_var, func_closure_call,
int_arith, int_arith,
float_arith, float_arith,
set_collection_add, set_collection_add,
@ -975,29 +1224,34 @@ function main(argc, argv, g)
array_for_in, array_for_in,
array_for_of, array_for_of,
math_min, math_min,
regexp_ascii,
regexp_utf16,
string_build1, string_build1,
string_build1x,
string_build2c,
string_build2, string_build2,
//string_build3, string_build3,
//string_build4, string_build4,
sort_bench,
int_to_string, int_to_string,
float_to_string, float_to_string,
string_to_int, string_to_int,
string_to_float, string_to_float,
]; ];
var tests = []; var tests = [];
var i, j, n, f, name; var i, j, n, f, name, found;
var ref_file, new_ref_file = "microbench-new.txt";
if (typeof BigInt == "function") {
if (typeof BigInt === "function") {
/* BigInt test */ /* BigInt test */
test_list.push(bigint64_arith); test_list.push(bigint64_arith);
test_list.push(bigint256_arith); test_list.push(bigint256_arith);
} }
if (typeof BigFloat == "function") { if (typeof BigFloat === "function") {
/* BigFloat test */ /* BigFloat test */
test_list.push(float256_arith); test_list.push(float256_arith);
} }
test_list.push(sort_bench);
for (i = 1; i < argc;) { for (i = 1; i < argc;) {
name = argv[i++]; name = argv[i++];
if (name == "-a") { if (name == "-a") {
@ -1007,7 +1261,7 @@ function main(argc, argv, g)
if (name == "-t") { if (name == "-t") {
name = argv[i++]; name = argv[i++];
sort_bench.array_type = g[name]; sort_bench.array_type = g[name];
if (typeof sort_bench.array_type != "function") { if (typeof sort_bench.array_type !== "function") {
console.log("unknown array type: " + name); console.log("unknown array type: " + name);
return 1; return 1;
} }
@ -1017,14 +1271,22 @@ function main(argc, argv, g)
sort_bench.array_size = +argv[i++]; sort_bench.array_size = +argv[i++];
continue; continue;
} }
for (j = 0; j < test_list.length; j++) { if (name == "-r") {
ref_file = argv[i++];
continue;
}
if (name == "-s") {
new_ref_file = argv[i++];
continue;
}
for (j = 0, found = false; j < test_list.length; j++) {
f = test_list[j]; f = test_list[j];
if (name === f.name) { if (f.name.startsWith(name)) {
tests.push(f); tests.push(f);
break; found = true;
} }
} }
if (j == test_list.length) { if (!found) {
console.log("unknown benchmark: " + name); console.log("unknown benchmark: " + name);
return 1; return 1;
} }
@ -1032,7 +1294,7 @@ function main(argc, argv, g)
if (tests.length == 0) if (tests.length == 0)
tests = test_list; tests = test_list;
ref_data = load_result("microbench.txt"); ref_data = load_result(ref_file);
log_data = {}; log_data = {};
log_line.apply(null, heads); log_line.apply(null, heads);
n = 0; n = 0;
@ -1044,14 +1306,17 @@ function main(argc, argv, g)
n++; n++;
} }
if (ref_data) if (ref_data)
log_line("total", "", total[2], total[3], total_score * 100 / total_scale); log_line("total", "", total[2], total[3], Math.round(total_scale * 1000 / total_score));
else else
log_line("total", "", total[2]); log_line("total", "", total[2]);
if (tests == test_list) if (tests == test_list && new_ref_file)
save_result("microbench-new.txt", log_data); save_result(new_ref_file, log_data);
} }
if (!scriptArgs) if (typeof scriptArgs === "undefined") {
scriptArgs = []; scriptArgs = [];
if (typeof process.argv === "object")
scriptArgs = process.argv.slice(1);
}
main(scriptArgs.length, scriptArgs, this); main(scriptArgs.length, scriptArgs, this);

View file

@ -28,7 +28,7 @@ index be7039fda0..7b38abf8df 100644
@@ -6,24 +6,27 @@ description: | @@ -6,24 +6,27 @@ description: |
defines: [buildString, testPropertyEscapes, matchValidator] defines: [buildString, testPropertyEscapes, matchValidator]
---*/ ---*/
+if ($262 && typeof $262.codePointRange === "function") { +if ($262 && typeof $262.codePointRange === "function") {
+ /* use C function to build the codePointRange (much faster with + /* use C function to build the codePointRange (much faster with
+ slow JS engines) */ + slow JS engines) */
@ -67,5 +67,5 @@ index be7039fda0..7b38abf8df 100644
- return result; - return result;
+ return result; + return result;
} }
function testPropertyEscapes(regex, string, expression) { function testPropertyEscapes(regex, string, expression) {

279
tests/test_bigfloat.js Normal file
View file

@ -0,0 +1,279 @@
"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) {}
/*----------------*/
/* 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_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_bigint_ext();
test_bigfloat();
test_bigdecimal();

View file

@ -94,7 +94,7 @@ function test_bigint1()
r = 1n << 31n; r = 1n << 31n;
assert(r, 2147483648n, "1 << 31n === 2147483648n"); assert(r, 2147483648n, "1 << 31n === 2147483648n");
r = 1n << 32n; r = 1n << 32n;
assert(r, 4294967296n, "1 << 32n === 4294967296n"); assert(r, 4294967296n, "1 << 32n === 4294967296n");
} }
@ -110,217 +110,5 @@ function test_bigint2()
assertThrows(SyntaxError, () => { BigInt(" 123 r") } ); 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_bigint1();
test_bigint2(); test_bigint2();
test_bigint_ext();
test_bigfloat();
test_bigdecimal();

View file

@ -34,7 +34,7 @@ function toHex(a)
function isArrayLike(a) function isArrayLike(a)
{ {
return Array.isArray(a) || return Array.isArray(a) ||
(a instanceof Uint8ClampedArray) || (a instanceof Uint8ClampedArray) ||
(a instanceof Uint8Array) || (a instanceof Uint8Array) ||
(a instanceof Uint16Array) || (a instanceof Uint16Array) ||
@ -147,7 +147,7 @@ function bjson_test_reference()
function bjson_test_all() function bjson_test_all()
{ {
var obj; var obj;
bjson_test({x:1, y:2, if:3}); bjson_test({x:1, y:2, if:3});
bjson_test([1, 2, 3]); bjson_test([1, 2, 3]);
bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]); bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]);
@ -174,7 +174,7 @@ function bjson_test_all()
bjson_test(new Int32Array([123123, 222111, -32222])); bjson_test(new Int32Array([123123, 222111, -32222]));
bjson_test(new Float64Array([123123, 222111.5])); bjson_test(new Float64Array([123123, 222111.5]));
/* tested with a circular reference */ /* tested with a circular reference */
obj = {}; obj = {};
obj.x = obj; obj.x = obj;

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");
} }
} }
@ -57,7 +94,7 @@ function test_function()
} }
var r, g; var r, g;
r = my_func.call(null, 1, 2); r = my_func.call(null, 1, 2);
assert(r, 3, "call"); assert(r, 3, "call");
@ -70,10 +107,10 @@ function test_function()
assert_throws(TypeError, (function() { assert_throws(TypeError, (function() {
Reflect.apply((function () { return 1; }), null, undefined); Reflect.apply((function () { return 1; }), null, undefined);
})); }));
r = new Function("a", "b", "return a + b;"); r = new Function("a", "b", "return a + b;");
assert(r(2,3), 5, "function"); assert(r(2,3), 5, "function");
g = f.bind(1, 2); g = f.bind(1, 2);
assert(g.length, 1); assert(g.length, 1);
assert(g.name, "bound f"); assert(g.name, "bound f");
@ -103,7 +140,7 @@ function test()
assert(a.z, 4, "get"); assert(a.z, 4, "get");
a.z = 5; a.z = 5;
assert(a.z_val, 5, "set"); assert(a.z_val, 5, "set");
a = { get z() { return 4; }, set z(val) { this.z_val = val; } }; a = { get z() { return 4; }, set z(val) { this.z_val = val; } };
assert(a.z, 4, "get"); assert(a.z, 4, "get");
a.z = 5; a.z = 5;
@ -207,7 +244,7 @@ function test_string()
assert(a.charAt(1), "b"); assert(a.charAt(1), "b");
assert(a.charAt(-1), ""); assert(a.charAt(-1), "");
assert(a.charAt(3), ""); assert(a.charAt(3), "");
a = "abcd"; a = "abcd";
assert(a.substring(1, 3), "bc", "substring"); assert(a.substring(1, 3), "bc", "substring");
a = String.fromCharCode(0x20ac); a = String.fromCharCode(0x20ac);
@ -216,7 +253,7 @@ function test_string()
assert(a, "\u20ac", "unicode"); assert(a, "\u20ac", "unicode");
assert(a, "\u{20ac}", "unicode"); assert(a, "\u{20ac}", "unicode");
assert("a", "\x61", "unicode"); assert("a", "\x61", "unicode");
a = "\u{10ffff}"; a = "\u{10ffff}";
assert(a.length, 2, "unicode"); assert(a.length, 2, "unicode");
assert(a, "\u{dbff}\u{dfff}", "unicode"); assert(a, "\u{dbff}\u{dfff}", "unicode");
@ -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,6 +381,11 @@ function test_number()
assert(Number.isNaN(Number("-"))); assert(Number.isNaN(Number("-")));
assert(Number.isNaN(Number("\x00a"))); assert(Number.isNaN(Number("\x00a")));
// TODO: Fix rounding errors on Windows/Cygwin.
if (typeof os !== 'undefined' && ['win32', 'cygwin'].includes(os.platform)) {
return;
}
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");
@ -379,7 +429,7 @@ function test_eval()
assert(eval("if (0) 2; else 3;"), 3); assert(eval("if (0) 2; else 3;"), 3);
assert(f.call(1, "this"), 1); assert(f.call(1, "this"), 1);
a = 2; a = 2;
assert(eval("a"), 2); assert(eval("a"), 2);
@ -424,7 +474,7 @@ function test_typed_array()
a[2] = 0.5; a[2] = 0.5;
a[3] = 1233.5; a[3] = 1233.5;
assert(a.toString(), "0,2,0,255"); assert(a.toString(), "0,2,0,255");
buffer = new ArrayBuffer(16); buffer = new ArrayBuffer(16);
assert(buffer.byteLength, 16); assert(buffer.byteLength, 16);
a = new Uint32Array(buffer, 12, 1); a = new Uint32Array(buffer, 12, 1);
@ -436,7 +486,7 @@ function test_typed_array()
a = new Float32Array(buffer, 8, 1); a = new Float32Array(buffer, 8, 1);
a[0] = 1; a[0] = 1;
a = new Uint8Array(buffer); a = new Uint8Array(buffer);
str = a.toString(); str = a.toString();
@ -481,26 +531,105 @@ function test_json()
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()
@ -525,7 +654,7 @@ function test_regexp()
a = /(\.(?!com|org)|\/)/.exec("ah.com"); a = /(\.(?!com|org)|\/)/.exec("ah.com");
assert(a, null); assert(a, null);
a = /(?=(a+))/.exec("baaabac"); a = /(?=(a+))/.exec("baaabac");
assert(a.index === 1 && a[0] === "" && a[1] === "aaa"); assert(a.index === 1 && a[0] === "" && a[1] === "aaa");
@ -587,6 +716,20 @@ function test_map()
{ {
var a, i, n, tab, o, v; var a, i, n, tab, o, v;
n = 1000; 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);
a = new Map(); a = new Map();
tab = []; tab = [];
for(i = 0; i < n; i++) { for(i = 0; i < n; i++) {
@ -602,7 +745,7 @@ function test_map()
} }
i = 0; i = 0;
a.forEach(function (v, o) { a.forEach(function (v, o) {
assert(o, tab[i++][0]); assert(o, tab[i++][0]);
assert(a.has(o)); assert(a.has(o));
assert(a.delete(o)); assert(a.delete(o));
@ -625,7 +768,7 @@ function test_weak_map()
a.set(o, v); a.set(o, v);
} }
o = null; o = null;
n2 = n >> 1; n2 = n >> 1;
for(i = 0; i < n2; i++) { for(i = 0; i < n2; i++) {
a.delete(tab[i][0]); a.delete(tab[i][0]);

View file

@ -54,7 +54,7 @@ function test_closure1()
function f2() function f2()
{ {
var val = 1; var val = 1;
function set(a) { function set(a) {
val = a; val = a;
} }
@ -63,7 +63,7 @@ function test_closure1()
} }
return { "set": set, "get": get }; return { "set": set, "get": get };
} }
var obj = f2(); var obj = f2();
obj.set(10); obj.set(10);
var r; var r;

View file

@ -68,10 +68,10 @@ function test_op1()
r = 1 << 31; r = 1 << 31;
assert(r, -2147483648, "1 << 31 === -2147483648"); assert(r, -2147483648, "1 << 31 === -2147483648");
r = 1 << 32; r = 1 << 32;
assert(r, 1, "1 << 32 === 1"); assert(r, 1, "1 << 32 === 1");
r = (1 << 31) < 0; r = (1 << 31) < 0;
assert(r, true, "(1 << 31) < 0 === true"); assert(r, true, "(1 << 31) < 0 === true");
@ -113,7 +113,7 @@ function test_cvt()
assert(("12345" | 0) === 12345); assert(("12345" | 0) === 12345);
assert(("0x12345" | 0) === 0x12345); assert(("0x12345" | 0) === 0x12345);
assert(((4294967296 * 3 - 4) | 0) === -4); assert(((4294967296 * 3 - 4) | 0) === -4);
assert(("12345" >>> 0) === 12345); assert(("12345" >>> 0) === 12345);
assert(("0x12345" >>> 0) === 0x12345); assert(("0x12345" >>> 0) === 0x12345);
assert((NaN >>> 0) === 0); assert((NaN >>> 0) === 0);
@ -141,7 +141,7 @@ function test_eq()
function test_inc_dec() function test_inc_dec()
{ {
var a, r; var a, r;
a = 1; a = 1;
r = a++; r = a++;
assert(r === 1 && a === 2, true, "++"); assert(r === 1 && a === 2, true, "++");
@ -169,19 +169,19 @@ function test_inc_dec()
a = [true]; a = [true];
a[0]++; a[0]++;
assert(a[0], 2, "++"); assert(a[0], 2, "++");
a = {x:true}; a = {x:true};
r = a.x++; r = a.x++;
assert(r === 1 && a.x === 2, true, "++"); assert(r === 1 && a.x === 2, true, "++");
a = {x:true}; a = {x:true};
r = a.x--; r = a.x--;
assert(r === 1 && a.x === 0, true, "--"); assert(r === 1 && a.x === 0, true, "--");
a = [true]; a = [true];
r = a[0]++; r = a[0]++;
assert(r === 1 && a[0] === 2, true, "++"); assert(r === 1 && a[0] === 2, true, "++");
a = [true]; a = [true];
r = a[0]--; r = a[0]--;
assert(r === 1 && a[0] === 0, true, "--"); assert(r === 1 && a[0] === 0, true, "--");
@ -213,7 +213,7 @@ function test_op2()
assert((typeof Object), "function", "typeof"); assert((typeof Object), "function", "typeof");
assert((typeof null), "object", "typeof"); assert((typeof null), "object", "typeof");
assert((typeof unknown_var), "undefined", "typeof"); assert((typeof unknown_var), "undefined", "typeof");
a = {x: 1, if: 2, async: 3}; a = {x: 1, if: 2, async: 3};
assert(a.if === 2); assert(a.if === 2);
assert(a.async === 3); assert(a.async === 3);
@ -226,7 +226,7 @@ function test_delete()
a = {x: 1, y: 1}; a = {x: 1, y: 1};
assert((delete a.x), true, "delete"); assert((delete a.x), true, "delete");
assert(("x" in a), false, "delete"); assert(("x" in a), false, "delete");
/* the following are not tested by test262 */ /* the following are not tested by test262 */
assert(delete "abc"[100], true); assert(delete "abc"[100], true);
@ -311,7 +311,7 @@ function test_class()
o = new C(); o = new C();
assert(o.f() === 1); assert(o.f() === 1);
assert(o.x === 10); assert(o.x === 10);
assert(D.F() === -1); assert(D.F() === -1);
assert(D.G() === -2); assert(D.G() === -2);
assert(D.H() === -1); assert(D.H() === -1);
@ -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}');
@ -374,7 +382,7 @@ function test_regexp_skip()
var a, b; var a, b;
[a, b = /abc\(/] = [1]; [a, b = /abc\(/] = [1];
assert(a === 1); assert(a === 1);
[a, b =/abc\(/] = [2]; [a, b =/abc\(/] = [2];
assert(a === 2); assert(a === 2);
} }
@ -419,9 +427,13 @@ 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);
@ -490,7 +502,7 @@ function test_function_expr_name()
/* non strict mode test : assignment to the function name silently /* non strict mode test : assignment to the function name silently
fails */ fails */
f = function myfunc() { f = function myfunc() {
myfunc = 1; myfunc = 1;
return myfunc; return myfunc;
@ -511,7 +523,7 @@ function test_function_expr_name()
return myfunc; return myfunc;
}; };
assert(f(), f); assert(f(), f);
/* strict mode test : assignment to the function name raises a /* strict mode test : assignment to the function name raises a
TypeError exception */ TypeError exception */
@ -558,6 +570,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()
{ {
@ -572,7 +593,7 @@ function test_optional_chaining()
assert(delete z?.b["c"], true); assert(delete z?.b["c"], true);
assert(delete a?.b["c"], true); assert(delete a?.b["c"], true);
assert(JSON.stringify(a), '{"b":{}}'); assert(JSON.stringify(a), '{"b":{}}');
a = { a = {
b() { return this._b; }, b() { return this._b; },
_b: { c: 42 } _b: { c: 42 }
@ -604,3 +625,4 @@ 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();

View file

@ -356,7 +356,7 @@ function test_try_catch7()
function test_try_catch8() function test_try_catch8()
{ {
var i, s; var i, s;
s = ""; s = "";
for(var i in {x:1, y:2}) { for(var i in {x:1, y:2}) {
try { try {

View file

@ -35,7 +35,7 @@ function test_operators_create() {
return "Vec2(" + this.x + "," + this.y + ")"; return "Vec2(" + this.x + "," + this.y + ")";
} }
} }
Vec2.prototype[Symbol.operatorSet] = Operators.create( Vec2.prototype[Symbol.operatorSet] = Operators.create(
{ {
"+"(p1, p2) { "+"(p1, p2) {
@ -172,7 +172,7 @@ function test_operators()
return "Vec2(" + this.x + "," + this.y + ")"; return "Vec2(" + this.x + "," + this.y + ")";
} }
} }
var a = new Vec2(1, 2); var a = new Vec2(1, 2);
var b = new Vec2(3, 4); var b = new Vec2(3, 4);
var r; var r;

View file

@ -60,10 +60,10 @@ function test_integer()
r = 1 << 31; r = 1 << 31;
assert(r, 2147483648, "1 << 31 === 2147483648"); assert(r, 2147483648, "1 << 31 === 2147483648");
r = 1 << 32; r = 1 << 32;
assert(r, 4294967296, "1 << 32 === 4294967296"); assert(r, 4294967296, "1 << 32 === 4294967296");
r = (1 << 31) < 0; r = (1 << 31) < 0;
assert(r, false, "(1 << 31) < 0 === false"); assert(r, false, "(1 << 31) < 0 === false");
@ -115,7 +115,7 @@ function test_fraction()
function test_mod() function test_mod()
{ {
var a, b, p; var a, b, p;
a = Mod(3, 101); a = Mod(3, 101);
b = Mod(-1, 101); b = Mod(-1, 101);
assert((a + b) == Mod(2, 101)); assert((a + b) == Mod(2, 101));
@ -131,7 +131,7 @@ function test_polynomial()
var a, b, q, r, t, i; var a, b, q, r, t, i;
a = (1 + X) ^ 4; a = (1 + X) ^ 4;
assert(a == X^4+4*X^3+6*X^2+4*X+1); assert(a == X^4+4*X^3+6*X^2+4*X+1);
r = (1 + X); r = (1 + X);
q = (1+X+X^2); q = (1+X+X^2);
b = (1 - X^2); b = (1 - X^2);

View file

@ -46,7 +46,7 @@ function test_file1()
f.seek(0, std.SEEK_SET); f.seek(0, std.SEEK_SET);
str1 = f.readAsString(); str1 = f.readAsString();
assert(str1 === str); assert(str1 === str);
f.seek(0, std.SEEK_END); f.seek(0, std.SEEK_END);
size = f.tell(); size = f.tell();
assert(size === str.length); assert(size === str.length);
@ -81,7 +81,7 @@ function test_file2()
function test_getline() function test_getline()
{ {
var f, line, line_count, lines, i; var f, line, line_count, lines, i;
lines = ["hello world", "line 1", "line 2" ]; lines = ["hello world", "line 1", "line 2" ];
f = std.tmpfile(); f = std.tmpfile();
for(i = 0; i < lines.length; i++) { for(i = 0; i < lines.length; i++) {
@ -103,7 +103,7 @@ function test_getline()
f.close(); f.close();
} }
function test_popen() function test_popen()
{ {
var str, f, fname = "tmp_file.txt"; var str, f, fname = "tmp_file.txt";
@ -115,7 +115,7 @@ function test_popen()
/* test loadFile */ /* test loadFile */
assert(std.loadFile(fname), content); assert(std.loadFile(fname), content);
/* execute the 'cat' shell command */ /* execute the 'cat' shell command */
f = std.popen("cat " + fname, "r"); f = std.popen("cat " + fname, "r");
str = f.readAsString(); str = f.readAsString();
@ -144,23 +144,25 @@ 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";
fpath = fdir + "/" + fname; fpath = fdir + "/" + fname;
link_path = fdir + "/test_link"; link_path = fdir + "/test_link";
os.remove(link_path); os.remove(link_path);
os.remove(fpath); os.remove(fpath);
os.remove(fdir); os.remove(fdir);
err = os.mkdir(fdir, 0o755); err = os.mkdir(fdir, 0o755);
assert(err === 0); assert(err === 0);
fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC); fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC);
assert(fd >= 0); assert(fd >= 0);
buf = new Uint8Array(10); buf = new Uint8Array(10);
for(i = 0; i < buf.length; i++) for(i = 0; i < buf.length; i++)
buf[i] = i; buf[i] = i;
@ -169,16 +171,16 @@ function test_os()
assert(os.seek(fd, 0, std.SEEK_SET) === 0); assert(os.seek(fd, 0, std.SEEK_SET) === 0);
buf2 = new Uint8Array(buf.length); buf2 = new Uint8Array(buf.length);
assert(os.read(fd, buf2.buffer, 0, buf2.length) === buf2.length); assert(os.read(fd, buf2.buffer, 0, buf2.length) === buf2.length);
for(i = 0; i < buf.length; i++) for(i = 0; i < buf.length; i++)
assert(buf[i] == buf2[i]); assert(buf[i] == buf2[i]);
if (typeof BigInt !== "undefined") { if (typeof BigInt !== "undefined") {
assert(os.seek(fd, BigInt(6), std.SEEK_SET), BigInt(6)); assert(os.seek(fd, BigInt(6), std.SEEK_SET), BigInt(6));
assert(os.read(fd, buf2.buffer, 0, 1) === 1); assert(os.read(fd, buf2.buffer, 0, 1) === 1);
assert(buf[6] == buf2[0]); assert(buf[6] == buf2[0]);
} }
assert(os.close(fd) === 0); assert(os.close(fd) === 0);
[files, err] = os.readdir(fdir); [files, err] = os.readdir(fdir);
@ -189,7 +191,7 @@ function test_os()
err = os.utimes(fpath, fdate, fdate); err = os.utimes(fpath, fdate, fdate);
assert(err, 0); assert(err, 0);
[st, err] = os.stat(fpath); [st, err] = os.stat(fpath);
assert(err, 0); assert(err, 0);
assert(st.mode & os.S_IFMT, os.S_IFREG); assert(st.mode & os.S_IFMT, os.S_IFREG);
@ -197,7 +199,7 @@ function test_os()
err = os.symlink(fname, link_path); err = os.symlink(fname, link_path);
assert(err === 0); assert(err === 0);
[st, err] = os.lstat(link_path); [st, err] = os.lstat(link_path);
assert(err, 0); assert(err, 0);
assert(st.mode & os.S_IFMT, os.S_IFLNK); assert(st.mode & os.S_IFMT, os.S_IFLNK);
@ -205,7 +207,7 @@ function test_os()
[buf, err] = os.readlink(link_path); [buf, err] = os.readlink(link_path);
assert(err, 0); assert(err, 0);
assert(buf, fname); assert(buf, fname);
assert(os.remove(link_path) === 0); assert(os.remove(link_path) === 0);
[buf, err] = os.getcwd(); [buf, err] = os.getcwd();
@ -215,7 +217,7 @@ function test_os()
assert(err, 0); assert(err, 0);
assert(buf, buf2); assert(buf, buf2);
assert(os.remove(fpath) === 0); assert(os.remove(fpath) === 0);
fd = os.open(fpath, os.O_RDONLY); fd = os.open(fpath, os.O_RDONLY);
@ -233,7 +235,7 @@ function test_os_exec()
ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false }); ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false });
assert(ret, 1); assert(ret, 1);
fds = os.pipe(); fds = os.pipe();
pid = os.exec(["sh", "-c", "echo $FOO"], { pid = os.exec(["sh", "-c", "echo $FOO"], {
stdout: fds[1], stdout: fds[1],
@ -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()
@ -277,16 +280,16 @@ function test_async_gc()
{ {
(async function run () { (async function run () {
let obj = {} let obj = {}
let done = () => { let done = () => {
obj obj
std.gc(); std.gc();
} }
Promise.resolve().then(done) Promise.resolve().then(done)
const p = new Promise(() => {}) const p = new Promise(() => {})
await p await p
})(); })();
} }

View file

@ -22,10 +22,10 @@ function handle_msg(e) {
function worker_main() { function worker_main() {
var i; var i;
parent.onmessage = handle_msg; parent.onmessage = handle_msg;
for(i = 0; i < 10; i++) { for(i = 0; i < 10; i++) {
parent.postMessage({ type: "num", num: i }); parent.postMessage({ type: "num", num: i });
} }
} }

View file

@ -15,5 +15,5 @@ for f in $files; do
g="${url}/${f}" g="${url}/${f}"
wget $g -O unicode/$f wget $g -O unicode/$f
done done
wget $emoji_url -O unicode/emoji-data.txt wget $emoji_url -O unicode/emoji-data.txt

View file

@ -1,6 +1,6 @@
/* /*
* Generation of Unicode tables * Generation of Unicode tables
* *
* Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon * Copyright (c) 2017-2018 Charlie Gordon
* *
@ -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;
} }
@ -291,7 +296,7 @@ void parse_unicode_data(const char *filename)
const char *p; const char *p;
int code, lc, uc, last_code; int code, lc, uc, last_code;
CCInfo *ci, *tab = unicode_db; CCInfo *ci, *tab = unicode_db;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -314,7 +319,7 @@ void parse_unicode_data(const char *filename)
code = strtoul(p, NULL, 16); code = strtoul(p, NULL, 16);
lc = 0; lc = 0;
uc = 0; uc = 0;
p = get_field(line, 12); p = get_field(line, 12);
if (p && *p != ';') { if (p && *p != ';') {
uc = strtoul(p, NULL, 16); uc = strtoul(p, NULL, 16);
@ -350,7 +355,7 @@ void parse_unicode_data(const char *filename)
} }
ci->general_category = i; ci->general_category = i;
} }
p = get_field(line, 3); p = get_field(line, 3);
if (p && *p != ';' && *p != '\0') { if (p && *p != ';' && *p != '\0') {
int cc; int cc;
@ -402,7 +407,7 @@ void parse_unicode_data(const char *filename)
if (p && *p == 'Y') { if (p && *p == 'Y') {
set_prop(code, PROP_Bidi_Mirrored, 1); set_prop(code, PROP_Bidi_Mirrored, 1);
} }
/* handle ranges */ /* handle ranges */
get_field_buf(buf1, sizeof(buf1), line, 1); get_field_buf(buf1, sizeof(buf1), line, 1);
if (strstr(buf1, " Last>")) { if (strstr(buf1, " Last>")) {
@ -416,7 +421,7 @@ void parse_unicode_data(const char *filename)
} }
last_code = code; last_code = code;
} }
fclose(f); fclose(f);
} }
@ -427,7 +432,7 @@ void parse_special_casing(CCInfo *tab, const char *filename)
const char *p; const char *p;
int code; int code;
CCInfo *ci; CCInfo *ci;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -458,8 +463,8 @@ void parse_special_casing(CCInfo *tab, const char *filename)
if (*p != '#' && *p != '\0') if (*p != '#' && *p != '\0')
continue; continue;
} }
p = get_field(line, 1); p = get_field(line, 1);
if (p && *p != ';') { if (p && *p != ';') {
ci->l_len = 0; ci->l_len = 0;
@ -492,7 +497,7 @@ void parse_special_casing(CCInfo *tab, const char *filename)
ci->u_len = 0; ci->u_len = 0;
} }
} }
fclose(f); fclose(f);
} }
@ -503,7 +508,7 @@ void parse_case_folding(CCInfo *tab, const char *filename)
const char *p; const char *p;
int code, status; int code, status;
CCInfo *ci; CCInfo *ci;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -535,7 +540,7 @@ void parse_case_folding(CCInfo *tab, const char *filename)
status = *p; status = *p;
if (status != 'C' && status != 'S' && status != 'F') if (status != 'C' && status != 'S' && status != 'F')
continue; continue;
p = get_field(line, 2); p = get_field(line, 2);
assert(p != NULL); assert(p != NULL);
if (status == 'S') { if (status == 'S') {
@ -555,7 +560,7 @@ void parse_case_folding(CCInfo *tab, const char *filename)
ci->f_data[ci->f_len++] = strtoul(p, (char **)&p, 16); ci->f_data[ci->f_len++] = strtoul(p, (char **)&p, 16);
} }
} }
fclose(f); fclose(f);
} }
@ -564,7 +569,7 @@ void parse_composition_exclusions(const char *filename)
FILE *f; FILE *f;
char line[4096], *p; char line[4096], *p;
uint32_t c0; uint32_t c0;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -592,7 +597,7 @@ void parse_derived_core_properties(const char *filename)
char line[4096], *p, buf[256], *q; char line[4096], *p, buf[256], *q;
uint32_t c0, c1, c; uint32_t c0, c1, c;
int i; int i;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -648,7 +653,7 @@ void parse_derived_norm_properties(const char *filename)
FILE *f; FILE *f;
char line[4096], *p, buf[256], *q; char line[4096], *p, buf[256], *q;
uint32_t c0, c1, c; uint32_t c0, c1, c;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -698,7 +703,7 @@ void parse_prop_list(const char *filename)
char line[4096], *p, buf[256], *q; char line[4096], *p, buf[256], *q;
uint32_t c0, c1, c; uint32_t c0, c1, c;
int i; int i;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -752,7 +757,7 @@ void parse_scripts(const char *filename)
char line[4096], *p, buf[256], *q; char line[4096], *p, buf[256], *q;
uint32_t c0, c1, c; uint32_t c0, c1, c;
int i; int i;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -807,7 +812,7 @@ void parse_script_extensions(const char *filename)
int i; int i;
uint8_t script_ext[255]; uint8_t script_ext[255];
int script_ext_len; int script_ext_len;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -972,7 +977,7 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
ci1 = &tab[code + 1]; ci1 = &tab[code + 1];
ci2 = &tab[code + 2]; ci2 = &tab[code + 2];
te->code = code; te->code = code;
if (ci->l_len == 1 && ci->l_data[0] == code + 2 && if (ci->l_len == 1 && ci->l_data[0] == code + 2 &&
ci->f_len == 1 && ci->f_data[0] == ci->l_data[0] && ci->f_len == 1 && ci->f_data[0] == ci->l_data[0] &&
ci->u_len == 0 && ci->u_len == 0 &&
@ -1140,7 +1145,7 @@ void find_run_type(TableEntry *te, CCInfo *tab, int code)
te->data = 0; te->data = 0;
return; return;
} }
ci = &tab[code]; ci = &tab[code];
is_lower = ci->l_len > 0; is_lower = ci->l_len > 0;
len = 1; len = 1;
@ -1221,7 +1226,7 @@ void build_conv_table(CCInfo *tab)
int code, i, j; int code, i, j;
CCInfo *ci; CCInfo *ci;
TableEntry *te; TableEntry *te;
te = conv_table; te = conv_table;
for(code = 0; code <= CHARCODE_MAX; code++) { for(code = 0; code <= CHARCODE_MAX; code++) {
ci = &tab[code]; ci = &tab[code];
@ -1245,7 +1250,7 @@ void build_conv_table(CCInfo *tab)
for(i = 0; i < conv_table_len; i++) { for(i = 0; i < conv_table_len; i++) {
int data_index; int data_index;
te = &conv_table[i]; te = &conv_table[i];
switch(te->type) { switch(te->type) {
case RUN_TYPE_U: case RUN_TYPE_U:
case RUN_TYPE_L: case RUN_TYPE_L:
@ -1328,7 +1333,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 +1348,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 +1359,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 ");
@ -1374,7 +1385,7 @@ static int sp_cc_cmp(const void *p1, const void *p2)
return memcmp(c1->f_data, c2->f_data, sizeof(c1->f_data[0]) * c1->f_len); return memcmp(c1->f_data, c2->f_data, sizeof(c1->f_data[0]) * c1->f_len);
} }
} }
/* dump the case special cases (multi character results which are /* dump the case special cases (multi character results which are
identical and need specific handling in lre_canonicalize() */ identical and need specific handling in lre_canonicalize() */
void dump_case_folding_special_cases(CCInfo *tab) void dump_case_folding_special_cases(CCInfo *tab)
@ -1394,7 +1405,7 @@ void dump_case_folding_special_cases(CCInfo *tab)
len = 1; len = 1;
while ((i + len) <= CHARCODE_MAX && !sp_cc_cmp(&perm[i], &perm[i + len])) while ((i + len) <= CHARCODE_MAX && !sp_cc_cmp(&perm[i], &perm[i + len]))
len++; len++;
if (len > 1) { if (len > 1) {
for(j = i; j < i + len; j++) for(j = i; j < i + len; j++)
dump_cc_info(&tab[perm[j]], perm[j]); dump_cc_info(&tab[perm[j]], perm[j]);
@ -1405,7 +1416,7 @@ void dump_case_folding_special_cases(CCInfo *tab)
free(perm); free(perm);
global_tab = NULL; global_tab = NULL;
} }
int tabcmp(const int *tab1, const int *tab2, int n) int tabcmp(const int *tab1, const int *tab2, int n)
{ {
@ -1470,6 +1481,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 +1493,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;
@ -1490,7 +1521,7 @@ void build_prop_table(FILE *f, int prop_index, BOOL add_index)
const uint32_t *buf; const uint32_t *buf;
int buf_len, block_end_pos, bit; int buf_len, block_end_pos, bit;
char cname[128]; char cname[128];
dbuf_init(dbuf1); dbuf_init(dbuf1);
for(i = 0; i <= CHARCODE_MAX;) { for(i = 0; i <= CHARCODE_MAX;) {
@ -1506,15 +1537,15 @@ void build_prop_table(FILE *f, int prop_index, BOOL add_index)
dbuf_put_u32(dbuf1, n - 1); dbuf_put_u32(dbuf1, n - 1);
i += n; i += n;
} }
dbuf_init(dbuf); dbuf_init(dbuf);
dbuf_init(dbuf2); dbuf_init(dbuf2);
buf = (uint32_t *)dbuf1->buf; buf = (uint32_t *)dbuf1->buf;
buf_len = dbuf1->size / sizeof(buf[0]); buf_len = dbuf1->size / sizeof(buf[0]);
/* the first value is assumed to be 0 */ /* the first value is assumed to be 0 */
assert(get_prop(0, prop_index) == 0); assert(get_prop(0, prop_index) == 0);
block_end_pos = PROP_BLOCK_LEN; block_end_pos = PROP_BLOCK_LEN;
i = 0; i = 0;
code = 0; code = 0;
@ -1533,6 +1564,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,9 +1612,9 @@ 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);
dbuf_free(dbuf1); dbuf_free(dbuf1);
dbuf_free(dbuf2); dbuf_free(dbuf2);
@ -1583,10 +1622,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 +1660,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 +1676,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 +1699,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,17 +1725,18 @@ 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);
#endif #endif
dump_byte_table(f, "unicode_gc_table", dbuf->buf, dbuf->size); dump_byte_table(f, "unicode_gc_table", dbuf->buf, dbuf->size);
dbuf_free(dbuf); dbuf_free(dbuf);
@ -1700,7 +1746,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 +1762,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 +1776,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,17 +1802,18 @@ 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);
#endif #endif
dump_byte_table(f, "unicode_script_table", dbuf->buf, dbuf->size); dump_byte_table(f, "unicode_script_table", dbuf->buf, dbuf->size);
dbuf_free(dbuf); dbuf_free(dbuf);
@ -1770,10 +1823,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 +1838,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,11 +1862,10 @@ 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
dump_byte_table(f, "unicode_script_ext_table", dbuf->buf, dbuf->size); dump_byte_table(f, "unicode_script_ext_table", dbuf->buf, dbuf->size);
dbuf_free(dbuf); dbuf_free(dbuf);
@ -1822,17 +1877,17 @@ void build_script_ext_table(FILE *f)
void build_prop_list_table(FILE *f) void build_prop_list_table(FILE *f)
{ {
int i; int i;
for(i = 0; i < PROP_TABLE_COUNT; i++) { for(i = 0; i < PROP_TABLE_COUNT; i++) {
if (i == PROP_ID_Start || if (i == PROP_ID_Start ||
i == PROP_Case_Ignorable || i == PROP_Case_Ignorable ||
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);
} }
} }
fprintf(f, "typedef enum {\n"); fprintf(f, "typedef enum {\n");
for(i = 0; i < PROP_COUNT; i++) for(i = 0; i < PROP_COUNT; i++)
fprintf(f, " UNICODE_PROP_%s,\n", unicode_prop_name[i]); fprintf(f, " UNICODE_PROP_%s,\n", unicode_prop_name[i]);
@ -1870,7 +1925,7 @@ void check_case_conv(void)
int l, error; int l, error;
CCInfo ci_s, *ci1, *ci = &ci_s; CCInfo ci_s, *ci1, *ci = &ci_s;
int code; int code;
for(code = 0; code <= CHARCODE_MAX; code++) { for(code = 0; code <= CHARCODE_MAX; code++) {
ci1 = &tab[code]; ci1 = &tab[code];
*ci = *ci1; *ci = *ci1;
@ -1926,7 +1981,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 +1989,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 +1997,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 +2005,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 +2019,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 +2036,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 +2093,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 +2116,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;
} }
@ -2058,9 +2130,9 @@ void build_cc_table(FILE *f)
dbuf_putc(dbuf1, v); dbuf_putc(dbuf1, v);
dbuf_putc(dbuf1, v >> 8); dbuf_putc(dbuf1, v >> 8);
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) [",
@ -2163,7 +2235,7 @@ const int decomp_incr_tab[4][4] = {
/* /*
entry size: entry size:
type bits type bits
code 18 code 18
len 7 len 7
compat 1 compat 1
type 5 type 5
@ -2272,7 +2344,7 @@ void find_decomp_run(DecompEntry *tab_de, int i)
DecompEntry de_s, *de = &de_s; DecompEntry de_s, *de = &de_s;
CCInfo *ci, *ci1, *ci2; CCInfo *ci, *ci1, *ci2;
int l, j, n, len_max; int l, j, n, len_max;
ci = &unicode_db[i]; ci = &unicode_db[i];
l = ci->decomp_len; l = ci->decomp_len;
if (l == 0) { if (l == 0) {
@ -2283,12 +2355,12 @@ void find_decomp_run(DecompEntry *tab_de, int i)
/* the offset for the compose table has only 6 bits, so we must /* the offset for the compose table has only 6 bits, so we must
limit if it can be used by the compose table */ limit if it can be used by the compose table */
if (!ci->is_compat && !ci->is_excluded && l == 2) if (!ci->is_compat && !ci->is_excluded && l == 2)
len_max = 64; len_max = 64;
else else
len_max = 127; len_max = 127;
tab_de[i].cost = 0x7fffffff; tab_de[i].cost = 0x7fffffff;
if (!is_16bit(ci->decomp_data, l)) { if (!is_16bit(ci->decomp_data, l)) {
assert(l <= 2); assert(l <= 2);
@ -2331,7 +2403,7 @@ void find_decomp_run(DecompEntry *tab_de, int i)
if (de->cost < tab_de[i].cost) { if (de->cost < tab_de[i].cost) {
tab_de[i] = *de; tab_de[i] = *de;
} }
if (!((i + n) <= CHARCODE_MAX && n < len_max)) if (!((i + n) <= CHARCODE_MAX && n < len_max))
break; break;
ci1 = &unicode_db[i + n]; ci1 = &unicode_db[i + n];
@ -2344,7 +2416,7 @@ void find_decomp_run(DecompEntry *tab_de, int i)
n++; n++;
} }
} }
if (l <= 8 || l == 18) { if (l <= 8 || l == 18) {
int c_min, c_max, c; int c_min, c_max, c;
c_min = c_max = -1; c_min = c_max = -1;
@ -2415,7 +2487,7 @@ void find_decomp_run(DecompEntry *tab_de, int i)
/* check if a single char is increasing */ /* check if a single char is increasing */
if (l <= 4) { if (l <= 4) {
int idx1, idx; int idx1, idx;
for(idx1 = 1; (idx = decomp_incr_tab[l - 1][idx1]) >= 0; idx1++) { for(idx1 = 1; (idx = decomp_incr_tab[l - 1][idx1]) >= 0; idx1++) {
n = 1; n = 1;
for(;;) { for(;;) {
@ -2499,7 +2571,7 @@ void find_decomp_run(DecompEntry *tab_de, int i)
if (l == 2) { if (l == 2) {
BOOL is_16bit; BOOL is_16bit;
n = 0; n = 0;
is_16bit = FALSE; is_16bit = FALSE;
for(;;) { for(;;) {
@ -2544,7 +2616,7 @@ void add_decomp_data(uint8_t *data_buf, int *pidx, DecompEntry *de)
{ {
int i, j, idx, c; int i, j, idx, c;
CCInfo *ci; CCInfo *ci;
idx = *pidx; idx = *pidx;
de->data_index = idx; de->data_index = idx;
if (de->type <= DECOMP_TYPE_C1) { if (de->type <= DECOMP_TYPE_C1) {
@ -2695,9 +2767,9 @@ void build_decompose_table(FILE *f)
int i, array_len, code_max, data_len, count; int i, array_len, code_max, data_len, count;
DecompEntry *tab_de, de_s, *de = &de_s; DecompEntry *tab_de, de_s, *de = &de_s;
uint8_t *data_buf; uint8_t *data_buf;
code_max = CHARCODE_MAX; code_max = CHARCODE_MAX;
tab_de = mallocz((code_max + 2) * sizeof(*tab_de)); tab_de = mallocz((code_max + 2) * sizeof(*tab_de));
for(i = code_max; i >= 0; i--) { for(i = code_max; i >= 0; i--) {
@ -2721,7 +2793,7 @@ void build_decompose_table(FILE *f)
/* dump */ /* dump */
{ {
int size, size1; int size, size1;
printf("START LEN TYPE L C SIZE\n"); printf("START LEN TYPE L C SIZE\n");
size = 0; size = 0;
for(i = 0; i <= code_max; i++) { for(i = 0; i <= code_max; i++) {
@ -2735,14 +2807,15 @@ void build_decompose_table(FILE *f)
size += size1; size += size1;
} }
} }
printf("array_len=%d estimated size=%d bytes actual=%d bytes\n", printf("array_len=%d estimated size=%d bytes actual=%d bytes\n",
array_len, size, array_len * 6 + data_len); array_len, size, array_len * 6 + data_len);
} }
#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 +2833,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];
@ -2773,9 +2847,10 @@ 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 ");
@ -2786,7 +2861,7 @@ void build_decompose_table(FILE *f)
build_compose_table(f, tab_de); build_compose_table(f, tab_de);
free(data_buf); free(data_buf);
free(tab_de); free(tab_de);
} }
@ -2817,7 +2892,7 @@ static int get_decomp_pos(const DecompEntry *tab_de, int c)
{ {
int i, v, k; int i, v, k;
const DecompEntry *de; const DecompEntry *de;
k = 0; k = 0;
for(i = 0; i <= CHARCODE_MAX; i++) { for(i = 0; i <= CHARCODE_MAX; i++) {
de = &tab_de[i]; de = &tab_de[i];
@ -2840,14 +2915,14 @@ void build_compose_table(FILE *f, const DecompEntry *tab_de)
{ {
int i, v, tab_ce_len; int i, v, tab_ce_len;
ComposeEntry *ce, *tab_ce; ComposeEntry *ce, *tab_ce;
tab_ce = malloc(sizeof(*tab_ce) * COMPOSE_LEN_MAX); tab_ce = malloc(sizeof(*tab_ce) * COMPOSE_LEN_MAX);
tab_ce_len = 0; tab_ce_len = 0;
for(i = 0; i <= CHARCODE_MAX; i++) { for(i = 0; i <= CHARCODE_MAX; i++) {
CCInfo *ci = &unicode_db[i]; CCInfo *ci = &unicode_db[i];
if (ci->decomp_len == 2 && !ci->is_compat && if (ci->decomp_len == 2 && !ci->is_compat &&
!ci->is_excluded) { !ci->is_excluded) {
assert(tab_ce_len < COMPOSE_LEN_MAX); assert(tab_ce_len < COMPOSE_LEN_MAX);
ce = &tab_ce[tab_ce_len++]; ce = &tab_ce[tab_ce_len++];
ce->c[0] = ci->decomp_data[0]; ce->c[0] = ci->decomp_data[0];
ce->c[1] = ci->decomp_data[1]; ce->c[1] = ci->decomp_data[1];
@ -2865,9 +2940,10 @@ 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 ");
@ -2880,7 +2956,7 @@ void build_compose_table(FILE *f, const DecompEntry *tab_de)
fprintf(f, " 0x%04x,", v); fprintf(f, " 0x%04x,", v);
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
free(tab_ce); free(tab_ce);
} }
@ -2929,7 +3005,7 @@ void check_compose_table(void)
} }
} }
} }
} }
@ -2969,7 +3045,7 @@ void check_cc_table(void)
#ifdef PROFILE #ifdef PROFILE
{ {
int64_t ti, count; int64_t ti, count;
ti = get_time_ns(); ti = get_time_ns();
count = 0; count = 0;
/* only do it on meaningful chars */ /* only do it on meaningful chars */
@ -2992,7 +3068,7 @@ void normalization_test(const char *filename)
int *in_str, *nfc_str, *nfd_str, *nfkc_str, *nfkd_str; int *in_str, *nfc_str, *nfd_str, *nfkc_str, *nfkd_str;
int in_len, nfc_len, nfd_len, nfkc_len, nfkd_len; int in_len, nfc_len, nfd_len, nfkc_len, nfkd_len;
int *buf, buf_len, pos; int *buf, buf_len, pos;
f = fopen(filename, "rb"); f = fopen(filename, "rb");
if (!f) { if (!f) {
perror(filename); perror(filename);
@ -3023,7 +3099,7 @@ void normalization_test(const char *filename)
buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFKD, NULL, NULL); buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFKD, NULL, NULL);
check_str("nfkd", pos, in_str, in_len, buf, buf_len, nfkd_str, nfkd_len); check_str("nfkd", pos, in_str, in_len, buf, buf_len, nfkd_str, nfkd_len);
free(buf); free(buf);
buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFC, NULL, NULL); buf_len = unicode_normalize((uint32_t **)&buf, (uint32_t *)in_str, in_len, UNICODE_NFC, NULL, NULL);
check_str("nfc", pos, in_str, in_len, buf, buf_len, nfc_str, nfc_len); check_str("nfc", pos, in_str, in_len, buf, buf_len, nfc_str, nfc_len);
free(buf); free(buf);
@ -3042,22 +3118,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) {
printf("usage: %s unicode_db_path [output_file]\n" if (arg >= argc || (!strcmp(argv[arg], "-h") || !strcmp(argv[arg], "--help"))) {
"\n" printf("usage: %s PATH [OUTPUT]\n"
"If no output_file is given, a self test is done using the current unicode library\n", " PATH path to the Unicode database directory\n"
argv[0]); " OUTPUT name of the output file. If omitted, a self test is performed\n"
exit(1); " using the files from the Unicode library\n"
, 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));
@ -3067,13 +3145,13 @@ int main(int argc, char **argv)
snprintf(filename, sizeof(filename), "%s/SpecialCasing.txt", unicode_db_path); snprintf(filename, sizeof(filename), "%s/SpecialCasing.txt", unicode_db_path);
parse_special_casing(unicode_db, filename); parse_special_casing(unicode_db, filename);
snprintf(filename, sizeof(filename), "%s/CaseFolding.txt", unicode_db_path); snprintf(filename, sizeof(filename), "%s/CaseFolding.txt", unicode_db_path);
parse_case_folding(unicode_db, filename); parse_case_folding(unicode_db, filename);
snprintf(filename, sizeof(filename), "%s/CompositionExclusions.txt", unicode_db_path); snprintf(filename, sizeof(filename), "%s/CompositionExclusions.txt", unicode_db_path);
parse_composition_exclusions(filename); parse_composition_exclusions(filename);
snprintf(filename, sizeof(filename), "%s/DerivedCoreProperties.txt", unicode_db_path); snprintf(filename, sizeof(filename), "%s/DerivedCoreProperties.txt", unicode_db_path);
parse_derived_core_properties(filename); parse_derived_core_properties(filename);
@ -3089,7 +3167,7 @@ int main(int argc, char **argv)
snprintf(filename, sizeof(filename), "%s/ScriptExtensions.txt", snprintf(filename, sizeof(filename), "%s/ScriptExtensions.txt",
unicode_db_path); unicode_db_path);
parse_script_extensions(filename); parse_script_extensions(filename);
snprintf(filename, sizeof(filename), "%s/emoji-data.txt", snprintf(filename, sizeof(filename), "%s/emoji-data.txt",
unicode_db_path); unicode_db_path);
parse_prop_list(filename); parse_prop_list(filename);
@ -3098,7 +3176,7 @@ int main(int argc, char **argv)
build_conv_table(unicode_db); build_conv_table(unicode_db);
#ifdef DUMP_CASE_FOLDING_SPECIAL_CASES #ifdef DUMP_CASE_FOLDING_SPECIAL_CASES
dump_case_folding_special_cases(unicode_db); dump_case_folding_special_cases(unicode_db);
#endif #endif
if (!outfilename) { if (!outfilename) {
@ -3117,7 +3195,7 @@ int main(int argc, char **argv)
} else } else
{ {
FILE *fo = fopen(outfilename, "wb"); FILE *fo = fopen(outfilename, "wb");
if (!fo) { if (!fo) {
perror(outfilename); perror(outfilename);
exit(1); exit(1);
@ -3139,6 +3217,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;