@ -0,0 +1,463 @@ | |||
# | |||
# QuickJS Javascript Engine | |||
# | |||
# Copyright (c) 2017-2019 Fabrice Bellard | |||
# Copyright (c) 2017-2019 Charlie Gordon | |||
# | |||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||
# of this software and associated documentation files (the "Software"), to deal | |||
# in the Software without restriction, including without limitation the rights | |||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
# copies of the Software, and to permit persons to whom the Software is | |||
# furnished to do so, subject to the following conditions: | |||
# | |||
# The above copyright notice and this permission notice shall be included in | |||
# all copies or substantial portions of the Software. | |||
# | |||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
# THE SOFTWARE. | |||
ifeq ($(shell uname -s),Darwin) | |||
CONFIG_DARWIN=y | |||
endif | |||
# Windows cross compilation from Linux | |||
#CONFIG_WIN32=y | |||
# use link time optimization (smaller and faster executables but slower build) | |||
CONFIG_LTO=y | |||
# consider warnings as errors (for development) | |||
#CONFIG_WERROR=y | |||
ifndef CONFIG_WIN32 | |||
# force 32 bit build for some utilities | |||
CONFIG_M32=y | |||
endif | |||
ifdef CONFIG_DARWIN | |||
# use clang instead of gcc | |||
CONFIG_CLANG=y | |||
CONFIG_DEFAULT_AR=y | |||
endif | |||
# installation directory | |||
prefix=/usr/local | |||
# use the gprof profiler | |||
#CONFIG_PROFILE=y | |||
# use address sanitizer | |||
#CONFIG_ASAN=y | |||
OBJDIR=.obj | |||
ifdef CONFIG_WIN32 | |||
CROSS_PREFIX=i686-w64-mingw32- | |||
EXE=.exe | |||
else | |||
CROSS_PREFIX= | |||
EXE= | |||
endif | |||
ifdef CONFIG_CLANG | |||
CC=$(CROSS_PREFIX)clang | |||
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d | |||
CFLAGS += -Wextra | |||
CFLAGS += -Wno-sign-compare | |||
CFLAGS += -Wno-missing-field-initializers | |||
CFLAGS += -Wundef -Wuninitialized | |||
CFLAGS += -Wunused -Wno-unused-parameter | |||
CFLAGS += -Wwrite-strings | |||
CFLAGS += -Wchar-subscripts -funsigned-char | |||
CFLAGS += -MMD -MF $(OBJDIR)/$(@F).d | |||
ifdef CONFIG_DEFAULT_AR | |||
AR=$(CROSS_PREFIX)ar | |||
else | |||
ifdef CONFIG_LTO | |||
AR=$(CROSS_PREFIX)llvm-ar | |||
else | |||
AR=$(CROSS_PREFIX)ar | |||
endif | |||
endif | |||
else | |||
CC=$(CROSS_PREFIX)gcc | |||
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d | |||
CFLAGS += -Wno-array-bounds | |||
ifdef CONFIG_LTO | |||
AR=$(CROSS_PREFIX)gcc-ar | |||
else | |||
AR=$(CROSS_PREFIX)ar | |||
endif | |||
endif | |||
ifdef CONFIG_WERROR | |||
CFLAGS+=-Werror | |||
endif | |||
DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\" | |||
CFLAGS+=$(DEFINES) | |||
CFLAGS_DEBUG=$(CFLAGS) -O0 | |||
CFLAGS_SMALL=$(CFLAGS) -Os | |||
CFLAGS_OPT=$(CFLAGS) -O2 | |||
CFLAGS_NOLTO:=$(CFLAGS_OPT) | |||
LDFLAGS=-g | |||
ifdef CONFIG_LTO | |||
CFLAGS_SMALL+=-flto | |||
CFLAGS_OPT+=-flto | |||
LDFLAGS+=-flto | |||
endif | |||
ifdef CONFIG_PROFILE | |||
CFLAGS+=-p | |||
LDFLAGS+=-p | |||
endif | |||
ifdef CONFIG_ASAN | |||
CFLAGS+=-fsanitize=address | |||
LDFLAGS+=-fsanitize=address | |||
endif | |||
ifdef CONFIG_WIN32 | |||
LDEXPORT= | |||
else | |||
LDEXPORT=-rdynamic | |||
endif | |||
PROGS=qjs$(EXE) qjsbn$(EXE) qjsc qjsbnc run-test262 run-test262-bn | |||
ifndef CONFIG_WIN32 | |||
PROGS+=qjscalc | |||
endif | |||
ifdef CONFIG_M32 | |||
PROGS+=qjs32 qjs32_s qjsbn32 | |||
endif | |||
PROGS+=libquickjs.a libquickjs.bn.a | |||
ifdef CONFIG_LTO | |||
PROGS+=libquickjs.lto.a libquickjs.bn.lto.a | |||
endif | |||
# examples | |||
ifdef CONFIG_ASAN | |||
PROGS+= | |||
else ifdef CONFIG_WIN32 | |||
PROGS+= | |||
else | |||
PROGS+=examples/hello examples/hello_module examples/c_module | |||
endif | |||
all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS) | |||
QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o | |||
QJSBN_LIB_OBJS=$(patsubst %.o, %.bn.o, $(QJS_LIB_OBJS)) $(OBJDIR)/libbf.bn.o | |||
QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS) | |||
QJSBN_OBJS=$(OBJDIR)/qjs.bn.o $(OBJDIR)/repl-bn.bn.o $(OBJDIR)/qjscalc.bn.o $(QJSBN_LIB_OBJS) | |||
LIBS=-lm | |||
ifndef CONFIG_WIN32 | |||
LIBS+=-ldl | |||
endif | |||
$(OBJDIR): | |||
mkdir -p $(OBJDIR) | |||
qjs$(EXE): $(QJS_OBJS) | |||
$(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) | |||
qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS)) | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) | |||
qjsc: $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS) | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) | |||
qjsbnc: $(OBJDIR)/qjsc.bn.o $(QJSBN_LIB_OBJS) | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) | |||
QJSC_DEFINES:=-DCONFIG_CC=\"$(CC)\" | |||
ifdef CONFIG_LTO | |||
QJSC_DEFINES+=-DCONFIG_LTO | |||
endif | |||
QJSC_DEFINES+=-DCONFIG_PREFIX=\"$(prefix)\" | |||
$(OBJDIR)/qjsc.o $(OBJDIR)/qjsc.bn.o: CFLAGS+=$(QJSC_DEFINES) | |||
qjs32: $(patsubst %.o, %.m32.o, $(QJS_OBJS)) | |||
$(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) | |||
qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS)) | |||
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) | |||
@size $@ | |||
qjsbn$(EXE): $(QJSBN_OBJS) | |||
$(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) | |||
qjsbn32: $(patsubst %.o, %.m32.o, $(QJSBN_OBJS)) | |||
$(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS) | |||
qjscalc: qjsbn | |||
ln -sf $< $@ | |||
qjsbn-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJSBN_OBJS)) | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) | |||
ifdef CONFIG_LTO | |||
LTOEXT=.lto | |||
else | |||
LTOEXT= | |||
endif | |||
libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS) | |||
$(AR) rcs $@ $^ | |||
libquickjs.bn$(LTOEXT).a: $(QJSBN_LIB_OBJS) | |||
$(AR) rcs $@ $^ | |||
ifdef CONFIG_LTO | |||
libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS)) | |||
$(AR) rcs $@ $^ | |||
libquickjs.bn.a: $(patsubst %.o, %.nolto.o, $(QJSBN_LIB_OBJS)) | |||
$(AR) rcs $@ $^ | |||
endif # CONFIG_LTO | |||
repl.c: qjsc repl.js | |||
./qjsc -c -o $@ -m repl.js | |||
repl-bn.c: qjsbnc repl.js | |||
./qjsbnc -c -o $@ -m repl.js | |||
qjscalc.c: qjsbnc qjscalc.js | |||
./qjsbnc -c -o $@ qjscalc.js | |||
ifneq ($(wildcard unicode/UnicodeData.txt),) | |||
$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o $(OBJDIR)/libunicode.bn.o \ | |||
$(OBJDIR)/libunicode.nolto.o $(OBJDIR)/libunicode.bn.nolto.o: libunicode-table.h | |||
libunicode-table.h: unicode_gen | |||
./unicode_gen unicode $@ | |||
endif | |||
run-test262: $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS) | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread | |||
run-test262-bn: $(OBJDIR)/run-test262.bn.o $(QJSBN_LIB_OBJS) | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread | |||
run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread | |||
run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)) | |||
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread | |||
run-test262-bn32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.bn.o $(QJSBN_LIB_OBJS)) | |||
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread | |||
# object suffix order: bn, nolto, [m32|m32s] | |||
$(OBJDIR)/%.o: %.c | $(OBJDIR) | |||
$(CC) $(CFLAGS_OPT) -c -o $@ $< | |||
$(OBJDIR)/%.pic.o: %.c | $(OBJDIR) | |||
$(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $< | |||
$(OBJDIR)/%.bn.o: %.c | $(OBJDIR) | |||
$(CC) $(CFLAGS_OPT) -DCONFIG_BIGNUM -c -o $@ $< | |||
$(OBJDIR)/%.nolto.o: %.c | $(OBJDIR) | |||
$(CC) $(CFLAGS_NOLTO) -c -o $@ $< | |||
$(OBJDIR)/%.bn.nolto.o: %.c | $(OBJDIR) | |||
$(CC) $(CFLAGS_NOLTO) -DCONFIG_BIGNUM -c -o $@ $< | |||
$(OBJDIR)/%.m32.o: %.c | $(OBJDIR) | |||
$(CC) -m32 $(CFLAGS_OPT) -c -o $@ $< | |||
$(OBJDIR)/%.m32s.o: %.c | $(OBJDIR) | |||
$(CC) -m32 $(CFLAGS_SMALL) -c -o $@ $< | |||
$(OBJDIR)/%.bn.m32.o: %.c | $(OBJDIR) | |||
$(CC) -m32 $(CFLAGS_OPT) -DCONFIG_BIGNUM -c -o $@ $< | |||
$(OBJDIR)/%.debug.o: %.c | $(OBJDIR) | |||
$(CC) $(CFLAGS_DEBUG) -c -o $@ $< | |||
$(OBJDIR)/%.bn.debug.o: %.c | $(OBJDIR) | |||
$(CC) $(CFLAGS_DEBUG) -DCONFIG_BIGNUM -c -o $@ $< | |||
$(OBJDIR)/%.check.o: %.c | $(OBJDIR) | |||
$(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $< | |||
regexp_test: libregexp.c libunicode.c cutils.c | |||
$(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS) | |||
jscompress: jscompress.c | |||
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ jscompress.c | |||
unicode_gen: unicode_gen.c cutils.c libunicode.c | |||
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ unicode_gen.c cutils.c | |||
clean: | |||
rm -f repl.c repl-bn.c qjscalc.c out.c | |||
rm -f *.a *.so *.o *.d *~ jscompress unicode_gen regexp_test $(PROGS) | |||
rm -f hello.c hello_module.c c_module.c | |||
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug qjsbn-debug | |||
rm -rf run-test262-debug run-test262-32 run-test262-bn32 | |||
install: all | |||
mkdir -p "$(prefix)/bin" | |||
install -m755 -s qjs qjsc qjsbn qjsbnc "$(prefix)/bin" | |||
ln -sf qjsbn "$(prefix)/bin/qjscalc" | |||
mkdir -p "$(prefix)/lib/quickjs" | |||
install -m755 libquickjs.a libquickjs.bn.a "$(prefix)/lib/quickjs" | |||
ifdef CONFIG_LTO | |||
install -m755 libquickjs.lto.a libquickjs.bn.lto.a "$(prefix)/lib/quickjs" | |||
endif | |||
mkdir -p "$(prefix)/include/quickjs" | |||
install -m755 quickjs.h quickjs-libc.h "$(prefix)/include/quickjs" | |||
############################################################################### | |||
# examples | |||
# example of static JS compilation | |||
HELLO_SRCS=examples/hello.js | |||
HELLO_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ | |||
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy | |||
hello.c: qjsc $(HELLO_SRCS) | |||
./qjsc -e $(HELLO_OPTS) -o $@ $(HELLO_SRCS) | |||
ifdef CONFIG_M32 | |||
examples/hello: $(OBJDIR)/hello.m32s.o $(patsubst %.o, %.m32s.o, $(QJS_LIB_OBJS)) | |||
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) | |||
else | |||
examples/hello: $(OBJDIR)/hello.o $(QJS_LIB_OBJS) | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) | |||
endif | |||
# example of static JS compilation with modules | |||
HELLO_MODULE_SRCS=examples/hello_module.js | |||
HELLO_MODULE_OPTS=-fno-string-normalize -fno-map -fno-promise -fno-typedarray \ | |||
-fno-typedarray -fno-regexp -fno-json -fno-eval -fno-proxy -m | |||
examples/hello_module: qjsc libquickjs$(LTOEXT).a $(HELLO_MODULE_SRCS) | |||
./qjsc $(HELLO_MODULE_OPTS) -o $@ $(HELLO_MODULE_SRCS) | |||
# use of an external C module (static compilation) | |||
c_module.c: qjsc examples/c_module.js | |||
./qjsc -e -M examples/fib.so,fib -m -o $@ examples/c_module.js | |||
examples/c_module: $(OBJDIR)/c_module.o $(OBJDIR)/fib.o libquickjs$(LTOEXT).a | |||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) | |||
$(OBJDIR)/fib.o: examples/fib.c | |||
$(CC) $(CFLAGS_OPT) -c -o $@ $< | |||
############################################################################### | |||
# documentation | |||
DOCS=doc/quickjs.pdf doc/quickjs.html doc/jsbignum.pdf doc/jsbignum.html | |||
build_doc: $(DOCS) | |||
clean_doc: | |||
rm -f $(DOCS) | |||
doc/%.pdf: doc/%.texi | |||
texi2pdf --clean -o $@ -q $< | |||
doc/%.html: doc/%.texi | |||
makeinfo --html --no-headers --no-split --number-sections -o $@ $< | |||
############################################################################### | |||
# tests | |||
ifndef CONFIG_DARWIN | |||
test: bjson.so | |||
endif | |||
test: qjs qjsbn | |||
./qjs tests/test_closure.js | |||
./qjs tests/test_op.js | |||
./qjs tests/test_builtin.js | |||
./qjs tests/test_loop.js | |||
./qjs -m tests/test_std.js | |||
ifndef CONFIG_DARWIN | |||
./qjs -m tests/test_bjson.js | |||
endif | |||
./qjsbn tests/test_closure.js | |||
./qjsbn tests/test_op.js | |||
./qjsbn tests/test_builtin.js | |||
./qjsbn tests/test_loop.js | |||
./qjsbn -m tests/test_std.js | |||
./qjsbn --qjscalc tests/test_bignum.js | |||
test-32: qjs32 qjsbn32 | |||
./qjs32 tests/test_closure.js | |||
./qjs32 tests/test_op.js | |||
./qjs32 tests/test_builtin.js | |||
./qjs32 tests/test_loop.js | |||
./qjs32 -m tests/test_std.js | |||
./qjsbn32 tests/test_closure.js | |||
./qjsbn32 tests/test_op.js | |||
./qjsbn32 tests/test_builtin.js | |||
./qjsbn32 tests/test_loop.js | |||
./qjsbn32 -m tests/test_std.js | |||
./qjsbn32 --qjscalc tests/test_bignum.js | |||
stats: qjs qjs32 | |||
./qjs -qd | |||
./qjs32 -qd | |||
microbench: qjs | |||
./qjs tests/microbench.js | |||
microbench-32: qjs32 | |||
./qjs32 tests/microbench.js | |||
# ES5 tests (obsolete) | |||
test2o: run-test262 | |||
time ./run-test262 -m -c test262o.conf | |||
test2o-32: run-test262-32 | |||
time ./run-test262-32 -m -c test262o.conf | |||
test2o-update: run-test262 | |||
./run-test262 -u -c test262o.conf | |||
# Test262 tests | |||
test2-default: run-test262 | |||
time ./run-test262 -m -c test262.conf | |||
test2: run-test262 | |||
time ./run-test262 -m -c test262.conf -a | |||
test2-32: run-test262-32 | |||
time ./run-test262-32 -m -c test262.conf -a | |||
test2-update: run-test262 | |||
./run-test262 -u -c test262.conf -a | |||
test2-check: run-test262 | |||
time ./run-test262 -m -c test262.conf -E -a | |||
# Test262 + BigInt tests | |||
test2bn-default: run-test262-bn | |||
time ./run-test262-bn -m -c test262bn.conf | |||
test2bn: run-test262-bn | |||
time ./run-test262-bn -m -c test262bn.conf -a | |||
test2bn-32: run-test262-bn32 | |||
time ./run-test262-bn32 -m -c test262bn.conf -a | |||
testall: all test microbench test2o test2 test2bn | |||
testall-32: all test-32 microbench-32 test2o-32 test2-32 test2bn-32 | |||
testall-complete: testall testall-32 | |||
bench-v8: qjs qjs32 | |||
make -C tests/bench-v8 | |||
./qjs -d tests/bench-v8/combined.js | |||
bjson.so: $(OBJDIR)/bjson.pic.o | |||
$(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS) | |||
-include $(wildcard $(OBJDIR)/*.d) |
@ -0,0 +1,83 @@ | |||
- 64-bit atoms in 64-bit mode? | |||
- rename CONFIG_ALL_UNICODE, CONFIG_BIGNUM, CONFIG_ATOMICS, CONFIG_CHECK_JSVALUE ? | |||
- unify coding style and naming conventions | |||
- use names from the ECMA spec in library implementation | |||
- modules: if no ".", use a well known module loading path ? | |||
- use JSHoistedDef only for global variables (JSHoistedDef.var_name != JS_ATOM_NULL) | |||
- add index in JSVarDef and is_arg flag to merge args and vars in JSFunctionDef | |||
- replace most JSVarDef flags with var_type enumeration | |||
- use byte code emitters with typed arguments (for clarity) | |||
- use 2 bytecode DynBufs in JSFunctionDef, one for reading, one for writing | |||
and use the same wrappers in all phases | |||
- use more generic method for line numbers in resolve_variables and resolve_labels | |||
- bignum: | |||
- fix div/pow div by zero exception in doc & code in bigint mode | |||
- fix Atomics support | |||
Memory: | |||
- test border cases for max number of atoms, object properties, string length | |||
- add emergency malloc mode for out of memory exceptions. | |||
- test all DynBuf memory errors | |||
- test all js_realloc memory errors | |||
- bignum: handle memory errors | |||
- use memory pools for objects, etc? | |||
- improve JS_ComputeMemoryUsage() with more info | |||
Optimizations: | |||
- use auto-init properties for more global objects | |||
- reuse stack slots for disjoint scopes, if strip | |||
- optimize `for of` iterator for built-in array objects | |||
- add heuristic to avoid some cycles in closures | |||
- small String (0-2 charcodes) with immediate storage | |||
- perform static string concatenation at compile time | |||
- optimize string concatenation with ropes or miniropes? | |||
- add implicit numeric strings for Uint32 numbers? | |||
- optimize `s += a + b`, `s += a.b` and similar simple expressions | |||
- ensure string canonical representation and optimise comparisons and hashes? | |||
- remove JSObject.first_weak_ref, use bit+context based hashed array for weak references | |||
- optimize function storage with length and name accessors? | |||
- property access optimization on the global object, functions, | |||
prototypes and special non extensible objects. | |||
- create object literals with the correct length by backpatching length argument | |||
- remove redundant set_loc_uninitialized/check_uninitialized opcodes | |||
- peephole optim: push_atom_value, to_propkey -> push_atom_value | |||
- peephole optim: put_loc x, get_loc_check x -> set_loc x | |||
- comparative performance benchmark | |||
- use variable name when throwing uninitialized exception if available | |||
- convert slow array to fast array when all properties != length are numeric | |||
- optimize destructuring assignments for global and local variables | |||
- implement some form of tail-call-optimization | |||
- debugger keyword support | |||
- optmize OP_apply | |||
- optimize f(...b) | |||
Extensions: | |||
- support more features in [features] section | |||
- add TC39 stage 3 proposals: String.prototype.matchAll, Symbol.matchAll | |||
- fix preprocessor bug if nested #ifdef in jscompress.c | |||
- add built-in preprocessor in compiler, get rid of jscompress | |||
handle #if, #ifdef, #line, limited support for #define | |||
- limited support for web assembly | |||
- get rid of __loadScript, use more common name | |||
- BSD sockets | |||
- Process or thread control | |||
- use custom printf to avoid C library compatibility issues | |||
- use custom timezone support to avoid C library compatibility issues | |||
REPL: | |||
- strip internal functions from stack trace | |||
- readline: support MS Windows terminal | |||
- readline: handle dynamic terminal resizing | |||
- multiline editing | |||
- debugger | |||
- runtime object and function inspectors | |||
- interactive object browser | |||
- use more generic approach to display evaluation results | |||
- improve directive handling: dispatch, colorize, completion... | |||
- save history | |||
- close all predefined methods in repl.js and jscalc.js | |||
Test262o: 0/11266 errors, 459 excluded | |||
Test262: 0/55855 errors, 504 excluded, 1559 skipped | |||
Test262bn: 0/57176 errors, 724 excluded, 675 skipped | |||
test262 commit: 94b1e80ab3440413df916cd56d29c5a2fa2ac451 |
@ -0,0 +1 @@ | |||
2019-07-09 |
@ -0,0 +1,88 @@ | |||
/* | |||
* QuickJS: binary JSON module (test only) | |||
* | |||
* Copyright (c) 2017-2019 Fabrice Bellard | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to deal | |||
* in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
* copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included in | |||
* all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | |||
*/ | |||
#include "quickjs-libc.h" | |||
#include "cutils.h" | |||
static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val, | |||
int argc, JSValueConst *argv) | |||
{ | |||
uint8_t *buf; | |||
uint64_t pos, len; | |||
JSValue obj; | |||
size_t size; | |||
if (JS_ToIndex(ctx, &pos, argv[1])) | |||
return JS_EXCEPTION; | |||
if (JS_ToIndex(ctx, &len, argv[2])) | |||
return JS_EXCEPTION; | |||
buf = JS_GetArrayBuffer(ctx, &size, argv[0]); | |||
if (!buf) | |||
return JS_EXCEPTION; | |||
if (pos + len > size) | |||
return JS_ThrowRangeError(ctx, "array buffer overflow"); | |||
obj = JS_ReadObject(ctx, buf + pos, len, 0); | |||
return obj; | |||
} | |||
static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val, | |||
int argc, JSValueConst *argv) | |||
{ | |||
size_t len; | |||
uint8_t *buf; | |||
JSValue array; | |||
buf = JS_WriteObject(ctx, &len, argv[0], 0); | |||
if (!buf) | |||
return JS_EXCEPTION; | |||
array = JS_NewArrayBufferCopy(ctx, buf, len); | |||
js_free(ctx, buf); | |||
return array; | |||
} | |||
static const JSCFunctionListEntry js_bjson_funcs[] = { | |||
JS_CFUNC_DEF("read", 3, js_bjson_read ), | |||
JS_CFUNC_DEF("write", 1, js_bjson_write ), | |||
}; | |||
static int js_bjson_init(JSContext *ctx, JSModuleDef *m) | |||
{ | |||
return JS_SetModuleExportList(ctx, m, js_bjson_funcs, | |||
countof(js_bjson_funcs)); | |||
} | |||
#ifdef JS_SHARED_LIBRARY | |||
#define JS_INIT_MODULE js_init_module | |||
#else | |||
#define JS_INIT_MODULE js_init_module_bjson | |||
#endif | |||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) | |||
{ | |||
JSModuleDef *m; | |||
m = JS_NewCModule(ctx, module_name, js_bjson_init); | |||
if (!m) | |||
return NULL; | |||
JS_AddModuleExportList(ctx, m, js_bjson_funcs, countof(js_bjson_funcs)); | |||
return m; | |||
} |
@ -0,0 +1,621 @@ | |||
/* | |||
* C utilities | |||
* | |||
* Copyright (c) 2017 Fabrice Bellard | |||
* Copyright (c) 2018 Charlie Gordon | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to deal | |||
* in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
* copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included in | |||
* all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | |||
*/ | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <stdarg.h> | |||
#include <string.h> | |||
#include "cutils.h" | |||
void pstrcpy(char *buf, int buf_size, const char *str) | |||
{ | |||
int c; | |||
char *q = buf; | |||
if (buf_size <= 0) | |||
return; | |||
for(;;) { | |||
c = *str++; | |||
if (c == 0 || q >= buf + buf_size - 1) | |||
break; | |||
*q++ = c; | |||
} | |||
*q = '\0'; | |||
} | |||
/* strcat and truncate. */ | |||
char *pstrcat(char *buf, int buf_size, const char *s) | |||
{ | |||
int len; | |||
len = strlen(buf); | |||
if (len < buf_size) | |||
pstrcpy(buf + len, buf_size - len, s); | |||
return buf; | |||
} | |||
int strstart(const char *str, const char *val, const char **ptr) | |||
{ | |||
const char *p, *q; | |||
p = str; | |||
q = val; | |||
while (*q != '\0') { | |||
if (*p != *q) | |||
return 0; | |||
p++; | |||
q++; | |||
} | |||
if (ptr) | |||
*ptr = p; | |||
return 1; | |||
} | |||
int has_suffix(const char *str, const char *suffix) | |||
{ | |||
size_t len = strlen(str); | |||
size_t slen = strlen(suffix); | |||
return (len >= slen && !memcmp(str + len - slen, suffix, slen)); | |||
} | |||
/* Dynamic buffer package */ | |||
static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) | |||
{ | |||
return realloc(ptr, size); | |||
} | |||
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func) | |||
{ | |||
memset(s, 0, sizeof(*s)); | |||
if (!realloc_func) | |||
realloc_func = dbuf_default_realloc; | |||
s->opaque = opaque; | |||
s->realloc_func = realloc_func; | |||
} | |||
void dbuf_init(DynBuf *s) | |||
{ | |||
dbuf_init2(s, NULL, NULL); | |||
} | |||
/* return < 0 if error */ | |||
int dbuf_realloc(DynBuf *s, size_t new_size) | |||
{ | |||
size_t size; | |||
uint8_t *new_buf; | |||
if (new_size > s->allocated_size) { | |||
if (s->error) | |||
return -1; | |||
size = s->allocated_size * 3 / 2; | |||
if (size > new_size) | |||
new_size = size; | |||
new_buf = s->realloc_func(s->opaque, s->buf, new_size); | |||
if (!new_buf) { | |||
s->error = TRUE; | |||
return -1; | |||
} | |||
s->buf = new_buf; | |||
s->allocated_size = new_size; | |||
} | |||
return 0; | |||
} | |||
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len) | |||
{ | |||
size_t end; | |||
end = offset + len; | |||
if (dbuf_realloc(s, end)) | |||
return -1; | |||
memcpy(s->buf + offset, data, len); | |||
if (end > s->size) | |||
s->size = end; | |||
return 0; | |||
} | |||
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len) | |||
{ | |||
if (unlikely((s->size + len) > s->allocated_size)) { | |||
if (dbuf_realloc(s, s->size + len)) | |||
return -1; | |||
} | |||
memcpy(s->buf + s->size, data, len); | |||
s->size += len; | |||
return 0; | |||
} | |||
int dbuf_put_self(DynBuf *s, size_t offset, size_t len) | |||
{ | |||
if (unlikely((s->size + len) > s->allocated_size)) { | |||
if (dbuf_realloc(s, s->size + len)) | |||
return -1; | |||
} | |||
memcpy(s->buf + s->size, s->buf + offset, len); | |||
s->size += len; | |||
return 0; | |||
} | |||
int dbuf_putc(DynBuf *s, uint8_t c) | |||
{ | |||
return dbuf_put(s, &c, 1); | |||
} | |||
int dbuf_putstr(DynBuf *s, const char *str) | |||
{ | |||
return dbuf_put(s, (const uint8_t *)str, strlen(str)); | |||
} | |||
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, | |||
const char *fmt, ...) | |||
{ | |||
va_list ap; | |||
char buf[128]; | |||
int len; | |||
va_start(ap, fmt); | |||
len = vsnprintf(buf, sizeof(buf), fmt, ap); | |||
va_end(ap); | |||
if (len < sizeof(buf)) { | |||
/* fast case */ | |||
return dbuf_put(s, (uint8_t *)buf, len); | |||
} else { | |||
if (dbuf_realloc(s, s->size + len + 1)) | |||
return -1; | |||
va_start(ap, fmt); | |||
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size, | |||
fmt, ap); | |||
va_end(ap); | |||
s->size += len; | |||
} | |||
return 0; | |||
} | |||
void dbuf_free(DynBuf *s) | |||
{ | |||
/* we test s->buf as a fail safe to avoid crashing if dbuf_free() | |||
is called twice */ | |||
if (s->buf) { | |||
s->realloc_func(s->opaque, s->buf, 0); | |||
} | |||
memset(s, 0, sizeof(*s)); | |||
} | |||
/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes | |||
are output. */ | |||
int unicode_to_utf8(uint8_t *buf, unsigned int c) | |||
{ | |||
uint8_t *q = buf; | |||
if (c < 0x80) { | |||
*q++ = c; | |||
} else { | |||
if (c < 0x800) { | |||
*q++ = (c >> 6) | 0xc0; | |||
} else { | |||
if (c < 0x10000) { | |||
*q++ = (c >> 12) | 0xe0; | |||
} else { | |||
if (c < 0x00200000) { | |||
*q++ = (c >> 18) | 0xf0; | |||
} else { | |||
if (c < 0x04000000) { | |||
*q++ = (c >> 24) | 0xf8; | |||
} else if (c < 0x80000000) { | |||
*q++ = (c >> 30) | 0xfc; | |||
*q++ = ((c >> 24) & 0x3f) | 0x80; | |||
} else { | |||
return 0; | |||
} | |||
*q++ = ((c >> 18) & 0x3f) | 0x80; | |||
} | |||
*q++ = ((c >> 12) & 0x3f) | 0x80; | |||
} | |||
*q++ = ((c >> 6) & 0x3f) | 0x80; | |||
} | |||
*q++ = (c & 0x3f) | 0x80; | |||
} | |||
return q - buf; | |||
} | |||
static const unsigned int utf8_min_code[5] = { | |||
0x80, 0x800, 0x10000, 0x00200000, 0x04000000, | |||
}; | |||
static const unsigned char utf8_first_code_mask[5] = { | |||
0x1f, 0xf, 0x7, 0x3, 0x1, | |||
}; | |||
/* return -1 if error. *pp is not updated in this case. max_len must | |||
be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */ | |||
int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp) | |||
{ | |||
int l, c, b, i; | |||
c = *p++; | |||
if (c < 0x80) { | |||
*pp = p; | |||
return c; | |||
} | |||
switch(c) { | |||
case 0xc0 ... 0xdf: | |||
l = 1; | |||
break; | |||
case 0xe0 ... 0xef: | |||
l = 2; | |||
break; | |||
case 0xf0 ... 0xf7: | |||
l = 3; | |||
break; | |||
case 0xf8 ... 0xfb: | |||
l = 4; | |||
break; | |||
case 0xfc ... 0xfd: | |||
l = 5; | |||
break; | |||
default: | |||
return -1; | |||
} | |||
/* check that we have enough characters */ | |||
if (l > (max_len - 1)) | |||
return -1; | |||
c &= utf8_first_code_mask[l - 1]; | |||
for(i = 0; i < l; i++) { | |||
b = *p++; | |||
if (b < 0x80 || b >= 0xc0) | |||
return -1; | |||
c = (c << 6) | (b & 0x3f); | |||
} | |||
if (c < utf8_min_code[l - 1]) | |||
return -1; | |||
*pp = p; | |||
return c; | |||
} | |||
#if 0 | |||
#if defined(EMSCRIPTEN) || defined(__ANDROID__) | |||
static void *rqsort_arg; | |||
static int (*rqsort_cmp)(const void *, const void *, void *); | |||
static int rqsort_cmp2(const void *p1, const void *p2) | |||
{ | |||
return rqsort_cmp(p1, p2, rqsort_arg); | |||
} | |||
/* not reentrant, but not needed with emscripten */ | |||
void rqsort(void *base, size_t nmemb, size_t size, | |||
int (*cmp)(const void *, const void *, void *), | |||
void *arg) | |||
{ | |||
rqsort_arg = arg; | |||
rqsort_cmp = cmp; | |||
qsort(base, nmemb, size, rqsort_cmp2); | |||
} | |||
#endif | |||
#else | |||
typedef void (*exchange_f)(void *a, void *b, size_t size); | |||
typedef int (*cmp_f)(const void *, const void *, void *opaque); | |||
static void exchange_bytes(void *a, void *b, size_t size) { | |||
uint8_t *ap = (uint8_t *)a; | |||
uint8_t *bp = (uint8_t *)b; | |||
while (size-- != 0) { | |||
uint8_t t = *ap; | |||
*ap++ = *bp; | |||
*bp++ = t; | |||
} | |||
} | |||
static void exchange_one_byte(void *a, void *b, size_t size) { | |||
uint8_t *ap = (uint8_t *)a; | |||
uint8_t *bp = (uint8_t *)b; | |||
uint8_t t = *ap; | |||
*ap = *bp; | |||
*bp = t; | |||
} | |||
static void exchange_int16s(void *a, void *b, size_t size) { | |||
uint16_t *ap = (uint16_t *)a; | |||
uint16_t *bp = (uint16_t *)b; | |||
for (size /= sizeof(uint16_t); size-- != 0;) { | |||
uint16_t t = *ap; | |||
*ap++ = *bp; | |||
*bp++ = t; | |||
} | |||
} | |||
static void exchange_one_int16(void *a, void *b, size_t size) { | |||
uint16_t *ap = (uint16_t *)a; | |||
uint16_t *bp = (uint16_t *)b; | |||
uint16_t t = *ap; | |||
*ap = *bp; | |||
*bp = t; | |||
} | |||
static void exchange_int32s(void *a, void *b, size_t size) { | |||
uint32_t *ap = (uint32_t *)a; | |||
uint32_t *bp = (uint32_t *)b; | |||
for (size /= sizeof(uint32_t); size-- != 0;) { | |||
uint32_t t = *ap; | |||
*ap++ = *bp; | |||
*bp++ = t; | |||
} | |||
} | |||
static void exchange_one_int32(void *a, void *b, size_t size) { | |||
uint32_t *ap = (uint32_t *)a; | |||
uint32_t *bp = (uint32_t *)b; | |||
uint32_t t = *ap; | |||
*ap = *bp; | |||
*bp = t; | |||
} | |||
static void exchange_int64s(void *a, void *b, size_t size) { | |||
uint64_t *ap = (uint64_t *)a; | |||
uint64_t *bp = (uint64_t *)b; | |||
for (size /= sizeof(uint64_t); size-- != 0;) { | |||
uint64_t t = *ap; | |||
*ap++ = *bp; | |||
*bp++ = t; | |||
} | |||
} | |||
static void exchange_one_int64(void *a, void *b, size_t size) { | |||
uint64_t *ap = (uint64_t *)a; | |||
uint64_t *bp = (uint64_t *)b; | |||
uint64_t t = *ap; | |||
*ap = *bp; | |||
*bp = t; | |||
} | |||
static void exchange_int128s(void *a, void *b, size_t size) { | |||
uint64_t *ap = (uint64_t *)a; | |||
uint64_t *bp = (uint64_t *)b; | |||
for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) { | |||
uint64_t t = ap[0]; | |||
uint64_t u = ap[1]; | |||
ap[0] = bp[0]; | |||
ap[1] = bp[1]; | |||
bp[0] = t; | |||
bp[1] = u; | |||
} | |||
} | |||
static void exchange_one_int128(void *a, void *b, size_t size) { | |||
uint64_t *ap = (uint64_t *)a; | |||
uint64_t *bp = (uint64_t *)b; | |||
uint64_t t = ap[0]; | |||
uint64_t u = ap[1]; | |||
ap[0] = bp[0]; | |||
ap[1] = bp[1]; | |||
bp[0] = t; | |||
bp[1] = u; | |||
} | |||
static inline exchange_f exchange_func(const void *base, size_t size) { | |||
switch (((uintptr_t)base | (uintptr_t)size) & 15) { | |||
case 0: | |||
if (size == sizeof(uint64_t) * 2) | |||
return exchange_one_int128; | |||
else | |||
return exchange_int128s; | |||
case 8: | |||
if (size == sizeof(uint64_t)) | |||
return exchange_one_int64; | |||
else | |||
return exchange_int64s; | |||
case 4: | |||
case 12: | |||
if (size == sizeof(uint32_t)) | |||
return exchange_one_int32; | |||
else | |||
return exchange_int32s; | |||
case 2: | |||
case 6: | |||
case 10: | |||
case 14: | |||
if (size == sizeof(uint16_t)) | |||
return exchange_one_int16; | |||
else | |||
return exchange_int16s; | |||
default: | |||
if (size == 1) | |||
return exchange_one_byte; | |||
else | |||
return exchange_bytes; | |||
} | |||
} | |||
static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) | |||
{ | |||
uint8_t *basep = (uint8_t *)base; | |||
size_t i, n, c, r; | |||
exchange_f swap = exchange_func(base, size); | |||
if (nmemb > 1) { | |||
i = (nmemb / 2) * size; | |||
n = nmemb * size; | |||
while (i > 0) { | |||
i -= size; | |||
for (r = i; (c = r * 2 + size) < n; r = c) { | |||
if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0) | |||
c += size; | |||
if (cmp(basep + r, basep + c, opaque) > 0) | |||
break; | |||
swap(basep + r, basep + c, size); | |||
} | |||
} | |||
for (i = n - size; i > 0; i -= size) { | |||
swap(basep, basep + i, size); | |||
for (r = 0; (c = r * 2 + size) < i; r = c) { | |||
if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0) | |||
c += size; | |||
if (cmp(basep + r, basep + c, opaque) > 0) | |||
break; | |||
swap(basep + r, basep + c, size); | |||
} | |||
} | |||
} | |||
} | |||
static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque) | |||
{ | |||
return cmp(a, b, opaque) < 0 ? | |||
(cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) : | |||
(cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c )); | |||
} | |||
/* pointer based version with local stack and insertion sort threshhold */ | |||
void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) | |||
{ | |||
struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack; | |||
uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m; | |||
size_t m4, i, lt, gt, span, span2; | |||
int c, depth; | |||
exchange_f swap = exchange_func(base, size); | |||
exchange_f swap_block = exchange_func(base, size | 128); | |||
if (nmemb < 2 || size <= 0) | |||
return; | |||
sp->base = (uint8_t *)base; | |||
sp->count = nmemb; | |||
sp->depth = 0; | |||
sp++; | |||
while (sp > stack) { | |||
sp--; | |||
ptr = sp->base; | |||
nmemb = sp->count; | |||
depth = sp->depth; | |||
while (nmemb > 6) { | |||
if (++depth > 50) { | |||
/* depth check to ensure worst case logarithmic time */ | |||
heapsortx(ptr, nmemb, size, cmp, opaque); | |||
nmemb = 0; | |||
break; | |||
} | |||
/* select median of 3 from 1/4, 1/2, 3/4 positions */ | |||
/* should use median of 5 or 9? */ | |||
m4 = (nmemb >> 2) * size; | |||
m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque); | |||
swap(ptr, m, size); /* move the pivot to the start or the array */ | |||
i = lt = 1; | |||
pi = plt = ptr + size; | |||
gt = nmemb; | |||
pj = pgt = top = ptr + nmemb * size; | |||
for (;;) { | |||
while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) { | |||
if (c == 0) { | |||
swap(plt, pi, size); | |||
lt++; | |||
plt += size; | |||
} | |||
i++; | |||
pi += size; | |||
} | |||
while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) { | |||
if (c == 0) { | |||
gt--; | |||
pgt -= size; | |||
swap(pgt, pj, size); | |||
} | |||
} | |||
if (pi >= pj) | |||
break; | |||
swap(pi, pj, size); | |||
i++; | |||
pi += size; | |||
} | |||
/* array has 4 parts: | |||
* from 0 to lt excluded: elements identical to pivot | |||
* from lt to pi excluded: elements smaller than pivot | |||
* from pi to gt excluded: elements greater than pivot | |||
* from gt to n excluded: elements identical to pivot | |||
*/ | |||
/* move elements identical to pivot in the middle of the array: */ | |||
/* swap values in ranges [0..lt[ and [i-lt..i[ | |||
swapping the smallest span between lt and i-lt is sufficient | |||
*/ | |||
span = plt - ptr; | |||
span2 = pi - plt; | |||
plt = pi - span; | |||
lt = i - lt; | |||
if (span > span2) | |||
span = span2; | |||
swap_block(ptr, pi - span, span); | |||
/* swap values in ranges [gt..top[ and [i..top-(top-gt)[ | |||
swapping the smallest span between top-gt and gt-i is sufficient | |||
*/ | |||
span = top - pgt; | |||
span2 = pgt - pi; | |||
pgt = top - span2; | |||
gt = nmemb - (gt - i); | |||
if (span > span2) | |||
span = span2; | |||
swap_block(pi, top - span, span); | |||
/* now array has 3 parts: | |||
* from 0 to lt excluded: elements smaller than pivot | |||
* from lt to gt excluded: elements identical to pivot | |||
* from gt to n excluded: elements greater than pivot | |||
*/ | |||
/* stack the larger segment and keep processing the smaller one | |||
to minimize stack use for pathological distributions */ | |||
if (lt > nmemb - gt) { | |||
sp->base = ptr; | |||
sp->count = lt; | |||
sp->depth = depth; | |||
sp++; | |||
ptr = pgt; | |||
nmemb -= gt; | |||
} else { | |||
sp->base = pgt; | |||
sp->count = nmemb - gt; | |||
sp->depth = depth; | |||
sp++; | |||
nmemb = lt; | |||
} | |||
} | |||
/* Use insertion sort for small fragments */ | |||
for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) { | |||
for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size) | |||
swap(pj, pj - size, size); | |||
} | |||
} | |||
} | |||
#endif |
@ -0,0 +1,292 @@ | |||
/* | |||
* C utilities | |||
* | |||
* Copyright (c) 2017 Fabrice Bellard | |||
* Copyright (c) 2018 Charlie Gordon | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to deal | |||
* in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
* copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included in | |||
* all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | |||
*/ | |||
#ifndef CUTILS_H | |||
#define CUTILS_H | |||
#include <stdlib.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)) | |||
#define no_inline __attribute__((noinline)) | |||
#define xglue(x, y) x ## y | |||
#define glue(x, y) xglue(x, y) | |||
#define stringify(s) tostring(s) | |||
#define tostring(s) #s | |||
#ifndef offsetof | |||
#define offsetof(type, field) ((size_t) &((type *)0)->field) | |||
#endif | |||
#ifndef countof | |||
#define countof(x) (sizeof(x) / sizeof((x)[0])) | |||
#endif | |||
typedef int BOOL; | |||
#ifndef FALSE | |||
enum { | |||
FALSE = 0, | |||
TRUE = 1, | |||
}; | |||
#endif | |||
void pstrcpy(char *buf, int buf_size, const char *str); | |||
char *pstrcat(char *buf, int buf_size, const char *s); | |||
int strstart(const char *str, const char *val, const char **ptr); | |||
int has_suffix(const char *str, const char *suffix); | |||
static inline int max_int(int a, int b) | |||
{ | |||
if (a > b) | |||
return a; | |||
else | |||
return b; | |||
} | |||
static inline int min_int(int a, int b) | |||
{ | |||
if (a < b) | |||
return a; | |||
else | |||
return b; | |||
} | |||
static inline uint32_t max_uint32(uint32_t a, uint32_t b) | |||
{ | |||
if (a > b) | |||
return a; | |||
else | |||
return b; | |||
} | |||
static inline uint32_t min_uint32(uint32_t a, uint32_t b) | |||
{ | |||
if (a < b) | |||
return a; | |||
else | |||
return b; | |||
} | |||
static inline int64_t max_int64(int64_t a, int64_t b) | |||
{ | |||
if (a > b) | |||
return a; | |||
else | |||
return b; | |||
} | |||
static inline int64_t min_int64(int64_t a, int64_t b) | |||
{ | |||
if (a < b) | |||
return a; | |||
else | |||
return b; | |||
} | |||
/* WARNING: undefined if a = 0 */ | |||
static inline int clz32(unsigned int a) | |||
{ | |||
return __builtin_clz(a); | |||
} | |||
/* WARNING: undefined if a = 0 */ | |||
static inline int clz64(uint64_t a) | |||
{ | |||
return __builtin_clzll(a); | |||
} | |||
/* WARNING: undefined if a = 0 */ | |||
static inline int ctz32(unsigned int a) | |||
{ | |||
return __builtin_ctz(a); | |||
} | |||
/* WARNING: undefined if a = 0 */ | |||
static inline int ctz64(uint64_t a) | |||
{ | |||
return __builtin_ctzll(a); | |||
} | |||
struct __attribute__((packed)) packed_u64 { | |||
uint64_t v; | |||
}; | |||
struct __attribute__((packed)) packed_u32 { | |||
uint32_t v; | |||
}; | |||
struct __attribute__((packed)) packed_u16 { | |||
uint16_t v; | |||
}; | |||
static inline uint64_t get_u64(const uint8_t *tab) | |||
{ | |||
return ((const struct packed_u64 *)tab)->v; | |||
} | |||
static inline int64_t get_i64(const uint8_t *tab) | |||
{ | |||
return (int64_t)((const struct packed_u64 *)tab)->v; | |||
} | |||
static inline void put_u64(uint8_t *tab, uint64_t val) | |||
{ | |||
((struct packed_u64 *)tab)->v = val; | |||
} | |||
static inline uint32_t get_u32(const uint8_t *tab) | |||
{ | |||
return ((const struct packed_u32 *)tab)->v; | |||
} | |||
static inline int32_t get_i32(const uint8_t *tab) | |||
{ | |||
return (int32_t)((const struct packed_u32 *)tab)->v; | |||
} | |||
static inline void put_u32(uint8_t *tab, uint32_t val) | |||
{ | |||
((struct packed_u32 *)tab)->v = val; | |||
} | |||
static inline uint32_t get_u16(const uint8_t *tab) | |||
{ | |||
return ((const struct packed_u16 *)tab)->v; | |||
} | |||
static inline int32_t get_i16(const uint8_t *tab) | |||
{ | |||
return (int16_t)((const struct packed_u16 *)tab)->v; | |||
} | |||
static inline void put_u16(uint8_t *tab, uint16_t val) | |||
{ | |||
((struct packed_u16 *)tab)->v = val; | |||
} | |||
static inline uint32_t get_u8(const uint8_t *tab) | |||
{ | |||
return *tab; | |||
} | |||
static inline int32_t get_i8(const uint8_t *tab) | |||
{ | |||
return (int8_t)*tab; | |||
} | |||
static inline void put_u8(uint8_t *tab, uint8_t val) | |||
{ | |||
*tab = val; | |||
} | |||
static inline uint16_t bswap16(uint16_t x) | |||
{ | |||
return (x >> 8) | (x << 8); | |||
} | |||
static inline uint32_t bswap32(uint32_t v) | |||
{ | |||
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | | |||
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); | |||
} | |||
static inline uint64_t bswap64(uint64_t v) | |||
{ | |||
return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | | |||
((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | | |||
((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | | |||
((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | | |||
((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | | |||
((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | | |||
((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | | |||
((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); | |||
} | |||
/* XXX: should take an extra argument to pass slack information to the caller */ | |||
typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); | |||
typedef struct DynBuf { | |||
uint8_t *buf; | |||
size_t size; | |||
size_t allocated_size; | |||
BOOL error; /* true if a memory allocation error occurred */ | |||
DynBufReallocFunc *realloc_func; | |||
void *opaque; /* for realloc_func */ | |||
} DynBuf; | |||
void dbuf_init(DynBuf *s); | |||
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); | |||
int dbuf_realloc(DynBuf *s, size_t new_size); | |||
int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len); | |||
int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); | |||
int dbuf_put_self(DynBuf *s, size_t offset, size_t len); | |||
int dbuf_putc(DynBuf *s, uint8_t c); | |||
int dbuf_putstr(DynBuf *s, const char *str); | |||
static inline int dbuf_put_u16(DynBuf *s, uint16_t val) | |||
{ | |||
return dbuf_put(s, (uint8_t *)&val, 2); | |||
} | |||
static inline int dbuf_put_u32(DynBuf *s, uint32_t val) | |||
{ | |||
return dbuf_put(s, (uint8_t *)&val, 4); | |||
} | |||
static inline int dbuf_put_u64(DynBuf *s, uint64_t val) | |||
{ | |||
return dbuf_put(s, (uint8_t *)&val, 8); | |||
} | |||
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, | |||
const char *fmt, ...); | |||
void dbuf_free(DynBuf *s); | |||
static inline BOOL dbuf_error(DynBuf *s) { | |||
return s->error; | |||
} | |||
#define UTF8_CHAR_LEN_MAX 6 | |||
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 int from_hex(int c) | |||
{ | |||
if (c >= '0' && c <= '9') | |||
return c - '0'; | |||
else if (c >= 'A' && c <= 'F') | |||
return c - 'A' + 10; | |||
else if (c >= 'a' && c <= 'f') | |||
return c - 'a' + 10; | |||
else | |||
return -1; | |||
} | |||
void rqsort(void *base, size_t nmemb, size_t size, | |||
int (*cmp)(const void *, const void *, void *), | |||
void *arg); | |||
#endif /* CUTILS_H */ |
@ -0,0 +1,844 @@ | |||
\input texinfo | |||
@iftex | |||
@afourpaper | |||
@headings double | |||
@end iftex | |||
@titlepage | |||
@afourpaper | |||
@sp 7 | |||
@center @titlefont{Javascript Bignum Extensions} | |||
@sp 3 | |||
@center Version 2018-06-16 | |||
@sp 3 | |||
@center Author: Fabrice Bellard | |||
@end titlepage | |||
@setfilename spec.info | |||
@settitle Javascript Bignum Extensions | |||
@contents | |||
@chapter Introduction | |||
The Bignum extensions add the following features to the Javascript | |||
language while being 100% backward compatible: | |||
@itemize | |||
@item Overloading of the standard operators | |||
to support new types such as complex numbers, fractions or matrixes. | |||
@item Bigint mode where arbitrarily large integers are available by default (no @code{n} suffix is necessary as in the TC39 BigInt proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}}). | |||
@item Arbitrarily large floating point numbers (@code{BigFloat}) in base 2 using the IEEE 754 semantics. | |||
@item Optional @code{math} mode which modifies the semantics of the division, modulo and power operator. The division and power operator return a fraction with integer operands and the modulo operator is defined as the Euclidian remainder. | |||
@end itemize | |||
The extensions are independent from each other except the @code{math} | |||
mode which relies on the bigint mode and the operator overloading. | |||
@chapter Operator overloading | |||
@section Introduction | |||
If the operands of an operator have at least one object type, a custom | |||
operator method is searched before doing the legacy Javascript | |||
@code{ToNumber} conversion. | |||
For unary operators, the custom function is looked up in the object | |||
and has the following name: | |||
@table @code | |||
@item unary + | |||
@code{Symbol.operatorPlus} | |||
@item unary - | |||
@code{Symbol.operatorNeg} | |||
@item ++ | |||
@code{Symbol.operatorInc} | |||
@item -- | |||
@code{Symbol.operatorDec} | |||
@item ~ | |||
@code{Symbol.operatorNot} | |||
@end table | |||
For binary operators: | |||
@itemize | |||
@item | |||
If both operands have the same constructor function, then the operator | |||
is looked up in the constructor. | |||
@item | |||
Otherwise, the property @code{Symbol.operatorOrder} is looked up in both | |||
constructors and converted to @code{Int32}. The operator is then | |||
looked in the constructor with the larger @code{Symbol.operatorOrder} | |||
value. A @code{TypeError} is raised if both constructors have the same | |||
@code{Symbol.operatorOrder} value. | |||