Compare commits
1 commit
master
...
revert-265
Author | SHA1 | Date | |
---|---|---|---|
|
e02472dabc |
63 changed files with 4330 additions and 5085 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,15 +1,17 @@
|
|||
*.a
|
||||
.obj/
|
||||
tests/bjson.so
|
||||
examples/test_fib
|
||||
test_fib.c
|
||||
examples/*.so
|
||||
examples/hello
|
||||
examples/hello_module
|
||||
hello.c
|
||||
microbench*.txt
|
||||
qjs
|
||||
qjsc
|
||||
qjscalc
|
||||
qjscalc.c
|
||||
hello.c
|
||||
hello
|
||||
hello_module
|
||||
repl.c
|
||||
run-test262
|
||||
test262
|
||||
|
|
|
@ -13,7 +13,7 @@ TypedArray.prototype.{with,toReversed,toSorted}
|
|||
- added RegExp 'd' flag
|
||||
- fixed RegExp zero length match logic
|
||||
- fixed RegExp case insensitive flag
|
||||
- added os.getpid() and os.now()
|
||||
- added os.sleepAsync(), os.getpid() and os.now()
|
||||
- added cosmopolitan build
|
||||
- misc bug fixes
|
||||
|
||||
|
|
3
LICENSE
3
LICENSE
|
@ -1,6 +1,5 @@
|
|||
QuickJS Javascript Engine (DoneJS Edition)
|
||||
QuickJS Javascript Engine
|
||||
|
||||
Copyright (c) 2024 Sneed Group
|
||||
Copyright (c) 2017-2021 Fabrice Bellard
|
||||
Copyright (c) 2017-2021 Charlie Gordon
|
||||
|
||||
|
|
153
Makefile
153
Makefile
|
@ -25,10 +25,13 @@
|
|||
ifeq ($(shell uname -s),Darwin)
|
||||
CONFIG_DARWIN=y
|
||||
endif
|
||||
ifeq ($(shell uname -s),FreeBSD)
|
||||
CONFIG_FREEBSD=y
|
||||
endif
|
||||
# Windows cross compilation from Linux
|
||||
#CONFIG_WIN32=y
|
||||
# use link time optimization (smaller and faster executables but slower build)
|
||||
CONFIG_LTO=y
|
||||
#CONFIG_LTO=y
|
||||
# consider warnings as errors (for development)
|
||||
#CONFIG_WERROR=y
|
||||
# force 32 bit build for some utilities
|
||||
|
@ -43,16 +46,37 @@ PREFIX?=/usr/local
|
|||
#CONFIG_PROFILE=y
|
||||
# use address sanitizer
|
||||
#CONFIG_ASAN=y
|
||||
# include the code for BigFloat/BigDecimal, math mode and faster large integers
|
||||
# use memory sanitizer
|
||||
#CONFIG_MSAN=y
|
||||
# use UB sanitizer
|
||||
#CONFIG_UBSAN=y
|
||||
|
||||
# include the code for BigFloat/BigDecimal and math mode
|
||||
CONFIG_BIGNUM=y
|
||||
|
||||
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
|
||||
# use clang instead of gcc
|
||||
CONFIG_CLANG=y
|
||||
CONFIG_DEFAULT_AR=y
|
||||
endif
|
||||
ifdef CONFIG_FREEBSD
|
||||
# use clang instead of gcc
|
||||
CONFIG_CLANG=y
|
||||
CONFIG_DEFAULT_AR=y
|
||||
CONFIG_LTO=
|
||||
endif
|
||||
|
||||
ifdef CONFIG_WIN32
|
||||
ifdef CONFIG_M32
|
||||
|
@ -87,6 +111,7 @@ ifdef CONFIG_CLANG
|
|||
AR=$(CROSS_PREFIX)ar
|
||||
endif
|
||||
endif
|
||||
LIB_FUZZING_ENGINE ?= "-fsanitize=fuzzer"
|
||||
else ifdef CONFIG_COSMO
|
||||
CONFIG_LTO=
|
||||
HOST_CC=gcc
|
||||
|
@ -106,7 +131,7 @@ else
|
|||
AR=$(CROSS_PREFIX)ar
|
||||
endif
|
||||
endif
|
||||
STRIP=$(CROSS_PREFIX)strip
|
||||
STRIP?=$(CROSS_PREFIX)strip
|
||||
CFLAGS+=-fwrapv # ensure that signed overflows behave as expected
|
||||
ifdef CONFIG_WERROR
|
||||
CFLAGS+=-Werror
|
||||
|
@ -118,6 +143,11 @@ endif
|
|||
ifdef CONFIG_WIN32
|
||||
DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior
|
||||
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_DEBUG=$(CFLAGS) -O0
|
||||
|
@ -142,6 +172,14 @@ ifdef CONFIG_ASAN
|
|||
CFLAGS+=-fsanitize=address -fno-omit-frame-pointer
|
||||
LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer
|
||||
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
|
||||
LDEXPORT=
|
||||
else
|
||||
|
@ -176,12 +214,15 @@ endif
|
|||
|
||||
# examples
|
||||
ifeq ($(CROSS_PREFIX),)
|
||||
PROGS+=examples/hello
|
||||
ifndef CONFIG_ASAN
|
||||
PROGS+=examples/hello_module
|
||||
endif
|
||||
ifndef CONFIG_MSAN
|
||||
ifndef CONFIG_UBSAN
|
||||
PROGS+=examples/hello examples/hello_module examples/test_fib
|
||||
ifdef CONFIG_SHARED_LIBS
|
||||
PROGS+=examples/test_fib examples/fib.so examples/point.so
|
||||
PROGS+=examples/fib.so examples/point.so
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -213,6 +254,17 @@ qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS))
|
|||
qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS)
|
||||
$(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),)
|
||||
|
||||
$(QJSC): $(OBJDIR)/qjsc.host.o \
|
||||
|
@ -254,6 +306,9 @@ libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
|
|||
$(AR) rcs $@ $^
|
||||
endif # CONFIG_LTO
|
||||
|
||||
libquickjs.fuzz.a: $(patsubst %.o, %.fuzz.o, $(QJS_LIB_OBJS))
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
repl.c: $(QJSC) repl.js
|
||||
$(QJSC) -c -o $@ -m repl.js
|
||||
|
||||
|
@ -282,6 +337,9 @@ run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)
|
|||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS_OPT) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/fuzz_%.o: fuzz/fuzz_%.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS_OPT) -c -I. -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.host.o: %.c | $(OBJDIR)
|
||||
$(HOST_CC) $(CFLAGS_OPT) -c -o $@ $<
|
||||
|
||||
|
@ -300,6 +358,9 @@ $(OBJDIR)/%.m32s.o: %.c | $(OBJDIR)
|
|||
$(OBJDIR)/%.debug.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS_DEBUG) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.fuzz.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS_OPT) -fsanitize=fuzzer-no-link -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.check.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $<
|
||||
|
||||
|
@ -311,17 +372,18 @@ unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c u
|
|||
|
||||
clean:
|
||||
rm -f repl.c qjscalc.c out.c
|
||||
rm -f *.a *.o *.d *~ unicode_gen regexp_test $(PROGS)
|
||||
rm -f *.a *.o *.d *~ unicode_gen regexp_test fuzz_eval fuzz_compile fuzz_regexp $(PROGS)
|
||||
rm -f hello.c test_fib.c
|
||||
rm -f examples/*.so tests/*.so
|
||||
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug
|
||||
rm -rf run-test262-debug run-test262-32
|
||||
rm -f run_octane run_sunspider_like
|
||||
|
||||
install: all
|
||||
mkdir -p "$(DESTDIR)$(PREFIX)/bin"
|
||||
$(STRIP) qjs qjsc
|
||||
install -m755 qjs qjsc "$(DESTDIR)$(PREFIX)/bin"
|
||||
ln -sf qjs "$(DESTDIR)$(PREFIX)/bin/qjscalc"
|
||||
$(STRIP) qjs$(EXE) qjsc$(EXE)
|
||||
install -m755 qjs$(EXE) qjsc$(EXE) "$(DESTDIR)$(PREFIX)/bin"
|
||||
ln -sf qjs$(EXE) "$(DESTDIR)$(PREFIX)/bin/qjscalc$(EXE)"
|
||||
mkdir -p "$(DESTDIR)$(PREFIX)/lib/quickjs"
|
||||
install -m644 libquickjs.a "$(DESTDIR)$(PREFIX)/lib/quickjs"
|
||||
ifdef CONFIG_LTO
|
||||
|
@ -404,8 +466,9 @@ endif
|
|||
test: qjs
|
||||
./qjs tests/test_closure.js
|
||||
./qjs tests/test_language.js
|
||||
./qjs tests/test_builtin.js
|
||||
./qjs --std tests/test_builtin.js
|
||||
./qjs tests/test_loop.js
|
||||
./qjs tests/test_bignum.js
|
||||
./qjs tests/test_std.js
|
||||
./qjs tests/test_worker.js
|
||||
ifdef CONFIG_SHARED_LIBS
|
||||
|
@ -418,19 +481,20 @@ endif
|
|||
endif
|
||||
ifdef CONFIG_BIGNUM
|
||||
./qjs --bignum tests/test_op_overloading.js
|
||||
./qjs --bignum tests/test_bignum.js
|
||||
./qjs --bignum tests/test_bigfloat.js
|
||||
./qjs --qjscalc tests/test_qjscalc.js
|
||||
endif
|
||||
ifdef CONFIG_M32
|
||||
./qjs32 tests/test_closure.js
|
||||
./qjs32 tests/test_language.js
|
||||
./qjs32 tests/test_builtin.js
|
||||
./qjs32 --std tests/test_builtin.js
|
||||
./qjs32 tests/test_loop.js
|
||||
./qjs32 tests/test_bignum.js
|
||||
./qjs32 tests/test_std.js
|
||||
./qjs32 tests/test_worker.js
|
||||
ifdef CONFIG_BIGNUM
|
||||
./qjs32 --bignum tests/test_op_overloading.js
|
||||
./qjs32 --bignum tests/test_bignum.js
|
||||
./qjs32 --bignum tests/test_bigfloat.js
|
||||
./qjs32 --qjscalc tests/test_qjscalc.js
|
||||
endif
|
||||
endif
|
||||
|
@ -440,36 +504,46 @@ stats: qjs qjs32
|
|||
./qjs32 -qd
|
||||
|
||||
microbench: qjs
|
||||
./qjs tests/microbench.js
|
||||
./qjs --std tests/microbench.js
|
||||
|
||||
microbench-32: qjs32
|
||||
./qjs32 tests/microbench.js
|
||||
./qjs32 --std tests/microbench.js
|
||||
|
||||
ifeq ($(wildcard test262o/tests.txt),)
|
||||
test2o test2o-32 test2o-update:
|
||||
@echo test262o tests not installed
|
||||
else
|
||||
# ES5 tests (obsolete)
|
||||
test2o: run-test262
|
||||
time ./run-test262 -m -c test262o.conf
|
||||
time ./run-test262 -t -m -c test262o.conf
|
||||
|
||||
test2o-32: run-test262-32
|
||||
time ./run-test262-32 -m -c test262o.conf
|
||||
time ./run-test262-32 -t -m -c test262o.conf
|
||||
|
||||
test2o-update: run-test262
|
||||
./run-test262 -u -c test262o.conf
|
||||
./run-test262 -t -u -c test262o.conf
|
||||
endif
|
||||
|
||||
ifeq ($(wildcard test262o/tests.txt),)
|
||||
test2 test2-32 test2-update test2-default test2-check:
|
||||
@echo test262 tests not installed
|
||||
else
|
||||
# Test262 tests
|
||||
test2-default: run-test262
|
||||
time ./run-test262 -m -c test262.conf
|
||||
time ./run-test262 -t -m -c test262.conf
|
||||
|
||||
test2: run-test262
|
||||
time ./run-test262 -m -c test262.conf -a
|
||||
time ./run-test262 -t -m -c test262.conf -a
|
||||
|
||||
test2-32: run-test262-32
|
||||
time ./run-test262-32 -m -c test262.conf -a
|
||||
time ./run-test262-32 -t -m -c test262.conf -a
|
||||
|
||||
test2-update: run-test262
|
||||
./run-test262 -u -c test262.conf -a
|
||||
./run-test262 -t -u -c test262.conf -a
|
||||
|
||||
test2-check: run-test262
|
||||
time ./run-test262 -m -c test262.conf -E -a
|
||||
time ./run-test262 -t -m -c test262.conf -E -a
|
||||
endif
|
||||
|
||||
testall: all test microbench test2o test2
|
||||
|
||||
|
@ -477,11 +551,40 @@ testall-32: all test-32 microbench-32 test2o-32 test2-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
|
||||
make -C tests/bench-v8
|
||||
./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
|
||||
$(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)
|
||||
|
|
14
README.md
14
README.md
|
@ -1,14 +0,0 @@
|
|||
# QuickJS (DoneJS edition)
|
||||
|
||||
An *unofficial* fork of QuickJS that contains polyfills and modifications to the source code to make it more like something such as NodeJS or De*no*.
|
||||
|
||||
# FAQ
|
||||
|
||||
## Documentation Location
|
||||
|
||||
The main documentation is in "doc/quickjs.pdf" for the PDF version or "doc/quickjs.html" for the HTML one.
|
||||
|
||||
|
||||
## Location of Polyfills
|
||||
|
||||
The polyfills are located in the "qjs.c" file.
|
2
TODO
2
TODO
|
@ -63,5 +63,5 @@ Optimization ideas:
|
|||
Test262o: 0/11262 errors, 463 excluded
|
||||
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
|
||||
|
||||
Result: 10/76947 errors, 1497 excluded, 8117 skipped
|
||||
Result: 8/76947 errors, 1497 excluded, 8117 skipped
|
||||
Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
2024-01-13
|
||||
2024-02-14
|
||||
|
|
2
cutils.c
2
cutils.c
|
@ -140,7 +140,7 @@ int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
|
|||
if (dbuf_realloc(s, s->size + len))
|
||||
return -1;
|
||||
}
|
||||
memcpy(s->buf + s->size, data, len);
|
||||
memcpy_no_ub(s->buf + s->size, data, len);
|
||||
s->size += len;
|
||||
return 0;
|
||||
}
|
||||
|
|
55
cutils.h
55
cutils.h
|
@ -26,11 +26,9 @@
|
|||
#define CUTILS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/* set if CPU is big endian */
|
||||
#undef WORDS_BIGENDIAN
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define force_inline inline __attribute__((always_inline))
|
||||
|
@ -48,9 +46,16 @@
|
|||
#ifndef countof
|
||||
#define countof(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
|
||||
#ifndef container_of
|
||||
/* return the pointer of type 'type *' containing 'ptr' as field '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;
|
||||
|
||||
|
@ -66,6 +71,12 @@ char *pstrcat(char *buf, int buf_size, const char *s);
|
|||
int strstart(const char *str, const char *val, const char **ptr);
|
||||
int 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)
|
||||
{
|
||||
if (a > b)
|
||||
|
@ -210,17 +221,22 @@ static inline void put_u8(uint8_t *tab, uint8_t val)
|
|||
*tab = val;
|
||||
}
|
||||
|
||||
#ifndef bswap16
|
||||
static inline uint16_t bswap16(uint16_t x)
|
||||
{
|
||||
return (x >> 8) | (x << 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef bswap32
|
||||
static inline uint32_t bswap32(uint32_t v)
|
||||
{
|
||||
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
|
||||
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef bswap64
|
||||
static inline uint64_t bswap64(uint64_t v)
|
||||
{
|
||||
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
|
||||
|
@ -232,6 +248,7 @@ static inline uint64_t bswap64(uint64_t v)
|
|||
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
|
||||
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: should take an extra argument to pass slack information to the caller */
|
||||
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
|
||||
|
@ -281,6 +298,36 @@ static inline void dbuf_set_error(DynBuf *s)
|
|||
int unicode_to_utf8(uint8_t *buf, unsigned int c);
|
||||
int unicode_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)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
|
|
|
@ -1,734 +0,0 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<!-- Created by GNU Texinfo 6.1, http://www.gnu.org/software/texinfo/ -->
|
||||
<head>
|
||||
<title>Javascript Bignum Extensions</title>
|
||||
|
||||
<meta name="description" content="Javascript Bignum Extensions">
|
||||
<meta name="keywords" content="Javascript Bignum Extensions">
|
||||
<meta name="resource-type" content="document">
|
||||
<meta name="distribution" content="global">
|
||||
<meta name="Generator" content="makeinfo">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link href="#SEC_Contents" rel="contents" title="Table of Contents">
|
||||
<style type="text/css">
|
||||
<!--
|
||||
a.summary-letter {text-decoration: none}
|
||||
blockquote.indentedblock {margin-right: 0em}
|
||||
blockquote.smallindentedblock {margin-right: 0em; font-size: smaller}
|
||||
blockquote.smallquotation {font-size: smaller}
|
||||
div.display {margin-left: 3.2em}
|
||||
div.example {margin-left: 3.2em}
|
||||
div.lisp {margin-left: 3.2em}
|
||||
div.smalldisplay {margin-left: 3.2em}
|
||||
div.smallexample {margin-left: 3.2em}
|
||||
div.smalllisp {margin-left: 3.2em}
|
||||
kbd {font-style: oblique}
|
||||
pre.display {font-family: inherit}
|
||||
pre.format {font-family: inherit}
|
||||
pre.menu-comment {font-family: serif}
|
||||
pre.menu-preformatted {font-family: serif}
|
||||
pre.smalldisplay {font-family: inherit; font-size: smaller}
|
||||
pre.smallexample {font-size: smaller}
|
||||
pre.smallformat {font-family: inherit; font-size: smaller}
|
||||
pre.smalllisp {font-size: smaller}
|
||||
span.nolinebreak {white-space: nowrap}
|
||||
span.roman {font-family: initial; font-weight: normal}
|
||||
span.sansserif {font-family: sans-serif; font-weight: normal}
|
||||
ul.no-bullet {list-style: none}
|
||||
-->
|
||||
</style>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<h1 class="settitle" align="center">Javascript Bignum Extensions</h1>
|
||||
|
||||
<a name="SEC_Contents"></a>
|
||||
<h2 class="contents-heading">Table of Contents</h2>
|
||||
|
||||
<div class="contents">
|
||||
<ul class="no-bullet">
|
||||
<li><a name="toc-Introduction" href="#Introduction">1 Introduction</a></li>
|
||||
<li><a name="toc-Operator-overloading" href="#Operator-overloading">2 Operator overloading</a></li>
|
||||
<li><a name="toc-BigInt-extensions" href="#BigInt-extensions">3 BigInt extensions</a></li>
|
||||
<li><a name="toc-BigFloat" href="#BigFloat">4 BigFloat</a>
|
||||
<ul class="no-bullet">
|
||||
<li><a name="toc-Introduction-1" href="#Introduction-1">4.1 Introduction</a></li>
|
||||
<li><a name="toc-Floating-point-rounding" href="#Floating-point-rounding">4.2 Floating point rounding</a></li>
|
||||
<li><a name="toc-Operators" href="#Operators">4.3 Operators</a></li>
|
||||
<li><a name="toc-BigFloat-literals" href="#BigFloat-literals">4.4 BigFloat literals</a></li>
|
||||
<li><a name="toc-Builtin-Object-changes" href="#Builtin-Object-changes">4.5 Builtin Object changes</a>
|
||||
<ul class="no-bullet">
|
||||
<li><a name="toc-BigFloat-function" href="#BigFloat-function">4.5.1 <code>BigFloat</code> function</a></li>
|
||||
<li><a name="toc-BigFloat_002eprototype" href="#BigFloat_002eprototype">4.5.2 <code>BigFloat.prototype</code></a></li>
|
||||
<li><a name="toc-BigFloatEnv-constructor" href="#BigFloatEnv-constructor">4.5.3 <code>BigFloatEnv</code> constructor</a></li>
|
||||
</ul></li>
|
||||
</ul></li>
|
||||
<li><a name="toc-BigDecimal" href="#BigDecimal">5 BigDecimal</a>
|
||||
<ul class="no-bullet">
|
||||
<li><a name="toc-Operators-1" href="#Operators-1">5.1 Operators</a></li>
|
||||
<li><a name="toc-BigDecimal-literals" href="#BigDecimal-literals">5.2 BigDecimal literals</a></li>
|
||||
<li><a name="toc-Builtin-Object-changes-1" href="#Builtin-Object-changes-1">5.3 Builtin Object changes</a>
|
||||
<ul class="no-bullet">
|
||||
<li><a name="toc-The-BigDecimal-function_002e" href="#The-BigDecimal-function_002e">5.3.1 The <code>BigDecimal</code> function.</a></li>
|
||||
<li><a name="toc-Properties-of-the-BigDecimal-object" href="#Properties-of-the-BigDecimal-object">5.3.2 Properties of the <code>BigDecimal</code> object</a></li>
|
||||
<li><a name="toc-Properties-of-the-BigDecimal_002eprototype-object" href="#Properties-of-the-BigDecimal_002eprototype-object">5.3.3 Properties of the <code>BigDecimal.prototype</code> object</a></li>
|
||||
</ul></li>
|
||||
</ul></li>
|
||||
<li><a name="toc-Math-mode" href="#Math-mode">6 Math mode</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<a name="Introduction"></a>
|
||||
<h2 class="chapter">1 Introduction</h2>
|
||||
|
||||
<p>The Bignum extensions add the following features to the Javascript
|
||||
language while being 100% backward compatible:
|
||||
</p>
|
||||
<ul>
|
||||
<li> Operator overloading with a dispatch logic inspired from the proposal available at <a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>.
|
||||
|
||||
</li><li> Arbitrarily large floating point numbers (<code>BigFloat</code>) in base 2 using the IEEE 754 semantics.
|
||||
|
||||
</li><li> Arbitrarily large floating point numbers (<code>BigDecimal</code>) in base 10 based on the proposal available at
|
||||
<a href="https://github.com/littledan/proposal-bigdecimal">https://github.com/littledan/proposal-bigdecimal</a>.
|
||||
|
||||
</li><li> <code>math</code> mode: arbitrarily large integers and floating point numbers are available by default. The integer division and power can be overloaded for example to return a fraction. The modulo operator (<code>%</code>) is defined as the Euclidian
|
||||
remainder. <code>^</code> is an alias to the power operator
|
||||
(<code>**</code>). <code>^^</code> is used as the exclusive or operator.
|
||||
|
||||
</li></ul>
|
||||
|
||||
<p>The extensions are independent from each other except the <code>math</code>
|
||||
mode which relies on BigFloat and operator overloading.
|
||||
</p>
|
||||
<a name="Operator-overloading"></a>
|
||||
<h2 class="chapter">2 Operator overloading</h2>
|
||||
|
||||
<p>Operator overloading is inspired from the proposal available at
|
||||
<a href="https://github.com/tc39/proposal-operator-overloading/">https://github.com/tc39/proposal-operator-overloading/</a>. It
|
||||
implements the same dispatch logic but finds the operator sets by
|
||||
looking at the <code>Symbol.operatorSet</code> property in the objects. The
|
||||
changes were done in order to simplify the implementation.
|
||||
</p>
|
||||
<p>More precisely, the following modifications were made:
|
||||
</p>
|
||||
<ul>
|
||||
<li> <code>with operators from</code> is not supported. Operator overloading is always enabled.
|
||||
|
||||
</li><li> The dispatch is not based on a static <code>[[OperatorSet]]</code> field in all instances. Instead, a dynamic lookup of the <code>Symbol.operatorSet</code> property is done. This property is typically added in the prototype of each object.
|
||||
|
||||
</li><li> <code>Operators.create(...dictionaries)</code> is used to create a new OperatorSet object. The <code>Operators</code> function is supported as an helper to be closer to the TC39 proposal.
|
||||
|
||||
</li><li> <code>[]</code> cannot be overloaded.
|
||||
|
||||
</li><li> In math mode, the BigInt division and power operators can be overloaded with <code>Operators.updateBigIntOperators(dictionary)</code>.
|
||||
|
||||
</li></ul>
|
||||
|
||||
<a name="BigInt-extensions"></a>
|
||||
<h2 class="chapter">3 BigInt extensions</h2>
|
||||
|
||||
<p>A few properties are added to the BigInt object:
|
||||
</p>
|
||||
<dl compact="compact">
|
||||
<dt><code>tdiv(a, b)</code></dt>
|
||||
<dd><p>Return <em>trunc(a/b)</em>. <code>b = 0</code> raises a RangeError
|
||||
exception.
|
||||
</p>
|
||||
</dd>
|
||||
<dt><code>fdiv(a, b)</code></dt>
|
||||
<dd><p>Return <em>\lfloor a/b \rfloor</em>. <code>b = 0</code> raises a RangeError
|
||||
exception.
|
||||
</p>
|
||||
</dd>
|
||||
<dt><code>cdiv(a, b)</code></dt>
|
||||
<dd><p>Return <em>\lceil a/b \rceil</em>. <code>b = 0</code> raises a RangeError
|
||||
exception.
|
||||
</p>
|
||||
</dd>
|
||||
<dt><code>ediv(a, b)</code></dt>
|
||||
<dd><p>Return <em>sgn(b) \lfloor a/{|b|} \rfloor</em> (Euclidian
|
||||
division). <code>b = 0</code> raises a RangeError exception.
|
||||
</p>
|
||||
</dd>
|
||||
<dt><code>tdivrem(a, b)</code></dt>
|
||||
<dt><code>fdivrem(a, b)</code></dt>
|
||||
<dt><code>cdivrem(a, b)</code></dt>
|
||||
<dt><code>edivrem(a, b)</code></dt>
|
||||
<dd><p>Return an array of two elements. The first element is the quotient,
|
||||
the second is the remainder. The same rounding is done as the
|
||||
corresponding division operation.
|
||||
</p>
|
||||
</dd>
|
||||
<dt><code>sqrt(a)</code></dt>
|
||||
<dd><p>Return <em>\lfloor \sqrt(a) \rfloor</em>. A RangeError exception is
|
||||
raised if <em>a < 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 < 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’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> (“round to nearest with ties to even”)<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><=</code></dt>
|
||||
<dt><code>>=</code></dt>
|
||||
<dt><code><</code></dt>
|
||||
<dt><code>></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>"floor"</code>, <code>"ceiling"</code>, <code>"down"</code>, <code>"up"</code>,
|
||||
<code>"half-even"</code>, <code>"half-up"</code>). Either
|
||||
<code>maximumSignificantDigits</code> or <code>maximumFractionDigits</code> must
|
||||
be present to specify respectively the number of significant digits
|
||||
(must be >= 1) or the number of digits after the decimal point (must
|
||||
be >= 0).
|
||||
</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 = "half-up")</code></dt>
|
||||
<dt><code>toFixed(p, rnd_mode = "half-up")</code></dt>
|
||||
<dt><code>toExponential(p, rnd_mode = "half-up")</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>"use math"</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 === "bigfloat"</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 === "number "</code>, <code>typeof 1n === "number"</code> but <code>typeof 9007199254740992 === "bigint" </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
BIN
doc/jsbignum.pdf
Binary file not shown.
1410
doc/quickjs.html
1410
doc/quickjs.html
File diff suppressed because it is too large
Load diff
BIN
doc/quickjs.pdf
BIN
doc/quickjs.pdf
Binary file not shown.
|
@ -379,7 +379,9 @@ optional properties:
|
|||
stack frames below the evalScript.
|
||||
@item async
|
||||
Boolean (default = false). If true, @code{await} is accepted in the
|
||||
script and a promise is returned.
|
||||
script and a promise is returned. The promise is resolved with an
|
||||
object whose @code{value} property holds the value returned by the
|
||||
script.
|
||||
@end table
|
||||
|
||||
@item loadScript(filename)
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
function fib(n)
|
||||
{
|
||||
if (n <= 0)
|
||||
return 0;
|
||||
else if (n == 1)
|
||||
return 1;
|
||||
else
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
/* example of JS module */
|
||||
|
||||
const fibModule = require("./fib_require.js");
|
||||
|
||||
console.log("Hello World");
|
||||
console.log("fib(10)=", fib(10));
|
4
libbf.c
4
libbf.c
|
@ -136,6 +136,7 @@ static inline slimb_t ceil_div(slimb_t a, slimb_t b)
|
|||
return a / b;
|
||||
}
|
||||
|
||||
#ifdef USE_BF_DEC
|
||||
/* b must be >= 1 */
|
||||
static inline slimb_t floor_div(slimb_t a, slimb_t b)
|
||||
{
|
||||
|
@ -145,6 +146,7 @@ static inline slimb_t floor_div(slimb_t a, slimb_t b)
|
|||
return (a - b + 1) / b;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */
|
||||
static inline limb_t smod(slimb_t a, slimb_t b)
|
||||
|
@ -309,7 +311,7 @@ int bf_set(bf_t *r, const bf_t *a)
|
|||
}
|
||||
r->sign = a->sign;
|
||||
r->expn = a->expn;
|
||||
memcpy(r->tab, a->tab, a->len * sizeof(limb_t));
|
||||
memcpy_no_ub(r->tab, a->tab, a->len * sizeof(limb_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
298
libregexp.c
298
libregexp.c
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "cutils.h"
|
||||
#include "libregexp.h"
|
||||
#include "libunicode.h"
|
||||
|
||||
/*
|
||||
TODO:
|
||||
|
@ -66,7 +67,7 @@ typedef struct {
|
|||
const uint8_t *buf_end;
|
||||
const uint8_t *buf_start;
|
||||
int re_flags;
|
||||
BOOL is_utf16;
|
||||
BOOL is_unicode;
|
||||
BOOL ignore_case;
|
||||
BOOL dotall;
|
||||
int capture_count;
|
||||
|
@ -100,6 +101,7 @@ static const REOpCode reopcode_info[REOP_COUNT] = {
|
|||
#define RE_HEADER_FLAGS 0
|
||||
#define RE_HEADER_CAPTURE_COUNT 1
|
||||
#define RE_HEADER_STACK_SIZE 2
|
||||
#define RE_HEADER_BYTECODE_LEN 3
|
||||
|
||||
#define RE_HEADER_LEN 7
|
||||
|
||||
|
@ -140,32 +142,6 @@ static const uint16_t char_range_s[] = {
|
|||
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[] = {
|
||||
4,
|
||||
0x0030, 0x0039 + 1,
|
||||
|
@ -185,7 +161,7 @@ typedef enum {
|
|||
CHAR_RANGE_W,
|
||||
} CharRangeEnum;
|
||||
|
||||
static const uint16_t *char_range_table[] = {
|
||||
static const uint16_t * const char_range_table[] = {
|
||||
char_range_d,
|
||||
char_range_s,
|
||||
char_range_w,
|
||||
|
@ -224,16 +200,16 @@ static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
|
|||
|
||||
assert(buf_len >= RE_HEADER_LEN);
|
||||
|
||||
re_flags= buf[0];
|
||||
bc_len = get_u32(buf + 3);
|
||||
re_flags = lre_get_flags(buf);
|
||||
bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN);
|
||||
assert(bc_len + RE_HEADER_LEN <= buf_len);
|
||||
printf("flags: 0x%x capture_count=%d stack_size=%d\n",
|
||||
re_flags, buf[1], buf[2]);
|
||||
re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]);
|
||||
if (re_flags & LRE_FLAG_NAMED_GROUPS) {
|
||||
const char *p;
|
||||
p = (char *)buf + RE_HEADER_LEN + bc_len;
|
||||
printf("named groups: ");
|
||||
for(i = 1; i < buf[1]; i++) {
|
||||
for(i = 1; i < buf[RE_HEADER_CAPTURE_COUNT]; i++) {
|
||||
if (i != 1)
|
||||
printf(",");
|
||||
printf("<%s>", p);
|
||||
|
@ -494,7 +470,7 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
|
|||
}
|
||||
c = (c << 4) | h;
|
||||
}
|
||||
if (c >= 0xd800 && c < 0xdc00 &&
|
||||
if (is_hi_surrogate(c) &&
|
||||
allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') {
|
||||
/* convert an escaped surrogate pair into a
|
||||
unicode char */
|
||||
|
@ -505,9 +481,9 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16)
|
|||
break;
|
||||
c1 = (c1 << 4) | h;
|
||||
}
|
||||
if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) {
|
||||
if (i == 4 && is_lo_surrogate(c1)) {
|
||||
p += 6;
|
||||
c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
|
||||
c = from_surrogate(c, c1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -696,10 +672,10 @@ static int get_class_atom(REParseState *s, CharRange *cr,
|
|||
if ((c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(((c >= '0' && c <= '9') || c == '_') &&
|
||||
inclass && !s->is_utf16)) { /* Annex B.1.4 */
|
||||
inclass && !s->is_unicode)) { /* Annex B.1.4 */
|
||||
c &= 0x1f;
|
||||
p++;
|
||||
} else if (s->is_utf16) {
|
||||
} else if (s->is_unicode) {
|
||||
goto invalid_escape;
|
||||
} else {
|
||||
/* otherwise return '\' and 'c' */
|
||||
|
@ -710,7 +686,7 @@ static int get_class_atom(REParseState *s, CharRange *cr,
|
|||
#ifdef CONFIG_ALL_UNICODE
|
||||
case 'p':
|
||||
case 'P':
|
||||
if (s->is_utf16) {
|
||||
if (s->is_unicode) {
|
||||
if (parse_unicode_property(s, cr, &p, (c == 'P')))
|
||||
return -1;
|
||||
c = CLASS_RANGE_BASE;
|
||||
|
@ -720,14 +696,14 @@ static int get_class_atom(REParseState *s, CharRange *cr,
|
|||
#endif
|
||||
default:
|
||||
p--;
|
||||
ret = lre_parse_escape(&p, s->is_utf16 * 2);
|
||||
ret = lre_parse_escape(&p, s->is_unicode * 2);
|
||||
if (ret >= 0) {
|
||||
c = ret;
|
||||
} else {
|
||||
if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) {
|
||||
/* always valid to escape these characters */
|
||||
goto normal_char;
|
||||
} else if (s->is_utf16) {
|
||||
} else if (s->is_unicode) {
|
||||
invalid_escape:
|
||||
return re_parse_error(s, "invalid escape sequence in regular expression");
|
||||
} else {
|
||||
|
@ -749,7 +725,7 @@ static int get_class_atom(REParseState *s, CharRange *cr,
|
|||
/* normal char */
|
||||
if (c >= 128) {
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
||||
if ((unsigned)c > 0xffff && !s->is_utf16) {
|
||||
if ((unsigned)c > 0xffff && !s->is_unicode) {
|
||||
/* XXX: should handle non BMP-1 code points */
|
||||
return re_parse_error(s, "malformed unicode char");
|
||||
}
|
||||
|
@ -811,11 +787,13 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
|
|||
cr_init(cr, s->opaque, lre_realloc);
|
||||
p = *pp;
|
||||
p++; /* skip '[' */
|
||||
|
||||
invert = FALSE;
|
||||
if (*p == '^') {
|
||||
p++;
|
||||
invert = TRUE;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
if (*p == ']')
|
||||
break;
|
||||
|
@ -825,7 +803,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
|
|||
if (*p == '-' && p[1] != ']') {
|
||||
const uint8_t *p0 = p + 1;
|
||||
if (c1 >= CLASS_RANGE_BASE) {
|
||||
if (s->is_utf16) {
|
||||
if (s->is_unicode) {
|
||||
cr_free(cr1);
|
||||
goto invalid_class_range;
|
||||
}
|
||||
|
@ -837,7 +815,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
|
|||
goto fail;
|
||||
if (c2 >= CLASS_RANGE_BASE) {
|
||||
cr_free(cr1);
|
||||
if (s->is_utf16) {
|
||||
if (s->is_unicode) {
|
||||
goto invalid_class_range;
|
||||
}
|
||||
/* Annex B: match '-' character */
|
||||
|
@ -866,7 +844,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
|
|||
}
|
||||
}
|
||||
if (s->ignore_case) {
|
||||
if (cr_regexp_canonicalize(cr, s->is_utf16))
|
||||
if (cr_regexp_canonicalize(cr, s->is_unicode))
|
||||
goto memory_error;
|
||||
}
|
||||
if (invert) {
|
||||
|
@ -934,7 +912,7 @@ static BOOL re_need_check_advance(const uint8_t *bc_buf, int bc_buf_len)
|
|||
case REOP_backward_back_reference:
|
||||
break;
|
||||
default:
|
||||
/* safe behvior: we cannot predict the outcome */
|
||||
/* safe behavior: we cannot predict the outcome */
|
||||
return TRUE;
|
||||
}
|
||||
pos += len;
|
||||
|
@ -1003,10 +981,10 @@ static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp)
|
|||
break;
|
||||
} else if (c >= 128) {
|
||||
c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
|
||||
if (c >= 0xD800 && c <= 0xDBFF) {
|
||||
if (is_hi_surrogate(c)) {
|
||||
d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
|
||||
if (d >= 0xDC00 && d <= 0xDFFF) {
|
||||
c = 0x10000 + 0x400 * (c - 0xD800) + (d - 0xDC00);
|
||||
if (is_lo_surrogate(d)) {
|
||||
c = from_surrogate(c, d);
|
||||
p = p1;
|
||||
}
|
||||
}
|
||||
|
@ -1114,9 +1092,10 @@ static int find_group_name(REParseState *s, const char *name)
|
|||
size_t len, name_len;
|
||||
int capture_index;
|
||||
|
||||
name_len = strlen(name);
|
||||
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);
|
||||
capture_index = 1;
|
||||
while (p < buf_end) {
|
||||
len = strlen(p);
|
||||
|
@ -1161,7 +1140,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
re_emit_op(s, REOP_prev);
|
||||
break;
|
||||
case '{':
|
||||
if (s->is_utf16) {
|
||||
if (s->is_unicode) {
|
||||
return re_parse_error(s, "syntax error");
|
||||
} else if (!is_digit(p[1])) {
|
||||
/* Annex B: we accept '{' not followed by digits as a
|
||||
|
@ -1213,7 +1192,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
lookahead:
|
||||
/* Annex B allows lookahead to be used as an atom for
|
||||
the quantifiers */
|
||||
if (!s->is_utf16 && !is_backward_lookahead) {
|
||||
if (!s->is_unicode && !is_backward_lookahead) {
|
||||
last_atom_start = s->byte_code.size;
|
||||
last_capture_count = s->capture_count;
|
||||
}
|
||||
|
@ -1289,7 +1268,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
/* annex B: we tolerate invalid group names in non
|
||||
unicode mode if there is no named capture
|
||||
definition */
|
||||
if (s->is_utf16 || re_has_named_captures(s))
|
||||
if (s->is_unicode || re_has_named_captures(s))
|
||||
return re_parse_error(s, "expecting group name");
|
||||
else
|
||||
goto parse_class_atom;
|
||||
|
@ -1297,7 +1276,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
p1 += 3;
|
||||
if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
|
||||
&p1)) {
|
||||
if (s->is_utf16 || re_has_named_captures(s))
|
||||
if (s->is_unicode || re_has_named_captures(s))
|
||||
return re_parse_error(s, "invalid group name");
|
||||
else
|
||||
goto parse_class_atom;
|
||||
|
@ -1308,7 +1287,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
after (inefficient, but hopefully not common */
|
||||
c = re_parse_captures(s, &dummy_res, s->u.tmp_buf);
|
||||
if (c < 0) {
|
||||
if (s->is_utf16 || re_has_named_captures(s))
|
||||
if (s->is_unicode || re_has_named_captures(s))
|
||||
return re_parse_error(s, "group name not defined");
|
||||
else
|
||||
goto parse_class_atom;
|
||||
|
@ -1320,7 +1299,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
case '0':
|
||||
p += 2;
|
||||
c = 0;
|
||||
if (s->is_utf16) {
|
||||
if (s->is_unicode) {
|
||||
if (is_digit(*p)) {
|
||||
return re_parse_error(s, "invalid decimal escape in regular expression");
|
||||
}
|
||||
|
@ -1342,7 +1321,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
|
||||
c = parse_digits(&p, FALSE);
|
||||
if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) {
|
||||
if (!s->is_utf16) {
|
||||
if (!s->is_unicode) {
|
||||
/* Annex B.1.4: accept legacy octal */
|
||||
p = q;
|
||||
if (*p <= '7') {
|
||||
|
@ -1384,7 +1363,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
break;
|
||||
case ']':
|
||||
case '}':
|
||||
if (s->is_utf16)
|
||||
if (s->is_unicode)
|
||||
return re_parse_error(s, "syntax error");
|
||||
goto parse_class_atom;
|
||||
default:
|
||||
|
@ -1406,7 +1385,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
return -1;
|
||||
} else {
|
||||
if (s->ignore_case)
|
||||
c = lre_canonicalize(c, s->is_utf16);
|
||||
c = lre_canonicalize(c, s->is_unicode);
|
||||
if (c <= 0xffff)
|
||||
re_emit_op_u16(s, REOP_char, c);
|
||||
else
|
||||
|
@ -1442,7 +1421,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
/* As an extension (see ES6 annex B), we accept '{' not
|
||||
followed by digits as a normal atom */
|
||||
if (!is_digit(p[1])) {
|
||||
if (s->is_utf16)
|
||||
if (s->is_unicode)
|
||||
goto invalid_quant_count;
|
||||
break;
|
||||
}
|
||||
|
@ -1461,7 +1440,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
|
|||
quant_max = INT32_MAX; /* infinity */
|
||||
}
|
||||
}
|
||||
if (*p != '}' && !s->is_utf16) {
|
||||
if (*p != '}' && !s->is_unicode) {
|
||||
/* Annex B: normal atom if invalid '{' syntax */
|
||||
p = p1;
|
||||
break;
|
||||
|
@ -1753,7 +1732,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
|||
s->buf_end = s->buf_ptr + buf_len;
|
||||
s->buf_start = s->buf_ptr;
|
||||
s->re_flags = re_flags;
|
||||
s->is_utf16 = ((re_flags & LRE_FLAG_UTF16) != 0);
|
||||
s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0);
|
||||
is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0);
|
||||
s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0);
|
||||
s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0);
|
||||
|
@ -1811,7 +1790,8 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
|||
|
||||
s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count;
|
||||
s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size;
|
||||
put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_LEN);
|
||||
put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN,
|
||||
s->byte_code.size - RE_HEADER_LEN);
|
||||
|
||||
/* add the named groups if needed */
|
||||
if (s->group_names.size > (s->capture_count - 1)) {
|
||||
|
@ -1842,93 +1822,86 @@ static BOOL is_word_char(uint32_t c)
|
|||
(c == '_'));
|
||||
}
|
||||
|
||||
#define GET_CHAR(c, cptr, cbuf_end) \
|
||||
#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
c = *cptr++; \
|
||||
} else { \
|
||||
uint32_t __c1; \
|
||||
c = *(uint16_t *)cptr; \
|
||||
cptr += 2; \
|
||||
if (c >= 0xd800 && c < 0xdc00 && \
|
||||
cbuf_type == 2 && cptr < cbuf_end) { \
|
||||
__c1 = *(uint16_t *)cptr; \
|
||||
if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
|
||||
c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
|
||||
cptr += 2; \
|
||||
const uint16_t *_p = (const uint16_t *)cptr; \
|
||||
const uint16_t *_end = (const uint16_t *)cbuf_end; \
|
||||
c = *_p++; \
|
||||
if (is_hi_surrogate(c) && cbuf_type == 2) { \
|
||||
if (_p < _end && is_lo_surrogate(*_p)) { \
|
||||
c = from_surrogate(c, *_p++); \
|
||||
} \
|
||||
} \
|
||||
cptr = (const void *)_p; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
c = cptr[0]; \
|
||||
} else { \
|
||||
const uint16_t *_p = (const uint16_t *)cptr; \
|
||||
const uint16_t *_end = (const uint16_t *)cbuf_end; \
|
||||
c = *_p++; \
|
||||
if (is_hi_surrogate(c) && cbuf_type == 2) { \
|
||||
if (_p < _end && is_lo_surrogate(*_p)) { \
|
||||
c = from_surrogate(c, *_p); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PEEK_CHAR(c, cptr, cbuf_end) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
c = cptr[0]; \
|
||||
} else { \
|
||||
uint32_t __c1; \
|
||||
c = ((uint16_t *)cptr)[0]; \
|
||||
if (c >= 0xd800 && c < 0xdc00 && \
|
||||
cbuf_type == 2 && (cptr + 2) < cbuf_end) { \
|
||||
__c1 = ((uint16_t *)cptr)[1]; \
|
||||
if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
|
||||
c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PEEK_PREV_CHAR(c, cptr, cbuf_start) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
c = cptr[-1]; \
|
||||
} else { \
|
||||
uint32_t __c1; \
|
||||
c = ((uint16_t *)cptr)[-1]; \
|
||||
if (c >= 0xdc00 && c < 0xe000 && \
|
||||
cbuf_type == 2 && (cptr - 4) >= cbuf_start) { \
|
||||
__c1 = ((uint16_t *)cptr)[-2]; \
|
||||
if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
|
||||
c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
|
||||
#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
c = cptr[-1]; \
|
||||
} else { \
|
||||
const uint16_t *_p = (const uint16_t *)cptr - 1; \
|
||||
const uint16_t *_start = (const uint16_t *)cbuf_start; \
|
||||
c = *_p; \
|
||||
if (is_lo_surrogate(c) && cbuf_type == 2) { \
|
||||
if (_p > _start && is_hi_surrogate(_p[-1])) { \
|
||||
c = from_surrogate(*--_p, c); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define GET_PREV_CHAR(c, cptr, cbuf_start) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
cptr--; \
|
||||
c = cptr[0]; \
|
||||
} else { \
|
||||
uint32_t __c1; \
|
||||
cptr -= 2; \
|
||||
c = ((uint16_t *)cptr)[0]; \
|
||||
if (c >= 0xdc00 && c < 0xe000 && \
|
||||
cbuf_type == 2 && cptr > cbuf_start) { \
|
||||
__c1 = ((uint16_t *)cptr)[-1]; \
|
||||
if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
|
||||
cptr -= 2; \
|
||||
c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
|
||||
#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
cptr--; \
|
||||
c = cptr[0]; \
|
||||
} else { \
|
||||
const uint16_t *_p = (const uint16_t *)cptr - 1; \
|
||||
const uint16_t *_start = (const uint16_t *)cbuf_start; \
|
||||
c = *_p; \
|
||||
if (is_lo_surrogate(c) && cbuf_type == 2) { \
|
||||
if (_p > _start && is_hi_surrogate(_p[-1])) { \
|
||||
c = from_surrogate(*--_p, c); \
|
||||
} \
|
||||
} \
|
||||
cptr = (const void *)_p; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PREV_CHAR(cptr, cbuf_start) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
cptr--; \
|
||||
} else { \
|
||||
cptr -= 2; \
|
||||
if (cbuf_type == 2) { \
|
||||
c = ((uint16_t *)cptr)[0]; \
|
||||
if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) { \
|
||||
c = ((uint16_t *)cptr)[-1]; \
|
||||
if (c >= 0xd800 && c < 0xdc00) \
|
||||
cptr -= 2; \
|
||||
#define PREV_CHAR(cptr, cbuf_start, cbuf_type) \
|
||||
do { \
|
||||
if (cbuf_type == 0) { \
|
||||
cptr--; \
|
||||
} else { \
|
||||
const uint16_t *_p = (const uint16_t *)cptr - 1; \
|
||||
const uint16_t *_start = (const uint16_t *)cbuf_start; \
|
||||
if (is_lo_surrogate(*_p) && cbuf_type == 2) { \
|
||||
if (_p > _start && is_hi_surrogate(_p[-1])) { \
|
||||
--_p; \
|
||||
} \
|
||||
} \
|
||||
cptr = (const void *)_p; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
@ -1959,7 +1932,7 @@ typedef struct {
|
|||
int stack_size_max;
|
||||
BOOL multi_line;
|
||||
BOOL ignore_case;
|
||||
BOOL is_utf16;
|
||||
BOOL is_unicode;
|
||||
void *opaque; /* used for stack overflow check */
|
||||
|
||||
size_t state_size;
|
||||
|
@ -2068,7 +2041,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
/* go backward */
|
||||
char_count = get_u32(pc + 12);
|
||||
for(i = 0; i < char_count; i++) {
|
||||
PREV_CHAR(cptr, s->cbuf);
|
||||
PREV_CHAR(cptr, s->cbuf, cbuf_type);
|
||||
}
|
||||
pc = (pc + 16) + (int)get_u32(pc);
|
||||
rs->cptr = cptr;
|
||||
|
@ -2103,9 +2076,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
test_char:
|
||||
if (cptr >= cbuf_end)
|
||||
goto no_match;
|
||||
GET_CHAR(c, cptr, cbuf_end);
|
||||
GET_CHAR(c, cptr, cbuf_end, cbuf_type);
|
||||
if (s->ignore_case) {
|
||||
c = lre_canonicalize(c, s->is_utf16);
|
||||
c = lre_canonicalize(c, s->is_unicode);
|
||||
}
|
||||
if (val != c)
|
||||
goto no_match;
|
||||
|
@ -2150,7 +2123,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
break;
|
||||
if (!s->multi_line)
|
||||
goto no_match;
|
||||
PEEK_PREV_CHAR(c, cptr, s->cbuf);
|
||||
PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type);
|
||||
if (!is_line_terminator(c))
|
||||
goto no_match;
|
||||
break;
|
||||
|
@ -2159,21 +2132,21 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
break;
|
||||
if (!s->multi_line)
|
||||
goto no_match;
|
||||
PEEK_CHAR(c, cptr, cbuf_end);
|
||||
PEEK_CHAR(c, cptr, cbuf_end, cbuf_type);
|
||||
if (!is_line_terminator(c))
|
||||
goto no_match;
|
||||
break;
|
||||
case REOP_dot:
|
||||
if (cptr == cbuf_end)
|
||||
goto no_match;
|
||||
GET_CHAR(c, cptr, cbuf_end);
|
||||
GET_CHAR(c, cptr, cbuf_end, cbuf_type);
|
||||
if (is_line_terminator(c))
|
||||
goto no_match;
|
||||
break;
|
||||
case REOP_any:
|
||||
if (cptr == cbuf_end)
|
||||
goto no_match;
|
||||
GET_CHAR(c, cptr, cbuf_end);
|
||||
GET_CHAR(c, cptr, cbuf_end, cbuf_type);
|
||||
break;
|
||||
case REOP_save_start:
|
||||
case REOP_save_end:
|
||||
|
@ -2225,14 +2198,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
if (cptr == s->cbuf) {
|
||||
v1 = FALSE;
|
||||
} else {
|
||||
PEEK_PREV_CHAR(c, cptr, s->cbuf);
|
||||
PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type);
|
||||
v1 = is_word_char(c);
|
||||
}
|
||||
/* current char */
|
||||
if (cptr >= cbuf_end) {
|
||||
v2 = FALSE;
|
||||
} else {
|
||||
PEEK_CHAR(c, cptr, cbuf_end);
|
||||
PEEK_CHAR(c, cptr, cbuf_end, cbuf_type);
|
||||
v2 = is_word_char(c);
|
||||
}
|
||||
if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode))
|
||||
|
@ -2257,11 +2230,11 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
while (cptr1 < cptr1_end) {
|
||||
if (cptr >= cbuf_end)
|
||||
goto no_match;
|
||||
GET_CHAR(c1, cptr1, cptr1_end);
|
||||
GET_CHAR(c2, cptr, cbuf_end);
|
||||
GET_CHAR(c1, cptr1, cptr1_end, cbuf_type);
|
||||
GET_CHAR(c2, cptr, cbuf_end, cbuf_type);
|
||||
if (s->ignore_case) {
|
||||
c1 = lre_canonicalize(c1, s->is_utf16);
|
||||
c2 = lre_canonicalize(c2, s->is_utf16);
|
||||
c1 = lre_canonicalize(c1, s->is_unicode);
|
||||
c2 = lre_canonicalize(c2, s->is_unicode);
|
||||
}
|
||||
if (c1 != c2)
|
||||
goto no_match;
|
||||
|
@ -2271,11 +2244,11 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
while (cptr1 > cptr1_start) {
|
||||
if (cptr == s->cbuf)
|
||||
goto no_match;
|
||||
GET_PREV_CHAR(c1, cptr1, cptr1_start);
|
||||
GET_PREV_CHAR(c2, cptr, s->cbuf);
|
||||
GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type);
|
||||
GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type);
|
||||
if (s->ignore_case) {
|
||||
c1 = lre_canonicalize(c1, s->is_utf16);
|
||||
c2 = lre_canonicalize(c2, s->is_utf16);
|
||||
c1 = lre_canonicalize(c1, s->is_unicode);
|
||||
c2 = lre_canonicalize(c2, s->is_unicode);
|
||||
}
|
||||
if (c1 != c2)
|
||||
goto no_match;
|
||||
|
@ -2292,9 +2265,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
pc += 2;
|
||||
if (cptr >= cbuf_end)
|
||||
goto no_match;
|
||||
GET_CHAR(c, cptr, cbuf_end);
|
||||
GET_CHAR(c, cptr, cbuf_end, cbuf_type);
|
||||
if (s->ignore_case) {
|
||||
c = lre_canonicalize(c, s->is_utf16);
|
||||
c = lre_canonicalize(c, s->is_unicode);
|
||||
}
|
||||
idx_min = 0;
|
||||
low = get_u16(pc + 0 * 4);
|
||||
|
@ -2332,9 +2305,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
pc += 2;
|
||||
if (cptr >= cbuf_end)
|
||||
goto no_match;
|
||||
GET_CHAR(c, cptr, cbuf_end);
|
||||
GET_CHAR(c, cptr, cbuf_end, cbuf_type);
|
||||
if (s->ignore_case) {
|
||||
c = lre_canonicalize(c, s->is_utf16);
|
||||
c = lre_canonicalize(c, s->is_unicode);
|
||||
}
|
||||
idx_min = 0;
|
||||
low = get_u32(pc + 0 * 8);
|
||||
|
@ -2364,7 +2337,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
|
|||
/* go to the previous char */
|
||||
if (cptr == s->cbuf)
|
||||
goto no_match;
|
||||
PREV_CHAR(cptr, s->cbuf);
|
||||
PREV_CHAR(cptr, s->cbuf, cbuf_type);
|
||||
break;
|
||||
case REOP_simple_greedy_quant:
|
||||
{
|
||||
|
@ -2423,16 +2396,16 @@ int lre_exec(uint8_t **capture,
|
|||
int re_flags, i, alloca_size, ret;
|
||||
StackInt *stack_buf;
|
||||
|
||||
re_flags = bc_buf[RE_HEADER_FLAGS];
|
||||
re_flags = lre_get_flags(bc_buf);
|
||||
s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0;
|
||||
s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0;
|
||||
s->is_utf16 = (re_flags & LRE_FLAG_UTF16) != 0;
|
||||
s->is_unicode = (re_flags & LRE_FLAG_UNICODE) != 0;
|
||||
s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT];
|
||||
s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE];
|
||||
s->cbuf = cbuf;
|
||||
s->cbuf_end = cbuf + (clen << cbuf_type);
|
||||
s->cbuf_type = cbuf_type;
|
||||
if (s->cbuf_type == 1 && s->is_utf16)
|
||||
if (s->cbuf_type == 1 && s->is_unicode)
|
||||
s->cbuf_type = 2;
|
||||
s->opaque = opaque;
|
||||
|
||||
|
@ -2470,8 +2443,8 @@ const char *lre_get_groupnames(const uint8_t *bc_buf)
|
|||
uint32_t re_bytecode_len;
|
||||
if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0)
|
||||
return NULL;
|
||||
re_bytecode_len = get_u32(bc_buf + 3);
|
||||
return (const char *)(bc_buf + 7 + re_bytecode_len);
|
||||
re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN);
|
||||
return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len);
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
@ -2488,25 +2461,26 @@ void *lre_realloc(void *opaque, void *ptr, size_t size)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int len, ret, i;
|
||||
int len, flags, ret, i;
|
||||
uint8_t *bc;
|
||||
char error_msg[64];
|
||||
uint8_t *capture[CAPTURE_COUNT_MAX * 2];
|
||||
const char *input;
|
||||
int input_len, capture_count;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("usage: %s regexp input\n", argv[0]);
|
||||
exit(1);
|
||||
if (argc < 4) {
|
||||
printf("usage: %s regexp flags input\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
flags = atoi(argv[2]);
|
||||
bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1],
|
||||
strlen(argv[1]), 0, NULL);
|
||||
strlen(argv[1]), flags, NULL);
|
||||
if (!bc) {
|
||||
fprintf(stderr, "error: %s\n", error_msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
input = argv[2];
|
||||
input = argv[3];
|
||||
input_len = strlen(input);
|
||||
|
||||
ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL);
|
||||
|
|
46
libregexp.h
46
libregexp.h
|
@ -25,19 +25,15 @@
|
|||
#define LIBREGEXP_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "libunicode.h"
|
||||
|
||||
#define LRE_BOOL int /* for documentation purposes */
|
||||
#include <stdint.h>
|
||||
|
||||
#define LRE_FLAG_GLOBAL (1 << 0)
|
||||
#define LRE_FLAG_IGNORECASE (1 << 1)
|
||||
#define LRE_FLAG_MULTILINE (1 << 2)
|
||||
#define LRE_FLAG_DOTALL (1 << 3)
|
||||
#define LRE_FLAG_UTF16 (1 << 4)
|
||||
#define LRE_FLAG_UNICODE (1 << 4)
|
||||
#define LRE_FLAG_STICKY (1 << 5)
|
||||
#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 */
|
||||
|
||||
uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
|
||||
|
@ -51,43 +47,9 @@ int lre_exec(uint8_t **capture,
|
|||
int cbuf_type, void *opaque);
|
||||
|
||||
int lre_parse_escape(const uint8_t **pp, int allow_utf16);
|
||||
LRE_BOOL lre_is_space(int c);
|
||||
|
||||
/* must be provided by the user */
|
||||
LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size);
|
||||
/* must be provided by the user, return non zero if overflow */
|
||||
int lre_check_stack_overflow(void *opaque, size_t alloca_size);
|
||||
void *lre_realloc(void *opaque, void *ptr, size_t size);
|
||||
|
||||
/* JS identifier test */
|
||||
extern uint32_t const lre_id_start_table_ascii[4];
|
||||
extern uint32_t const lre_id_continue_table_ascii[4];
|
||||
|
||||
static inline int lre_js_is_ident_first(int c)
|
||||
{
|
||||
if ((uint32_t)c < 128) {
|
||||
return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1;
|
||||
} else {
|
||||
#ifdef CONFIG_ALL_UNICODE
|
||||
return lre_is_id_start(c);
|
||||
#else
|
||||
return !lre_is_space(c);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline int lre_js_is_ident_next(int c)
|
||||
{
|
||||
if ((uint32_t)c < 128) {
|
||||
return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1;
|
||||
} else {
|
||||
/* ZWNJ and ZWJ are accepted in identifiers */
|
||||
#ifdef CONFIG_ALL_UNICODE
|
||||
return lre_is_id_continue(c) || c == 0x200C || c == 0x200D;
|
||||
#else
|
||||
return !lre_is_space(c) || c == 0x200C || c == 0x200D;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#undef LRE_BOOL
|
||||
|
||||
#endif /* LIBREGEXP_H */
|
||||
|
|
|
@ -189,9 +189,13 @@ static const uint8_t unicode_prop_Cased1_table[196] = {
|
|||
};
|
||||
|
||||
static const uint8_t unicode_prop_Cased1_index[21] = {
|
||||
0xb9, 0x02, 0xe0, 0xc0, 0x1d, 0x20, 0xe5, 0x2c,
|
||||
0x20, 0xb1, 0x07, 0x21, 0xc1, 0xd6, 0x21, 0x4a,
|
||||
0xf1, 0x01, 0x8a, 0xf1, 0x01,
|
||||
0xb9, 0x02, 0xe0, // 002B9 at 39
|
||||
0xc0, 0x1d, 0x20, // 01DC0 at 65
|
||||
0xe5, 0x2c, 0x20, // 02CE5 at 97
|
||||
0xb1, 0x07, 0x21, // 107B1 at 129
|
||||
0xc1, 0xd6, 0x21, // 1D6C1 at 161
|
||||
0x4a, 0xf1, 0x01, // 1F14A at 192
|
||||
0x8a, 0xf1, 0x01, // 1F18A at 224 (upper bound)
|
||||
};
|
||||
|
||||
static const uint8_t unicode_prop_Case_Ignorable_table[737] = {
|
||||
|
@ -291,15 +295,29 @@ static const uint8_t unicode_prop_Case_Ignorable_table[737] = {
|
|||
};
|
||||
|
||||
static const uint8_t unicode_prop_Case_Ignorable_index[69] = {
|
||||
0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a,
|
||||
0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f,
|
||||
0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20,
|
||||
0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0,
|
||||
0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56,
|
||||
0xfe, 0x20, 0xb1, 0x07, 0x01, 0x75, 0x10, 0x01,
|
||||
0xeb, 0x12, 0x21, 0x41, 0x16, 0x01, 0x5c, 0x1a,
|
||||
0x01, 0x43, 0x1f, 0x01, 0x2e, 0xcf, 0x41, 0x25,
|
||||
0xe0, 0x01, 0xf0, 0x01, 0x0e,
|
||||
0xbe, 0x05, 0x00, // 005BE at 32
|
||||
0xfe, 0x07, 0x00, // 007FE at 64
|
||||
0x52, 0x0a, 0xa0, // 00A52 at 101
|
||||
0xc1, 0x0b, 0x00, // 00BC1 at 128
|
||||
0x82, 0x0d, 0x00, // 00D82 at 160
|
||||
0x3f, 0x10, 0x80, // 0103F at 196
|
||||
0xd4, 0x17, 0x40, // 017D4 at 226
|
||||
0xcf, 0x1a, 0x20, // 01ACF at 257
|
||||
0xf5, 0x1c, 0x00, // 01CF5 at 288
|
||||
0x80, 0x20, 0x00, // 02080 at 320
|
||||
0x16, 0xa0, 0x00, // 0A016 at 352
|
||||
0xc6, 0xa8, 0x00, // 0A8C6 at 384
|
||||
0xc2, 0xaa, 0x60, // 0AAC2 at 419
|
||||
0x56, 0xfe, 0x20, // 0FE56 at 449
|
||||
0xb1, 0x07, 0x01, // 107B1 at 480
|
||||
0x75, 0x10, 0x01, // 11075 at 512
|
||||
0xeb, 0x12, 0x21, // 112EB at 545
|
||||
0x41, 0x16, 0x01, // 11641 at 576
|
||||
0x5c, 0x1a, 0x01, // 11A5C at 608
|
||||
0x43, 0x1f, 0x01, // 11F43 at 640
|
||||
0x2e, 0xcf, 0x41, // 1CF2E at 674
|
||||
0x25, 0xe0, 0x01, // 1E025 at 704
|
||||
0xf0, 0x01, 0x0e, // E01F0 at 736 (upper bound)
|
||||
};
|
||||
|
||||
static const uint8_t unicode_prop_ID_Start_table[1100] = {
|
||||
|
@ -444,20 +462,41 @@ static const uint8_t unicode_prop_ID_Start_table[1100] = {
|
|||
};
|
||||
|
||||
static const uint8_t unicode_prop_ID_Start_index[105] = {
|
||||
0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09,
|
||||
0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b,
|
||||
0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00,
|
||||
0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d,
|
||||
0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00,
|
||||
0x32, 0x00, 0xda, 0xa7, 0x00, 0x4c, 0xaa, 0x20,
|
||||
0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02,
|
||||
0x21, 0x96, 0x05, 0x01, 0xf3, 0x08, 0x01, 0xb3,
|
||||
0x0c, 0x21, 0x73, 0x11, 0x61, 0x34, 0x13, 0x01,
|
||||
0x1b, 0x17, 0x21, 0x8a, 0x1a, 0x01, 0x34, 0x1f,
|
||||
0x21, 0xbf, 0x6a, 0x01, 0x23, 0xb1, 0xa1, 0xad,
|
||||
0xd4, 0x01, 0x6f, 0xd7, 0x01, 0xff, 0xe7, 0x61,
|
||||
0x5e, 0xee, 0x01, 0xe1, 0xeb, 0x22, 0xb0, 0x23,
|
||||
0x03,
|
||||
0xf6, 0x03, 0x20, // 003F6 at 33
|
||||
0xa6, 0x07, 0x00, // 007A6 at 64
|
||||
0xa9, 0x09, 0x20, // 009A9 at 97
|
||||
0xb1, 0x0a, 0x00, // 00AB1 at 128
|
||||
0xba, 0x0b, 0x20, // 00BBA at 161
|
||||
0x3b, 0x0d, 0x20, // 00D3B at 193
|
||||
0xc7, 0x0e, 0x20, // 00EC7 at 225
|
||||
0x49, 0x12, 0x00, // 01249 at 256
|
||||
0x9b, 0x16, 0x00, // 0169B at 288
|
||||
0xac, 0x19, 0x00, // 019AC at 320
|
||||
0xc0, 0x1d, 0x80, // 01DC0 at 356
|
||||
0x80, 0x20, 0x20, // 02080 at 385
|
||||
0x70, 0x2d, 0x00, // 02D70 at 416
|
||||
0x00, 0x32, 0x00, // 03200 at 448
|
||||
0xda, 0xa7, 0x00, // 0A7DA at 480
|
||||
0x4c, 0xaa, 0x20, // 0AA4C at 513
|
||||
0xc7, 0xd7, 0x20, // 0D7C7 at 545
|
||||
0xfc, 0xfd, 0x20, // 0FDFC at 577
|
||||
0x9d, 0x02, 0x21, // 1029D at 609
|
||||
0x96, 0x05, 0x01, // 10596 at 640
|
||||
0xf3, 0x08, 0x01, // 108F3 at 672
|
||||
0xb3, 0x0c, 0x21, // 10CB3 at 705
|
||||
0x73, 0x11, 0x61, // 11173 at 739
|
||||
0x34, 0x13, 0x01, // 11334 at 768
|
||||
0x1b, 0x17, 0x21, // 1171B at 801
|
||||
0x8a, 0x1a, 0x01, // 11A8A at 832
|
||||
0x34, 0x1f, 0x21, // 11F34 at 865
|
||||
0xbf, 0x6a, 0x01, // 16ABF at 896
|
||||
0x23, 0xb1, 0xa1, // 1B123 at 933
|
||||
0xad, 0xd4, 0x01, // 1D4AD at 960
|
||||
0x6f, 0xd7, 0x01, // 1D76F at 992
|
||||
0xff, 0xe7, 0x61, // 1E7FF at 1027
|
||||
0x5e, 0xee, 0x01, // 1EE5E at 1056
|
||||
0xe1, 0xeb, 0x22, // 2EBE1 at 1089
|
||||
0xb0, 0x23, 0x03, // 323B0 at 1120 (upper bound)
|
||||
};
|
||||
|
||||
static const uint8_t unicode_prop_ID_Continue1_table[660] = {
|
||||
|
@ -547,14 +586,27 @@ static const uint8_t unicode_prop_ID_Continue1_table[660] = {
|
|||
};
|
||||
|
||||
static const uint8_t unicode_prop_ID_Continue1_index[63] = {
|
||||
0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a,
|
||||
0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7,
|
||||
0x0f, 0x20, 0xea, 0x17, 0x40, 0x05, 0x1b, 0x00,
|
||||
0x41, 0x20, 0x00, 0x0c, 0xa8, 0x80, 0x37, 0xaa,
|
||||
0x20, 0x50, 0xfe, 0x20, 0x3a, 0x0d, 0x21, 0x74,
|
||||
0x11, 0x01, 0x5a, 0x14, 0x21, 0x44, 0x19, 0x81,
|
||||
0x5a, 0x1d, 0xa1, 0xf5, 0x6a, 0x21, 0x45, 0xd2,
|
||||
0x41, 0xaf, 0xe2, 0x21, 0xf0, 0x01, 0x0e,
|
||||
0xfa, 0x06, 0x00, // 006FA at 32
|
||||
0x70, 0x09, 0x00, // 00970 at 64
|
||||
0xf0, 0x0a, 0x40, // 00AF0 at 98
|
||||
0x57, 0x0c, 0x00, // 00C57 at 128
|
||||
0xf0, 0x0d, 0x60, // 00DF0 at 163
|
||||
0xc7, 0x0f, 0x20, // 00FC7 at 193
|
||||
0xea, 0x17, 0x40, // 017EA at 226
|
||||
0x05, 0x1b, 0x00, // 01B05 at 256
|
||||
0x41, 0x20, 0x00, // 02041 at 288
|
||||
0x0c, 0xa8, 0x80, // 0A80C at 324
|
||||
0x37, 0xaa, 0x20, // 0AA37 at 353
|
||||
0x50, 0xfe, 0x20, // 0FE50 at 385
|
||||
0x3a, 0x0d, 0x21, // 10D3A at 417
|
||||
0x74, 0x11, 0x01, // 11174 at 448
|
||||
0x5a, 0x14, 0x21, // 1145A at 481
|
||||
0x44, 0x19, 0x81, // 11944 at 516
|
||||
0x5a, 0x1d, 0xa1, // 11D5A at 549
|
||||
0xf5, 0x6a, 0x21, // 16AF5 at 577
|
||||
0x45, 0xd2, 0x41, // 1D245 at 610
|
||||
0xaf, 0xe2, 0x21, // 1E2AF at 641
|
||||
0xf0, 0x01, 0x0e, // E01F0 at 672 (upper bound)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ALL_UNICODE
|
||||
|
@ -676,17 +728,35 @@ static const uint8_t unicode_cc_table[899] = {
|
|||
};
|
||||
|
||||
static const uint8_t unicode_cc_index[87] = {
|
||||
0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05,
|
||||
0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c,
|
||||
0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00,
|
||||
0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10,
|
||||
0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3,
|
||||
0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00,
|
||||
0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab,
|
||||
0x00, 0x39, 0x0a, 0x01, 0x51, 0x0f, 0x01, 0x73,
|
||||
0x11, 0x01, 0x75, 0x13, 0x01, 0x2b, 0x17, 0x21,
|
||||
0x3f, 0x1c, 0x21, 0x9e, 0xbc, 0x21, 0x08, 0xe0,
|
||||
0x01, 0x44, 0xe9, 0x01, 0x4b, 0xe9, 0x01,
|
||||
0x4d, 0x03, 0x00, // 0034D at 32
|
||||
0x97, 0x05, 0x20, // 00597 at 65
|
||||
0xc6, 0x05, 0x00, // 005C6 at 96
|
||||
0xe7, 0x06, 0x00, // 006E7 at 128
|
||||
0x45, 0x07, 0x00, // 00745 at 160
|
||||
0x9c, 0x08, 0x00, // 0089C at 192
|
||||
0x4d, 0x09, 0x00, // 0094D at 224
|
||||
0x3c, 0x0b, 0x00, // 00B3C at 256
|
||||
0x3d, 0x0d, 0x00, // 00D3D at 288
|
||||
0x36, 0x0f, 0x00, // 00F36 at 320
|
||||
0x38, 0x10, 0x20, // 01038 at 353
|
||||
0x3a, 0x19, 0x00, // 0193A at 384
|
||||
0xcb, 0x1a, 0x20, // 01ACB at 417
|
||||
0xd3, 0x1c, 0x00, // 01CD3 at 448
|
||||
0xcf, 0x1d, 0x00, // 01DCF at 480
|
||||
0xe2, 0x20, 0x00, // 020E2 at 512
|
||||
0x2e, 0x30, 0x20, // 0302E at 545
|
||||
0x2b, 0xa9, 0x20, // 0A92B at 577
|
||||
0xed, 0xab, 0x00, // 0ABED at 608
|
||||
0x39, 0x0a, 0x01, // 10A39 at 640
|
||||
0x51, 0x0f, 0x01, // 10F51 at 672
|
||||
0x73, 0x11, 0x01, // 11173 at 704
|
||||
0x75, 0x13, 0x01, // 11375 at 736
|
||||
0x2b, 0x17, 0x21, // 1172B at 769
|
||||
0x3f, 0x1c, 0x21, // 11C3F at 801
|
||||
0x9e, 0xbc, 0x21, // 1BC9E at 833
|
||||
0x08, 0xe0, 0x01, // 1E008 at 864
|
||||
0x44, 0xe9, 0x01, // 1E944 at 896
|
||||
0x4b, 0xe9, 0x01, // 1E94B at 928 (upper bound)
|
||||
};
|
||||
|
||||
static const uint32_t unicode_decomp_table1[699] = {
|
||||
|
@ -4484,3 +4554,4 @@ static const uint16_t unicode_prop_len_table[] = {
|
|||
};
|
||||
|
||||
#endif /* CONFIG_ALL_UNICODE */
|
||||
/* 62 tables / 32261 bytes, 5 index / 345 bytes */
|
||||
|
|
130
libunicode.c
130
libunicode.c
|
@ -262,11 +262,7 @@ int lre_canonicalize(uint32_t c, BOOL is_unicode)
|
|||
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define UNICODE_INDEX_BLOCK_LEN 32
|
||||
|
@ -317,6 +313,14 @@ static BOOL lre_is_in_table(uint32_t c, const uint8_t *table,
|
|||
return FALSE; /* outside the table */
|
||||
p = table + pos;
|
||||
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(;;) {
|
||||
b = *p++;
|
||||
if (b < 64) {
|
||||
|
@ -833,6 +837,13 @@ static int unicode_get_cc(uint32_t c)
|
|||
if (pos < 0)
|
||||
return 0;
|
||||
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(;;) {
|
||||
b = *p++;
|
||||
type = b >> 6;
|
||||
|
@ -1185,6 +1196,15 @@ static int unicode_general_category1(CharRange *cr, uint32_t gc_mask)
|
|||
p = unicode_gc_table;
|
||||
p_end = unicode_gc_table + countof(unicode_gc_table);
|
||||
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) {
|
||||
b = *p++;
|
||||
n = b >> 5;
|
||||
|
@ -1238,6 +1258,14 @@ static int unicode_prop1(CharRange *cr, int prop_idx)
|
|||
p_end = p + unicode_prop_len_table[prop_idx];
|
||||
c = 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) {
|
||||
c0 = c;
|
||||
b = *p++;
|
||||
|
@ -1786,3 +1814,97 @@ int unicode_prop(CharRange *cr, const char *prop_name)
|
|||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
103
libunicode.h
103
libunicode.h
|
@ -24,27 +24,13 @@
|
|||
#ifndef LIBUNICODE_H
|
||||
#define LIBUNICODE_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LRE_BOOL int /* for documentation purposes */
|
||||
#include <stdint.h>
|
||||
|
||||
/* define it to include all the unicode tables (40KB larger) */
|
||||
#define CONFIG_ALL_UNICODE
|
||||
|
||||
#define LRE_CC_RES_LEN_MAX 3
|
||||
|
||||
typedef enum {
|
||||
UNICODE_NFC,
|
||||
UNICODE_NFD,
|
||||
UNICODE_NFKC,
|
||||
UNICODE_NFKD,
|
||||
} UnicodeNormalizationEnum;
|
||||
|
||||
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
|
||||
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 */
|
||||
|
||||
typedef struct {
|
||||
|
@ -102,12 +88,14 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
|
|||
|
||||
int cr_invert(CharRange *cr);
|
||||
|
||||
int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode);
|
||||
int cr_regexp_canonicalize(CharRange *cr, int is_unicode);
|
||||
|
||||
#ifdef CONFIG_ALL_UNICODE
|
||||
|
||||
LRE_BOOL lre_is_id_start(uint32_t c);
|
||||
LRE_BOOL lre_is_id_continue(uint32_t c);
|
||||
typedef enum {
|
||||
UNICODE_NFC,
|
||||
UNICODE_NFD,
|
||||
UNICODE_NFKC,
|
||||
UNICODE_NFKD,
|
||||
} UnicodeNormalizationEnum;
|
||||
|
||||
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
|
||||
UnicodeNormalizationEnum n_type,
|
||||
|
@ -115,13 +103,80 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
|
|||
|
||||
/* Unicode character range functions */
|
||||
|
||||
int unicode_script(CharRange *cr,
|
||||
const char *script_name, LRE_BOOL is_ext);
|
||||
int unicode_script(CharRange *cr, const char *script_name, int is_ext);
|
||||
int unicode_general_category(CharRange *cr, const char *gc_name);
|
||||
int unicode_prop(CharRange *cr, const char *prop_name);
|
||||
|
||||
#endif /* CONFIG_ALL_UNICODE */
|
||||
int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
|
||||
int lre_canonicalize(uint32_t c, int is_unicode);
|
||||
|
||||
#undef LRE_BOOL
|
||||
/* Code point type categories */
|
||||
enum {
|
||||
UNICODE_C_SPACE = (1 << 0),
|
||||
UNICODE_C_DIGIT = (1 << 1),
|
||||
UNICODE_C_UPPER = (1 << 2),
|
||||
UNICODE_C_LOWER = (1 << 3),
|
||||
UNICODE_C_UNDER = (1 << 4),
|
||||
UNICODE_C_DOLLAR = (1 << 5),
|
||||
UNICODE_C_XDIGIT = (1 << 6),
|
||||
};
|
||||
extern uint8_t const lre_ctype_bits[256];
|
||||
|
||||
/* zero or non-zero return value */
|
||||
int lre_is_cased(uint32_t c);
|
||||
int lre_is_case_ignorable(uint32_t c);
|
||||
int lre_is_id_start(uint32_t c);
|
||||
int lre_is_id_continue(uint32_t c);
|
||||
|
||||
static inline int lre_is_space_byte(uint8_t c) {
|
||||
return lre_ctype_bits[c] & UNICODE_C_SPACE;
|
||||
}
|
||||
|
||||
static inline int lre_is_id_start_byte(uint8_t c) {
|
||||
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
|
||||
UNICODE_C_UNDER | UNICODE_C_DOLLAR);
|
||||
}
|
||||
|
||||
static inline int lre_is_id_continue_byte(uint8_t c) {
|
||||
return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER |
|
||||
UNICODE_C_UNDER | UNICODE_C_DOLLAR |
|
||||
UNICODE_C_DIGIT);
|
||||
}
|
||||
|
||||
int lre_is_space_non_ascii(uint32_t c);
|
||||
|
||||
static inline int lre_is_space(uint32_t c) {
|
||||
if (c < 256)
|
||||
return lre_is_space_byte(c);
|
||||
else
|
||||
return lre_is_space_non_ascii(c);
|
||||
}
|
||||
|
||||
static inline int lre_js_is_ident_first(uint32_t c) {
|
||||
if (c < 128) {
|
||||
return lre_is_id_start_byte(c);
|
||||
} else {
|
||||
#ifdef CONFIG_ALL_UNICODE
|
||||
return lre_is_id_start(c);
|
||||
#else
|
||||
return !lre_is_space_non_ascii(c);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline int lre_js_is_ident_next(uint32_t c) {
|
||||
if (c < 128) {
|
||||
return lre_is_id_continue_byte(c);
|
||||
} else {
|
||||
/* ZWNJ and ZWJ are accepted in identifiers */
|
||||
if (c >= 0x200C && c <= 0x200D)
|
||||
return TRUE;
|
||||
#ifdef CONFIG_ALL_UNICODE
|
||||
return lre_is_id_continue(c);
|
||||
#else
|
||||
return !lre_is_space_non_ascii(c);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LIBUNICODE_H */
|
||||
|
|
78
qjs.c
78
qjs.c
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* QuickJS stand alone interpreter
|
||||
*
|
||||
* Copyright (c) 2024 Sneed Group
|
||||
* Copyright (c) 2017-2021 Fabrice Bellard
|
||||
* Copyright (c) 2017-2021 Charlie Gordon
|
||||
*
|
||||
|
@ -37,6 +36,8 @@
|
|||
#include <malloc/malloc.h>
|
||||
#elif defined(__linux__)
|
||||
#include <malloc.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <malloc_np.h>
|
||||
#endif
|
||||
|
||||
#include "cutils.h"
|
||||
|
@ -65,6 +66,7 @@ static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
|
|||
js_module_set_import_meta(ctx, val, TRUE, TRUE);
|
||||
val = JS_EvalFunction(ctx, val);
|
||||
}
|
||||
val = js_std_await(ctx, val);
|
||||
} else {
|
||||
val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
|
||||
}
|
||||
|
@ -98,34 +100,6 @@ static int eval_file(JSContext *ctx, const char *filename, int module)
|
|||
eval_flags = JS_EVAL_TYPE_MODULE;
|
||||
else
|
||||
eval_flags = JS_EVAL_TYPE_GLOBAL;
|
||||
|
||||
//POLYFILLS FOR QJS FILES BEGIN
|
||||
const char *pf = "globalThis.global = globalThis;\n"
|
||||
"global.console.error = console.log\n"
|
||||
"global.console.warn = console.log\n"
|
||||
"globalThis.breakFunction = () => { throw new Error('Function Break'); };\n"
|
||||
"\n"
|
||||
"if (typeof os !== 'undefined') {\n"
|
||||
" globalThis.sleep = os.sleep;\n"
|
||||
" async function setTimeout2(func, ms) {globalThis.clearTimeout = false; await sleep(ms); if (!clearTimeout) { func(); } }\n"
|
||||
" globalThis.setTimeout = setTimeout2\n"
|
||||
"} else {\n"
|
||||
" console.error('os is not defined.');\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"if (typeof std !== 'undefined') {\n"
|
||||
" globalThis.urlGet = std.urlGet;\n"
|
||||
" globalThis.loadFile = std.loadFile;\n"
|
||||
" globalThis.printf = console.log;\n"
|
||||
" globalThis.evalFile = std.loadScript;\n"
|
||||
" globalThis.require = std.loadScript;\n"
|
||||
" globalThis.getURL = std.urlGet;\n"
|
||||
"} else {\n"
|
||||
" console.error('std is not defined.');\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
eval_buf(ctx, pf, strlen(pf), "<input>", JS_EVAL_TYPE_MODULE);
|
||||
ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
|
||||
js_free(ctx, buf);
|
||||
return ret;
|
||||
|
@ -334,7 +308,7 @@ int main(int argc, char **argv)
|
|||
int trace_memory = 0;
|
||||
int empty_run = 0;
|
||||
int module = -1;
|
||||
//int load_std = 0; //we don't need this anymore, we ignore this and always load std libs
|
||||
int load_std = 0;
|
||||
int dump_unhandled_promise_rejection = 0;
|
||||
size_t memory_limit = 0;
|
||||
char *include_list[32];
|
||||
|
@ -425,10 +399,10 @@ int main(int argc, char **argv)
|
|||
trace_memory++;
|
||||
continue;
|
||||
}
|
||||
//if (!strcmp(longopt, "std")) {
|
||||
//load_std = 1;
|
||||
//continue;
|
||||
//}
|
||||
if (!strcmp(longopt, "std")) {
|
||||
load_std = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(longopt, "unhandled-rejection")) {
|
||||
dump_unhandled_promise_rejection = 1;
|
||||
continue;
|
||||
|
@ -515,15 +489,14 @@ int main(int argc, char **argv)
|
|||
#endif
|
||||
js_std_add_helpers(ctx, argc - optind, argv + optind);
|
||||
|
||||
/* make 'std' and 'os' visible to all code */
|
||||
//if (load_std) {
|
||||
/* make 'std' and 'os' visible to non module code */
|
||||
if (load_std) {
|
||||
const char *str = "import * as std from 'std';\n"
|
||||
"import * as os from 'os';\n"
|
||||
"globalThis.std = std;\n"
|
||||
"globalThis.os = os;\n";
|
||||
eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
for(i = 0; i < include_count; i++) {
|
||||
if (eval_file(ctx, include_list[i], module))
|
||||
|
@ -546,35 +519,6 @@ int main(int argc, char **argv)
|
|||
if (interactive) {
|
||||
js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
|
||||
}
|
||||
|
||||
//POLYFILLS FOR QJS REPL BEGIN
|
||||
const char *pf = "globalThis.global = globalThis;\n"
|
||||
"global.console.error = console.log\n"
|
||||
"global.console.warn = console.log\n"
|
||||
"globalThis.breakFunction = () => { throw new Error('Function Break'); };\n"
|
||||
"\n"
|
||||
"if (typeof os !== 'undefined') {\n"
|
||||
" globalThis.sleep = os.sleep;\n"
|
||||
" async function setTimeout2(func, ms) {globalThis.clearTimeout = false; await sleep(ms); if (!clearTimeout) { func(); } }\n"
|
||||
" globalThis.setTimeout = setTimeout2\n"
|
||||
"} else {\n"
|
||||
" console.error('os is not defined.');\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"if (typeof std !== 'undefined') {\n"
|
||||
" globalThis.urlGet = std.urlGet;\n"
|
||||
" globalThis.loadFile = std.loadFile;\n"
|
||||
" globalThis.printf = console.log;\n"
|
||||
" globalThis.evalFile = std.loadScript;\n"
|
||||
" globalThis.require = std.loadScript;\n"
|
||||
" globalThis.getURL = std.urlGet;\n"
|
||||
"} else {\n"
|
||||
" console.error('std is not defined.');\n"
|
||||
"}\n";
|
||||
|
||||
|
||||
eval_buf(ctx, pf, strlen(pf), "<input>", JS_EVAL_TYPE_MODULE);
|
||||
|
||||
js_std_loop(ctx);
|
||||
}
|
||||
|
||||
|
|
247
quickjs-libc.c
247
quickjs-libc.c
|
@ -47,8 +47,15 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__FreeBSD__)
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
typedef sig_t sighandler_t;
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if !defined(environ)
|
||||
#include <crt_externs.h>
|
||||
#define environ (*_NSGetEnviron())
|
||||
|
@ -89,7 +96,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
struct list_head link;
|
||||
BOOL has_object;
|
||||
int timer_id;
|
||||
int64_t timeout;
|
||||
JSValue func;
|
||||
} JSOSTimer;
|
||||
|
@ -125,6 +132,7 @@ typedef struct JSThreadState {
|
|||
struct list_head os_timers; /* list of JSOSTimer.link */
|
||||
struct list_head port_list; /* list of JSWorkerMessageHandler.link */
|
||||
int eval_script_recurse; /* only used in the main thread */
|
||||
int next_timer_id; /* for setTimeout() */
|
||||
/* not used in the main thread */
|
||||
JSWorkerMessagePipe *recv_pipe, *send_pipe;
|
||||
} JSThreadState;
|
||||
|
@ -149,7 +157,7 @@ static JSValue js_printf_internal(JSContext *ctx,
|
|||
uint8_t cbuf[UTF8_CHAR_LEN_MAX+1];
|
||||
JSValue res;
|
||||
DynBuf dbuf;
|
||||
const char *fmt_str;
|
||||
const char *fmt_str = NULL;
|
||||
const uint8_t *fmt, *fmt_end;
|
||||
const uint8_t *p;
|
||||
char *q;
|
||||
|
@ -250,7 +258,7 @@ static JSValue js_printf_internal(JSContext *ctx,
|
|||
string_arg = JS_ToCString(ctx, argv[i++]);
|
||||
if (!string_arg)
|
||||
goto fail;
|
||||
int32_arg = unicode_from_utf8((uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);
|
||||
int32_arg = unicode_from_utf8((const uint8_t *)string_arg, UTF8_CHAR_LEN_MAX, &p);
|
||||
JS_FreeCString(ctx, string_arg);
|
||||
} else {
|
||||
if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
|
||||
|
@ -354,6 +362,7 @@ static JSValue js_printf_internal(JSContext *ctx,
|
|||
return res;
|
||||
|
||||
fail:
|
||||
JS_FreeCString(ctx, fmt_str);
|
||||
dbuf_free(&dbuf);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
@ -1280,7 +1289,7 @@ static JSValue js_std_file_putByte(JSContext *ctx, JSValueConst this_val,
|
|||
|
||||
/* urlGet */
|
||||
|
||||
#define URL_GET_PROGRAM "curl -s -i"
|
||||
#define URL_GET_PROGRAM "curl -s -i --"
|
||||
#define URL_GET_BUF_SIZE 4096
|
||||
|
||||
static int http_get_header_line(FILE *f, char *buf, size_t buf_size,
|
||||
|
@ -1326,7 +1335,7 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,
|
|||
DynBuf header_buf_s, *header_buf = &header_buf_s;
|
||||
char *buf;
|
||||
size_t i, len;
|
||||
int c, status;
|
||||
int status;
|
||||
JSValue response = JS_UNDEFINED, ret_obj;
|
||||
JSValueConst options_obj;
|
||||
FILE *f;
|
||||
|
@ -1353,16 +1362,25 @@ static JSValue js_std_urlGet(JSContext *ctx, JSValueConst this_val,
|
|||
}
|
||||
|
||||
js_std_dbuf_init(ctx, &cmd_buf);
|
||||
dbuf_printf(&cmd_buf, "%s ''", URL_GET_PROGRAM);
|
||||
len = strlen(url);
|
||||
for(i = 0; i < len; i++) {
|
||||
c = url[i];
|
||||
if (c == '\'' || c == '\\')
|
||||
dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM);
|
||||
for(i = 0; url[i] != '\0'; i++) {
|
||||
unsigned char c = url[i];
|
||||
switch (c) {
|
||||
case '\'':
|
||||
/* shell single quoted string does not support \' */
|
||||
dbuf_putstr(&cmd_buf, "'\\''");
|
||||
break;
|
||||
case '[': case ']': case '{': case '}': case '\\':
|
||||
/* prevent interpretation by curl as range or set specification */
|
||||
dbuf_putc(&cmd_buf, '\\');
|
||||
dbuf_putc(&cmd_buf, c);
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
dbuf_putc(&cmd_buf, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
JS_FreeCString(ctx, url);
|
||||
dbuf_putstr(&cmd_buf, "''");
|
||||
dbuf_putstr(&cmd_buf, "'");
|
||||
dbuf_putc(&cmd_buf, '\0');
|
||||
if (dbuf_error(&cmd_buf)) {
|
||||
dbuf_free(&cmd_buf);
|
||||
|
@ -1637,7 +1655,7 @@ static JSValue js_os_seek(JSContext *ctx, JSValueConst this_val,
|
|||
}
|
||||
|
||||
static JSValue js_os_read_write(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
int argc, JSValueConst *argv, int magic)
|
||||
{
|
||||
int fd;
|
||||
uint64_t pos, len;
|
||||
|
@ -1669,7 +1687,7 @@ static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val,
|
|||
int fd;
|
||||
if (JS_ToInt32(ctx, &fd, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
return JS_NewBool(ctx, (isatty(fd) != 0));
|
||||
return JS_NewBool(ctx, isatty(fd));
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
@ -1778,7 +1796,7 @@ static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val,
|
|||
#endif /* !_WIN32 */
|
||||
|
||||
static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
const char *filename;
|
||||
int ret;
|
||||
|
@ -2006,41 +2024,13 @@ static JSValue js_os_now(JSContext *ctx, JSValue this_val,
|
|||
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)
|
||||
{
|
||||
list_del(&th->link);
|
||||
JS_FreeValueRT(rt, th->func);
|
||||
js_free_rt(rt, th);
|
||||
}
|
||||
|
||||
static JSClassID js_os_timer_class_id;
|
||||
|
||||
static void js_os_timer_finalizer(JSRuntime *rt, JSValue val)
|
||||
{
|
||||
JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
|
||||
if (th) {
|
||||
th->has_object = FALSE;
|
||||
if (!th->link.prev)
|
||||
free_timer(rt, th);
|
||||
}
|
||||
}
|
||||
|
||||
static void js_os_timer_mark(JSRuntime *rt, JSValueConst val,
|
||||
JS_MarkFunc *mark_func)
|
||||
{
|
||||
JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id);
|
||||
if (th) {
|
||||
JS_MarkValue(rt, th->func, mark_func);
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
|
@ -2049,45 +2039,56 @@ static JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
|
|||
int64_t delay;
|
||||
JSValueConst func;
|
||||
JSOSTimer *th;
|
||||
JSValue obj;
|
||||
|
||||
func = argv[0];
|
||||
if (!JS_IsFunction(ctx, func))
|
||||
return JS_ThrowTypeError(ctx, "not a function");
|
||||
if (JS_ToInt64(ctx, &delay, argv[1]))
|
||||
return JS_EXCEPTION;
|
||||
obj = JS_NewObjectClass(ctx, js_os_timer_class_id);
|
||||
if (JS_IsException(obj))
|
||||
return obj;
|
||||
th = js_mallocz(ctx, sizeof(*th));
|
||||
if (!th) {
|
||||
JS_FreeValue(ctx, obj);
|
||||
if (!th)
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
th->has_object = TRUE;
|
||||
th->timer_id = ts->next_timer_id;
|
||||
if (ts->next_timer_id == INT32_MAX)
|
||||
ts->next_timer_id = 1;
|
||||
else
|
||||
ts->next_timer_id++;
|
||||
th->timeout = get_time_ms() + delay;
|
||||
th->func = JS_DupValue(ctx, func);
|
||||
list_add_tail(&th->link, &ts->os_timers);
|
||||
JS_SetOpaque(obj, th);
|
||||
return obj;
|
||||
return JS_NewInt32(ctx, th->timer_id);
|
||||
}
|
||||
|
||||
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,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id);
|
||||
if (!th)
|
||||
JSRuntime *rt = JS_GetRuntime(ctx);
|
||||
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
|
||||
JSOSTimer *th;
|
||||
int timer_id;
|
||||
|
||||
if (JS_ToInt32(ctx, &timer_id, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
unlink_timer(JS_GetRuntime(ctx), th);
|
||||
th = find_timer_by_id(ts, timer_id);
|
||||
if (!th)
|
||||
return JS_UNDEFINED;
|
||||
free_timer(rt, th);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSClassDef js_os_timer_class = {
|
||||
"OSTimer",
|
||||
.finalizer = js_os_timer_finalizer,
|
||||
.gc_mark = js_os_timer_mark,
|
||||
};
|
||||
|
||||
/* return a promise */
|
||||
static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
|
@ -2111,7 +2112,7 @@ static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val,
|
|||
JS_FreeValue(ctx, resolving_funcs[1]);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
th->has_object = FALSE;
|
||||
th->timer_id = -1;
|
||||
th->timeout = get_time_ms() + delay;
|
||||
th->func = JS_DupValue(ctx, resolving_funcs[0]);
|
||||
list_add_tail(&th->link, &ts->os_timers);
|
||||
|
@ -2161,9 +2162,7 @@ static int js_os_poll(JSContext *ctx)
|
|||
/* the timer expired */
|
||||
func = th->func;
|
||||
th->func = JS_UNDEFINED;
|
||||
unlink_timer(rt, th);
|
||||
if (!th->has_object)
|
||||
free_timer(rt, th);
|
||||
free_timer(rt, th);
|
||||
call_handler(ctx, func);
|
||||
JS_FreeValue(ctx, func);
|
||||
return 0;
|
||||
|
@ -2330,9 +2329,7 @@ static int js_os_poll(JSContext *ctx)
|
|||
/* the timer expired */
|
||||
func = th->func;
|
||||
th->func = JS_UNDEFINED;
|
||||
unlink_timer(rt, th);
|
||||
if (!th->has_object)
|
||||
free_timer(rt, th);
|
||||
free_timer(rt, th);
|
||||
call_handler(ctx, func);
|
||||
JS_FreeValue(ctx, func);
|
||||
return 0;
|
||||
|
@ -2552,12 +2549,14 @@ static JSValue js_os_stat(JSContext *ctx, JSValueConst this_val,
|
|||
else
|
||||
res = stat(path, &st);
|
||||
#endif
|
||||
if (res < 0)
|
||||
err = errno;
|
||||
else
|
||||
err = 0;
|
||||
JS_FreeCString(ctx, path);
|
||||
if (res < 0) {
|
||||
err = errno;
|
||||
obj = JS_NULL;
|
||||
} else {
|
||||
err = 0;
|
||||
obj = JS_NewObject(ctx);
|
||||
if (JS_IsException(obj))
|
||||
return JS_EXCEPTION;
|
||||
|
@ -2668,7 +2667,7 @@ static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val,
|
|||
|
||||
/* sleep(delay_ms) */
|
||||
static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int64_t delay;
|
||||
int ret;
|
||||
|
@ -2732,7 +2731,7 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val,
|
|||
|
||||
#if !defined(_WIN32)
|
||||
static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
const char *target, *linkpath;
|
||||
int err;
|
||||
|
@ -3016,7 +3015,6 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
|
|||
}
|
||||
if (pid == 0) {
|
||||
/* child */
|
||||
int fd_max = sysconf(_SC_OPEN_MAX);
|
||||
|
||||
/* remap the stdin/stdout/stderr handles if necessary */
|
||||
for(i = 0; i < 3; i++) {
|
||||
|
@ -3025,9 +3023,28 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
|
|||
_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.
|
||||
*/
|
||||
|
||||
for(i = 3; i < fd_max; i++)
|
||||
close(i);
|
||||
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++)
|
||||
close(i);
|
||||
}
|
||||
#endif
|
||||
if (cwd) {
|
||||
if (chdir(cwd) < 0)
|
||||
_exit(127);
|
||||
|
@ -3339,7 +3356,7 @@ static void *worker_func(void *opaque)
|
|||
JSRuntime *rt;
|
||||
JSThreadState *ts;
|
||||
JSContext *ctx;
|
||||
JSValue promise;
|
||||
JSValue val;
|
||||
|
||||
rt = JS_NewRuntime();
|
||||
if (rt == NULL) {
|
||||
|
@ -3366,14 +3383,14 @@ static void *worker_func(void *opaque)
|
|||
|
||||
js_std_add_helpers(ctx, -1, NULL);
|
||||
|
||||
promise = JS_LoadModule(ctx, args->basename, args->filename);
|
||||
if (JS_IsException(promise))
|
||||
js_std_dump_error(ctx);
|
||||
/* XXX: check */
|
||||
JS_FreeValue(ctx, promise);
|
||||
val = JS_LoadModule(ctx, args->basename, args->filename);
|
||||
free(args->filename);
|
||||
free(args->basename);
|
||||
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);
|
||||
|
||||
|
@ -3528,10 +3545,12 @@ static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
|
|||
memcpy(msg->data, data, data_len);
|
||||
msg->data_len = data_len;
|
||||
|
||||
msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len);
|
||||
if (!msg->sab_tab)
|
||||
goto fail;
|
||||
memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len);
|
||||
if (sab_tab_len > 0) {
|
||||
msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab_len);
|
||||
if (!msg->sab_tab)
|
||||
goto fail;
|
||||
memcpy(msg->sab_tab, sab_tab, sizeof(msg->sab_tab[0]) * sab_tab_len);
|
||||
}
|
||||
msg->sab_tab_len = sab_tab_len;
|
||||
|
||||
js_free(ctx, data);
|
||||
|
@ -3735,10 +3754,6 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
|
|||
{
|
||||
os_poll_func = js_os_poll;
|
||||
|
||||
/* OSTimer class */
|
||||
JS_NewClassID(&js_os_timer_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), js_os_timer_class_id, &js_os_timer_class);
|
||||
|
||||
#ifdef USE_WORKER
|
||||
{
|
||||
JSRuntime *rt = JS_GetRuntime(ctx);
|
||||
|
@ -3787,7 +3802,7 @@ JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name)
|
|||
/**********************************************************/
|
||||
|
||||
static JSValue js_print(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int i;
|
||||
const char *str;
|
||||
|
@ -3850,6 +3865,7 @@ void js_std_init_handlers(JSRuntime *rt)
|
|||
init_list_head(&ts->os_signal_handlers);
|
||||
init_list_head(&ts->os_timers);
|
||||
init_list_head(&ts->port_list);
|
||||
ts->next_timer_id = 1;
|
||||
|
||||
JS_SetRuntimeOpaque(rt, ts);
|
||||
|
||||
|
@ -3883,9 +3899,7 @@ void js_std_free_handlers(JSRuntime *rt)
|
|||
|
||||
list_for_each_safe(el, el1, &ts->os_timers) {
|
||||
JSOSTimer *th = list_entry(el, JSOSTimer, link);
|
||||
unlink_timer(rt, th);
|
||||
if (!th->has_object)
|
||||
free_timer(rt, th);
|
||||
free_timer(rt, th);
|
||||
}
|
||||
|
||||
#ifdef USE_WORKER
|
||||
|
@ -3969,6 +3983,42 @@ 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,
|
||||
int load_only)
|
||||
{
|
||||
|
@ -3987,8 +4037,11 @@ void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
|||
goto exception;
|
||||
}
|
||||
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)) {
|
||||
exception:
|
||||
js_std_dump_error(ctx);
|
||||
|
|
|
@ -37,6 +37,7 @@ JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
|
|||
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
|
||||
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
|
||||
void js_std_loop(JSContext *ctx);
|
||||
JSValue js_std_await(JSContext *ctx, JSValue obj);
|
||||
void js_std_init_handlers(JSRuntime *rt);
|
||||
void js_std_free_handlers(JSRuntime *rt);
|
||||
void js_std_dump_error(JSContext *ctx);
|
||||
|
|
48
quickjs.h
48
quickjs.h
|
@ -499,7 +499,10 @@ typedef struct JSClassDef {
|
|||
JSClassExoticMethods *exotic;
|
||||
} JSClassDef;
|
||||
|
||||
#define JS_INVALID_CLASS_ID 0
|
||||
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_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
|
||||
|
||||
|
@ -547,23 +550,21 @@ JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
|
|||
|
||||
static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
|
||||
{
|
||||
JSValue v;
|
||||
int32_t val;
|
||||
union {
|
||||
double d;
|
||||
uint64_t u;
|
||||
} u, t;
|
||||
u.d = d;
|
||||
val = (int32_t)d;
|
||||
t.d = val;
|
||||
/* -0 cannot be represented as integer, so we compare the bit
|
||||
representation */
|
||||
if (u.u == t.u) {
|
||||
v = JS_MKVAL(JS_TAG_INT, val);
|
||||
} else {
|
||||
v = __JS_NewFloat64(ctx, d);
|
||||
if (d >= INT32_MIN && d <= INT32_MAX) {
|
||||
u.d = d;
|
||||
val = (int32_t)d;
|
||||
t.d = val;
|
||||
/* -0 cannot be represented as integer, so we compare the bit
|
||||
representation */
|
||||
if (u.u == t.u)
|
||||
return JS_MKVAL(JS_TAG_INT, val);
|
||||
}
|
||||
return v;
|
||||
return __JS_NewFloat64(ctx, d);
|
||||
}
|
||||
|
||||
static inline JS_BOOL JS_IsNumber(JSValueConst v)
|
||||
|
@ -633,6 +634,7 @@ static inline JS_BOOL JS_IsObject(JSValueConst v)
|
|||
JSValue JS_Throw(JSContext *ctx, JSValue obj);
|
||||
JSValue JS_GetException(JSContext *ctx);
|
||||
JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
|
||||
void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, JS_BOOL flag);
|
||||
void JS_ResetUncatchableError(JSContext *ctx);
|
||||
JSValue JS_NewError(JSContext *ctx);
|
||||
JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
|
||||
|
@ -681,6 +683,10 @@ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
|
|||
return (JSValue)v;
|
||||
}
|
||||
|
||||
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_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
|
||||
static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
|
||||
|
@ -723,6 +729,8 @@ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val)
|
|||
JSValue JS_NewArray(JSContext *ctx);
|
||||
int JS_IsArray(JSContext *ctx, JSValueConst val);
|
||||
|
||||
JSValue JS_NewDate(JSContext *ctx, double epoch_ms);
|
||||
|
||||
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
|
||||
JSAtom prop, JSValueConst receiver,
|
||||
JS_BOOL throw_ref_error);
|
||||
|
@ -821,6 +829,23 @@ JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
|
|||
JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len);
|
||||
void JS_DetachArrayBuffer(JSContext *ctx, 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,
|
||||
size_t *pbyte_offset,
|
||||
size_t *pbyte_length,
|
||||
|
@ -876,6 +901,7 @@ void JS_SetModuleLoaderFunc(JSRuntime *rt,
|
|||
/* return the import.meta object of a module */
|
||||
JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);
|
||||
JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);
|
||||
JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m);
|
||||
|
||||
/* JS Job support */
|
||||
|
||||
|
|
1
readme.txt
Normal file
1
readme.txt
Normal file
|
@ -0,0 +1 @@
|
|||
The main documentation is in doc/quickjs.pdf or doc/quickjs.html.
|
26
release.sh
26
release.sh
|
@ -8,12 +8,12 @@ version=`cat VERSION`
|
|||
if [ "$1" = "-h" ] ; then
|
||||
echo "release.sh [release_list]"
|
||||
echo ""
|
||||
echo "release_list: extras binary win_binary cosmo_binary quickjs"
|
||||
echo "release_list: extras binary win_binary quickjs"
|
||||
|
||||
exit 1
|
||||
fi
|
||||
|
||||
release_list="extras binary win_binary cosmo_binary quickjs"
|
||||
release_list="extras binary win_binary quickjs"
|
||||
|
||||
if [ "$1" != "" ] ; then
|
||||
release_list="$1"
|
||||
|
@ -84,28 +84,6 @@ cp $dlldir/libwinpthread-1.dll $outdir
|
|||
|
||||
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
|
||||
|
||||
|
|
82
repl.js
82
repl.js
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* QuickJS Read Eval Print Loop
|
||||
*
|
||||
* Copyright (c) 2024 Sneed Group
|
||||
* Copyright (c) 2017-2020 Fabrice Bellard
|
||||
* Copyright (c) 2017-2020 Charlie Gordon
|
||||
*
|
||||
|
@ -68,38 +67,20 @@ import * as os from "os";
|
|||
bright_white: "\x1b[37;1m",
|
||||
};
|
||||
|
||||
var styles;
|
||||
if (config_numcalc) {
|
||||
styles = {
|
||||
'default': 'black',
|
||||
'comment': 'white',
|
||||
'string': 'green',
|
||||
'regex': 'cyan',
|
||||
'number': 'green',
|
||||
'keyword': 'blue',
|
||||
'function': 'gray',
|
||||
'type': 'bright_magenta',
|
||||
'identifier': 'yellow',
|
||||
'error': 'bright_red',
|
||||
'result': 'black',
|
||||
'error_msg': 'bright_red',
|
||||
};
|
||||
} else {
|
||||
styles = {
|
||||
'default': 'bright_green',
|
||||
'comment': 'white',
|
||||
'string': 'bright_cyan',
|
||||
'regex': 'cyan',
|
||||
'number': 'green',
|
||||
'keyword': 'bright_white',
|
||||
'function': 'bright_yellow',
|
||||
'type': 'bright_magenta',
|
||||
'identifier': 'bright_green',
|
||||
'error': 'red',
|
||||
'result': 'bright_white',
|
||||
'error_msg': 'bright_red',
|
||||
};
|
||||
}
|
||||
var styles = {
|
||||
'default': 'bright_green',
|
||||
'comment': 'white',
|
||||
'string': 'bright_cyan',
|
||||
'regex': 'cyan',
|
||||
'number': 'green',
|
||||
'keyword': 'bright_white',
|
||||
'function': 'bright_yellow',
|
||||
'type': 'bright_magenta',
|
||||
'identifier': 'bright_green',
|
||||
'error': 'red',
|
||||
'result': 'bright_white',
|
||||
'error_msg': 'bright_red',
|
||||
};
|
||||
|
||||
var history = [];
|
||||
var clip_board = "";
|
||||
|
@ -110,11 +91,7 @@ import * as os from "os";
|
|||
var pstate = "";
|
||||
var prompt = "";
|
||||
var plen = 0;
|
||||
var ps1;
|
||||
if (config_numcalc)
|
||||
ps1 = "> ";
|
||||
else
|
||||
ps1 = "donejs > ";
|
||||
var ps1 = "qjs > ";
|
||||
var ps2 = " ... ";
|
||||
var utf8 = true;
|
||||
var show_time = false;
|
||||
|
@ -614,6 +591,9 @@ import * as os from "os";
|
|||
base = get_context_word(line, pos);
|
||||
if (["true", "false", "null", "this"].includes(base) || !isNaN(+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);
|
||||
if (obj === null || obj === void 0)
|
||||
return obj;
|
||||
|
@ -1006,6 +986,8 @@ import * as os from "os";
|
|||
std.puts(a);
|
||||
} else if (stack.indexOf(a) >= 0) {
|
||||
std.puts("[circular]");
|
||||
} else if (a instanceof Date) {
|
||||
std.puts("Date " + a.toGMTString().__quote());
|
||||
} else if (has_jscalc && (a instanceof Fraction ||
|
||||
a instanceof Complex ||
|
||||
a instanceof Mod ||
|
||||
|
@ -1180,6 +1162,23 @@ import * as os from "os";
|
|||
}
|
||||
|
||||
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 */
|
||||
g.execCmd = function (cmd) {
|
||||
switch(cmd) {
|
||||
|
@ -1227,9 +1226,9 @@ import * as os from "os";
|
|||
function cmd_start() {
|
||||
if (!config_numcalc) {
|
||||
if (has_jscalc)
|
||||
std.puts('QJSCalc (DoneJS Edition) - Type "\\h" for help\n');
|
||||
std.puts('QJSCalc - Type "\\h" for help\n');
|
||||
else
|
||||
std.puts('QuickJS (DoneJS Edition) - Type "\\h" for help\n');
|
||||
std.puts('QuickJS - Type "\\h" for help\n');
|
||||
}
|
||||
if (has_bignum) {
|
||||
log2_10 = Math.log(10) / Math.log(2);
|
||||
|
@ -1311,7 +1310,7 @@ import * as os from "os";
|
|||
/* result is a promise */
|
||||
result.then(print_eval_result, print_eval_error);
|
||||
} else {
|
||||
print_eval_result(result);
|
||||
print_eval_result({ value: result });
|
||||
}
|
||||
} catch (error) {
|
||||
print_eval_error(error);
|
||||
|
@ -1319,6 +1318,7 @@ import * as os from "os";
|
|||
}
|
||||
|
||||
function print_eval_result(result) {
|
||||
result = result.value;
|
||||
eval_time = os.now() - eval_start_time;
|
||||
std.puts(colors[styles.result]);
|
||||
print(result);
|
||||
|
|
|
@ -63,6 +63,8 @@ enum test_mode_t {
|
|||
TEST_STRICT, /* run tests as strict, skip nostrict tests */
|
||||
TEST_ALL, /* run tests in both strict and nostrict, unless restricted by spec */
|
||||
} test_mode = TEST_DEFAULT_NOSTRICT;
|
||||
int compact;
|
||||
int show_timings;
|
||||
int skip_async;
|
||||
int skip_module;
|
||||
int new_style;
|
||||
|
@ -530,6 +532,7 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
|
|||
{
|
||||
const char *script;
|
||||
Test262Agent *agent;
|
||||
pthread_attr_t attr;
|
||||
|
||||
if (JS_GetContextOpaque(ctx) != NULL)
|
||||
return JS_ThrowTypeError(ctx, "cannot be called inside an agent");
|
||||
|
@ -544,7 +547,12 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
|
|||
agent->script = strdup(script);
|
||||
JS_FreeCString(ctx, script);
|
||||
list_add_tail(&agent->link, &agent_list);
|
||||
pthread_create(&agent->tid, NULL, agent_start, agent);
|
||||
pthread_attr_init(&attr);
|
||||
// musl libc gives threads 80 kb stacks, much smaller than
|
||||
// JS_DEFAULT_STACK_SIZE (256 kb)
|
||||
pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default
|
||||
pthread_create(&agent->tid, &attr, agent_start, agent);
|
||||
pthread_attr_destroy(&attr);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
|
@ -813,6 +821,19 @@ static JSModuleDef *js_module_loader_test(JSContext *ctx,
|
|||
uint8_t *buf;
|
||||
JSModuleDef *m;
|
||||
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);
|
||||
if (!buf) {
|
||||
|
@ -910,7 +931,7 @@ void update_exclude_dirs(void)
|
|||
lp->count = count;
|
||||
}
|
||||
|
||||
void load_config(const char *filename)
|
||||
void load_config(const char *filename, const char *ignore)
|
||||
{
|
||||
char buf[1024];
|
||||
FILE *f;
|
||||
|
@ -965,6 +986,10 @@ void load_config(const char *filename)
|
|||
printf("%s:%d: syntax error\n", filename, lineno);
|
||||
continue;
|
||||
}
|
||||
if (strstr(ignore, p)) {
|
||||
printf("%s:%d: ignoring %s=%s\n", filename, lineno, p, q);
|
||||
continue;
|
||||
}
|
||||
if (str_equal(p, "style")) {
|
||||
new_style = str_equal(q, "new");
|
||||
continue;
|
||||
|
@ -1540,7 +1565,7 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
|
|||
JS_SetCanBlock(rt, can_block);
|
||||
|
||||
/* loader for ES6 modules */
|
||||
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL);
|
||||
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, (void *)filename);
|
||||
|
||||
add_helpers(ctx);
|
||||
|
||||
|
@ -1656,7 +1681,7 @@ int run_test(const char *filename, int index)
|
|||
/* XXX: should extract the phase */
|
||||
char *q = find_tag(p, "type:", &state);
|
||||
if (q) {
|
||||
while (isspace(*q))
|
||||
while (isspace((unsigned char)*q))
|
||||
q++;
|
||||
error_type = strdup_len(q, strcspn(q, " \n"));
|
||||
}
|
||||
|
@ -1841,7 +1866,7 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
|
|||
JS_SetCanBlock(rt, can_block);
|
||||
|
||||
/* loader for ES6 modules */
|
||||
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, NULL);
|
||||
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader_test, (void *)filename);
|
||||
|
||||
add_helpers(ctx);
|
||||
|
||||
|
@ -1900,9 +1925,27 @@ void show_progress(int force) {
|
|||
clock_t t = clock();
|
||||
if (force || !last_clock || (t - last_clock) > CLOCKS_PER_SEC / 20) {
|
||||
last_clock = t;
|
||||
/* output progress indicator: erase end of line and return to col 0 */
|
||||
fprintf(stderr, "%d/%d/%d\033[K\r",
|
||||
test_failed, test_count, test_skipped);
|
||||
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 */
|
||||
fprintf(stderr, "%d/%d/%d\033[K\r",
|
||||
test_failed, test_count, test_skipped);
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
@ -1953,6 +1996,8 @@ void help(void)
|
|||
"-N run test prepared by test262-harness+eshost\n"
|
||||
"-s run tests in strict mode, skip @nostrict tests\n"
|
||||
"-E only run tests from the error file\n"
|
||||
"-C use compact progress indicator\n"
|
||||
"-t show timings\n"
|
||||
"-u update error file\n"
|
||||
"-v verbose: output error messages\n"
|
||||
"-T duration display tests taking more than 'duration' ms\n"
|
||||
|
@ -1979,14 +2024,29 @@ int main(int argc, char **argv)
|
|||
BOOL is_dir_list;
|
||||
BOOL only_check_errors = FALSE;
|
||||
const char *filename;
|
||||
const char *ignore = "";
|
||||
BOOL is_test262_harness = FALSE;
|
||||
BOOL is_module = FALSE;
|
||||
clock_t clocks;
|
||||
|
||||
#if !defined(_WIN32)
|
||||
compact = !isatty(STDERR_FILENO);
|
||||
/* Date tests assume California local time */
|
||||
setenv("TZ", "America/Los_Angeles", 1);
|
||||
#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
|
||||
the script */
|
||||
optind = 1;
|
||||
|
@ -2006,12 +2066,16 @@ int main(int argc, char **argv)
|
|||
test_mode = TEST_STRICT;
|
||||
} else if (str_equal(arg, "-a")) {
|
||||
test_mode = TEST_ALL;
|
||||
} else if (str_equal(arg, "-t")) {
|
||||
show_timings++;
|
||||
} else if (str_equal(arg, "-u")) {
|
||||
update_errors++;
|
||||
} else if (str_equal(arg, "-v")) {
|
||||
verbose++;
|
||||
} else if (str_equal(arg, "-C")) {
|
||||
compact = 1;
|
||||
} else if (str_equal(arg, "-c")) {
|
||||
load_config(get_opt_arg(arg, argv[optind++]));
|
||||
load_config(get_opt_arg(arg, argv[optind++]), ignore);
|
||||
} else if (str_equal(arg, "-d")) {
|
||||
enumerate_tests(get_opt_arg(arg, argv[optind++]));
|
||||
} else if (str_equal(arg, "-e")) {
|
||||
|
@ -2062,8 +2126,10 @@ int main(int argc, char **argv)
|
|||
|
||||
update_exclude_dirs();
|
||||
|
||||
clocks = clock();
|
||||
|
||||
if (is_dir_list) {
|
||||
if (optind < argc && !isdigit(argv[optind][0])) {
|
||||
if (optind < argc && !isdigit((unsigned char)argv[optind][0])) {
|
||||
filename = argv[optind++];
|
||||
namelist_load(&test_list, filename);
|
||||
}
|
||||
|
@ -2098,6 +2164,8 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
clocks = clock() - clocks;
|
||||
|
||||
if (dump_memory) {
|
||||
if (dump_memory > 1 && stats_count > 1) {
|
||||
printf("\nMininum memory statistics for %s:\n\n", stats_min_filename);
|
||||
|
@ -2126,6 +2194,8 @@ int main(int argc, char **argv)
|
|||
fprintf(stderr, ", %d fixed", fixed_errors);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
if (show_timings)
|
||||
fprintf(stderr, "Total time: %.3fs\n", (double)clocks / CLOCKS_PER_SEC);
|
||||
}
|
||||
|
||||
if (error_out && error_out != stdout) {
|
||||
|
@ -2141,5 +2211,6 @@ int main(int argc, char **argv)
|
|||
free(harness_exclude);
|
||||
free(error_file);
|
||||
|
||||
return 0;
|
||||
/* Signal that the error file is out of date. */
|
||||
return new_errors || changed_errors || fixed_errors;
|
||||
}
|
||||
|
|
5
test.js
5
test.js
|
@ -1,5 +0,0 @@
|
|||
function test() {
|
||||
breakFunction();
|
||||
console.warn("lol it works!");
|
||||
}
|
||||
test()
|
|
@ -406,5 +406,8 @@ test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-2.js
|
|||
test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-3.js
|
||||
test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-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]
|
||||
# list test files or use config.testdir
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
import * as std from "std";
|
||||
import * as os from "os";
|
||||
|
||||
if (typeof require !== 'undefined') {
|
||||
var fs = require('fs');
|
||||
}
|
||||
|
||||
function pad(str, n) {
|
||||
str += "";
|
||||
|
@ -65,17 +67,13 @@ function toPrec(n, prec) {
|
|||
var ref_data;
|
||||
var log_data;
|
||||
|
||||
var heads = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (%)" ];
|
||||
var heads = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (1000)" ];
|
||||
var widths = [ 22, 10, 9, 9, 9 ];
|
||||
var precs = [ 0, 0, 2, 2, 2 ];
|
||||
var precs = [ 0, 0, 2, 2, 0 ];
|
||||
var total = [ 0, 0, 0, 0, 0 ];
|
||||
var total_score = 0;
|
||||
var total_scale = 0;
|
||||
|
||||
if (typeof console == "undefined") {
|
||||
var console = { log: print };
|
||||
}
|
||||
|
||||
function log_line() {
|
||||
var i, n, s, a;
|
||||
s = "";
|
||||
|
@ -83,7 +81,7 @@ function log_line() {
|
|||
if (i > 0)
|
||||
s += " ";
|
||||
a = arguments[i];
|
||||
if (typeof a == "number") {
|
||||
if (typeof a === "number") {
|
||||
total[i] += a;
|
||||
a = toPrec(a, precs[i]);
|
||||
s += pad_left(a, widths[i]);
|
||||
|
@ -95,11 +93,28 @@ function log_line() {
|
|||
}
|
||||
|
||||
var clocks_per_sec = 1000;
|
||||
var max_iterations = 10;
|
||||
var clock_threshold = 100; /* favoring short measuring spans */
|
||||
var max_iterations = 100;
|
||||
var clock_threshold = 2; /* favoring short measuring spans */
|
||||
var min_n_argument = 1;
|
||||
//var get_clock = Date.now;
|
||||
var get_clock = os.now;
|
||||
var get_clock;
|
||||
if (typeof performance !== "undefined") {
|
||||
// use more precise clock on NodeJS
|
||||
// need a method call on performance object
|
||||
get_clock = () => performance.now();
|
||||
} else
|
||||
if (typeof os !== "undefined") {
|
||||
// use more precise clock on QuickJS
|
||||
get_clock = os.now;
|
||||
} else {
|
||||
// use Date.now and round up to the next millisecond
|
||||
get_clock = () => {
|
||||
var t0 = Date.now();
|
||||
var t;
|
||||
while ((t = Date.now()) == t0)
|
||||
continue;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
function log_one(text, n, ti) {
|
||||
var ref;
|
||||
|
@ -112,7 +127,7 @@ function log_one(text, n, ti) {
|
|||
ti = Math.round(ti * 100) / 100;
|
||||
log_data[text] = ti;
|
||||
if (typeof ref === "number") {
|
||||
log_line(text, n, ti, ref, ti * 100 / ref);
|
||||
log_line(text, n, ti, ref, Math.round(ref * 1000 / ti));
|
||||
total_score += ti * 100 / ref;
|
||||
total_scale += 100;
|
||||
} else {
|
||||
|
@ -124,28 +139,27 @@ function log_one(text, n, ti) {
|
|||
|
||||
function bench(f, text)
|
||||
{
|
||||
var i, j, n, t, t1, ti, nb_its, ref, ti_n, ti_n1, min_ti;
|
||||
var i, j, n, t, ti, nb_its, ref, ti_n, ti_n1;
|
||||
|
||||
nb_its = n = 1;
|
||||
if (f.bench) {
|
||||
ti_n = f(text);
|
||||
} else {
|
||||
// measure ti_n: the shortest time for an individual operation
|
||||
ti_n = 1000000000;
|
||||
min_ti = clock_threshold / 10;
|
||||
for(i = 0; i < 30; i++) {
|
||||
// measure ti: the shortest time for max_iterations iterations
|
||||
ti = 1000000000;
|
||||
for (j = 0; j < max_iterations; j++) {
|
||||
t = get_clock();
|
||||
while ((t1 = get_clock()) == t)
|
||||
continue;
|
||||
nb_its = f(n);
|
||||
t = get_clock() - t;
|
||||
if (nb_its < 0)
|
||||
return; // test failure
|
||||
t1 = get_clock() - t1;
|
||||
if (ti > t1)
|
||||
ti = t1;
|
||||
if (ti > t)
|
||||
ti = t;
|
||||
}
|
||||
if (ti >= min_ti) {
|
||||
if (ti >= clock_threshold / 10) {
|
||||
ti_n1 = ti / nb_its;
|
||||
if (ti_n > ti_n1)
|
||||
ti_n = ti_n1;
|
||||
|
@ -171,6 +185,26 @@ function empty_loop(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) {
|
||||
var j;
|
||||
for(j = 0; j < n; j++) {
|
||||
|
@ -179,6 +213,32 @@ function date_now(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)
|
||||
{
|
||||
var obj, sum, j;
|
||||
|
@ -207,30 +267,78 @@ function prop_write(n)
|
|||
return n * 4;
|
||||
}
|
||||
|
||||
function prop_create(n)
|
||||
function prop_update(n)
|
||||
{
|
||||
var obj, j;
|
||||
obj = {a: 1, b: 2, c:3, d:4 };
|
||||
for(j = 0; j < n; j++) {
|
||||
obj = new Object();
|
||||
obj.a = 1;
|
||||
obj.b = 2;
|
||||
obj.c = 3;
|
||||
obj.d = 4;
|
||||
obj.a += j;
|
||||
obj.b += j;
|
||||
obj.c += j;
|
||||
obj.d += j;
|
||||
}
|
||||
return n * 4;
|
||||
}
|
||||
|
||||
function prop_create(n)
|
||||
{
|
||||
var obj, i, j;
|
||||
for(j = 0; j < n; j++) {
|
||||
obj = {};
|
||||
obj.a = 1;
|
||||
obj.b = 2;
|
||||
obj.c = 3;
|
||||
obj.d = 4;
|
||||
obj.e = 5;
|
||||
obj.f = 6;
|
||||
obj.g = 7;
|
||||
obj.h = 8;
|
||||
obj.i = 9;
|
||||
obj.j = 10;
|
||||
for(i = 0; i < 10; i++) {
|
||||
obj[i] = i;
|
||||
}
|
||||
}
|
||||
return n * 20;
|
||||
}
|
||||
|
||||
function prop_clone(n)
|
||||
{
|
||||
var ref, obj, j, k;
|
||||
ref = { a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10 };
|
||||
for(k = 0; k < 10; k++) {
|
||||
ref[k] = k;
|
||||
}
|
||||
for (j = 0; j < n; j++) {
|
||||
global_res = { ...ref };
|
||||
}
|
||||
return n * 20;
|
||||
}
|
||||
|
||||
function prop_delete(n)
|
||||
{
|
||||
var obj, j;
|
||||
obj = {};
|
||||
for(j = 0; j < n; j++) {
|
||||
obj[j] = 1;
|
||||
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++) {
|
||||
delete obj[j];
|
||||
for (j = 0; j < n; j++) {
|
||||
obj = { ...ref };
|
||||
delete obj.a;
|
||||
delete obj.b;
|
||||
delete obj.c;
|
||||
delete obj.d;
|
||||
delete obj.e;
|
||||
delete obj.f;
|
||||
delete obj.g;
|
||||
delete obj.h;
|
||||
delete obj.i;
|
||||
delete obj.j;
|
||||
for(k = 0; k < 10; k++) {
|
||||
delete obj[k];
|
||||
}
|
||||
}
|
||||
return n;
|
||||
return n * 20;
|
||||
}
|
||||
|
||||
function array_read(n)
|
||||
|
@ -291,15 +399,32 @@ function array_prop_create(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)
|
||||
{
|
||||
var tab, i, j, len;
|
||||
var tab, ref, i, j, len;
|
||||
len = 1000;
|
||||
tab = [];
|
||||
ref = [];
|
||||
for(i = 0; i < len; i++)
|
||||
tab[i] = i;
|
||||
ref[i] = i;
|
||||
for(j = 0; j < n; j++) {
|
||||
for(i = len - 1; i >= 0; i--)
|
||||
tab = ref.slice();
|
||||
for(i = len; i --> 0;)
|
||||
tab.length = i;
|
||||
}
|
||||
return len * n;
|
||||
|
@ -307,15 +432,16 @@ function array_length_decr(n)
|
|||
|
||||
function array_hole_length_decr(n)
|
||||
{
|
||||
var tab, i, j, len;
|
||||
var tab, ref, i, j, len;
|
||||
len = 1000;
|
||||
tab = [];
|
||||
ref = [];
|
||||
for(i = 0; i < len; i++) {
|
||||
if (i != 3)
|
||||
tab[i] = i;
|
||||
if (i % 10 == 9)
|
||||
ref[i] = i;
|
||||
}
|
||||
for(j = 0; j < n; j++) {
|
||||
for(i = len - 1; i >= 0; i--)
|
||||
tab = ref.slice();
|
||||
for(i = len; i --> 0;)
|
||||
tab.length = i;
|
||||
}
|
||||
return len * n;
|
||||
|
@ -335,12 +461,13 @@ function array_push(n)
|
|||
|
||||
function array_pop(n)
|
||||
{
|
||||
var tab, i, j, len, sum;
|
||||
var tab, ref, i, j, len, sum;
|
||||
len = 500;
|
||||
ref = [];
|
||||
for(i = 0; i < len; i++)
|
||||
ref[i] = i;
|
||||
for(j = 0; j < n; j++) {
|
||||
tab = [];
|
||||
for(i = 0; i < len; i++)
|
||||
tab[i] = i;
|
||||
tab = ref.slice();
|
||||
sum = 0;
|
||||
for(i = 0; i < len; i++)
|
||||
sum += tab.pop();
|
||||
|
@ -412,6 +539,7 @@ function global_read(n)
|
|||
return n * 4;
|
||||
}
|
||||
|
||||
// non strict version
|
||||
var global_write =
|
||||
(1, eval)(`(function global_write(n)
|
||||
{
|
||||
|
@ -454,6 +582,7 @@ function local_destruct(n)
|
|||
var global_v1, global_v2, global_v3, global_v4;
|
||||
var global_a, global_b, global_c, global_d;
|
||||
|
||||
// non strict version
|
||||
var global_destruct =
|
||||
(1, eval)(`(function global_destruct(n)
|
||||
{
|
||||
|
@ -481,6 +610,25 @@ function global_destruct_strict(n)
|
|||
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 f(a)
|
||||
|
@ -500,7 +648,7 @@ function func_call(n)
|
|||
return n * 4;
|
||||
}
|
||||
|
||||
function closure_var(n)
|
||||
function func_closure_call(n)
|
||||
{
|
||||
function f(a)
|
||||
{
|
||||
|
@ -605,8 +753,8 @@ function bigint256_arith(n)
|
|||
function set_collection_add(n)
|
||||
{
|
||||
var s, i, j, len = 100;
|
||||
s = new Set();
|
||||
for(j = 0; j < n; j++) {
|
||||
s = new Set();
|
||||
for(i = 0; i < len; i++) {
|
||||
s.add(String(i), i);
|
||||
}
|
||||
|
@ -620,25 +768,25 @@ function set_collection_add(n)
|
|||
|
||||
function array_for(n)
|
||||
{
|
||||
var r, i, j, sum;
|
||||
var r, i, j, sum, len = 100;
|
||||
r = [];
|
||||
for(i = 0; i < 100; i++)
|
||||
for(i = 0; i < len; i++)
|
||||
r[i] = i;
|
||||
for(j = 0; j < n; j++) {
|
||||
sum = 0;
|
||||
for(i = 0; i < 100; i++) {
|
||||
for(i = 0; i < len; i++) {
|
||||
sum += r[i];
|
||||
}
|
||||
global_res = sum;
|
||||
}
|
||||
return n * 100;
|
||||
return n * len;
|
||||
}
|
||||
|
||||
function array_for_in(n)
|
||||
{
|
||||
var r, i, j, sum;
|
||||
var r, i, j, sum, len = 100;
|
||||
r = [];
|
||||
for(i = 0; i < 100; i++)
|
||||
for(i = 0; i < len; i++)
|
||||
r[i] = i;
|
||||
for(j = 0; j < n; j++) {
|
||||
sum = 0;
|
||||
|
@ -647,14 +795,14 @@ function array_for_in(n)
|
|||
}
|
||||
global_res = sum;
|
||||
}
|
||||
return n * 100;
|
||||
return n * len;
|
||||
}
|
||||
|
||||
function array_for_of(n)
|
||||
{
|
||||
var r, i, j, sum;
|
||||
var r, i, j, sum, len = 100;
|
||||
r = [];
|
||||
for(i = 0; i < 100; i++)
|
||||
for(i = 0; i < len; i++)
|
||||
r[i] = i;
|
||||
for(j = 0; j < n; j++) {
|
||||
sum = 0;
|
||||
|
@ -663,7 +811,7 @@ function array_for_of(n)
|
|||
}
|
||||
global_res = sum;
|
||||
}
|
||||
return n * 100;
|
||||
return n * len;
|
||||
}
|
||||
|
||||
function math_min(n)
|
||||
|
@ -678,58 +826,108 @@ function math_min(n)
|
|||
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 */
|
||||
function string_build1(n)
|
||||
{
|
||||
var i, j, r;
|
||||
r = "";
|
||||
for(j = 0; j < n; j++) {
|
||||
for(i = 0; i < 100; i++)
|
||||
r = "";
|
||||
for(i = 0; i < 1000; i++)
|
||||
r += "x";
|
||||
global_res = r;
|
||||
}
|
||||
return n * 100;
|
||||
return n * 1000;
|
||||
}
|
||||
|
||||
/* incremental string contruction using + */
|
||||
function string_build1x(n)
|
||||
{
|
||||
var i, j, r;
|
||||
for(j = 0; j < n; j++) {
|
||||
r = "";
|
||||
for(i = 0; i < 1000; i++)
|
||||
r = r + "x";
|
||||
global_res = r;
|
||||
}
|
||||
return n * 1000;
|
||||
}
|
||||
|
||||
/* incremental string contruction using +2c */
|
||||
function string_build2c(n)
|
||||
{
|
||||
var i, j;
|
||||
for(j = 0; j < n; j++) {
|
||||
var r = "";
|
||||
for(i = 0; i < 1000; i++)
|
||||
r += "xy";
|
||||
global_res = r;
|
||||
}
|
||||
return n * 1000;
|
||||
}
|
||||
|
||||
/* incremental string contruction as arg */
|
||||
function string_build2(n, r)
|
||||
{
|
||||
var i, j;
|
||||
r = "";
|
||||
for(j = 0; j < n; j++) {
|
||||
for(i = 0; i < 100; i++)
|
||||
r = "";
|
||||
for(i = 0; i < 1000; i++)
|
||||
r += "x";
|
||||
global_res = r;
|
||||
}
|
||||
return n * 100;
|
||||
return n * 1000;
|
||||
}
|
||||
|
||||
/* incremental string contruction by prepending */
|
||||
function string_build3(n, r)
|
||||
function string_build3(n)
|
||||
{
|
||||
var i, j;
|
||||
r = "";
|
||||
var i, j, r;
|
||||
for(j = 0; j < n; j++) {
|
||||
for(i = 0; i < 100; i++)
|
||||
r = "";
|
||||
for(i = 0; i < 1000; i++)
|
||||
r = "x" + r;
|
||||
global_res = r;
|
||||
}
|
||||
return n * 100;
|
||||
return n * 1000;
|
||||
}
|
||||
|
||||
/* incremental string contruction with multiple reference */
|
||||
function string_build4(n)
|
||||
{
|
||||
var i, j, r, s;
|
||||
r = "";
|
||||
for(j = 0; j < n; j++) {
|
||||
for(i = 0; i < 100; i++) {
|
||||
r = "";
|
||||
for(i = 0; i < 1000; i++) {
|
||||
s = r;
|
||||
r += "x";
|
||||
}
|
||||
global_res = r;
|
||||
}
|
||||
return n * 100;
|
||||
return n * 1000;
|
||||
}
|
||||
|
||||
/* sort bench */
|
||||
|
@ -862,32 +1060,33 @@ function sort_bench(text) {
|
|||
": " + arr[i - 1] + " > " + arr[i]);
|
||||
}
|
||||
if (sort_bench.verbose)
|
||||
log_one("sort_" + f.name, n, ti, n * 100);
|
||||
log_one("sort_" + f.name, 1, ti / 100);
|
||||
}
|
||||
total_score = save_total_score;
|
||||
total_scale = save_total_scale;
|
||||
return total / n / 1000;
|
||||
return total / n / 100;
|
||||
}
|
||||
sort_bench.bench = true;
|
||||
sort_bench.verbose = false;
|
||||
|
||||
function int_to_string(n)
|
||||
{
|
||||
var s, r, j;
|
||||
r = 0;
|
||||
var s, j;
|
||||
for(j = 0; j < n; j++) {
|
||||
s = (j + 1).toString();
|
||||
s = (j % 1000).toString();
|
||||
s = (1234000 + j % 1000).toString();
|
||||
}
|
||||
return n;
|
||||
global_res = s;
|
||||
return n * 2;
|
||||
}
|
||||
|
||||
function float_to_string(n)
|
||||
{
|
||||
var s, r, j;
|
||||
r = 0;
|
||||
var s, j;
|
||||
for(j = 0; j < n; j++) {
|
||||
s = (j + 0.1).toString();
|
||||
}
|
||||
global_res = s;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -896,7 +1095,6 @@ function string_to_int(n)
|
|||
var s, r, j;
|
||||
r = 0;
|
||||
s = "12345";
|
||||
r = 0;
|
||||
for(j = 0; j < n; j++) {
|
||||
r += (s | 0);
|
||||
}
|
||||
|
@ -909,7 +1107,6 @@ function string_to_float(n)
|
|||
var s, r, j;
|
||||
r = 0;
|
||||
s = "12345.6";
|
||||
r = 0;
|
||||
for(j = 0; j < n; j++) {
|
||||
r -= s;
|
||||
}
|
||||
|
@ -919,41 +1116,92 @@ function string_to_float(n)
|
|||
|
||||
function load_result(filename)
|
||||
{
|
||||
var f, str, res;
|
||||
if (typeof std === "undefined")
|
||||
var has_filename = filename;
|
||||
var has_error = false;
|
||||
var str, res;
|
||||
|
||||
if (!filename)
|
||||
filename = "microbench.txt";
|
||||
|
||||
if (typeof fs !== "undefined") {
|
||||
// read the file in Node.js
|
||||
try {
|
||||
str = fs.readFileSync(filename, { encoding: "utf8" });
|
||||
} catch {
|
||||
has_error = true;
|
||||
}
|
||||
} else
|
||||
if (typeof std !== "undefined") {
|
||||
// read the file in QuickJS
|
||||
var f = std.open(filename, "r");
|
||||
if (f) {
|
||||
str = f.readAsString();
|
||||
f.close();
|
||||
} else {
|
||||
has_error = true;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
f = std.open(filename, "r");
|
||||
if (!f)
|
||||
}
|
||||
if (has_error) {
|
||||
if (has_filename) {
|
||||
// Should throw exception?
|
||||
console.log("cannot load " + filename);
|
||||
}
|
||||
return null;
|
||||
str = f.readAsString();
|
||||
}
|
||||
res = JSON.parse(str);
|
||||
f.close();
|
||||
return res;
|
||||
}
|
||||
|
||||
function save_result(filename, obj)
|
||||
{
|
||||
var f;
|
||||
if (typeof std === "undefined")
|
||||
var str = JSON.stringify(obj, null, 2) + "\n";
|
||||
var has_error = false;
|
||||
|
||||
if (typeof fs !== "undefined") {
|
||||
// save the file in Node.js
|
||||
try {
|
||||
str = fs.writeFileSync(filename, str, { encoding: "utf8" });
|
||||
} catch {
|
||||
has_error = true;
|
||||
}
|
||||
} else
|
||||
if (typeof std !== "undefined") {
|
||||
// save the file in QuickJS
|
||||
var f = std.open(filename, "w");
|
||||
if (f) {
|
||||
f.puts(str);
|
||||
f.close();
|
||||
} else {
|
||||
has_error = 'true';
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
f = std.open(filename, "w");
|
||||
f.puts(JSON.stringify(obj, null, 2));
|
||||
f.puts("\n");
|
||||
f.close();
|
||||
}
|
||||
if (has_error)
|
||||
console.log("cannot save " + filename);
|
||||
}
|
||||
|
||||
function main(argc, argv, g)
|
||||
{
|
||||
var test_list = [
|
||||
empty_loop,
|
||||
empty_down_loop,
|
||||
empty_down_loop2,
|
||||
empty_do_loop,
|
||||
date_now,
|
||||
date_parse,
|
||||
prop_read,
|
||||
prop_write,
|
||||
prop_update,
|
||||
prop_create,
|
||||
prop_clone,
|
||||
prop_delete,
|
||||
array_read,
|
||||
array_write,
|
||||
array_prop_create,
|
||||
array_slice,
|
||||
array_length_decr,
|
||||
array_hole_length_decr,
|
||||
array_push,
|
||||
|
@ -966,8 +1214,9 @@ function main(argc, argv, g)
|
|||
local_destruct,
|
||||
global_destruct,
|
||||
global_destruct_strict,
|
||||
global_func_call,
|
||||
func_call,
|
||||
closure_var,
|
||||
func_closure_call,
|
||||
int_arith,
|
||||
float_arith,
|
||||
set_collection_add,
|
||||
|
@ -975,28 +1224,33 @@ function main(argc, argv, g)
|
|||
array_for_in,
|
||||
array_for_of,
|
||||
math_min,
|
||||
regexp_ascii,
|
||||
regexp_utf16,
|
||||
string_build1,
|
||||
string_build1x,
|
||||
string_build2c,
|
||||
string_build2,
|
||||
//string_build3,
|
||||
//string_build4,
|
||||
sort_bench,
|
||||
string_build3,
|
||||
string_build4,
|
||||
int_to_string,
|
||||
float_to_string,
|
||||
string_to_int,
|
||||
string_to_float,
|
||||
];
|
||||
var tests = [];
|
||||
var i, j, n, f, name;
|
||||
var i, j, n, f, name, found;
|
||||
var ref_file, new_ref_file = "microbench-new.txt";
|
||||
|
||||
if (typeof BigInt == "function") {
|
||||
if (typeof BigInt === "function") {
|
||||
/* BigInt test */
|
||||
test_list.push(bigint64_arith);
|
||||
test_list.push(bigint256_arith);
|
||||
}
|
||||
if (typeof BigFloat == "function") {
|
||||
if (typeof BigFloat === "function") {
|
||||
/* BigFloat test */
|
||||
test_list.push(float256_arith);
|
||||
}
|
||||
test_list.push(sort_bench);
|
||||
|
||||
for (i = 1; i < argc;) {
|
||||
name = argv[i++];
|
||||
|
@ -1007,7 +1261,7 @@ function main(argc, argv, g)
|
|||
if (name == "-t") {
|
||||
name = argv[i++];
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1017,14 +1271,22 @@ function main(argc, argv, g)
|
|||
sort_bench.array_size = +argv[i++];
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < test_list.length; j++) {
|
||||
if (name == "-r") {
|
||||
ref_file = argv[i++];
|
||||
continue;
|
||||
}
|
||||
if (name == "-s") {
|
||||
new_ref_file = argv[i++];
|
||||
continue;
|
||||
}
|
||||
for (j = 0, found = false; j < test_list.length; j++) {
|
||||
f = test_list[j];
|
||||
if (name === f.name) {
|
||||
if (f.name.startsWith(name)) {
|
||||
tests.push(f);
|
||||
break;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (j == test_list.length) {
|
||||
if (!found) {
|
||||
console.log("unknown benchmark: " + name);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1032,7 +1294,7 @@ function main(argc, argv, g)
|
|||
if (tests.length == 0)
|
||||
tests = test_list;
|
||||
|
||||
ref_data = load_result("microbench.txt");
|
||||
ref_data = load_result(ref_file);
|
||||
log_data = {};
|
||||
log_line.apply(null, heads);
|
||||
n = 0;
|
||||
|
@ -1044,14 +1306,17 @@ function main(argc, argv, g)
|
|||
n++;
|
||||
}
|
||||
if (ref_data)
|
||||
log_line("total", "", total[2], total[3], total_score * 100 / total_scale);
|
||||
log_line("total", "", total[2], total[3], Math.round(total_scale * 1000 / total_score));
|
||||
else
|
||||
log_line("total", "", total[2]);
|
||||
|
||||
if (tests == test_list)
|
||||
save_result("microbench-new.txt", log_data);
|
||||
if (tests == test_list && new_ref_file)
|
||||
save_result(new_ref_file, log_data);
|
||||
}
|
||||
|
||||
if (!scriptArgs)
|
||||
if (typeof scriptArgs === "undefined") {
|
||||
scriptArgs = [];
|
||||
if (typeof process.argv === "object")
|
||||
scriptArgs = process.argv.slice(1);
|
||||
}
|
||||
main(scriptArgs.length, scriptArgs, this);
|
||||
|
|
279
tests/test_bigfloat.js
Normal file
279
tests/test_bigfloat.js
Normal file
|
@ -0,0 +1,279 @@
|
|||
"use strict";
|
||||
|
||||
function assert(actual, expected, message) {
|
||||
if (arguments.length == 1)
|
||||
expected = true;
|
||||
|
||||
if (actual === expected)
|
||||
return;
|
||||
|
||||
if (actual !== null && expected !== null
|
||||
&& typeof actual == 'object' && typeof expected == 'object'
|
||||
&& actual.toString() === expected.toString())
|
||||
return;
|
||||
|
||||
throw Error("assertion failed: got |" + actual + "|" +
|
||||
", expected |" + expected + "|" +
|
||||
(message ? " (" + message + ")" : ""));
|
||||
}
|
||||
|
||||
function assertThrows(err, func)
|
||||
{
|
||||
var ex;
|
||||
ex = false;
|
||||
try {
|
||||
func();
|
||||
} catch(e) {
|
||||
ex = true;
|
||||
assert(e instanceof err);
|
||||
}
|
||||
assert(ex, true, "exception expected");
|
||||
}
|
||||
|
||||
// load more elaborate version of assert if available
|
||||
try { __loadScript("test_assert.js"); } catch(e) {}
|
||||
|
||||
/*----------------*/
|
||||
|
||||
/* a must be < b */
|
||||
function test_less(a, b)
|
||||
{
|
||||
assert(a < b);
|
||||
assert(!(b < a));
|
||||
assert(a <= b);
|
||||
assert(!(b <= a));
|
||||
assert(b > a);
|
||||
assert(!(a > b));
|
||||
assert(b >= a);
|
||||
assert(!(a >= b));
|
||||
assert(a != b);
|
||||
assert(!(a == b));
|
||||
}
|
||||
|
||||
/* a must be numerically equal to b */
|
||||
function test_eq(a, b)
|
||||
{
|
||||
assert(a == b);
|
||||
assert(b == a);
|
||||
assert(!(a != b));
|
||||
assert(!(b != a));
|
||||
assert(a <= b);
|
||||
assert(b <= a);
|
||||
assert(!(a < b));
|
||||
assert(a >= b);
|
||||
assert(b >= a);
|
||||
assert(!(a > b));
|
||||
}
|
||||
|
||||
function test_divrem(div1, a, b, q)
|
||||
{
|
||||
var div, divrem, t;
|
||||
div = BigInt[div1];
|
||||
divrem = BigInt[div1 + "rem"];
|
||||
assert(div(a, b) == q);
|
||||
t = divrem(a, b);
|
||||
assert(t[0] == q);
|
||||
assert(a == b * q + t[1]);
|
||||
}
|
||||
|
||||
function test_idiv1(div, a, b, r)
|
||||
{
|
||||
test_divrem(div, a, b, r[0]);
|
||||
test_divrem(div, -a, b, r[1]);
|
||||
test_divrem(div, a, -b, r[2]);
|
||||
test_divrem(div, -a, -b, r[3]);
|
||||
}
|
||||
|
||||
/* QuickJS BigInt extensions */
|
||||
function test_bigint_ext()
|
||||
{
|
||||
var r;
|
||||
assert(BigInt.floorLog2(0n) === -1n);
|
||||
assert(BigInt.floorLog2(7n) === 2n);
|
||||
|
||||
assert(BigInt.sqrt(0xffffffc000000000000000n) === 17592185913343n);
|
||||
r = BigInt.sqrtrem(0xffffffc000000000000000n);
|
||||
assert(r[0] === 17592185913343n);
|
||||
assert(r[1] === 35167191957503n);
|
||||
|
||||
test_idiv1("tdiv", 3n, 2n, [1n, -1n, -1n, 1n]);
|
||||
test_idiv1("fdiv", 3n, 2n, [1n, -2n, -2n, 1n]);
|
||||
test_idiv1("cdiv", 3n, 2n, [2n, -1n, -1n, 2n]);
|
||||
test_idiv1("ediv", 3n, 2n, [1n, -2n, -1n, 2n]);
|
||||
}
|
||||
|
||||
function test_bigfloat()
|
||||
{
|
||||
var e, a, b, sqrt2;
|
||||
|
||||
assert(typeof 1n === "bigint");
|
||||
assert(typeof 1l === "bigfloat");
|
||||
assert(1 == 1.0l);
|
||||
assert(1 !== 1.0l);
|
||||
|
||||
test_less(2l, 3l);
|
||||
test_eq(3l, 3l);
|
||||
|
||||
test_less(2, 3l);
|
||||
test_eq(3, 3l);
|
||||
|
||||
test_less(2.1, 3l);
|
||||
test_eq(Math.sqrt(9), 3l);
|
||||
|
||||
test_less(2n, 3l);
|
||||
test_eq(3n, 3l);
|
||||
|
||||
e = new BigFloatEnv(128);
|
||||
assert(e.prec == 128);
|
||||
a = BigFloat.sqrt(2l, e);
|
||||
assert(a === BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e));
|
||||
assert(e.inexact === true);
|
||||
assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcc908b2fb1366ea95l);
|
||||
|
||||
b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128);
|
||||
assert(a === b);
|
||||
|
||||
assert(BigFloat.isNaN(BigFloat(NaN)));
|
||||
assert(BigFloat.isFinite(1l));
|
||||
assert(!BigFloat.isFinite(1l/0l));
|
||||
|
||||
assert(BigFloat.abs(-3l) === 3l);
|
||||
assert(BigFloat.sign(-3l) === -1l);
|
||||
|
||||
assert(BigFloat.exp(0.2l) === 1.2214027581601698339210719946396742l);
|
||||
assert(BigFloat.log(3l) === 1.0986122886681096913952452369225256l);
|
||||
assert(BigFloat.pow(2.1l, 1.6l) === 3.277561666451861947162828744873745l);
|
||||
|
||||
assert(BigFloat.sin(-1l) === -0.841470984807896506652502321630299l);
|
||||
assert(BigFloat.cos(1l) === 0.5403023058681397174009366074429766l);
|
||||
assert(BigFloat.tan(0.1l) === 0.10033467208545054505808004578111154l);
|
||||
|
||||
assert(BigFloat.asin(0.3l) === 0.30469265401539750797200296122752915l);
|
||||
assert(BigFloat.acos(0.4l) === 1.1592794807274085998465837940224159l);
|
||||
assert(BigFloat.atan(0.7l) === 0.610725964389208616543758876490236l);
|
||||
assert(BigFloat.atan2(7.1l, -5.1l) === 2.1937053809751415549388104628759813l);
|
||||
|
||||
assert(BigFloat.floor(2.5l) === 2l);
|
||||
assert(BigFloat.ceil(2.5l) === 3l);
|
||||
assert(BigFloat.trunc(-2.5l) === -2l);
|
||||
assert(BigFloat.round(2.5l) === 3l);
|
||||
|
||||
assert(BigFloat.fmod(3l,2l) === 1l);
|
||||
assert(BigFloat.remainder(3l,2l) === -1l);
|
||||
|
||||
/* string conversion */
|
||||
assert((1234.125l).toString(), "1234.125");
|
||||
assert((1234.125l).toFixed(2), "1234.13");
|
||||
assert((1234.125l).toFixed(2, "down"), "1234.12");
|
||||
assert((1234.125l).toExponential(), "1.234125e+3");
|
||||
assert((1234.125l).toExponential(5), "1.23413e+3");
|
||||
assert((1234.125l).toExponential(5, BigFloatEnv.RNDZ), "1.23412e+3");
|
||||
assert((1234.125l).toPrecision(6), "1234.13");
|
||||
assert((1234.125l).toPrecision(6, BigFloatEnv.RNDZ), "1234.12");
|
||||
|
||||
/* string conversion with binary base */
|
||||
assert((0x123.438l).toString(16), "123.438");
|
||||
assert((0x323.438l).toString(16), "323.438");
|
||||
assert((0x723.438l).toString(16), "723.438");
|
||||
assert((0xf23.438l).toString(16), "f23.438");
|
||||
assert((0x123.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "123.44");
|
||||
assert((0x323.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "323.44");
|
||||
assert((0x723.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "723.44");
|
||||
assert((0xf23.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "f23.44");
|
||||
assert((0x0.0000438l).toFixed(6, BigFloatEnv.RNDNA, 16), "0.000044");
|
||||
assert((0x1230000000l).toFixed(1, BigFloatEnv.RNDNA, 16), "1230000000.0");
|
||||
assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "123.44");
|
||||
assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDZ, 16), "123.43");
|
||||
assert((0x323.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "323.44");
|
||||
assert((0x723.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "723.44");
|
||||
assert((-0xf23.438l).toPrecision(5, BigFloatEnv.RNDD, 16), "-f23.44");
|
||||
assert((0x123.438l).toExponential(4, BigFloatEnv.RNDNA, 16), "1.2344p+8");
|
||||
}
|
||||
|
||||
function test_bigdecimal()
|
||||
{
|
||||
assert(1m === 1m);
|
||||
assert(1m !== 2m);
|
||||
test_less(1m, 2m);
|
||||
test_eq(2m, 2m);
|
||||
|
||||
test_less(1, 2m);
|
||||
test_eq(2, 2m);
|
||||
|
||||
test_less(1.1, 2m);
|
||||
test_eq(Math.sqrt(4), 2m);
|
||||
|
||||
test_less(2n, 3m);
|
||||
test_eq(3n, 3m);
|
||||
|
||||
assert(BigDecimal("1234.1") === 1234.1m);
|
||||
assert(BigDecimal(" 1234.1") === 1234.1m);
|
||||
assert(BigDecimal(" 1234.1 ") === 1234.1m);
|
||||
|
||||
assert(BigDecimal(0.1) === 0.1m);
|
||||
assert(BigDecimal(123) === 123m);
|
||||
assert(BigDecimal(true) === 1m);
|
||||
|
||||
assert(123m + 1m === 124m);
|
||||
assert(123m - 1m === 122m);
|
||||
|
||||
assert(3.2m * 3m === 9.6m);
|
||||
assert(10m / 2m === 5m);
|
||||
assertThrows(RangeError, () => { 10m / 3m } );
|
||||
|
||||
assert(10m % 3m === 1m);
|
||||
assert(-10m % 3m === -1m);
|
||||
|
||||
assert(1234.5m ** 3m === 1881365963.625m);
|
||||
assertThrows(RangeError, () => { 2m ** 3.1m } );
|
||||
assertThrows(RangeError, () => { 2m ** -3m } );
|
||||
|
||||
assert(BigDecimal.sqrt(2m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumSignificantDigits: 4 }) === 1.414m);
|
||||
assert(BigDecimal.sqrt(101m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 3 }) === 10.050m);
|
||||
assert(BigDecimal.sqrt(0.002m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 3 }) === 0.045m);
|
||||
|
||||
assert(BigDecimal.round(3.14159m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 3 }) === 3.142m);
|
||||
|
||||
assert(BigDecimal.add(3.14159m, 0.31212m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 2 }) === 3.45m);
|
||||
assert(BigDecimal.sub(3.14159m, 0.31212m,
|
||||
{ roundingMode: "down",
|
||||
maximumFractionDigits: 2 }) === 2.82m);
|
||||
assert(BigDecimal.mul(3.14159m, 0.31212m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 3 }) === 0.981m);
|
||||
assert(BigDecimal.mod(3.14159m, 0.31211m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 4 }) === 0.0205m);
|
||||
assert(BigDecimal.div(20m, 3m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumSignificantDigits: 3 }) === 6.67m);
|
||||
assert(BigDecimal.div(20m, 3m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 50 }) ===
|
||||
6.66666666666666666666666666666666666666666666666667m);
|
||||
|
||||
/* string conversion */
|
||||
assert((1234.125m).toString(), "1234.125");
|
||||
assert((1234.125m).toFixed(2), "1234.13");
|
||||
assert((1234.125m).toFixed(2, "down"), "1234.12");
|
||||
assert((1234.125m).toExponential(), "1.234125e+3");
|
||||
assert((1234.125m).toExponential(5), "1.23413e+3");
|
||||
assert((1234.125m).toExponential(5, "down"), "1.23412e+3");
|
||||
assert((1234.125m).toPrecision(6), "1234.13");
|
||||
assert((1234.125m).toPrecision(6, "down"), "1234.12");
|
||||
assert((-1234.125m).toPrecision(6, "floor"), "-1234.13");
|
||||
}
|
||||
|
||||
test_bigint_ext();
|
||||
test_bigfloat();
|
||||
test_bigdecimal();
|
|
@ -110,217 +110,5 @@ function test_bigint2()
|
|||
assertThrows(SyntaxError, () => { BigInt(" 123 r") } );
|
||||
}
|
||||
|
||||
function test_divrem(div1, a, b, q)
|
||||
{
|
||||
var div, divrem, t;
|
||||
div = BigInt[div1];
|
||||
divrem = BigInt[div1 + "rem"];
|
||||
assert(div(a, b) == q);
|
||||
t = divrem(a, b);
|
||||
assert(t[0] == q);
|
||||
assert(a == b * q + t[1]);
|
||||
}
|
||||
|
||||
function test_idiv1(div, a, b, r)
|
||||
{
|
||||
test_divrem(div, a, b, r[0]);
|
||||
test_divrem(div, -a, b, r[1]);
|
||||
test_divrem(div, a, -b, r[2]);
|
||||
test_divrem(div, -a, -b, r[3]);
|
||||
}
|
||||
|
||||
/* QuickJS BigInt extensions */
|
||||
function test_bigint_ext()
|
||||
{
|
||||
var r;
|
||||
assert(BigInt.floorLog2(0n) === -1n);
|
||||
assert(BigInt.floorLog2(7n) === 2n);
|
||||
|
||||
assert(BigInt.sqrt(0xffffffc000000000000000n) === 17592185913343n);
|
||||
r = BigInt.sqrtrem(0xffffffc000000000000000n);
|
||||
assert(r[0] === 17592185913343n);
|
||||
assert(r[1] === 35167191957503n);
|
||||
|
||||
test_idiv1("tdiv", 3n, 2n, [1n, -1n, -1n, 1n]);
|
||||
test_idiv1("fdiv", 3n, 2n, [1n, -2n, -2n, 1n]);
|
||||
test_idiv1("cdiv", 3n, 2n, [2n, -1n, -1n, 2n]);
|
||||
test_idiv1("ediv", 3n, 2n, [1n, -2n, -1n, 2n]);
|
||||
}
|
||||
|
||||
function test_bigfloat()
|
||||
{
|
||||
var e, a, b, sqrt2;
|
||||
|
||||
assert(typeof 1n === "bigint");
|
||||
assert(typeof 1l === "bigfloat");
|
||||
assert(1 == 1.0l);
|
||||
assert(1 !== 1.0l);
|
||||
|
||||
test_less(2l, 3l);
|
||||
test_eq(3l, 3l);
|
||||
|
||||
test_less(2, 3l);
|
||||
test_eq(3, 3l);
|
||||
|
||||
test_less(2.1, 3l);
|
||||
test_eq(Math.sqrt(9), 3l);
|
||||
|
||||
test_less(2n, 3l);
|
||||
test_eq(3n, 3l);
|
||||
|
||||
e = new BigFloatEnv(128);
|
||||
assert(e.prec == 128);
|
||||
a = BigFloat.sqrt(2l, e);
|
||||
assert(a === BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e));
|
||||
assert(e.inexact === true);
|
||||
assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcc908b2fb1366ea95l);
|
||||
|
||||
b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128);
|
||||
assert(a === b);
|
||||
|
||||
assert(BigFloat.isNaN(BigFloat(NaN)));
|
||||
assert(BigFloat.isFinite(1l));
|
||||
assert(!BigFloat.isFinite(1l/0l));
|
||||
|
||||
assert(BigFloat.abs(-3l) === 3l);
|
||||
assert(BigFloat.sign(-3l) === -1l);
|
||||
|
||||
assert(BigFloat.exp(0.2l) === 1.2214027581601698339210719946396742l);
|
||||
assert(BigFloat.log(3l) === 1.0986122886681096913952452369225256l);
|
||||
assert(BigFloat.pow(2.1l, 1.6l) === 3.277561666451861947162828744873745l);
|
||||
|
||||
assert(BigFloat.sin(-1l) === -0.841470984807896506652502321630299l);
|
||||
assert(BigFloat.cos(1l) === 0.5403023058681397174009366074429766l);
|
||||
assert(BigFloat.tan(0.1l) === 0.10033467208545054505808004578111154l);
|
||||
|
||||
assert(BigFloat.asin(0.3l) === 0.30469265401539750797200296122752915l);
|
||||
assert(BigFloat.acos(0.4l) === 1.1592794807274085998465837940224159l);
|
||||
assert(BigFloat.atan(0.7l) === 0.610725964389208616543758876490236l);
|
||||
assert(BigFloat.atan2(7.1l, -5.1l) === 2.1937053809751415549388104628759813l);
|
||||
|
||||
assert(BigFloat.floor(2.5l) === 2l);
|
||||
assert(BigFloat.ceil(2.5l) === 3l);
|
||||
assert(BigFloat.trunc(-2.5l) === -2l);
|
||||
assert(BigFloat.round(2.5l) === 3l);
|
||||
|
||||
assert(BigFloat.fmod(3l,2l) === 1l);
|
||||
assert(BigFloat.remainder(3l,2l) === -1l);
|
||||
|
||||
/* string conversion */
|
||||
assert((1234.125l).toString(), "1234.125");
|
||||
assert((1234.125l).toFixed(2), "1234.13");
|
||||
assert((1234.125l).toFixed(2, "down"), "1234.12");
|
||||
assert((1234.125l).toExponential(), "1.234125e+3");
|
||||
assert((1234.125l).toExponential(5), "1.23413e+3");
|
||||
assert((1234.125l).toExponential(5, BigFloatEnv.RNDZ), "1.23412e+3");
|
||||
assert((1234.125l).toPrecision(6), "1234.13");
|
||||
assert((1234.125l).toPrecision(6, BigFloatEnv.RNDZ), "1234.12");
|
||||
|
||||
/* string conversion with binary base */
|
||||
assert((0x123.438l).toString(16), "123.438");
|
||||
assert((0x323.438l).toString(16), "323.438");
|
||||
assert((0x723.438l).toString(16), "723.438");
|
||||
assert((0xf23.438l).toString(16), "f23.438");
|
||||
assert((0x123.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "123.44");
|
||||
assert((0x323.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "323.44");
|
||||
assert((0x723.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "723.44");
|
||||
assert((0xf23.438l).toFixed(2, BigFloatEnv.RNDNA, 16), "f23.44");
|
||||
assert((0x0.0000438l).toFixed(6, BigFloatEnv.RNDNA, 16), "0.000044");
|
||||
assert((0x1230000000l).toFixed(1, BigFloatEnv.RNDNA, 16), "1230000000.0");
|
||||
assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "123.44");
|
||||
assert((0x123.438l).toPrecision(5, BigFloatEnv.RNDZ, 16), "123.43");
|
||||
assert((0x323.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "323.44");
|
||||
assert((0x723.438l).toPrecision(5, BigFloatEnv.RNDNA, 16), "723.44");
|
||||
assert((-0xf23.438l).toPrecision(5, BigFloatEnv.RNDD, 16), "-f23.44");
|
||||
assert((0x123.438l).toExponential(4, BigFloatEnv.RNDNA, 16), "1.2344p+8");
|
||||
}
|
||||
|
||||
function test_bigdecimal()
|
||||
{
|
||||
assert(1m === 1m);
|
||||
assert(1m !== 2m);
|
||||
test_less(1m, 2m);
|
||||
test_eq(2m, 2m);
|
||||
|
||||
test_less(1, 2m);
|
||||
test_eq(2, 2m);
|
||||
|
||||
test_less(1.1, 2m);
|
||||
test_eq(Math.sqrt(4), 2m);
|
||||
|
||||
test_less(2n, 3m);
|
||||
test_eq(3n, 3m);
|
||||
|
||||
assert(BigDecimal("1234.1") === 1234.1m);
|
||||
assert(BigDecimal(" 1234.1") === 1234.1m);
|
||||
assert(BigDecimal(" 1234.1 ") === 1234.1m);
|
||||
|
||||
assert(BigDecimal(0.1) === 0.1m);
|
||||
assert(BigDecimal(123) === 123m);
|
||||
assert(BigDecimal(true) === 1m);
|
||||
|
||||
assert(123m + 1m === 124m);
|
||||
assert(123m - 1m === 122m);
|
||||
|
||||
assert(3.2m * 3m === 9.6m);
|
||||
assert(10m / 2m === 5m);
|
||||
assertThrows(RangeError, () => { 10m / 3m } );
|
||||
|
||||
assert(10m % 3m === 1m);
|
||||
assert(-10m % 3m === -1m);
|
||||
|
||||
assert(1234.5m ** 3m === 1881365963.625m);
|
||||
assertThrows(RangeError, () => { 2m ** 3.1m } );
|
||||
assertThrows(RangeError, () => { 2m ** -3m } );
|
||||
|
||||
assert(BigDecimal.sqrt(2m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumSignificantDigits: 4 }) === 1.414m);
|
||||
assert(BigDecimal.sqrt(101m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 3 }) === 10.050m);
|
||||
assert(BigDecimal.sqrt(0.002m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 3 }) === 0.045m);
|
||||
|
||||
assert(BigDecimal.round(3.14159m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 3 }) === 3.142m);
|
||||
|
||||
assert(BigDecimal.add(3.14159m, 0.31212m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 2 }) === 3.45m);
|
||||
assert(BigDecimal.sub(3.14159m, 0.31212m,
|
||||
{ roundingMode: "down",
|
||||
maximumFractionDigits: 2 }) === 2.82m);
|
||||
assert(BigDecimal.mul(3.14159m, 0.31212m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 3 }) === 0.981m);
|
||||
assert(BigDecimal.mod(3.14159m, 0.31211m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 4 }) === 0.0205m);
|
||||
assert(BigDecimal.div(20m, 3m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumSignificantDigits: 3 }) === 6.67m);
|
||||
assert(BigDecimal.div(20m, 3m,
|
||||
{ roundingMode: "half-even",
|
||||
maximumFractionDigits: 50 }) ===
|
||||
6.66666666666666666666666666666666666666666666666667m);
|
||||
|
||||
/* string conversion */
|
||||
assert((1234.125m).toString(), "1234.125");
|
||||
assert((1234.125m).toFixed(2), "1234.13");
|
||||
assert((1234.125m).toFixed(2, "down"), "1234.12");
|
||||
assert((1234.125m).toExponential(), "1.234125e+3");
|
||||
assert((1234.125m).toExponential(5), "1.23413e+3");
|
||||
assert((1234.125m).toExponential(5, "down"), "1.23412e+3");
|
||||
assert((1234.125m).toPrecision(6), "1234.13");
|
||||
assert((1234.125m).toPrecision(6, "down"), "1234.12");
|
||||
assert((-1234.125m).toPrecision(6, "floor"), "-1234.13");
|
||||
}
|
||||
|
||||
test_bigint1();
|
||||
test_bigint2();
|
||||
test_bigint_ext();
|
||||
test_bigfloat();
|
||||
test_bigdecimal();
|
||||
|
|
|
@ -1,19 +1,51 @@
|
|||
"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 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)
|
||||
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 + "|" +
|
||||
if (typeof actual === typeof expected) {
|
||||
if (actual === expected) {
|
||||
if (actual !== 0 || (1 / actual) === (1 / expected))
|
||||
return;
|
||||
}
|
||||
if (typeof actual === 'number') {
|
||||
if (isNaN(actual) && isNaN(expected))
|
||||
return true;
|
||||
}
|
||||
if (typeof actual === 'object') {
|
||||
if (actual !== null && expected !== null
|
||||
&& actual.constructor === expected.constructor
|
||||
&& actual.toString() === expected.toString())
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Should output the source file and line number and extract
|
||||
// the expression from the assert call
|
||||
throw_error("assertion failed: got " +
|
||||
get_full_type(actual) + ":|" + actual + "|, expected " +
|
||||
get_full_type(expected) + ":|" + expected + "|" +
|
||||
(message ? " (" + message + ")" : ""));
|
||||
}
|
||||
|
||||
|
@ -25,11 +57,16 @@ function assert_throws(expected_error, func)
|
|||
} catch(e) {
|
||||
err = true;
|
||||
if (!(e instanceof expected_error)) {
|
||||
throw Error("unexpected exception type");
|
||||
// Should output the source file and line number and extract
|
||||
// the expression from the assert_throws() call
|
||||
throw_error("unexpected exception type");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!err) {
|
||||
throw Error("expected exception");
|
||||
// Should output the source file and line number and extract
|
||||
// the expression from the assert_throws() call
|
||||
throw_error("expected exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,10 +348,14 @@ function test_math()
|
|||
assert(Math.floor(a), 1);
|
||||
assert(Math.ceil(a), 2);
|
||||
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.hypot() == 0);
|
||||
assert(Math.hypot(-2) == 2);
|
||||
assert(Math.hypot(3, 4) == 5);
|
||||
assert(Math.hypot(), 0);
|
||||
assert(Math.hypot(-2), 2);
|
||||
assert(Math.hypot(3, 4), 5);
|
||||
assert(Math.abs(Math.hypot(3, 4, 5) - 7.0710678118654755) <= 1e-15);
|
||||
}
|
||||
|
||||
|
@ -327,6 +368,10 @@ function test_number()
|
|||
assert(+" 123 ", 123);
|
||||
assert(+"0b111", 7);
|
||||
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("Infinity"), Infinity);
|
||||
assert(parseFloat("-Infinity"), -Infinity);
|
||||
|
@ -336,6 +381,11 @@ function test_number()
|
|||
assert(Number.isNaN(Number("-")));
|
||||
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((2.5).toPrecision(1), "3");
|
||||
|
@ -481,26 +531,105 @@ function test_json()
|
|||
|
||||
function test_date()
|
||||
{
|
||||
var d = new Date(1506098258091), a, s;
|
||||
// Date Time String format is YYYY-MM-DDTHH:mm:ss.sssZ
|
||||
// accepted date formats are: YYYY, YYYY-MM and YYYY-MM-DD
|
||||
// accepted time formats are: THH:mm, THH:mm:ss, THH:mm:ss.sss
|
||||
// expanded years are represented with 6 digits prefixed by + or -
|
||||
// -000000 is invalid.
|
||||
// A string containing out-of-bounds or nonconforming elements
|
||||
// is not a valid instance of this format.
|
||||
// Hence the fractional part after . should have 3 digits and how
|
||||
// a different number of digits is handled is implementation defined.
|
||||
assert(Date.parse(""), NaN);
|
||||
assert(Date.parse("2000"), 946684800000);
|
||||
assert(Date.parse("2000-01"), 946684800000);
|
||||
assert(Date.parse("2000-01-01"), 946684800000);
|
||||
//assert(Date.parse("2000-01-01T"), NaN);
|
||||
//assert(Date.parse("2000-01-01T00Z"), NaN);
|
||||
assert(Date.parse("2000-01-01T00:00Z"), 946684800000);
|
||||
assert(Date.parse("2000-01-01T00:00:00Z"), 946684800000);
|
||||
assert(Date.parse("2000-01-01T00:00:00.1Z"), 946684800100);
|
||||
assert(Date.parse("2000-01-01T00:00:00.10Z"), 946684800100);
|
||||
assert(Date.parse("2000-01-01T00:00:00.100Z"), 946684800100);
|
||||
assert(Date.parse("2000-01-01T00:00:00.1000Z"), 946684800100);
|
||||
assert(Date.parse("2000-01-01T00:00:00+00:00"), 946684800000);
|
||||
//assert(Date.parse("2000-01-01T00:00:00+00:30"), 946686600000);
|
||||
var d = new Date("2000T00:00"); // Jan 1st 2000, 0:00:00 local time
|
||||
assert(typeof d === 'object' && d.toString() != 'Invalid Date');
|
||||
assert((new Date('Jan 1 2000')).toISOString(),
|
||||
d.toISOString());
|
||||
assert((new Date('Jan 1 2000 00:00')).toISOString(),
|
||||
d.toISOString());
|
||||
assert((new Date('Jan 1 2000 00:00:00')).toISOString(),
|
||||
d.toISOString());
|
||||
assert((new Date('Jan 1 2000 00:00:00 GMT+0100')).toISOString(),
|
||||
'1999-12-31T23:00:00.000Z');
|
||||
assert((new Date('Jan 1 2000 00:00:00 GMT+0200')).toISOString(),
|
||||
'1999-12-31T22:00:00.000Z');
|
||||
assert((new Date('Sat Jan 1 2000')).toISOString(),
|
||||
d.toISOString());
|
||||
assert((new Date('Sat Jan 1 2000 00:00')).toISOString(),
|
||||
d.toISOString());
|
||||
assert((new Date('Sat Jan 1 2000 00:00:00')).toISOString(),
|
||||
d.toISOString());
|
||||
assert((new Date('Sat Jan 1 2000 00:00:00 GMT+0100')).toISOString(),
|
||||
'1999-12-31T23:00:00.000Z');
|
||||
assert((new Date('Sat Jan 1 2000 00:00:00 GMT+0200')).toISOString(),
|
||||
'1999-12-31T22:00:00.000Z');
|
||||
|
||||
var d = new Date(1506098258091);
|
||||
assert(d.toISOString(), "2017-09-22T16:37:38.091Z");
|
||||
d.setUTCHours(18, 10, 11);
|
||||
assert(d.toISOString(), "2017-09-22T18:10:11.091Z");
|
||||
a = Date.parse(d.toISOString());
|
||||
var a = Date.parse(d.toISOString());
|
||||
assert((new Date(a)).toISOString(), d.toISOString());
|
||||
s = new Date("2020-01-01T01:01:01.1Z").toISOString();
|
||||
assert(s == "2020-01-01T01:01:01.100Z");
|
||||
s = new Date("2020-01-01T01:01:01.12Z").toISOString();
|
||||
assert(s == "2020-01-01T01:01:01.120Z");
|
||||
s = new Date("2020-01-01T01:01:01.123Z").toISOString();
|
||||
assert(s == "2020-01-01T01:01:01.123Z");
|
||||
s = new Date("2020-01-01T01:01:01.1234Z").toISOString();
|
||||
assert(s == "2020-01-01T01:01:01.123Z");
|
||||
s = new Date("2020-01-01T01:01:01.12345Z").toISOString();
|
||||
assert(s == "2020-01-01T01:01:01.123Z");
|
||||
s = new Date("2020-01-01T01:01:01.1235Z").toISOString();
|
||||
assert(s == "2020-01-01T01:01:01.124Z");
|
||||
s = new Date("2020-01-01T01:01:01.9999Z").toISOString();
|
||||
assert(s == "2020-01-01T01:01:02.000Z");
|
||||
|
||||
assert((new Date("2020-01-01T01:01:01.123Z")).toISOString(),
|
||||
"2020-01-01T01:01:01.123Z");
|
||||
/* implementation defined behavior */
|
||||
assert((new Date("2020-01-01T01:01:01.1Z")).toISOString(),
|
||||
"2020-01-01T01:01:01.100Z");
|
||||
assert((new Date("2020-01-01T01:01:01.12Z")).toISOString(),
|
||||
"2020-01-01T01:01:01.120Z");
|
||||
assert((new Date("2020-01-01T01:01:01.1234Z")).toISOString(),
|
||||
"2020-01-01T01:01:01.123Z");
|
||||
assert((new Date("2020-01-01T01:01:01.12345Z")).toISOString(),
|
||||
"2020-01-01T01:01:01.123Z");
|
||||
assert((new Date("2020-01-01T01:01:01.1235Z")).toISOString(),
|
||||
"2020-01-01T01:01:01.123Z");
|
||||
assert((new Date("2020-01-01T01:01:01.9999Z")).toISOString(),
|
||||
"2020-01-01T01:01:01.999Z");
|
||||
|
||||
assert(Date.UTC(2017), 1483228800000);
|
||||
assert(Date.UTC(2017, 9), 1506816000000);
|
||||
assert(Date.UTC(2017, 9, 22), 1508630400000);
|
||||
assert(Date.UTC(2017, 9, 22, 18), 1508695200000);
|
||||
assert(Date.UTC(2017, 9, 22, 18, 10), 1508695800000);
|
||||
assert(Date.UTC(2017, 9, 22, 18, 10, 11), 1508695811000);
|
||||
assert(Date.UTC(2017, 9, 22, 18, 10, 11, 91), 1508695811091);
|
||||
|
||||
assert(Date.UTC(NaN), NaN);
|
||||
assert(Date.UTC(2017, NaN), NaN);
|
||||
assert(Date.UTC(2017, 9, NaN), NaN);
|
||||
assert(Date.UTC(2017, 9, 22, NaN), NaN);
|
||||
assert(Date.UTC(2017, 9, 22, 18, NaN), NaN);
|
||||
assert(Date.UTC(2017, 9, 22, 18, 10, NaN), NaN);
|
||||
assert(Date.UTC(2017, 9, 22, 18, 10, 11, NaN), NaN);
|
||||
assert(Date.UTC(2017, 9, 22, 18, 10, 11, 91, NaN), 1508695811091);
|
||||
|
||||
// TODO: Fix rounding errors on Windows/Cygwin.
|
||||
if (!(typeof os !== 'undefined' && ['win32', 'cygwin'].includes(os.platform))) {
|
||||
// from test262/test/built-ins/Date/UTC/fp-evaluation-order.js
|
||||
assert(Date.UTC(1970, 0, 1, 80063993375, 29, 1, -288230376151711740), 29312,
|
||||
'order of operations / precision in MakeTime');
|
||||
assert(Date.UTC(1970, 0, 213503982336, 0, 0, 0, -18446744073709552000), 34447360,
|
||||
'precision in MakeDate');
|
||||
}
|
||||
//assert(Date.UTC(2017 - 1e9, 9 + 12e9), 1506816000000); // node fails this
|
||||
assert(Date.UTC(2017, 9, 22 - 1e10, 18 + 24e10), 1508695200000);
|
||||
assert(Date.UTC(2017, 9, 22, 18 - 1e10, 10 + 60e10), 1508695800000);
|
||||
assert(Date.UTC(2017, 9, 22, 18, 10 - 1e10, 11 + 60e10), 1508695811000);
|
||||
assert(Date.UTC(2017, 9, 22, 18, 10, 11 - 1e12, 91 + 1000e12), 1508695811091);
|
||||
}
|
||||
|
||||
function test_regexp()
|
||||
|
@ -587,6 +716,20 @@ function test_map()
|
|||
{
|
||||
var a, i, n, tab, o, v;
|
||||
n = 1000;
|
||||
|
||||
a = new Map();
|
||||
for (var i = 0; i < n; i++) {
|
||||
a.set(i, i);
|
||||
}
|
||||
a.set(-2147483648, 1);
|
||||
assert(a.get(-2147483648), 1);
|
||||
assert(a.get(-2147483647 - 1), 1);
|
||||
assert(a.get(-2147483647.5 - 0.5), 1);
|
||||
|
||||
a.set(1n, 1n);
|
||||
assert(a.get(1n), 1n);
|
||||
assert(a.get(2n**1000n - (2n**1000n - 1n)), 1n);
|
||||
|
||||
a = new Map();
|
||||
tab = [];
|
||||
for(i = 0; i < n; i++) {
|
||||
|
|
|
@ -335,6 +335,13 @@ function test_class()
|
|||
assert(S.x === 42);
|
||||
assert(S.y === 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()
|
||||
|
@ -362,8 +369,9 @@ function test_template_skip()
|
|||
function test_object_literal()
|
||||
{
|
||||
var x = 0, get = 1, set = 2; async = 3;
|
||||
a = { get: 2, set: 3, async: 4 };
|
||||
assert(JSON.stringify(a), '{"get":2,"set":3,"async":4}');
|
||||
a = { get: 2, set: 3, async: 4, get a(){ return this.get} };
|
||||
assert(JSON.stringify(a), '{"get":2,"set":3,"async":4,"a":2}');
|
||||
assert(a.a === 2);
|
||||
|
||||
a = { x, get, set, async };
|
||||
assert(JSON.stringify(a), '{"x":0,"get":1,"set":2,"async":3}');
|
||||
|
@ -420,8 +428,12 @@ function test_argument_scope()
|
|||
var f;
|
||||
var c = "global";
|
||||
|
||||
f = function(a = eval("var arguments")) {};
|
||||
assert_throws(SyntaxError, f);
|
||||
(function() {
|
||||
"use strict";
|
||||
// XXX: node only throws in strict mode
|
||||
f = function(a = eval("var arguments")) {};
|
||||
assert_throws(SyntaxError, f);
|
||||
})();
|
||||
|
||||
f = function(a = eval("1"), b = arguments[0]) { return b; };
|
||||
assert(f(12), 12);
|
||||
|
@ -558,6 +570,15 @@ function test_parse_semicolon()
|
|||
}
|
||||
}
|
||||
|
||||
function test_parse_arrow_function()
|
||||
{
|
||||
assert(typeof eval("() => {}\n() => {}"), "function");
|
||||
assert(eval("() => {}\n+1"), 1);
|
||||
assert(typeof eval("x => {}\n() => {}"), "function");
|
||||
assert(typeof eval("async () => {}\n() => {}"), "function");
|
||||
assert(typeof eval("async x => {}\n() => {}"), "function");
|
||||
}
|
||||
|
||||
/* optional chaining tests not present in test262 */
|
||||
function test_optional_chaining()
|
||||
{
|
||||
|
@ -604,3 +625,4 @@ test_argument_scope();
|
|||
test_function_expr_name();
|
||||
test_parse_semicolon();
|
||||
test_optional_chaining();
|
||||
test_parse_arrow_function();
|
||||
|
|
|
@ -144,7 +144,9 @@ function test_os()
|
|||
{
|
||||
var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path;
|
||||
|
||||
assert(os.isatty(0));
|
||||
const stdinIsTTY = !os.exec(["/bin/sh", "-c", "test -t 0"], { usePath: false });
|
||||
|
||||
assert(os.isatty(0), stdinIsTTY, `isatty(STDIN)`);
|
||||
|
||||
fdir = "test_tmp_dir";
|
||||
fname = "tmp_file.txt";
|
||||
|
@ -253,10 +255,11 @@ function test_os_exec()
|
|||
|
||||
pid = os.exec(["cat"], { block: false } );
|
||||
assert(pid >= 0);
|
||||
os.kill(pid, os.SIGQUIT);
|
||||
os.kill(pid, os.SIGTERM);
|
||||
[ret, status] = os.waitpid(pid, 0);
|
||||
assert(ret, pid);
|
||||
assert(status & 0x7f, os.SIGQUIT);
|
||||
assert(status !== 0, true, `expect nonzero exit code (got ${status})`);
|
||||
assert(status & 0x7f, os.SIGTERM);
|
||||
}
|
||||
|
||||
function test_timer()
|
||||
|
|
178
unicode_gen.c
178
unicode_gen.c
|
@ -33,6 +33,11 @@
|
|||
|
||||
#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 USE_TEST
|
||||
/* profile tests */
|
||||
|
@ -268,7 +273,7 @@ int find_name(const char **tab, int tab_len, const char *name)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int get_prop(uint32_t c, int prop_idx)
|
||||
static BOOL get_prop(uint32_t c, int prop_idx)
|
||||
{
|
||||
return (unicode_db[c].prop_bitmap_tab[prop_idx >> 5] >> (prop_idx & 0x1f)) & 1;
|
||||
}
|
||||
|
@ -1328,7 +1333,9 @@ void dump_case_conv_table(FILE *f)
|
|||
uint32_t v;
|
||||
const TableEntry *te;
|
||||
|
||||
fprintf(f, "static const uint32_t case_conv_table1[%u] = {", conv_table_len);
|
||||
total_tables++;
|
||||
total_table_bytes += conv_table_len * sizeof(uint32_t);
|
||||
fprintf(f, "static const uint32_t case_conv_table1[%d] = {", conv_table_len);
|
||||
for(i = 0; i < conv_table_len; i++) {
|
||||
if (i % 4 == 0)
|
||||
fprintf(f, "\n ");
|
||||
|
@ -1341,7 +1348,9 @@ void dump_case_conv_table(FILE *f)
|
|||
}
|
||||
fprintf(f, "\n};\n\n");
|
||||
|
||||
fprintf(f, "static const uint8_t case_conv_table2[%u] = {", conv_table_len);
|
||||
total_tables++;
|
||||
total_table_bytes += conv_table_len;
|
||||
fprintf(f, "static const uint8_t case_conv_table2[%d] = {", conv_table_len);
|
||||
for(i = 0; i < conv_table_len; i++) {
|
||||
if (i % 8 == 0)
|
||||
fprintf(f, "\n ");
|
||||
|
@ -1350,7 +1359,9 @@ void dump_case_conv_table(FILE *f)
|
|||
}
|
||||
fprintf(f, "\n};\n\n");
|
||||
|
||||
fprintf(f, "static const uint16_t case_conv_ext[%u] = {", ext_data_len);
|
||||
total_tables++;
|
||||
total_table_bytes += ext_data_len * sizeof(uint16_t);
|
||||
fprintf(f, "static const uint16_t case_conv_ext[%d] = {", ext_data_len);
|
||||
for(i = 0; i < ext_data_len; i++) {
|
||||
if (i % 8 == 0)
|
||||
fprintf(f, "\n ");
|
||||
|
@ -1470,6 +1481,9 @@ void compute_internal_props(void)
|
|||
void dump_byte_table(FILE *f, const char *cname, const uint8_t *tab, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
total_tables++;
|
||||
total_table_bytes += len;
|
||||
fprintf(f, "static const uint8_t %s[%d] = {", cname, len);
|
||||
for(i = 0; i < len; i++) {
|
||||
if (i % 8 == 0)
|
||||
|
@ -1479,9 +1493,26 @@ void dump_byte_table(FILE *f, const char *cname, const uint8_t *tab, int len)
|
|||
fprintf(f, "\n};\n\n");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
void build_prop_table(FILE *f, int prop_index, BOOL add_index)
|
||||
void build_prop_table(FILE *f, const char *name, int prop_index, BOOL add_index)
|
||||
{
|
||||
int i, j, n, v, offset, code;
|
||||
DynBuf dbuf_s, *dbuf = &dbuf_s;
|
||||
|
@ -1533,6 +1564,14 @@ void build_prop_table(FILE *f, int prop_index, BOOL add_index)
|
|||
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];
|
||||
code += v + 1;
|
||||
bit ^= 1;
|
||||
|
@ -1573,7 +1612,7 @@ void build_prop_table(FILE *f, int prop_index, BOOL add_index)
|
|||
dump_byte_table(f, cname, dbuf->buf, dbuf->size);
|
||||
if (add_index) {
|
||||
snprintf(cname, sizeof(cname), "unicode_prop_%s_index", unicode_prop_name[prop_index]);
|
||||
dump_byte_table(f, cname, dbuf2->buf, dbuf2->size);
|
||||
dump_index_table(f, cname, dbuf2->buf, dbuf2->size);
|
||||
}
|
||||
|
||||
dbuf_free(dbuf);
|
||||
|
@ -1583,10 +1622,10 @@ void build_prop_table(FILE *f, int prop_index, BOOL add_index)
|
|||
|
||||
void build_flags_tables(FILE *f)
|
||||
{
|
||||
build_prop_table(f, PROP_Cased1, TRUE);
|
||||
build_prop_table(f, PROP_Case_Ignorable, TRUE);
|
||||
build_prop_table(f, PROP_ID_Start, TRUE);
|
||||
build_prop_table(f, PROP_ID_Continue1, TRUE);
|
||||
build_prop_table(f, "Cased1", PROP_Cased1, TRUE);
|
||||
build_prop_table(f, "Case_Ignorable", PROP_Case_Ignorable, TRUE);
|
||||
build_prop_table(f, "ID_Start", PROP_ID_Start, TRUE);
|
||||
build_prop_table(f, "ID_Continue1", PROP_ID_Continue1, TRUE);
|
||||
}
|
||||
|
||||
void dump_name_table(FILE *f, const char *cname, const char **tab_name, int len,
|
||||
|
@ -1621,7 +1660,9 @@ void build_general_category_table(FILE *f)
|
|||
{
|
||||
int i, v, j, n, n1;
|
||||
DynBuf dbuf_s, *dbuf = &dbuf_s;
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
int cw_count, cw_len_count[4], cw_start;
|
||||
#endif
|
||||
|
||||
fprintf(f, "typedef enum {\n");
|
||||
for(i = 0; i < GCAT_COUNT; i++)
|
||||
|
@ -1635,9 +1676,11 @@ void build_general_category_table(FILE *f)
|
|||
|
||||
|
||||
dbuf_init(dbuf);
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
cw_count = 0;
|
||||
for(i = 0; i < 4; i++)
|
||||
cw_len_count[i] = 0;
|
||||
#endif
|
||||
for(i = 0; i <= CHARCODE_MAX;) {
|
||||
v = unicode_db[i].general_category;
|
||||
j = i + 1;
|
||||
|
@ -1656,9 +1699,11 @@ void build_general_category_table(FILE *f)
|
|||
}
|
||||
}
|
||||
// printf("%05x %05x %d\n", i, n, v);
|
||||
cw_count++;
|
||||
n--;
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
cw_count++;
|
||||
cw_start = dbuf->size;
|
||||
#endif
|
||||
if (n < 7) {
|
||||
dbuf_putc(dbuf, (n << 5) | v);
|
||||
} else if (n < 7 + 128) {
|
||||
|
@ -1680,12 +1725,13 @@ void build_general_category_table(FILE *f)
|
|||
dbuf_putc(dbuf, n1 >> 8);
|
||||
dbuf_putc(dbuf, n1);
|
||||
}
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
cw_len_count[dbuf->size - cw_start - 1]++;
|
||||
#endif
|
||||
i += n + 1;
|
||||
}
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
printf("general category: %d entries [",
|
||||
cw_count);
|
||||
printf("general category: %d entries [", cw_count);
|
||||
for(i = 0; i < 4; i++)
|
||||
printf(" %d", cw_len_count[i]);
|
||||
printf(" ], length=%d bytes\n", (int)dbuf->size);
|
||||
|
@ -1700,7 +1746,9 @@ void build_script_table(FILE *f)
|
|||
{
|
||||
int i, v, j, n, n1, type;
|
||||
DynBuf dbuf_s, *dbuf = &dbuf_s;
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
int cw_count, cw_len_count[4], cw_start;
|
||||
#endif
|
||||
|
||||
fprintf(f, "typedef enum {\n");
|
||||
for(i = 0; i < SCRIPT_COUNT; i++)
|
||||
|
@ -1714,9 +1762,11 @@ void build_script_table(FILE *f)
|
|||
unicode_script_short_name + i);
|
||||
|
||||
dbuf_init(dbuf);
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
cw_count = 0;
|
||||
for(i = 0; i < 4; i++)
|
||||
cw_len_count[i] = 0;
|
||||
#endif
|
||||
for(i = 0; i <= CHARCODE_MAX;) {
|
||||
v = unicode_db[i].script;
|
||||
j = i + 1;
|
||||
|
@ -1726,9 +1776,11 @@ void build_script_table(FILE *f)
|
|||
if (v == 0 && j == (CHARCODE_MAX + 1))
|
||||
break;
|
||||
// printf("%05x %05x %d\n", i, n, v);
|
||||
cw_count++;
|
||||
n--;
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
cw_count++;
|
||||
cw_start = dbuf->size;
|
||||
#endif
|
||||
if (v == 0)
|
||||
type = 0;
|
||||
else
|
||||
|
@ -1750,12 +1802,13 @@ void build_script_table(FILE *f)
|
|||
if (type != 0)
|
||||
dbuf_putc(dbuf, v);
|
||||
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
cw_len_count[dbuf->size - cw_start - 1]++;
|
||||
#endif
|
||||
i += n + 1;
|
||||
}
|
||||
#if defined(DUMP_TABLE_SIZE)
|
||||
printf("script: %d entries [",
|
||||
cw_count);
|
||||
#ifdef DUMP_TABLE_SIZE
|
||||
printf("script: %d entries [", cw_count);
|
||||
for(i = 0; i < 4; i++)
|
||||
printf(" %d", cw_len_count[i]);
|
||||
printf(" ], length=%d bytes\n", (int)dbuf->size);
|
||||
|
@ -1770,10 +1823,11 @@ void build_script_ext_table(FILE *f)
|
|||
{
|
||||
int i, j, n, n1, script_ext_len;
|
||||
DynBuf dbuf_s, *dbuf = &dbuf_s;
|
||||
int cw_count;
|
||||
#if defined(DUMP_TABLE_SIZE)
|
||||
int cw_count = 0;
|
||||
#endif
|
||||
|
||||
dbuf_init(dbuf);
|
||||
cw_count = 0;
|
||||
for(i = 0; i <= CHARCODE_MAX;) {
|
||||
script_ext_len = unicode_db[i].script_ext_len;
|
||||
j = i + 1;
|
||||
|
@ -1784,7 +1838,9 @@ void build_script_ext_table(FILE *f)
|
|||
j++;
|
||||
}
|
||||
n = j - i;
|
||||
#if defined(DUMP_TABLE_SIZE)
|
||||
cw_count++;
|
||||
#endif
|
||||
n--;
|
||||
if (n < 128) {
|
||||
dbuf_putc(dbuf, n);
|
||||
|
@ -1806,8 +1862,7 @@ void build_script_ext_table(FILE *f)
|
|||
i += n + 1;
|
||||
}
|
||||
#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);
|
||||
#endif
|
||||
|
||||
|
@ -1829,7 +1884,7 @@ void build_prop_list_table(FILE *f)
|
|||
i == PROP_ID_Continue1) {
|
||||
/* already generated */
|
||||
} else {
|
||||
build_prop_table(f, i, FALSE);
|
||||
build_prop_table(f, unicode_prop_name[i], i, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1926,7 +1981,7 @@ void check_flags(void)
|
|||
BOOL flag_ref, flag;
|
||||
for(c = 0; c <= CHARCODE_MAX; c++) {
|
||||
flag_ref = get_prop(c, PROP_Cased);
|
||||
flag = lre_is_cased(c);
|
||||
flag = !!lre_is_cased(c);
|
||||
if (flag != flag_ref) {
|
||||
printf("ERROR: c=%05x cased=%d ref=%d\n",
|
||||
c, flag, flag_ref);
|
||||
|
@ -1934,7 +1989,7 @@ void check_flags(void)
|
|||
}
|
||||
|
||||
flag_ref = get_prop(c, PROP_Case_Ignorable);
|
||||
flag = lre_is_case_ignorable(c);
|
||||
flag = !!lre_is_case_ignorable(c);
|
||||
if (flag != flag_ref) {
|
||||
printf("ERROR: c=%05x case_ignorable=%d ref=%d\n",
|
||||
c, flag, flag_ref);
|
||||
|
@ -1942,7 +1997,7 @@ void check_flags(void)
|
|||
}
|
||||
|
||||
flag_ref = get_prop(c, PROP_ID_Start);
|
||||
flag = lre_is_id_start(c);
|
||||
flag = !!lre_is_id_start(c);
|
||||
if (flag != flag_ref) {
|
||||
printf("ERROR: c=%05x id_start=%d ref=%d\n",
|
||||
c, flag, flag_ref);
|
||||
|
@ -1950,7 +2005,7 @@ void check_flags(void)
|
|||
}
|
||||
|
||||
flag_ref = get_prop(c, PROP_ID_Continue);
|
||||
flag = lre_is_id_continue(c);
|
||||
flag = !!lre_is_id_continue(c);
|
||||
if (flag != flag_ref) {
|
||||
printf("ERROR: c=%05x id_cont=%d ref=%d\n",
|
||||
c, flag, flag_ref);
|
||||
|
@ -1964,7 +2019,7 @@ void check_flags(void)
|
|||
count = 0;
|
||||
for(c = 0x20; c <= 0xffff; c++) {
|
||||
flag_ref = get_prop(c, PROP_ID_Start);
|
||||
flag = lre_is_id_start(c);
|
||||
flag = !!lre_is_id_start(c);
|
||||
assert(flag == flag_ref);
|
||||
count++;
|
||||
}
|
||||
|
@ -1981,17 +2036,23 @@ void check_flags(void)
|
|||
|
||||
void build_cc_table(FILE *f)
|
||||
{
|
||||
int i, cc, n, cc_table_len, type, n1;
|
||||
// Compress combining class table
|
||||
// see: https://www.unicode.org/reports/tr44/#Canonical_Combining_Class_Values
|
||||
int i, cc, n, type, n1, block_end_pos;
|
||||
DynBuf dbuf_s, *dbuf = &dbuf_s;
|
||||
DynBuf dbuf1_s, *dbuf1 = &dbuf1_s;
|
||||
int cw_len_tab[3], cw_start, block_end_pos;
|
||||
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
|
||||
int cw_len_tab[3], cw_start, cc_table_len;
|
||||
#endif
|
||||
uint32_t v;
|
||||
|
||||
dbuf_init(dbuf);
|
||||
dbuf_init(dbuf1);
|
||||
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
|
||||
cc_table_len = 0;
|
||||
for(i = 0; i < countof(cw_len_tab); i++)
|
||||
cw_len_tab[i] = 0;
|
||||
#endif
|
||||
block_end_pos = CC_BLOCK_LEN;
|
||||
for(i = 0; i <= CHARCODE_MAX;) {
|
||||
cc = unicode_db[i].combining_class;
|
||||
|
@ -2032,7 +2093,16 @@ void build_cc_table(FILE *f)
|
|||
dbuf_putc(dbuf1, v >> 16);
|
||||
block_end_pos += CC_BLOCK_LEN;
|
||||
}
|
||||
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_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) {
|
||||
dbuf_putc(dbuf, n1 | (type << 6));
|
||||
} else if (n1 < 48 + (1 << 11)) {
|
||||
|
@ -2046,10 +2116,12 @@ void build_cc_table(FILE *f)
|
|||
dbuf_putc(dbuf, n1 >> 8);
|
||||
dbuf_putc(dbuf, n1);
|
||||
}
|
||||
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
|
||||
cw_len_tab[dbuf->size - cw_start - 1]++;
|
||||
cc_table_len++;
|
||||
#endif
|
||||
if (type == 0 || type == 1)
|
||||
dbuf_putc(dbuf, cc);
|
||||
cc_table_len++;
|
||||
i += n;
|
||||
}
|
||||
|
||||
|
@ -2060,7 +2132,7 @@ void build_cc_table(FILE *f)
|
|||
dbuf_putc(dbuf1, v >> 16);
|
||||
|
||||
dump_byte_table(f, "unicode_cc_table", dbuf->buf, dbuf->size);
|
||||
dump_byte_table(f, "unicode_cc_index", dbuf1->buf, dbuf1->size);
|
||||
dump_index_table(f, "unicode_cc_index", dbuf1->buf, dbuf1->size);
|
||||
|
||||
#if defined(DUMP_CC_TABLE) || defined(DUMP_TABLE_SIZE)
|
||||
printf("CC table: size=%d (%d entries) [",
|
||||
|
@ -2741,8 +2813,9 @@ void build_decompose_table(FILE *f)
|
|||
}
|
||||
#endif
|
||||
|
||||
fprintf(f, "static const uint32_t unicode_decomp_table1[%u] = {",
|
||||
array_len);
|
||||
total_tables++;
|
||||
total_table_bytes += array_len * sizeof(uint32_t);
|
||||
fprintf(f, "static const uint32_t unicode_decomp_table1[%d] = {", array_len);
|
||||
count = 0;
|
||||
for(i = 0; i <= code_max; i++) {
|
||||
de = &tab_de[i];
|
||||
|
@ -2760,8 +2833,9 @@ void build_decompose_table(FILE *f)
|
|||
}
|
||||
fprintf(f, "\n};\n\n");
|
||||
|
||||
fprintf(f, "static const uint16_t unicode_decomp_table2[%u] = {",
|
||||
array_len);
|
||||
total_tables++;
|
||||
total_table_bytes += array_len * sizeof(uint16_t);
|
||||
fprintf(f, "static const uint16_t unicode_decomp_table2[%d] = {", array_len);
|
||||
count = 0;
|
||||
for(i = 0; i <= code_max; i++) {
|
||||
de = &tab_de[i];
|
||||
|
@ -2774,8 +2848,9 @@ void build_decompose_table(FILE *f)
|
|||
}
|
||||
fprintf(f, "\n};\n\n");
|
||||
|
||||
fprintf(f, "static const uint8_t unicode_decomp_data[%u] = {",
|
||||
data_len);
|
||||
total_tables++;
|
||||
total_table_bytes += data_len;
|
||||
fprintf(f, "static const uint8_t unicode_decomp_data[%d] = {", data_len);
|
||||
for(i = 0; i < data_len; i++) {
|
||||
if (i % 8 == 0)
|
||||
fprintf(f, "\n ");
|
||||
|
@ -2866,8 +2941,9 @@ void build_compose_table(FILE *f, const DecompEntry *tab_de)
|
|||
}
|
||||
#endif
|
||||
|
||||
fprintf(f, "static const uint16_t unicode_comp_table[%u] = {",
|
||||
tab_ce_len);
|
||||
total_tables++;
|
||||
total_table_bytes += tab_ce_len * sizeof(uint16_t);
|
||||
fprintf(f, "static const uint16_t unicode_comp_table[%u] = {", tab_ce_len);
|
||||
for(i = 0; i < tab_ce_len; i++) {
|
||||
if (i % 8 == 0)
|
||||
fprintf(f, "\n ");
|
||||
|
@ -3042,22 +3118,24 @@ void normalization_test(const char *filename)
|
|||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *unicode_db_path, *outfilename;
|
||||
char filename[1024];
|
||||
int arg = 1;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("usage: %s unicode_db_path [output_file]\n"
|
||||
"\n"
|
||||
"If no output_file is given, a self test is done using the current unicode library\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
if (arg >= argc || (!strcmp(argv[arg], "-h") || !strcmp(argv[arg], "--help"))) {
|
||||
printf("usage: %s PATH [OUTPUT]\n"
|
||||
" PATH path to the Unicode database directory\n"
|
||||
" OUTPUT name of the output file. If omitted, a self test is performed\n"
|
||||
" using the files from the Unicode library\n"
|
||||
, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
unicode_db_path = argv[1];
|
||||
unicode_db_path = argv[arg++];
|
||||
outfilename = NULL;
|
||||
if (argc >= 3)
|
||||
outfilename = argv[2];
|
||||
if (arg < argc)
|
||||
outfilename = argv[arg++];
|
||||
|
||||
unicode_db = mallocz(sizeof(unicode_db[0]) * (CHARCODE_MAX + 1));
|
||||
|
||||
|
@ -3139,6 +3217,8 @@ int main(int argc, char **argv)
|
|||
build_script_ext_table(fo);
|
||||
build_prop_list_table(fo);
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue