revert to previous version
Some checks are pending
ci / Linux (Ubuntu) (push) Waiting to run
ci / linux-asan (push) Waiting to run
ci / linux-msan (push) Waiting to run
ci / linux-ubsan (push) Waiting to run
ci / macOS (push) Waiting to run
ci / macos-asan (push) Waiting to run
ci / macos-ubsan (push) Waiting to run
ci / freebsd (push) Waiting to run
ci / qemu-alpine (arm32v6) (push) Waiting to run
ci / qemu-alpine (arm32v7) (push) Waiting to run
ci / qemu-alpine (arm64v8) (push) Waiting to run
ci / qemu-alpine (i386) (push) Waiting to run
ci / qemu-alpine (s390x) (push) Waiting to run

This commit is contained in:
The Ghost of FOSS' Past 2024-10-24 14:25:35 -05:00
parent 6e2e68fd08
commit 51ec4ed20b
58 changed files with 5011 additions and 4364 deletions

8
.gitignore vendored
View file

@ -1,18 +1,10 @@
*.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.c
repl.c
run-test262 run-test262
test262 test262
test262_*.txt test262_*.txt

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.sleepAsync(), os.getpid() and os.now() - added os.getpid() and os.now()
- added cosmopolitan build - added cosmopolitan build
- misc bug fixes - misc bug fixes

153
Makefile
View file

@ -25,13 +25,10 @@
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
@ -46,37 +43,16 @@ PREFIX?=/usr/local
#CONFIG_PROFILE=y #CONFIG_PROFILE=y
# use address sanitizer # use address sanitizer
#CONFIG_ASAN=y #CONFIG_ASAN=y
# use memory sanitizer # include the code for BigFloat/BigDecimal, math mode and faster large integers
#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
@ -111,7 +87,6 @@ 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
@ -131,7 +106,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
@ -143,11 +118,6 @@ 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
@ -172,14 +142,6 @@ 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
@ -214,15 +176,12 @@ endif
# examples # examples
ifeq ($(CROSS_PREFIX),) ifeq ($(CROSS_PREFIX),)
PROGS+=examples/hello
ifndef CONFIG_ASAN ifndef CONFIG_ASAN
ifndef CONFIG_MSAN PROGS+=examples/hello_module
ifndef CONFIG_UBSAN endif
PROGS+=examples/hello examples/hello_module examples/test_fib
ifdef CONFIG_SHARED_LIBS ifdef CONFIG_SHARED_LIBS
PROGS+=examples/fib.so examples/point.so PROGS+=examples/test_fib examples/fib.so examples/point.so
endif
endif
endif
endif endif
endif endif
@ -254,17 +213,6 @@ 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 \
@ -306,9 +254,6 @@ 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
@ -337,9 +282,6 @@ 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 $@ $<
@ -358,9 +300,6 @@ $(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 $@ $<
@ -372,18 +311,17 @@ 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 fuzz_eval fuzz_compile fuzz_regexp $(PROGS) rm -f *.a *.o *.d *~ unicode_gen regexp_test $(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$(EXE) qjsc$(EXE) $(STRIP) qjs qjsc
install -m755 qjs$(EXE) qjsc$(EXE) "$(DESTDIR)$(PREFIX)/bin" install -m755 qjs qjsc "$(DESTDIR)$(PREFIX)/bin"
ln -sf qjs$(EXE) "$(DESTDIR)$(PREFIX)/bin/qjscalc$(EXE)" ln -sf qjs "$(DESTDIR)$(PREFIX)/bin/qjscalc"
mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs" mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs"
install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs" install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs"
ifdef CONFIG_LTO ifdef CONFIG_LTO
@ -466,9 +404,8 @@ 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 --std tests/test_builtin.js ./qjs 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
@ -481,20 +418,19 @@ 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_bigfloat.js ./qjs --bignum tests/test_bignum.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 --std tests/test_builtin.js ./qjs32 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_bigfloat.js ./qjs32 --bignum tests/test_bignum.js
./qjs32 --qjscalc tests/test_qjscalc.js ./qjs32 --qjscalc tests/test_qjscalc.js
endif endif
endif endif
@ -504,46 +440,36 @@ stats: qjs qjs32
./qjs32 -qd ./qjs32 -qd
microbench: qjs microbench: qjs
./qjs --std tests/microbench.js ./qjs tests/microbench.js
microbench-32: qjs32 microbench-32: qjs32
./qjs32 --std tests/microbench.js ./qjs32 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 -t -m -c test262o.conf time ./run-test262 -m -c test262o.conf
test2o-32: run-test262-32 test2o-32: run-test262-32
time ./run-test262-32 -t -m -c test262o.conf time ./run-test262-32 -m -c test262o.conf
test2o-update: run-test262 test2o-update: run-test262
./run-test262 -t -u -c test262o.conf ./run-test262 -u -c test262o.conf
endif
ifeq ($(wildcard test262/features.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 -t -m -c test262.conf time ./run-test262 -m -c test262.conf
test2: run-test262 test2: run-test262
time ./run-test262 -t -m -c test262.conf -a time ./run-test262 -m -c test262.conf -a
test2-32: run-test262-32 test2-32: run-test262-32
time ./run-test262-32 -t -m -c test262.conf -a time ./run-test262-32 -m -c test262.conf -a
test2-update: run-test262 test2-update: run-test262
./run-test262 -t -u -c test262.conf -a ./run-test262 -u -c test262.conf -a
test2-check: run-test262 test2-check: run-test262
time ./run-test262 -t -m -c test262.conf -E -a time ./run-test262 -m -c test262.conf -E -a
endif
testall: all test microbench test2o test2 testall: all test microbench test2o test2
@ -551,40 +477,11 @@ 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)

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: 8/76947 errors, 1497 excluded, 8117 skipped Result: 10/76947 errors, 1497 excluded, 8117 skipped
Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb

View file

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

View file

@ -140,7 +140,7 @@ int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
if (dbuf_realloc(s, s->size + len)) if (dbuf_realloc(s, s->size + len))
return -1; return -1;
} }
memcpy_no_ub(s->buf + s->size, data, len); memcpy(s->buf + s->size, data, len);
s->size += len; s->size += len;
return 0; return 0;
} }

View file

@ -26,9 +26,11 @@
#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))
@ -46,16 +48,9 @@
#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;
@ -71,12 +66,6 @@ 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)
@ -221,22 +210,17 @@ 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)) |
@ -248,7 +232,6 @@ static inline uint64_t bswap64(uint64_t v)
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
} }
#endif
/* XXX: should take an extra argument to pass slack information to the caller */ /* XXX: should take an extra argument to pass slack information to the caller */
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
@ -298,36 +281,6 @@ 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')

734
doc/jsbignum.html Normal file
View file

@ -0,0 +1,734 @@
<!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>

BIN
doc/jsbignum.pdf Normal file

Binary file not shown.

1410
doc/quickjs.html Normal file

File diff suppressed because it is too large Load diff

BIN
doc/quickjs.pdf Normal file

Binary file not shown.

View file

@ -379,9 +379,7 @@ 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. The promise is resolved with an script and a promise is returned.
object whose @code{value} property holds the value returned by the
script.
@end table @end table
@item loadScript(filename) @item loadScript(filename)

View file

@ -136,7 +136,6 @@ static inline slimb_t ceil_div(slimb_t a, slimb_t b)
return a / b; return a / b;
} }
#ifdef USE_BF_DEC
/* b must be >= 1 */ /* b must be >= 1 */
static inline slimb_t floor_div(slimb_t a, slimb_t b) static inline slimb_t floor_div(slimb_t a, slimb_t b)
{ {
@ -146,7 +145,6 @@ static inline slimb_t floor_div(slimb_t a, slimb_t b)
return (a - b + 1) / b; return (a - b + 1) / b;
} }
} }
#endif
/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ /* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */
static inline limb_t smod(slimb_t a, slimb_t b) static inline limb_t smod(slimb_t a, slimb_t b)
@ -311,7 +309,7 @@ int bf_set(bf_t *r, const bf_t *a)
} }
r->sign = a->sign; r->sign = a->sign;
r->expn = a->expn; r->expn = a->expn;
memcpy_no_ub(r->tab, a->tab, a->len * sizeof(limb_t)); memcpy(r->tab, a->tab, a->len * sizeof(limb_t));
return 0; return 0;
} }

View file

@ -30,7 +30,6 @@
#include "cutils.h" #include "cutils.h"
#include "libregexp.h" #include "libregexp.h"
#include "libunicode.h"
/* /*
TODO: TODO:
@ -67,7 +66,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_unicode; BOOL is_utf16;
BOOL ignore_case; BOOL ignore_case;
BOOL dotall; BOOL dotall;
int capture_count; int capture_count;
@ -101,7 +100,6 @@ 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
@ -142,6 +140,32 @@ 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,
@ -161,7 +185,7 @@ typedef enum {
CHAR_RANGE_W, CHAR_RANGE_W,
} CharRangeEnum; } CharRangeEnum;
static const uint16_t * const char_range_table[] = { static const uint16_t *char_range_table[] = {
char_range_d, char_range_d,
char_range_s, char_range_s,
char_range_w, char_range_w,
@ -200,16 +224,16 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
assert(buf_len >= RE_HEADER_LEN); assert(buf_len >= RE_HEADER_LEN);
re_flags = lre_get_flags(buf); re_flags= buf[0];
bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN); bc_len = get_u32(buf + 3);
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[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]); re_flags, buf[1], buf[2]);
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[RE_HEADER_CAPTURE_COUNT]; i++) { for(i = 1; i < buf[1]; i++) {
if (i != 1) if (i != 1)
printf(","); printf(",");
printf("<%s>", p); printf("<%s>", p);
@ -470,7 +494,7 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
} }
c = (c << 4) | h; c = (c << 4) | h;
} }
if (is_hi_surrogate(c) && if (c >= 0xd800 && c < 0xdc00 &&
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 */
@ -481,9 +505,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 && is_lo_surrogate(c1)) { if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) {
p += 6; p += 6;
c = from_surrogate(c, c1); c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
} }
} }
} }
@ -672,10 +696,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_unicode)) { /* Annex B.1.4 */ inclass && !s->is_utf16)) { /* Annex B.1.4 */
c &= 0x1f; c &= 0x1f;
p++; p++;
} else if (s->is_unicode) { } else if (s->is_utf16) {
goto invalid_escape; goto invalid_escape;
} else { } else {
/* otherwise return '\' and 'c' */ /* otherwise return '\' and 'c' */
@ -686,7 +710,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_unicode) { if (s->is_utf16) {
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;
@ -696,14 +720,14 @@ static int get_class_atom(REParseState *s, CharRange *cr,
#endif #endif
default: default:
p--; p--;
ret = lre_parse_escape(&p, s->is_unicode * 2); ret = lre_parse_escape(&p, s->is_utf16 * 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_unicode) { } else if (s->is_utf16) {
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 {
@ -725,7 +749,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_unicode) { if ((unsigned)c > 0xffff && !s->is_utf16) {
/* 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");
} }
@ -787,13 +811,11 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
cr_init(cr, s->opaque, lre_realloc); cr_init(cr, s->opaque, lre_realloc);
p = *pp; p = *pp;
p++; /* skip '[' */ p++; /* skip '[' */
invert = FALSE; invert = FALSE;
if (*p == '^') { if (*p == '^') {
p++; p++;
invert = TRUE; invert = TRUE;
} }
for(;;) { for(;;) {
if (*p == ']') if (*p == ']')
break; break;
@ -803,7 +825,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_unicode) { if (s->is_utf16) {
cr_free(cr1); cr_free(cr1);
goto invalid_class_range; goto invalid_class_range;
} }
@ -815,7 +837,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_unicode) { if (s->is_utf16) {
goto invalid_class_range; goto invalid_class_range;
} }
/* Annex B: match '-' character */ /* Annex B: match '-' character */
@ -844,7 +866,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_unicode)) if (cr_regexp_canonicalize(cr, s->is_utf16))
goto memory_error; goto memory_error;
} }
if (invert) { if (invert) {
@ -912,7 +934,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 behavior: we cannot predict the outcome */ /* safe behvior: we cannot predict the outcome */
return TRUE; return TRUE;
} }
pos += len; pos += len;
@ -981,10 +1003,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 (is_hi_surrogate(c)) { if (c >= 0xD800 && c <= 0xDBFF) {
d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
if (is_lo_surrogate(d)) { if (d >= 0xDC00 && d <= 0xDFFF) {
c = from_surrogate(c, d); c = 0x10000 + 0x400 * (c - 0xD800) + (d - 0xDC00);
p = p1; p = p1;
} }
} }
@ -1092,10 +1114,9 @@ static int find_group_name(REParseState *s, const char *name)
size_t len, name_len; size_t len, name_len;
int capture_index; int capture_index;
p = (char *)s->group_names.buf;
if (!p) return -1;
buf_end = (char *)s->group_names.buf + s->group_names.size;
name_len = strlen(name); name_len = strlen(name);
p = (char *)s->group_names.buf;
buf_end = (char *)s->group_names.buf + s->group_names.size;
capture_index = 1; capture_index = 1;
while (p < buf_end) { while (p < buf_end) {
len = strlen(p); len = strlen(p);
@ -1140,7 +1161,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_unicode) { if (s->is_utf16) {
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
@ -1192,7 +1213,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_unicode && !is_backward_lookahead) { if (!s->is_utf16 && !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;
} }
@ -1268,7 +1289,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
/* annex B: we tolerate invalid group names in non /* annex B: we tolerate invalid group names in non
unicode mode if there is no named capture unicode mode if there is no named capture
definition */ definition */
if (s->is_unicode || re_has_named_captures(s)) if (s->is_utf16 || 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;
@ -1276,7 +1297,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_unicode || re_has_named_captures(s)) if (s->is_utf16 || 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;
@ -1287,7 +1308,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_unicode || re_has_named_captures(s)) if (s->is_utf16 || 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;
@ -1299,7 +1320,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_unicode) { if (s->is_utf16) {
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");
} }
@ -1321,7 +1342,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
c = parse_digits(&p, FALSE); c = parse_digits(&p, FALSE);
if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) {
if (!s->is_unicode) { if (!s->is_utf16) {
/* Annex B.1.4: accept legacy octal */ /* Annex B.1.4: accept legacy octal */
p = q; p = q;
if (*p <= '7') { if (*p <= '7') {
@ -1363,7 +1384,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
break; break;
case ']': case ']':
case '}': case '}':
if (s->is_unicode) if (s->is_utf16)
return re_parse_error(s, "syntax error"); return re_parse_error(s, "syntax error");
goto parse_class_atom; goto parse_class_atom;
default: default:
@ -1385,7 +1406,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_unicode); c = lre_canonicalize(c, s->is_utf16);
if (c <= 0xffff) if (c <= 0xffff)
re_emit_op_u16(s, REOP_char, c); re_emit_op_u16(s, REOP_char, c);
else else
@ -1421,7 +1442,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_unicode) if (s->is_utf16)
goto invalid_quant_count; goto invalid_quant_count;
break; break;
} }
@ -1440,7 +1461,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_unicode) { if (*p != '}' && !s->is_utf16) {
/* Annex B: normal atom if invalid '{' syntax */ /* Annex B: normal atom if invalid '{' syntax */
p = p1; p = p1;
break; break;
@ -1488,13 +1509,15 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
if (dbuf_error(&s->byte_code)) if (dbuf_error(&s->byte_code))
goto out_of_memory; goto out_of_memory;
}
/* the spec tells that if there is no advance when /* the spec tells that if there is no advance when
running the atom after the first quant_min times, running the atom after the first quant_min times,
then there is no match. We remove this test when we then there is no match. We remove this test when we
are sure the atom always advances the position. */ are sure the atom always advances the position. */
add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start, add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start,
s->byte_code.size - last_atom_start); s->byte_code.size - last_atom_start);
} else {
add_zero_advance_check = FALSE;
}
{ {
int len, pos; int len, pos;
@ -1730,7 +1753,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
s->buf_end = s->buf_ptr + buf_len; s->buf_end = s->buf_ptr + buf_len;
s->buf_start = s->buf_ptr; s->buf_start = s->buf_ptr;
s->re_flags = re_flags; s->re_flags = re_flags;
s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0); s->is_utf16 = ((re_flags & LRE_FLAG_UTF16) != 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);
@ -1788,8 +1811,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count;
s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size;
put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN, put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_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)) {
@ -1820,86 +1842,93 @@ static BOOL is_word_char(uint32_t c)
(c == '_')); (c == '_'));
} }
#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \ #define GET_CHAR(c, cptr, cbuf_end) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
c = *cptr++; \ c = *cptr++; \
} else { \ } else { \
const uint16_t *_p = (const uint16_t *)cptr; \ uint32_t __c1; \
const uint16_t *_end = (const uint16_t *)cbuf_end; \ c = *(uint16_t *)cptr; \
c = *_p++; \ cptr += 2; \
if (is_hi_surrogate(c) && cbuf_type == 2) { \ if (c >= 0xd800 && c < 0xdc00 && \
if (_p < _end && is_lo_surrogate(*_p)) { \ cbuf_type == 2 && cptr < cbuf_end) { \
c = from_surrogate(c, *_p++); \ __c1 = *(uint16_t *)cptr; \
if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
cptr += 2; \
} \ } \
} \ } \
cptr = (const void *)_p; \
} \ } \
} while (0) } while (0)
#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \ #define PEEK_CHAR(c, cptr, cbuf_end) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
c = cptr[0]; \ c = cptr[0]; \
} else { \ } else { \
const uint16_t *_p = (const uint16_t *)cptr; \ uint32_t __c1; \
const uint16_t *_end = (const uint16_t *)cbuf_end; \ c = ((uint16_t *)cptr)[0]; \
c = *_p++; \ if (c >= 0xd800 && c < 0xdc00 && \
if (is_hi_surrogate(c) && cbuf_type == 2) { \ cbuf_type == 2 && (cptr + 2) < cbuf_end) { \
if (_p < _end && is_lo_surrogate(*_p)) { \ __c1 = ((uint16_t *)cptr)[1]; \
c = from_surrogate(c, *_p); \ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
} \ } \
} \ } \
} \ } \
} while (0) } while (0)
#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ #define PEEK_PREV_CHAR(c, cptr, cbuf_start) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
c = cptr[-1]; \ c = cptr[-1]; \
} else { \ } else { \
const uint16_t *_p = (const uint16_t *)cptr - 1; \ uint32_t __c1; \
const uint16_t *_start = (const uint16_t *)cbuf_start; \ c = ((uint16_t *)cptr)[-1]; \
c = *_p; \ if (c >= 0xdc00 && c < 0xe000 && \
if (is_lo_surrogate(c) && cbuf_type == 2) { \ cbuf_type == 2 && (cptr - 4) >= cbuf_start) { \
if (_p > _start && is_hi_surrogate(_p[-1])) { \ __c1 = ((uint16_t *)cptr)[-2]; \
c = from_surrogate(*--_p, c); \ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
} \ } \
} \ } \
} \ } \
} while (0) } while (0)
#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ #define GET_PREV_CHAR(c, cptr, cbuf_start) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
cptr--; \ cptr--; \
c = cptr[0]; \ c = cptr[0]; \
} else { \ } else { \
const uint16_t *_p = (const uint16_t *)cptr - 1; \ uint32_t __c1; \
const uint16_t *_start = (const uint16_t *)cbuf_start; \ cptr -= 2; \
c = *_p; \ c = ((uint16_t *)cptr)[0]; \
if (is_lo_surrogate(c) && cbuf_type == 2) { \ if (c >= 0xdc00 && c < 0xe000 && \
if (_p > _start && is_hi_surrogate(_p[-1])) { \ cbuf_type == 2 && cptr > cbuf_start) { \
c = from_surrogate(*--_p, c); \ __c1 = ((uint16_t *)cptr)[-1]; \
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, cbuf_type) \ #define PREV_CHAR(cptr, cbuf_start) \
do { \ do { \
if (cbuf_type == 0) { \ if (cbuf_type == 0) { \
cptr--; \ cptr--; \
} else { \ } else { \
const uint16_t *_p = (const uint16_t *)cptr - 1; \ cptr -= 2; \
const uint16_t *_start = (const uint16_t *)cbuf_start; \ if (cbuf_type == 2) { \
if (is_lo_surrogate(*_p) && cbuf_type == 2) { \ c = ((uint16_t *)cptr)[0]; \
if (_p > _start && is_hi_surrogate(_p[-1])) { \ if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) { \
--_p; \ c = ((uint16_t *)cptr)[-1]; \
if (c >= 0xd800 && c < 0xdc00) \
cptr -= 2; \
} \ } \
} \ } \
cptr = (const void *)_p; \
} \ } \
} while (0) } while (0)
@ -1930,7 +1959,7 @@ typedef struct {
int stack_size_max; int stack_size_max;
BOOL multi_line; BOOL multi_line;
BOOL ignore_case; BOOL ignore_case;
BOOL is_unicode; BOOL is_utf16;
void *opaque; /* used for stack overflow check */ void *opaque; /* used for stack overflow check */
size_t state_size; size_t state_size;
@ -2039,7 +2068,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, cbuf_type); PREV_CHAR(cptr, s->cbuf);
} }
pc = (pc + 16) + (int)get_u32(pc); pc = (pc + 16) + (int)get_u32(pc);
rs->cptr = cptr; rs->cptr = cptr;
@ -2074,9 +2103,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, cbuf_type); GET_CHAR(c, cptr, cbuf_end);
if (s->ignore_case) { if (s->ignore_case) {
c = lre_canonicalize(c, s->is_unicode); c = lre_canonicalize(c, s->is_utf16);
} }
if (val != c) if (val != c)
goto no_match; goto no_match;
@ -2121,7 +2150,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, cbuf_type); PEEK_PREV_CHAR(c, cptr, s->cbuf);
if (!is_line_terminator(c)) if (!is_line_terminator(c))
goto no_match; goto no_match;
break; break;
@ -2130,21 +2159,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, cbuf_type); PEEK_CHAR(c, cptr, cbuf_end);
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, cbuf_type); GET_CHAR(c, cptr, cbuf_end);
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, cbuf_type); GET_CHAR(c, cptr, cbuf_end);
break; break;
case REOP_save_start: case REOP_save_start:
case REOP_save_end: case REOP_save_end:
@ -2196,14 +2225,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, cbuf_type); PEEK_PREV_CHAR(c, cptr, s->cbuf);
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, cbuf_type); PEEK_CHAR(c, cptr, cbuf_end);
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))
@ -2228,11 +2257,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, cbuf_type); GET_CHAR(c1, cptr1, cptr1_end);
GET_CHAR(c2, cptr, cbuf_end, cbuf_type); GET_CHAR(c2, cptr, cbuf_end);
if (s->ignore_case) { if (s->ignore_case) {
c1 = lre_canonicalize(c1, s->is_unicode); c1 = lre_canonicalize(c1, s->is_utf16);
c2 = lre_canonicalize(c2, s->is_unicode); c2 = lre_canonicalize(c2, s->is_utf16);
} }
if (c1 != c2) if (c1 != c2)
goto no_match; goto no_match;
@ -2242,11 +2271,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, cbuf_type); GET_PREV_CHAR(c1, cptr1, cptr1_start);
GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type); GET_PREV_CHAR(c2, cptr, s->cbuf);
if (s->ignore_case) { if (s->ignore_case) {
c1 = lre_canonicalize(c1, s->is_unicode); c1 = lre_canonicalize(c1, s->is_utf16);
c2 = lre_canonicalize(c2, s->is_unicode); c2 = lre_canonicalize(c2, s->is_utf16);
} }
if (c1 != c2) if (c1 != c2)
goto no_match; goto no_match;
@ -2263,9 +2292,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
pc += 2; pc += 2;
if (cptr >= cbuf_end) if (cptr >= cbuf_end)
goto no_match; goto no_match;
GET_CHAR(c, cptr, cbuf_end, cbuf_type); GET_CHAR(c, cptr, cbuf_end);
if (s->ignore_case) { if (s->ignore_case) {
c = lre_canonicalize(c, s->is_unicode); c = lre_canonicalize(c, s->is_utf16);
} }
idx_min = 0; idx_min = 0;
low = get_u16(pc + 0 * 4); low = get_u16(pc + 0 * 4);
@ -2303,9 +2332,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
pc += 2; pc += 2;
if (cptr >= cbuf_end) if (cptr >= cbuf_end)
goto no_match; goto no_match;
GET_CHAR(c, cptr, cbuf_end, cbuf_type); GET_CHAR(c, cptr, cbuf_end);
if (s->ignore_case) { if (s->ignore_case) {
c = lre_canonicalize(c, s->is_unicode); c = lre_canonicalize(c, s->is_utf16);
} }
idx_min = 0; idx_min = 0;
low = get_u32(pc + 0 * 8); low = get_u32(pc + 0 * 8);
@ -2335,7 +2364,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, cbuf_type); PREV_CHAR(cptr, s->cbuf);
break; break;
case REOP_simple_greedy_quant: case REOP_simple_greedy_quant:
{ {
@ -2394,16 +2423,16 @@ int lre_exec(uint8_t **capture,
int re_flags, i, alloca_size, ret; int re_flags, i, alloca_size, ret;
StackInt *stack_buf; StackInt *stack_buf;
re_flags = lre_get_flags(bc_buf); re_flags = bc_buf[RE_HEADER_FLAGS];
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_unicode = (re_flags & LRE_FLAG_UNICODE) != 0; s->is_utf16 = (re_flags & LRE_FLAG_UTF16) != 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_unicode) if (s->cbuf_type == 1 && s->is_utf16)
s->cbuf_type = 2; s->cbuf_type = 2;
s->opaque = opaque; s->opaque = opaque;
@ -2441,8 +2470,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 + RE_HEADER_BYTECODE_LEN); re_bytecode_len = get_u32(bc_buf + 3);
return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len); return (const char *)(bc_buf + 7 + re_bytecode_len);
} }
#ifdef TEST #ifdef TEST
@ -2459,26 +2488,25 @@ void *lre_realloc(void *opaque, void *ptr, size_t size)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int len, flags, ret, i; int len, 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 < 4) { if (argc < 3) {
printf("usage: %s regexp flags input\n", argv[0]); printf("usage: %s regexp input\n", argv[0]);
return 1; exit(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]), flags, NULL); strlen(argv[1]), 0, 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[3]; input = argv[2];
input_len = strlen(input); input_len = strlen(input);
ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL);

View file

@ -25,15 +25,19 @@
#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_UNICODE (1 << 4) #define LRE_FLAG_UTF16 (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,
@ -47,9 +51,43 @@ 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, return non zero if overflow */ /* must be provided by the user */
int lre_check_stack_overflow(void *opaque, size_t alloca_size); LRE_BOOL 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,13 +189,9 @@ 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, // 002B9 at 39 0xb9, 0x02, 0xe0, 0xc0, 0x1d, 0x20, 0xe5, 0x2c,
0xc0, 0x1d, 0x20, // 01DC0 at 65 0x20, 0xb1, 0x07, 0x21, 0xc1, 0xd6, 0x21, 0x4a,
0xe5, 0x2c, 0x20, // 02CE5 at 97 0xf1, 0x01, 0x8a, 0xf1, 0x01,
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] = {
@ -295,29 +291,15 @@ 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, // 005BE at 32 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a,
0xfe, 0x07, 0x00, // 007FE at 64 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f,
0x52, 0x0a, 0xa0, // 00A52 at 101 0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20,
0xc1, 0x0b, 0x00, // 00BC1 at 128 0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0,
0x82, 0x0d, 0x00, // 00D82 at 160 0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56,
0x3f, 0x10, 0x80, // 0103F at 196 0xfe, 0x20, 0xb1, 0x07, 0x01, 0x75, 0x10, 0x01,
0xd4, 0x17, 0x40, // 017D4 at 226 0xeb, 0x12, 0x21, 0x41, 0x16, 0x01, 0x5c, 0x1a,
0xcf, 0x1a, 0x20, // 01ACF at 257 0x01, 0x43, 0x1f, 0x01, 0x2e, 0xcf, 0x41, 0x25,
0xf5, 0x1c, 0x00, // 01CF5 at 288 0xe0, 0x01, 0xf0, 0x01, 0x0e,
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] = {
@ -462,41 +444,20 @@ 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, // 003F6 at 33 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09,
0xa6, 0x07, 0x00, // 007A6 at 64 0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b,
0xa9, 0x09, 0x20, // 009A9 at 97 0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00,
0xb1, 0x0a, 0x00, // 00AB1 at 128 0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d,
0xba, 0x0b, 0x20, // 00BBA at 161 0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00,
0x3b, 0x0d, 0x20, // 00D3B at 193 0x32, 0x00, 0xda, 0xa7, 0x00, 0x4c, 0xaa, 0x20,
0xc7, 0x0e, 0x20, // 00EC7 at 225 0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02,
0x49, 0x12, 0x00, // 01249 at 256 0x21, 0x96, 0x05, 0x01, 0xf3, 0x08, 0x01, 0xb3,
0x9b, 0x16, 0x00, // 0169B at 288 0x0c, 0x21, 0x73, 0x11, 0x61, 0x34, 0x13, 0x01,
0xac, 0x19, 0x00, // 019AC at 320 0x1b, 0x17, 0x21, 0x8a, 0x1a, 0x01, 0x34, 0x1f,
0xc0, 0x1d, 0x80, // 01DC0 at 356 0x21, 0xbf, 0x6a, 0x01, 0x23, 0xb1, 0xa1, 0xad,
0x80, 0x20, 0x20, // 02080 at 385 0xd4, 0x01, 0x6f, 0xd7, 0x01, 0xff, 0xe7, 0x61,
0x70, 0x2d, 0x00, // 02D70 at 416 0x5e, 0xee, 0x01, 0xe1, 0xeb, 0x22, 0xb0, 0x23,
0x00, 0x32, 0x00, // 03200 at 448 0x03,
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] = {
@ -586,27 +547,14 @@ 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, // 006FA at 32 0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a,
0x70, 0x09, 0x00, // 00970 at 64 0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7,
0xf0, 0x0a, 0x40, // 00AF0 at 98 0x0f, 0x20, 0xea, 0x17, 0x40, 0x05, 0x1b, 0x00,
0x57, 0x0c, 0x00, // 00C57 at 128 0x41, 0x20, 0x00, 0x0c, 0xa8, 0x80, 0x37, 0xaa,
0xf0, 0x0d, 0x60, // 00DF0 at 163 0x20, 0x50, 0xfe, 0x20, 0x3a, 0x0d, 0x21, 0x74,
0xc7, 0x0f, 0x20, // 00FC7 at 193 0x11, 0x01, 0x5a, 0x14, 0x21, 0x44, 0x19, 0x81,
0xea, 0x17, 0x40, // 017EA at 226 0x5a, 0x1d, 0xa1, 0xf5, 0x6a, 0x21, 0x45, 0xd2,
0x05, 0x1b, 0x00, // 01B05 at 256 0x41, 0xaf, 0xe2, 0x21, 0xf0, 0x01, 0x0e,
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
@ -728,35 +676,17 @@ 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, // 0034D at 32 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05,
0x97, 0x05, 0x20, // 00597 at 65 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c,
0xc6, 0x05, 0x00, // 005C6 at 96 0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00,
0xe7, 0x06, 0x00, // 006E7 at 128 0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10,
0x45, 0x07, 0x00, // 00745 at 160 0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3,
0x9c, 0x08, 0x00, // 0089C at 192 0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00,
0x4d, 0x09, 0x00, // 0094D at 224 0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab,
0x3c, 0x0b, 0x00, // 00B3C at 256 0x00, 0x39, 0x0a, 0x01, 0x51, 0x0f, 0x01, 0x73,
0x3d, 0x0d, 0x00, // 00D3D at 288 0x11, 0x01, 0x75, 0x13, 0x01, 0x2b, 0x17, 0x21,
0x36, 0x0f, 0x00, // 00F36 at 320 0x3f, 0x1c, 0x21, 0x9e, 0xbc, 0x21, 0x08, 0xe0,
0x38, 0x10, 0x20, // 01038 at 353 0x01, 0x44, 0xe9, 0x01, 0x4b, 0xe9, 0x01,
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] = {
@ -4554,4 +4484,3 @@ 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

@ -262,7 +262,11 @@ 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
@ -313,14 +317,6 @@ static BOOL lre_is_in_table(uint32_t c, const uint8_t *table,
return FALSE; /* outside the table */ return FALSE; /* outside the table */
p = table + pos; p = table + pos;
bit = 0; bit = 0;
/* Compressed run length encoding:
00..3F: 2 packed lengths: 3-bit + 3-bit
40..5F: 5-bits plus extra byte for length
60..7F: 5-bits plus 2 extra bytes for length
80..FF: 7-bit length
lengths must be incremented to get character count
Ranges alternate between false and true return value.
*/
for(;;) { for(;;) {
b = *p++; b = *p++;
if (b < 64) { if (b < 64) {
@ -837,13 +833,6 @@ static int unicode_get_cc(uint32_t c)
if (pos < 0) if (pos < 0)
return 0; return 0;
p = unicode_cc_table + pos; p = unicode_cc_table + pos;
/* Compressed run length encoding:
- 2 high order bits are combining class type
- 0:0, 1:230, 2:extra byte linear progression, 3:extra byte
- 00..2F: range length (add 1)
- 30..37: 3-bit range-length + 1 extra byte
- 38..3F: 3-bit range-length + 2 extra byte
*/
for(;;) { for(;;) {
b = *p++; b = *p++;
type = b >> 6; type = b >> 6;
@ -1196,15 +1185,6 @@ 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;
@ -1258,14 +1238,6 @@ 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++;
@ -1814,97 +1786,3 @@ int unicode_prop(CharRange *cr, const char *prop_name)
} }
#endif /* CONFIG_ALL_UNICODE */ #endif /* CONFIG_ALL_UNICODE */
/*---- lre codepoint categorizing functions ----*/
#define S UNICODE_C_SPACE
#define D UNICODE_C_DIGIT
#define X UNICODE_C_XDIGIT
#define U UNICODE_C_UPPER
#define L UNICODE_C_LOWER
#define _ UNICODE_C_UNDER
#define d UNICODE_C_DOLLAR
uint8_t const lre_ctype_bits[256] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, S, S, S, S, S, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
S, 0, 0, 0, d, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
X|D, X|D, X|D, X|D, X|D, X|D, X|D, X|D,
X|D, X|D, 0, 0, 0, 0, 0, 0,
0, X|U, X|U, X|U, X|U, X|U, X|U, U,
U, U, U, U, U, U, U, U,
U, U, U, U, U, U, U, U,
U, U, U, 0, 0, 0, 0, _,
0, X|L, X|L, X|L, X|L, X|L, X|L, L,
L, L, L, L, L, L, L, L,
L, L, L, L, L, L, L, L,
L, L, L, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
S, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
#undef S
#undef D
#undef X
#undef U
#undef L
#undef _
#undef d
/* code point ranges for Zs,Zl or Zp property */
static const uint16_t char_range_s[] = {
10,
0x0009, 0x000D + 1,
0x0020, 0x0020 + 1,
0x00A0, 0x00A0 + 1,
0x1680, 0x1680 + 1,
0x2000, 0x200A + 1,
/* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */
/* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */
0x2028, 0x2029 + 1,
0x202F, 0x202F + 1,
0x205F, 0x205F + 1,
0x3000, 0x3000 + 1,
/* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */
0xFEFF, 0xFEFF + 1,
};
BOOL lre_is_space_non_ascii(uint32_t c)
{
size_t i, n;
n = countof(char_range_s);
for(i = 5; i < n; i += 2) {
uint32_t low = char_range_s[i];
uint32_t high = char_range_s[i + 1];
if (c < low)
return FALSE;
if (c < high)
return TRUE;
}
return FALSE;
}

View file

@ -24,13 +24,27 @@
#ifndef LIBUNICODE_H #ifndef LIBUNICODE_H
#define LIBUNICODE_H #define LIBUNICODE_H
#include <stdint.h> #include <inttypes.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 {
@ -88,14 +102,12 @@ 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, int is_unicode); int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode);
typedef enum { #ifdef CONFIG_ALL_UNICODE
UNICODE_NFC,
UNICODE_NFD, LRE_BOOL lre_is_id_start(uint32_t c);
UNICODE_NFKC, LRE_BOOL lre_is_id_continue(uint32_t c);
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,
@ -103,80 +115,13 @@ 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, const char *script_name, int is_ext); int unicode_script(CharRange *cr,
const char *script_name, LRE_BOOL is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name); int unicode_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 lre_case_conv(uint32_t *res, uint32_t c, int conv_type); #endif /* CONFIG_ALL_UNICODE */
int lre_canonicalize(uint32_t c, int is_unicode);
/* Code point type categories */ #undef LRE_BOOL
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 */

13
qjs.c
View file

@ -34,10 +34,8 @@
#include <time.h> #include <time.h>
#if defined(__APPLE__) #if defined(__APPLE__)
#include <malloc/malloc.h> #include <malloc/malloc.h>
#elif defined(__linux__) || defined(__GLIBC__) #elif defined(__linux__)
#include <malloc.h> #include <malloc.h>
#elif defined(__FreeBSD__)
#include <malloc_np.h>
#endif #endif
#include "cutils.h" #include "cutils.h"
@ -66,7 +64,6 @@ 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);
} }
@ -151,7 +148,7 @@ static size_t js_trace_malloc_usable_size(const void *ptr)
return _msize((void *)ptr); return _msize((void *)ptr);
#elif defined(EMSCRIPTEN) #elif defined(EMSCRIPTEN)
return 0; return 0;
#elif defined(__linux__) || defined(__GLIBC__) #elif defined(__linux__)
return malloc_usable_size((void *)ptr); return malloc_usable_size((void *)ptr);
#else #else
/* change this to `return 0;` if compilation fails */ /* change this to `return 0;` if compilation fails */
@ -497,6 +494,12 @@ int main(int argc, char **argv)
"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);
} }
//POLYFILLS BEGIN
const char *reqPF = "function require(x) { \n"
" import { y } from `String(x)` \n"
" return y \n"
"}";
eval_buf(ctx, reqPF, strlen(reqPF), "<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))

View file

@ -47,15 +47,8 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/wait.h> #include <sys/wait.h>
#if defined(__FreeBSD__)
extern char **environ;
#endif
#if defined(__APPLE__) || defined(__FreeBSD__)
typedef sig_t sighandler_t;
#endif
#if defined(__APPLE__) #if defined(__APPLE__)
typedef sig_t sighandler_t;
#if !defined(environ) #if !defined(environ)
#include <crt_externs.h> #include <crt_externs.h>
#define environ (*_NSGetEnviron()) #define environ (*_NSGetEnviron())
@ -78,10 +71,6 @@ typedef sig_t sighandler_t;
#include "list.h" #include "list.h"
#include "quickjs-libc.h" #include "quickjs-libc.h"
#if !defined(PATH_MAX)
#define PATH_MAX 4096
#endif
/* TODO: /* TODO:
- add socket calls - add socket calls
*/ */
@ -100,7 +89,7 @@ typedef struct {
typedef struct { typedef struct {
struct list_head link; struct list_head link;
int timer_id; BOOL has_object;
int64_t timeout; int64_t timeout;
JSValue func; JSValue func;
} JSOSTimer; } JSOSTimer;
@ -136,7 +125,6 @@ typedef struct JSThreadState {
struct list_head os_timers; /* list of JSOSTimer.link */ struct list_head os_timers; /* list of JSOSTimer.link */
struct list_head port_list; /* list of JSWorkerMessageHandler.link */ struct list_head port_list; /* list of JSWorkerMessageHandler.link */
int eval_script_recurse; /* only used in the main thread */ int eval_script_recurse; /* only used in the main thread */
int next_timer_id; /* for setTimeout() */
/* not used in the main thread */ /* not used in the main thread */
JSWorkerMessagePipe *recv_pipe, *send_pipe; JSWorkerMessagePipe *recv_pipe, *send_pipe;
} JSThreadState; } JSThreadState;
@ -161,7 +149,7 @@ static JSValue js_printf_internal(JSContext *ctx,
uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; uint8_t cbuf[UTF8_CHAR_LEN_MAX+1];
JSValue res; JSValue res;
DynBuf dbuf; DynBuf dbuf;
const char *fmt_str = NULL; const char *fmt_str;
const uint8_t *fmt, *fmt_end; const uint8_t *fmt, *fmt_end;
const uint8_t *p; const uint8_t *p;
char *q; char *q;
@ -262,7 +250,7 @@ static JSValue js_printf_internal(JSContext *ctx,
string_arg = JS_ToCString(ctx, argv[i++]); string_arg = JS_ToCString(ctx, argv[i++]);
if (!string_arg) if (!string_arg)
goto fail; goto fail;
int32_arg = unicode_from_utf8((const uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p); int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);
JS_FreeCString(ctx, string_arg); JS_FreeCString(ctx, string_arg);
} else { } else {
if (JS_ToInt32(ctx, &int32_arg, argv[i++])) if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
@ -366,7 +354,6 @@ static JSValue js_printf_internal(JSContext *ctx,
return res; return res;
fail: fail:
JS_FreeCString(ctx, fmt_str);
dbuf_free(&dbuf); dbuf_free(&dbuf);
return JS_EXCEPTION; return JS_EXCEPTION;
} }
@ -1094,7 +1081,7 @@ static JSValue js_std_file_tell(JSContext *ctx, JSValueConst this_val,
int64_t pos; int64_t pos;
if (!f) if (!f)
return JS_EXCEPTION; return JS_EXCEPTION;
#if defined(__linux__) || defined(__GLIBC__) #if defined(__linux__)
pos = ftello(f); pos = ftello(f);
#else #else
pos = ftell(f); pos = ftell(f);
@ -1117,7 +1104,7 @@ static JSValue js_std_file_seek(JSContext *ctx, JSValueConst this_val,
return JS_EXCEPTION; return JS_EXCEPTION;
if (JS_ToInt32(ctx, &whence, argv[1])) if (JS_ToInt32(ctx, &whence, argv[1]))
return JS_EXCEPTION; return JS_EXCEPTION;
#if defined(__linux__) || defined(__GLIBC__) #if defined(__linux__)
ret = fseeko(f, pos, whence); ret = fseeko(f, pos, whence);
#else #else
ret = fseek(f, pos, whence); ret = fseek(f, pos, whence);
@ -1293,7 +1280,7 @@ static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val,
/* urlGet */ /* urlGet */
#define URL_GET_PROGRAM "curl -s -i --" #define URL_GET_PROGRAM "curl -s -i"
#define URL_GET_BUF_SIZE 4096 #define URL_GET_BUF_SIZE 4096
static int http_get_header_line(FILE *f, char *buf, size_t buf_size, static int http_get_header_line(FILE *f, char *buf, size_t buf_size,
@ -1339,7 +1326,7 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,
DynBuf header_buf_s, *header_buf = &header_buf_s; DynBuf header_buf_s, *header_buf = &header_buf_s;
char *buf; char *buf;
size_t i, len; size_t i, len;
int status; int c, status;
JSValue response = JS_UNDEFINED, ret_obj; JSValue response = JS_UNDEFINED, ret_obj;
JSValueConst options_obj; JSValueConst options_obj;
FILE *f; FILE *f;
@ -1366,25 +1353,16 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,
} }
js_std_dbuf_init(ctx, &cmd_buf); js_std_dbuf_init(ctx, &cmd_buf);
dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM); dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM);
for(i = 0; url[i] != '\0'; i++) { len = strlen(url);
unsigned char c = url[i]; for(i = 0; i < len; i++) {
switch (c) { c = url[i];
case '\'': if (c == '\'' || c == '\\')
/* shell single quoted string does not support \' */
dbuf_putstr(&cmd_buf, "'\\''");
break;
case '[': case ']': case '{': case '}': case '\\':
/* prevent interpretation by curl as range or set specification */
dbuf_putc(&cmd_buf, '\\'); dbuf_putc(&cmd_buf, '\\');
/* FALLTHROUGH */
default:
dbuf_putc(&cmd_buf, c); dbuf_putc(&cmd_buf, c);
break;
}
} }
JS_FreeCString(ctx, url); JS_FreeCString(ctx, url);
dbuf_putstr(&cmd_buf, "'"); dbuf_putstr(&cmd_buf, "''");
dbuf_putc(&cmd_buf, '\0'); dbuf_putc(&cmd_buf, '\0');
if (dbuf_error(&cmd_buf)) { if (dbuf_error(&cmd_buf)) {
dbuf_free(&cmd_buf); dbuf_free(&cmd_buf);
@ -1691,7 +1669,7 @@ static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val,
int fd; int fd;
if (JS_ToInt32(ctx, &fd, argv[0])) if (JS_ToInt32(ctx, &fd, argv[0]))
return JS_EXCEPTION; return JS_EXCEPTION;
return JS_NewBool(ctx, isatty(fd)); return JS_NewBool(ctx, (isatty(fd) != 0));
} }
#if defined(_WIN32) #if defined(_WIN32)
@ -2028,13 +2006,41 @@ static JSValue js_os_now(JSContext *ctx, JSValue this_val,
return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6); return JS_NewFloat64(ctx, (double)get_time_ns() / 1e6);
} }
static void unlink_timer(JSRuntime *rt, JSOSTimer *th)
{
if (th->link.prev) {
list_del(&th->link);
th->link.prev = th->link.next = NULL;
}
}
static void free_timer(JSRuntime *rt, JSOSTimer *th) static void free_timer(JSRuntime *rt, JSOSTimer *th)
{ {
list_del(&th->link);
JS_FreeValueRT(rt, th->func); JS_FreeValueRT(rt, th->func);
js_free_rt(rt, th); js_free_rt(rt, th);
} }
static JSClassID js_os_timer_class_id;
static void js_os_timer_finalizer(JSRuntime *rt, JSValue val)
{
JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
if (th) {
th->has_object = FALSE;
if (!th->link.prev)
free_timer(rt, th);
}
}
static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func)
{
JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
if (th) {
JS_MarkValue(rt, th->func, mark_func);
}
}
static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val, static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
@ -2043,56 +2049,45 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
int64_t delay; int64_t delay;
JSValueConst func; JSValueConst func;
JSOSTimer *th; JSOSTimer *th;
JSValue obj;
func = argv[0]; func = argv[0];
if (!JS_IsFunction(ctx, func)) if (!JS_IsFunction(ctx, func))
return JS_ThrowTypeError(ctx, "not a function"); return JS_ThrowTypeError(ctx, "not a function");
if (JS_ToInt64(ctx, &delay, argv[1])) if (JS_ToInt64(ctx, &delay, argv[1]))
return JS_EXCEPTION; return JS_EXCEPTION;
obj = JS_NewObjectClass(ctx, js_os_timer_class_id);
if (JS_IsException(obj))
return obj;
th = js_mallocz(ctx, sizeof(*th)); th = js_mallocz(ctx, sizeof(*th));
if (!th) if (!th) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION; return JS_EXCEPTION;
th->timer_id = ts->next_timer_id; }
if (ts->next_timer_id == INT32_MAX) th->has_object = TRUE;
ts->next_timer_id = 1;
else
ts->next_timer_id++;
th->timeout = get_time_ms() + delay; th->timeout = get_time_ms() + delay;
th->func = JS_DupValue(ctx, func); th->func = JS_DupValue(ctx, func);
list_add_tail(&th->link, &ts->os_timers); list_add_tail(&th->link, &ts->os_timers);
return JS_NewInt32(ctx, th->timer_id); JS_SetOpaque(obj, th);
} return obj;
static JSOSTimer *find_timer_by_id(JSThreadState *ts, int timer_id)
{
struct list_head *el;
if (timer_id <= 0)
return NULL;
list_for_each(el, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link);
if (th->timer_id == timer_id)
return th;
}
return NULL;
} }
static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val, static JSValue js_os_clearTimeout(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
{ {
JSRuntime *rt = JS_GetRuntime(ctx); JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id);
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
JSOSTimer *th;
int timer_id;
if (JS_ToInt32(ctx, &timer_id, argv[0]))
return JS_EXCEPTION;
th = find_timer_by_id(ts, timer_id);
if (!th) if (!th)
return JS_UNDEFINED; return JS_EXCEPTION;
free_timer(rt, th); unlink_timer(JS_GetRuntime(ctx), th);
return JS_UNDEFINED; return JS_UNDEFINED;
} }
static JSClassDef js_os_timer_class = {
"OSTimer",
.finalizer = js_os_timer_finalizer,
.gc_mark = js_os_timer_mark,
};
/* return a promise */ /* return a promise */
static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv) int argc, JSValueConst *argv)
@ -2116,7 +2111,7 @@ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
JS_FreeValue(ctx, resolving_funcs[1]); JS_FreeValue(ctx, resolving_funcs[1]);
return JS_EXCEPTION; return JS_EXCEPTION;
} }
th->timer_id = -1; th->has_object = FALSE;
th->timeout = get_time_ms() + delay; th->timeout = get_time_ms() + delay;
th->func = JS_DupValue(ctx, resolving_funcs[0]); th->func = JS_DupValue(ctx, resolving_funcs[0]);
list_add_tail(&th->link, &ts->os_timers); list_add_tail(&th->link, &ts->os_timers);
@ -2166,6 +2161,8 @@ static int js_os_poll(JSContext *ctx)
/* the timer expired */ /* the timer expired */
func = th->func; func = th->func;
th->func = JS_UNDEFINED; th->func = JS_UNDEFINED;
unlink_timer(rt, th);
if (!th->has_object)
free_timer(rt, th); free_timer(rt, th);
call_handler(ctx, func); call_handler(ctx, func);
JS_FreeValue(ctx, func); JS_FreeValue(ctx, func);
@ -2333,6 +2330,8 @@ static int js_os_poll(JSContext *ctx)
/* the timer expired */ /* the timer expired */
func = th->func; func = th->func;
th->func = JS_UNDEFINED; th->func = JS_UNDEFINED;
unlink_timer(rt, th);
if (!th->has_object)
free_timer(rt, th); free_timer(rt, th);
call_handler(ctx, func); call_handler(ctx, func);
JS_FreeValue(ctx, func); JS_FreeValue(ctx, func);
@ -2553,14 +2552,12 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
else else
res = stat(path, &st); res = stat(path, &st);
#endif #endif
if (res < 0)
err = errno;
else
err = 0;
JS_FreeCString(ctx, path); JS_FreeCString(ctx, path);
if (res < 0) { if (res < 0) {
err = errno;
obj = JS_NULL; obj = JS_NULL;
} else { } else {
err = 0;
obj = JS_NewObject(ctx); obj = JS_NewObject(ctx);
if (JS_IsException(obj)) if (JS_IsException(obj))
return JS_EXCEPTION; return JS_EXCEPTION;
@ -3019,6 +3016,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
} }
if (pid == 0) { if (pid == 0) {
/* child */ /* child */
int fd_max = sysconf(_SC_OPEN_MAX);
/* remap the stdin/stdout/stderr handles if necessary */ /* remap the stdin/stdout/stderr handles if necessary */
for(i = 0; i < 3; i++) { for(i = 0; i < 3; i++) {
@ -3027,28 +3025,9 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
_exit(127); _exit(127);
} }
} }
#if defined(HAVE_CLOSEFROM)
/* closefrom() is available on many recent unix systems:
Linux with glibc 2.34+, Solaris 9+, FreeBSD 7.3+,
NetBSD 3.0+, OpenBSD 3.5+.
Linux with the musl libc and macOS don't have it.
*/
closefrom(3);
#else
{
/* Close the file handles manually, limit to 1024 to avoid
costly loop on linux Alpine where sysconf(_SC_OPEN_MAX)
returns a huge value 1048576.
Patch inspired by nicolas-duteil-nova. See also:
https://stackoverflow.com/questions/73229353/
https://stackoverflow.com/questions/899038/#918469
*/
int fd_max = min_int(sysconf(_SC_OPEN_MAX), 1024);
for(i = 3; i < fd_max; i++) for(i = 3; i < fd_max; i++)
close(i); close(i);
}
#endif
if (cwd) { if (cwd) {
if (chdir(cwd) < 0) if (chdir(cwd) < 0)
_exit(127); _exit(127);
@ -3360,7 +3339,7 @@ static void *worker_func(void *opaque)
JSRuntime *rt; JSRuntime *rt;
JSThreadState *ts; JSThreadState *ts;
JSContext *ctx; JSContext *ctx;
JSValue val; JSValue promise;
rt = JS_NewRuntime(); rt = JS_NewRuntime();
if (rt == NULL) { if (rt == NULL) {
@ -3387,14 +3366,14 @@ static void *worker_func(void *opaque)
js_std_add_helpers(ctx, -1, NULL); js_std_add_helpers(ctx, -1, NULL);
val = JS_LoadModule(ctx, args->basename, args->filename); promise = JS_LoadModule(ctx, args->basename, args->filename);
if (JS_IsException(promise))
js_std_dump_error(ctx);
/* XXX: check */
JS_FreeValue(ctx, promise);
free(args->filename); free(args->filename);
free(args->basename); free(args->basename);
free(args); free(args);
val = js_std_await(ctx, val);
if (JS_IsException(val))
js_std_dump_error(ctx);
JS_FreeValue(ctx, val);
js_std_loop(ctx); js_std_loop(ctx);
@ -3549,12 +3528,10 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
memcpy(msg->data, data, data_len); memcpy(msg->data, data, data_len);
msg->data_len = data_len; msg->data_len = data_len;
if (sab_tab_len > 0) {
msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len); msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len);
if (!msg->sab_tab) if (!msg->sab_tab)
goto fail; goto fail;
memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len); memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len);
}
msg->sab_tab_len = sab_tab_len; msg->sab_tab_len = sab_tab_len;
js_free(ctx, data); js_free(ctx, data);
@ -3758,6 +3735,10 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
{ {
os_poll_func = js_os_poll; os_poll_func = js_os_poll;
/* OSTimer class */
JS_NewClassID(&js_os_timer_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);
#ifdef USE_WORKER #ifdef USE_WORKER
{ {
JSRuntime *rt = JS_GetRuntime(ctx); JSRuntime *rt = JS_GetRuntime(ctx);
@ -3869,7 +3850,6 @@ void js_std_init_handlers(JSRuntime *rt)
init_list_head(&ts->os_signal_handlers); init_list_head(&ts->os_signal_handlers);
init_list_head(&ts->os_timers); init_list_head(&ts->os_timers);
init_list_head(&ts->port_list); init_list_head(&ts->port_list);
ts->next_timer_id = 1;
JS_SetRuntimeOpaque(rt, ts); JS_SetRuntimeOpaque(rt, ts);
@ -3903,6 +3883,8 @@ void js_std_free_handlers(JSRuntime *rt)
list_for_each_safe(el, el1, &ts->os_timers) { list_for_each_safe(el, el1, &ts->os_timers) {
JSOSTimer *th = list_entry(el, JSOSTimer, link); JSOSTimer *th = list_entry(el, JSOSTimer, link);
unlink_timer(rt, th);
if (!th->has_object)
free_timer(rt, th); free_timer(rt, th);
} }
@ -3987,42 +3969,6 @@ void js_std_loop(JSContext *ctx)
} }
} }
/* Wait for a promise and execute pending jobs while waiting for
it. Return the promise result or JS_EXCEPTION in case of promise
rejection. */
JSValue js_std_await(JSContext *ctx, JSValue obj)
{
JSValue ret;
int state;
for(;;) {
state = JS_PromiseState(ctx, obj);
if (state == JS_PROMISE_FULFILLED) {
ret = JS_PromiseResult(ctx, obj);
JS_FreeValue(ctx, obj);
break;
} else if (state == JS_PROMISE_REJECTED) {
ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj));
JS_FreeValue(ctx, obj);
break;
} else if (state == JS_PROMISE_PENDING) {
JSContext *ctx1;
int err;
err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1);
if (err < 0) {
js_std_dump_error(ctx1);
}
if (os_poll_func)
os_poll_func(ctx);
} else {
/* not a promise */
ret = obj;
break;
}
}
return ret;
}
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int load_only) int load_only)
{ {
@ -4041,11 +3987,8 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
goto exception; goto exception;
} }
js_module_set_import_meta(ctx, obj, FALSE, TRUE); js_module_set_import_meta(ctx, obj, FALSE, TRUE);
val = JS_EvalFunction(ctx, obj);
val = js_std_await(ctx, val);
} else {
val = JS_EvalFunction(ctx, obj);
} }
val = JS_EvalFunction(ctx, obj);
if (JS_IsException(val)) { if (JS_IsException(val)) {
exception: exception:
js_std_dump_error(ctx); js_std_dump_error(ctx);

View file

@ -37,7 +37,6 @@ 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);

1866
quickjs.c

File diff suppressed because it is too large Load diff

View file

@ -499,10 +499,7 @@ 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);
@ -550,21 +547,23 @@ 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;
if (d >= INT32_MIN && d <= INT32_MAX) {
u.d = d; u.d = d;
val = (int32_t)d; val = (int32_t)d;
t.d = val; t.d = val;
/* -0 cannot be represented as integer, so we compare the bit /* -0 cannot be represented as integer, so we compare the bit
representation */ representation */
if (u.u == t.u) if (u.u == t.u) {
return JS_MKVAL(JS_TAG_INT, val); v = JS_MKVAL(JS_TAG_INT, val);
} else {
v = __JS_NewFloat64(ctx, d);
} }
return __JS_NewFloat64(ctx, d); return v;
} }
static inline JS_BOOL JS_IsNumber(JSValueConst v) static inline JS_BOOL JS_IsNumber(JSValueConst v)
@ -633,9 +632,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_HasException(JSContext *ctx);
JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, JS_BOOL flag);
void JS_ResetUncatchableError(JSContext *ctx); void JS_ResetUncatchableError(JSContext *ctx);
JSValue JS_NewError(JSContext *ctx); JSValue JS_NewError(JSContext *ctx);
JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
@ -684,10 +681,6 @@ 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)
@ -730,8 +723,6 @@ 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);
@ -830,23 +821,6 @@ 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,
@ -902,7 +876,6 @@ 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 */

View file

@ -8,12 +8,12 @@ version=`cat VERSION`
if [ "$1" = "-h" ] ; then if [ "$1" = "-h" ] ; then
echo "release.sh [release_list]" echo "release.sh [release_list]"
echo "" echo ""
echo "release_list: extras binary win_binary quickjs" echo "release_list: extras binary win_binary cosmo_binary quickjs"
exit 1 exit 1
fi fi
release_list="extras binary win_binary quickjs" release_list="extras binary win_binary cosmo_binary quickjs"
if [ "$1" != "" ] ; then if [ "$1" != "" ] ; then
release_list="$1" release_list="$1"
@ -84,6 +84,28 @@ cp $dlldir/libwinpthread-1.dll $outdir
fi 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
#################################################" #################################################"
# Linux binary release # Linux binary release

51
repl.js
View file

@ -67,7 +67,24 @@ import * as os from "os";
bright_white: "\x1b[37;1m", bright_white: "\x1b[37;1m",
}; };
var styles = { var styles;
if (config_numcalc) {
styles = {
'default': 'black',
'comment': 'white',
'string': 'green',
'regex': 'cyan',
'number': 'green',
'keyword': 'blue',
'function': 'gray',
'type': 'bright_magenta',
'identifier': 'yellow',
'error': 'bright_red',
'result': 'black',
'error_msg': 'bright_red',
};
} else {
styles = {
'default': 'bright_green', 'default': 'bright_green',
'comment': 'white', 'comment': 'white',
'string': 'bright_cyan', 'string': 'bright_cyan',
@ -81,6 +98,7 @@ import * as os from "os";
'result': 'bright_white', 'result': 'bright_white',
'error_msg': 'bright_red', 'error_msg': 'bright_red',
}; };
}
var history = []; var history = [];
var clip_board = ""; var clip_board = "";
@ -91,7 +109,11 @@ import * as os from "os";
var pstate = ""; var pstate = "";
var prompt = ""; var prompt = "";
var plen = 0; var plen = 0;
var ps1 = "qjs > "; var ps1;
if (config_numcalc)
ps1 = "> ";
else
ps1 = "qjs > ";
var ps2 = " ... "; var ps2 = " ... ";
var utf8 = true; var utf8 = true;
var show_time = false; var show_time = false;
@ -591,9 +613,6 @@ 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;
@ -986,8 +1005,6 @@ import * as os from "os";
std.puts(a); std.puts(a);
} else if (stack.indexOf(a) >= 0) { } else if (stack.indexOf(a) >= 0) {
std.puts("[circular]"); std.puts("[circular]");
} else if (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 ||
@ -1162,23 +1179,6 @@ 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) {
@ -1310,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({ value: result }); print_eval_result(result);
} }
} catch (error) { } catch (error) {
print_eval_error(error); print_eval_error(error);
@ -1318,7 +1318,6 @@ 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);

View file

@ -63,8 +63,6 @@ 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;
@ -532,7 +530,6 @@ 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");
@ -547,12 +544,7 @@ 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_attr_init(&attr); pthread_create(&agent->tid, NULL, agent_start, agent);
// 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;
} }
@ -821,19 +813,6 @@ 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) {
@ -931,7 +910,7 @@ void update_exclude_dirs(void)
lp->count = count; lp->count = count;
} }
void load_config(const char *filename, const char *ignore) void load_config(const char *filename)
{ {
char buf[1024]; char buf[1024];
FILE *f; FILE *f;
@ -986,10 +965,6 @@ void load_config(const char *filename, const char *ignore)
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;
@ -1565,7 +1540,7 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
JS_SetCanBlock(rt, can_block); JS_SetCanBlock(rt, can_block);
/* loader for ES6 modules */ /* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, (void *)filename); JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL);
add_helpers(ctx); add_helpers(ctx);
@ -1681,7 +1656,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((unsigned char)*q)) while (isspace(*q))
q++; q++;
error_type = strdup_len(q, strcspn(q, " \n")); error_type = strdup_len(q, strcspn(q, " \n"));
} }
@ -1866,7 +1841,7 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
JS_SetCanBlock(rt, can_block); JS_SetCanBlock(rt, can_block);
/* loader for ES6 modules */ /* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, (void *)filename); JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL);
add_helpers(ctx); add_helpers(ctx);
@ -1925,27 +1900,9 @@ 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;
if (compact) {
static int last_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 */ /* output progress indicator: erase end of line and return to col 0 */
fprintf(stderr, "%d/%d/%d\033[K\r", fprintf(stderr, "%d/%d/%d\033[K\r",
test_failed, test_count, test_skipped); test_failed, test_count, test_skipped);
}
fflush(stderr); fflush(stderr);
} }
} }
@ -1996,8 +1953,6 @@ 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"
@ -2024,29 +1979,14 @@ 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;
@ -2066,16 +2006,12 @@ 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++]), ignore); load_config(get_opt_arg(arg, argv[optind++]));
} 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")) {
@ -2126,10 +2062,8 @@ 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((unsigned char)argv[optind][0])) { if (optind < argc && !isdigit(argv[optind][0])) {
filename = argv[optind++]; filename = argv[optind++];
namelist_load(&test_list, filename); namelist_load(&test_list, filename);
} }
@ -2164,8 +2098,6 @@ 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);
@ -2194,8 +2126,6 @@ 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) {
@ -2211,6 +2141,5 @@ int main(int argc, char **argv)
free(harness_exclude); free(harness_exclude);
free(error_file); free(error_file);
/* Signal that the error file is out of date. */ return 0;
return new_errors || changed_errors || fixed_errors;
} }

View file

@ -406,8 +406,5 @@ 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

@ -22,10 +22,8 @@
* 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";
if (typeof require !== 'undefined') { import * as os from "os";
var fs = require('fs');
}
function pad(str, n) { function pad(str, n) {
str += ""; str += "";
@ -67,13 +65,17 @@ function toPrec(n, prec) {
var ref_data; var ref_data;
var log_data; var log_data;
var heads = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (1000)" ]; var heads = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (%)" ];
var widths = [ 22, 10, 9, 9, 9 ]; var widths = [ 22, 10, 9, 9, 9 ];
var precs = [ 0, 0, 2, 2, 0 ]; var precs = [ 0, 0, 2, 2, 2 ];
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 = "";
@ -81,7 +83,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]);
@ -93,28 +95,11 @@ function log_line() {
} }
var clocks_per_sec = 1000; var clocks_per_sec = 1000;
var max_iterations = 100; var max_iterations = 10;
var clock_threshold = 2; /* favoring short measuring spans */ var clock_threshold = 100; /* favoring short measuring spans */
var min_n_argument = 1; var min_n_argument = 1;
var get_clock; //var get_clock = Date.now;
if (typeof performance !== "undefined") { var get_clock = os.now;
// 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;
@ -127,7 +112,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, Math.round(ref * 1000 / ti)); log_line(text, n, ti, ref, ti * 100 / ref);
total_score += ti * 100 / ref; total_score += ti * 100 / ref;
total_scale += 100; total_scale += 100;
} else { } else {
@ -139,27 +124,28 @@ function log_one(text, n, ti) {
function bench(f, text) function bench(f, text)
{ {
var i, j, n, t, ti, nb_its, ref, ti_n, ti_n1; var i, j, n, t, t1, ti, nb_its, ref, ti_n, ti_n1, min_ti;
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
if (ti > t) t1 = get_clock() - t1;
ti = t; if (ti > t1)
ti = t1;
} }
if (ti >= clock_threshold / 10) { if (ti >= min_ti) {
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;
@ -185,26 +171,6 @@ 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++) {
@ -213,32 +179,6 @@ 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;
@ -267,78 +207,30 @@ function prop_write(n)
return n * 4; return n * 4;
} }
function prop_update(n)
{
var obj, j;
obj = {a: 1, b: 2, c:3, d:4 };
for(j = 0; j < n; j++) {
obj.a += j;
obj.b += j;
obj.c += j;
obj.d += j;
}
return n * 4;
}
function prop_create(n) function prop_create(n)
{ {
var obj, i, j; var obj, j;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
obj = {}; obj = new Object();
obj.a = 1; obj.a = 1;
obj.b = 2; obj.b = 2;
obj.c = 3; obj.c = 3;
obj.d = 4; 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 * 4;
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 ref, obj, j, k; var obj, j;
ref = { a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10 }; obj = {};
for(k = 0; k < 10; k++) { for(j = 0; j < n; j++) {
ref[k] = k; obj[j] = 1;
} }
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
obj = { ...ref }; delete obj[j];
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)
@ -399,32 +291,15 @@ 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, ref, i, j, len; var tab, i, j, len;
len = 1000; len = 1000;
ref = []; tab = [];
for(i = 0; i < len; i++) for(i = 0; i < len; i++)
ref[i] = i; tab[i] = i;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
tab = ref.slice(); for(i = len - 1; i >= 0; i--)
for(i = len; i --> 0;)
tab.length = i; tab.length = i;
} }
return len * n; return len * n;
@ -432,16 +307,15 @@ function array_length_decr(n)
function array_hole_length_decr(n) function array_hole_length_decr(n)
{ {
var tab, ref, i, j, len; var tab, i, j, len;
len = 1000; len = 1000;
ref = []; tab = [];
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
if (i % 10 == 9) if (i != 3)
ref[i] = i; tab[i] = i;
} }
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
tab = ref.slice(); for(i = len - 1; i >= 0; i--)
for(i = len; i --> 0;)
tab.length = i; tab.length = i;
} }
return len * n; return len * n;
@ -461,13 +335,12 @@ function array_push(n)
function array_pop(n) function array_pop(n)
{ {
var tab, ref, i, j, len, sum; var tab, 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 = ref.slice(); tab = [];
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();
@ -539,7 +412,6 @@ 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)
{ {
@ -582,7 +454,6 @@ 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)
{ {
@ -610,25 +481,6 @@ 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)
@ -648,7 +500,7 @@ function func_call(n)
return n * 4; return n * 4;
} }
function func_closure_call(n) function closure_var(n)
{ {
function f(a) function f(a)
{ {
@ -753,8 +605,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;
for(j = 0; j < n; j++) {
s = new Set(); s = new Set();
for(j = 0; j < n; j++) {
for(i = 0; i < len; i++) { for(i = 0; i < len; i++) {
s.add(String(i), i); s.add(String(i), i);
} }
@ -768,25 +620,25 @@ function set_collection_add(n)
function array_for(n) function array_for(n)
{ {
var r, i, j, sum, len = 100; var r, i, j, sum;
r = []; r = [];
for(i = 0; i < len; i++) for(i = 0; i < 100; 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 < len; i++) { for(i = 0; i < 100; i++) {
sum += r[i]; sum += r[i];
} }
global_res = sum; global_res = sum;
} }
return n * len; return n * 100;
} }
function array_for_in(n) function array_for_in(n)
{ {
var r, i, j, sum, len = 100; var r, i, j, sum;
r = []; r = [];
for(i = 0; i < len; i++) for(i = 0; i < 100; i++)
r[i] = i; r[i] = i;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
sum = 0; sum = 0;
@ -795,14 +647,14 @@ function array_for_in(n)
} }
global_res = sum; global_res = sum;
} }
return n * len; return n * 100;
} }
function array_for_of(n) function array_for_of(n)
{ {
var r, i, j, sum, len = 100; var r, i, j, sum;
r = []; r = [];
for(i = 0; i < len; i++) for(i = 0; i < 100; i++)
r[i] = i; r[i] = i;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
sum = 0; sum = 0;
@ -811,7 +663,7 @@ function array_for_of(n)
} }
global_res = sum; global_res = sum;
} }
return n * len; return n * 100;
} }
function math_min(n) function math_min(n)
@ -826,108 +678,58 @@ 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;
for(j = 0; j < n; j++) {
r = ""; r = "";
for(i = 0; i < 1000; i++) for(j = 0; j < n; j++) {
for(i = 0; i < 100; i++)
r += "x"; r += "x";
global_res = r; global_res = r;
} }
return n * 1000; return n * 100;
}
/* 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;
for(j = 0; j < n; j++) {
r = ""; r = "";
for(i = 0; i < 1000; i++) for(j = 0; j < n; j++) {
for(i = 0; i < 100; i++)
r += "x"; r += "x";
global_res = r; global_res = r;
} }
return n * 1000; return n * 100;
} }
/* incremental string contruction by prepending */ /* incremental string contruction by prepending */
function string_build3(n) function string_build3(n, r)
{ {
var i, j, r; var i, j;
for(j = 0; j < n; j++) {
r = ""; r = "";
for(i = 0; i < 1000; i++) for(j = 0; j < n; j++) {
for(i = 0; i < 100; i++)
r = "x" + r; r = "x" + r;
global_res = r; global_res = r;
} }
return n * 1000; return n * 100;
} }
/* 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;
for(j = 0; j < n; j++) {
r = ""; r = "";
for(i = 0; i < 1000; i++) { for(j = 0; j < n; j++) {
for(i = 0; i < 100; i++) {
s = r; s = r;
r += "x"; r += "x";
} }
global_res = r; global_res = r;
} }
return n * 1000; return n * 100;
} }
/* sort bench */ /* sort bench */
@ -1060,33 +862,32 @@ 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, 1, ti / 100); log_one("sort_" + f.name, n, ti, n * 100);
} }
total_score = save_total_score; total_score = save_total_score;
total_scale = save_total_scale; total_scale = save_total_scale;
return total / n / 100; return total / n / 1000;
} }
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, j; var s, r, j;
r = 0;
for(j = 0; j < n; j++) { for(j = 0; j < n; j++) {
s = (j % 1000).toString(); s = (j + 1).toString();
s = (1234000 + j % 1000).toString();
} }
global_res = s; return n;
return n * 2;
} }
function float_to_string(n) function float_to_string(n)
{ {
var s, j; var s, r, 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;
} }
@ -1095,6 +896,7 @@ 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);
} }
@ -1107,6 +909,7 @@ 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;
} }
@ -1116,92 +919,41 @@ function string_to_float(n)
function load_result(filename) function load_result(filename)
{ {
var has_filename = filename; var f, str, res;
var has_error = false; if (typeof std === "undefined")
var str, res; return null;
f = std.open(filename, "r");
if (!filename) if (!f)
filename = "microbench.txt"; return null;
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(); str = f.readAsString();
f.close();
} else {
has_error = true;
}
} else {
return null;
}
if (has_error) {
if (has_filename) {
// Should throw exception?
console.log("cannot load " + filename);
}
return null;
}
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 str = JSON.stringify(obj, null, 2) + "\n"; var f;
var has_error = false; if (typeof std === "undefined")
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");
if (has_error) f.puts(JSON.stringify(obj, null, 2));
console.log("cannot save " + filename); f.puts("\n");
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,
@ -1214,9 +966,8 @@ 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,
func_closure_call, closure_var,
int_arith, int_arith,
float_arith, float_arith,
set_collection_add, set_collection_add,
@ -1224,33 +975,28 @@ 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, found; var i, j, n, f, name;
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++];
@ -1261,7 +1007,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;
} }
@ -1271,22 +1017,14 @@ function main(argc, argv, g)
sort_bench.array_size = +argv[i++]; sort_bench.array_size = +argv[i++];
continue; continue;
} }
if (name == "-r") { for (j = 0; j < test_list.length; j++) {
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 (f.name.startsWith(name)) { if (name === f.name) {
tests.push(f); tests.push(f);
found = true; break;
} }
} }
if (!found) { if (j == test_list.length) {
console.log("unknown benchmark: " + name); console.log("unknown benchmark: " + name);
return 1; return 1;
} }
@ -1294,7 +1032,7 @@ function main(argc, argv, g)
if (tests.length == 0) if (tests.length == 0)
tests = test_list; tests = test_list;
ref_data = load_result(ref_file); ref_data = load_result("microbench.txt");
log_data = {}; log_data = {};
log_line.apply(null, heads); log_line.apply(null, heads);
n = 0; n = 0;
@ -1306,17 +1044,14 @@ function main(argc, argv, g)
n++; n++;
} }
if (ref_data) if (ref_data)
log_line("total", "", total[2], total[3], Math.round(total_scale * 1000 / total_score)); log_line("total", "", total[2], total[3], total_score * 100 / total_scale);
else else
log_line("total", "", total[2]); log_line("total", "", total[2]);
if (tests == test_list && new_ref_file) if (tests == test_list)
save_result(new_ref_file, log_data); save_result("microbench-new.txt", log_data);
} }
if (typeof scriptArgs === "undefined") { if (!scriptArgs)
scriptArgs = []; scriptArgs = [];
if (typeof process.argv === "object")
scriptArgs = process.argv.slice(1);
}
main(scriptArgs.length, scriptArgs, this); main(scriptArgs.length, scriptArgs, this);

View file

@ -1,8 +1,8 @@
diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js
index 9828b15..4a5919d 100644 index 9c1217351e..3c24755558 100644
--- a/harness/atomicsHelper.js --- a/harness/atomicsHelper.js
+++ b/harness/atomicsHelper.js +++ b/harness/atomicsHelper.js
@@ -272,10 +272,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) { @@ -227,10 +227,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) {
* } * }
*/ */
$262.agent.timeouts = { $262.agent.timeouts = {
@ -22,11 +22,11 @@ index 9828b15..4a5919d 100644
/** /**
diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js
index b55f3c6..396bad4 100644 index be7039fda0..7b38abf8df 100644
--- a/harness/regExpUtils.js --- a/harness/regExpUtils.js
+++ b/harness/regExpUtils.js +++ b/harness/regExpUtils.js
@@ -6,27 +6,30 @@ description: | @@ -6,24 +6,27 @@ description: |
defines: [buildString, testPropertyEscapes, testPropertyOfStrings, testExtendedCharacterClass, matchValidator] defines: [buildString, testPropertyEscapes, matchValidator]
---*/ ---*/
+if ($262 && typeof $262.codePointRange === "function") { +if ($262 && typeof $262.codePointRange === "function") {
@ -44,12 +44,7 @@ index b55f3c6..396bad4 100644
+ } + }
+} +}
+ +
function buildString(args) { function buildString({ loneCodePoints, ranges }) {
// Use member expressions rather than destructuring `args` for improved
// compatibility with engines that only implement assignment patterns
// partially or not at all.
const loneCodePoints = args.loneCodePoints;
const ranges = args.ranges;
- const CHUNK_SIZE = 10000; - const CHUNK_SIZE = 10000;
- let result = Reflect.apply(String.fromCodePoint, null, loneCodePoints); - let result = Reflect.apply(String.fromCodePoint, null, loneCodePoints);
- for (let i = 0; i < ranges.length; i++) { - for (let i = 0; i < ranges.length; i++) {
@ -63,11 +58,14 @@ index b55f3c6..396bad4 100644
- result += Reflect.apply(String.fromCodePoint, null, codePoints); - result += Reflect.apply(String.fromCodePoint, null, codePoints);
- codePoints.length = length = 0; - codePoints.length = length = 0;
- } - }
- }
- result += Reflect.apply(String.fromCodePoint, null, codePoints);
+ let result = String.fromCodePoint.apply(null, loneCodePoints); + let result = String.fromCodePoint.apply(null, loneCodePoints);
+ for (const [start, end] of ranges) { + for (const [start, end] of ranges) {
+ result += codePointRange(start, end + 1); + result += codePointRange(start, end + 1);
} }
return result; - result += Reflect.apply(String.fromCodePoint, null, codePoints);
- }
- return result;
+ return result;
} }
function testPropertyEscapes(regex, string, expression) {

View file

@ -1,279 +0,0 @@
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
function assertThrows(err, func)
{
var ex;
ex = false;
try {
func();
} catch(e) {
ex = true;
assert(e instanceof err);
}
assert(ex, true, "exception expected");
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
/* 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

@ -110,5 +110,217 @@ 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

@ -1,51 +1,19 @@
"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 (typeof actual === typeof expected) { if (actual === expected)
if (actual === expected) {
if (actual !== 0 || (1 / actual) === (1 / expected))
return; return;
}
if (typeof actual === 'number') {
if (isNaN(actual) && isNaN(expected))
return true;
}
if (typeof actual === 'object') {
if (actual !== null && expected !== null if (actual !== null && expected !== null
&& actual.constructor === expected.constructor && typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString()) && actual.toString() === expected.toString())
return; return;
}
} throw Error("assertion failed: got |" + actual + "|" +
// Should output the source file and line number and extract ", expected |" + expected + "|" +
// 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 + ")" : ""));
} }
@ -57,16 +25,11 @@ function assert_throws(expected_error, func)
} catch(e) { } catch(e) {
err = true; err = true;
if (!(e instanceof expected_error)) { if (!(e instanceof expected_error)) {
// Should output the source file and line number and extract throw Error("unexpected exception type");
// the expression from the assert_throws() call
throw_error("unexpected exception type");
return;
} }
} }
if (!err) { if (!err) {
// Should output the source file and line number and extract throw Error("expected exception");
// the expression from the assert_throws() call
throw_error("expected exception");
} }
} }
@ -348,14 +311,10 @@ 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);
} }
@ -368,10 +327,6 @@ 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);
@ -381,11 +336,6 @@ 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");
@ -531,105 +481,26 @@ function test_json()
function test_date() function test_date()
{ {
// Date Time String format is YYYY-MM-DDTHH:mm:ss.sssZ var d = new Date(1506098258091), a, s;
// 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");
var a = Date.parse(d.toISOString()); 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((new Date("2020-01-01T01:01:01.123Z")).toISOString(), assert(s == "2020-01-01T01:01:01.100Z");
"2020-01-01T01:01:01.123Z"); s = new Date("2020-01-01T01:01:01.12Z").toISOString();
/* implementation defined behavior */ assert(s == "2020-01-01T01:01:01.120Z");
assert((new Date("2020-01-01T01:01:01.1Z")).toISOString(), s = new Date("2020-01-01T01:01:01.123Z").toISOString();
"2020-01-01T01:01:01.100Z"); assert(s == "2020-01-01T01:01:01.123Z");
assert((new Date("2020-01-01T01:01:01.12Z")).toISOString(), s = new Date("2020-01-01T01:01:01.1234Z").toISOString();
"2020-01-01T01:01:01.120Z"); assert(s == "2020-01-01T01:01:01.123Z");
assert((new Date("2020-01-01T01:01:01.1234Z")).toISOString(), s = new Date("2020-01-01T01:01:01.12345Z").toISOString();
"2020-01-01T01:01:01.123Z"); assert(s == "2020-01-01T01:01:01.123Z");
assert((new Date("2020-01-01T01:01:01.12345Z")).toISOString(), s = new Date("2020-01-01T01:01:01.1235Z").toISOString();
"2020-01-01T01:01:01.123Z"); assert(s == "2020-01-01T01:01:01.124Z");
assert((new Date("2020-01-01T01:01:01.1235Z")).toISOString(), s = new Date("2020-01-01T01:01:01.9999Z").toISOString();
"2020-01-01T01:01:01.123Z"); assert(s == "2020-01-01T01:01:02.000Z");
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()
@ -678,8 +549,6 @@ function test_regexp()
assert(a, ["a", undefined]); assert(a, ["a", undefined]);
a = /(?:|[\w])+([0-9])/.exec("123a23"); a = /(?:|[\w])+([0-9])/.exec("123a23");
assert(a, ["123a23", "3"]); assert(a, ["123a23", "3"]);
a = /()*?a/.exec(",");
assert(a, null);
} }
function test_symbol() function test_symbol()
@ -718,20 +587,6 @@ 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++) {

View file

@ -335,13 +335,6 @@ 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()
@ -369,9 +362,8 @@ 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, get a(){ return this.get} }; a = { get: 2, set: 3, async: 4 };
assert(JSON.stringify(a), '{"get":2,"set":3,"async":4,"a":2}'); assert(JSON.stringify(a), '{"get":2,"set":3,"async":4}');
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}');
@ -428,12 +420,8 @@ function test_argument_scope()
var f; var f;
var c = "global"; var c = "global";
(function() {
"use strict";
// XXX: node only throws in strict mode
f = function(a = eval("var arguments")) {}; f = function(a = eval("var arguments")) {};
assert_throws(SyntaxError, f); 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);
@ -570,15 +558,6 @@ 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()
{ {
@ -625,4 +604,3 @@ 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

@ -144,9 +144,7 @@ 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;
const stdinIsTTY = !os.exec(["/bin/sh", "-c", "test -t 0"], { usePath: false }); assert(os.isatty(0));
assert(os.isatty(0), stdinIsTTY, `isatty(STDIN)`);
fdir = "test_tmp_dir"; fdir = "test_tmp_dir";
fname = "tmp_file.txt"; fname = "tmp_file.txt";
@ -255,11 +253,10 @@ 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.SIGTERM); os.kill(pid, os.SIGQUIT);
[ret, status] = os.waitpid(pid, 0); [ret, status] = os.waitpid(pid, 0);
assert(ret, pid); assert(ret, pid);
assert(status !== 0, true, `expect nonzero exit code (got ${status})`); assert(status & 0x7f, os.SIGQUIT);
assert(status & 0x7f, os.SIGTERM);
} }
function test_timer() function test_timer()

View file

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

View file

@ -33,11 +33,6 @@
#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 */
@ -273,7 +268,7 @@ int find_name(const char **tab, int tab_len, const char *name)
return -1; return -1;
} }
static BOOL get_prop(uint32_t c, int prop_idx) static int 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;
} }
@ -1333,9 +1328,7 @@ void dump_case_conv_table(FILE *f)
uint32_t v; uint32_t v;
const TableEntry *te; const TableEntry *te;
total_tables++; fprintf(f, "static const uint32_t case_conv_table1[%u] = {", conv_table_len);
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 ");
@ -1348,9 +1341,7 @@ void dump_case_conv_table(FILE *f)
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
total_tables++; fprintf(f, "static const uint8_t case_conv_table2[%u] = {", conv_table_len);
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 ");
@ -1359,9 +1350,7 @@ void dump_case_conv_table(FILE *f)
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
total_tables++; fprintf(f, "static const uint16_t case_conv_ext[%u] = {", ext_data_len);
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 ");
@ -1481,9 +1470,6 @@ 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)
@ -1493,26 +1479,9 @@ 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, const char *name, int prop_index, BOOL add_index) void build_prop_table(FILE *f, 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;
@ -1564,14 +1533,6 @@ void build_prop_table(FILE *f, const char *name, 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;
@ -1612,7 +1573,7 @@ void build_prop_table(FILE *f, const char *name, 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_index_table(f, cname, dbuf2->buf, dbuf2->size); dump_byte_table(f, cname, dbuf2->buf, dbuf2->size);
} }
dbuf_free(dbuf); dbuf_free(dbuf);
@ -1622,10 +1583,10 @@ void build_prop_table(FILE *f, const char *name, int prop_index, BOOL add_index)
void build_flags_tables(FILE *f) void build_flags_tables(FILE *f)
{ {
build_prop_table(f, "Cased1", PROP_Cased1, TRUE); build_prop_table(f, PROP_Cased1, TRUE);
build_prop_table(f, "Case_Ignorable", PROP_Case_Ignorable, TRUE); build_prop_table(f, PROP_Case_Ignorable, TRUE);
build_prop_table(f, "ID_Start", PROP_ID_Start, TRUE); build_prop_table(f, PROP_ID_Start, TRUE);
build_prop_table(f, "ID_Continue1", PROP_ID_Continue1, TRUE); build_prop_table(f, 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,
@ -1660,9 +1621,7 @@ 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++)
@ -1676,11 +1635,9 @@ 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;
@ -1699,11 +1656,9 @@ void build_general_category_table(FILE *f)
} }
} }
// printf("%05x %05x %d\n", i, n, v); // printf("%05x %05x %d\n", i, n, v);
n--;
#ifdef DUMP_TABLE_SIZE
cw_count++; cw_count++;
n--;
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) {
@ -1725,13 +1680,12 @@ 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 [", cw_count); printf("general category: %d entries [",
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);
@ -1746,9 +1700,7 @@ 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++)
@ -1762,11 +1714,9 @@ 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;
@ -1776,11 +1726,9 @@ 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);
n--;
#ifdef DUMP_TABLE_SIZE
cw_count++; cw_count++;
n--;
cw_start = dbuf->size; cw_start = dbuf->size;
#endif
if (v == 0) if (v == 0)
type = 0; type = 0;
else else
@ -1802,13 +1750,12 @@ 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;
} }
#ifdef DUMP_TABLE_SIZE #if defined(DUMP_TABLE_SIZE)
printf("script: %d entries [", cw_count); printf("script: %d entries [",
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);
@ -1823,11 +1770,10 @@ 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;
#if defined(DUMP_TABLE_SIZE) int cw_count;
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;
@ -1838,9 +1784,7 @@ 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);
@ -1862,7 +1806,8 @@ 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", cw_count); printf("script_ext: %d entries",
cw_count);
printf(", length=%d bytes\n", (int)dbuf->size); printf(", length=%d bytes\n", (int)dbuf->size);
#endif #endif
@ -1884,7 +1829,7 @@ void build_prop_list_table(FILE *f)
i == PROP_ID_Continue1) { i == PROP_ID_Continue1) {
/* already generated */ /* already generated */
} else { } else {
build_prop_table(f, unicode_prop_name[i], i, FALSE); build_prop_table(f, i, FALSE);
} }
} }
@ -1981,7 +1926,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);
@ -1989,7 +1934,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);
@ -1997,7 +1942,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);
@ -2005,7 +1950,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);
@ -2019,7 +1964,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++;
} }
@ -2036,23 +1981,17 @@ void check_flags(void)
void build_cc_table(FILE *f) void build_cc_table(FILE *f)
{ {
// Compress combining class table int i, cc, n, cc_table_len, type, n1;
// 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;
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE) int cw_len_tab[3], cw_start, block_end_pos;
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;
@ -2093,16 +2032,7 @@ 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)) {
@ -2116,12 +2046,10 @@ 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;
} }
@ -2132,7 +2060,7 @@ void build_cc_table(FILE *f)
dbuf_putc(dbuf1, v >> 16); dbuf_putc(dbuf1, v >> 16);
dump_byte_table(f, "unicode_cc_table", dbuf->buf, dbuf->size); dump_byte_table(f, "unicode_cc_table", dbuf->buf, dbuf->size);
dump_index_table(f, "unicode_cc_index", dbuf1->buf, dbuf1->size); dump_byte_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) [",
@ -2813,9 +2741,8 @@ void build_decompose_table(FILE *f)
} }
#endif #endif
total_tables++; fprintf(f, "static const uint32_t unicode_decomp_table1[%u] = {",
total_table_bytes += array_len * sizeof(uint32_t); array_len);
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];
@ -2833,9 +2760,8 @@ void build_decompose_table(FILE *f)
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
total_tables++; fprintf(f, "static const uint16_t unicode_decomp_table2[%u] = {",
total_table_bytes += array_len * sizeof(uint16_t); array_len);
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];
@ -2848,9 +2774,8 @@ void build_decompose_table(FILE *f)
} }
fprintf(f, "\n};\n\n"); fprintf(f, "\n};\n\n");
total_tables++; fprintf(f, "static const uint8_t unicode_decomp_data[%u] = {",
total_table_bytes += data_len; 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 ");
@ -2941,9 +2866,8 @@ void build_compose_table(FILE *f, const DecompEntry *tab_de)
} }
#endif #endif
total_tables++; fprintf(f, "static const uint16_t unicode_comp_table[%u] = {",
total_table_bytes += tab_ce_len * sizeof(uint16_t); tab_ce_len);
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 ");
@ -3118,24 +3042,22 @@ 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 (arg >= argc || (!strcmp(argv[arg], "-h") || !strcmp(argv[arg], "--help"))) { if (argc < 2) {
printf("usage: %s PATH [OUTPUT]\n" printf("usage: %s unicode_db_path [output_file]\n"
" PATH path to the Unicode database directory\n" "\n"
" OUTPUT name of the output file. If omitted, a self test is performed\n" "If no output_file is given, a self test is done using the current unicode library\n",
" using the files from the Unicode library\n" argv[0]);
, argv[0]); exit(1);
return 1;
} }
unicode_db_path = argv[arg++]; unicode_db_path = argv[1];
outfilename = NULL; outfilename = NULL;
if (arg < argc) if (argc >= 3)
outfilename = argv[arg++]; outfilename = argv[2];
unicode_db = mallocz(sizeof(unicode_db[0]) * (CHARCODE_MAX + 1)); unicode_db = mallocz(sizeof(unicode_db[0]) * (CHARCODE_MAX + 1));
@ -3217,8 +3139,6 @@ 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;