2020-01-05 release

This commit is contained in:
bellard 2020-09-06 18:53:08 +02:00
parent 9096e544ba
commit 91459fb672
63 changed files with 95633 additions and 0 deletions

86
Changelog Normal file
View File

@ -0,0 +1,86 @@
2020-01-05:
- always compile the bignum code. Added '--bignum' option to qjs.
- added BigDecimal
- added String.prototype.replaceAll
- misc bug fixes
2019-12-21:
- added nullish coalescing operator (ES2020)
- added optional chaining (ES2020)
- removed recursions in garbage collector
- test stack overflow in the parser
- improved backtrace logic
- added JS_SetHostPromiseRejectionTracker()
- allow exotic constructors
- improved c++ compatibility
- misc bug fixes
2019-10-27:
- added example of C class in a module (examples/test_point.js)
- added JS_GetTypedArrayBuffer()
- misc bug fixes
2019-09-18:
- added os.exec and other system calls
- exported JS_ValueToAtom()
- qjsc: added 'qjsc_' prefix to the generated C identifiers
- added cross-compilation support
- misc bug fixes
2019-09-01:
- added globalThis
- documented JS_EVAL_FLAG_COMPILE_ONLY
- added import.meta.url and import.meta.main
- added 'debugger' statement
- misc bug fixes
2019-08-18:
- added os.realpath, os.getcwd, os.mkdir, os.stat, os.lstat,
os.readlink, os.readdir, os.utimes, std.popen
- module autodetection
- added import.meta
- misc bug fixes
2019-08-10:
- added public class fields and private class fields, methods and
accessors (TC39 proposal)
- changed JS_ToCStringLen() prototype
- qjsc: handle '-' in module names and modules with the same filename
- added std.urlGet
- exported JS_GetOwnPropertyNames() and JS_GetOwnProperty()
- exported some bigint C functions
- added support for eshost in run-test262
- misc bug fixes
2019-07-28:
- added dynamic import
- added Promise.allSettled
- added String.prototype.matchAll
- added Object.fromEntries
- reduced number of ticks in await
- added BigInt support in Atomics
- exported JS_NewPromiseCapability()
- misc async function and async generator fixes
- enabled hashbang support by default
2019-07-21:
- updated test262 tests
- updated to Unicode version 12.1.0
- fixed missing Date object in qjsc
- fixed multi-context creation
- misc ES2020 related fixes
- simplified power and division operators in bignum extension
- fixed several crash conditions
2019-07-09:
- first public release

439
Makefile Normal file
View File

@ -0,0 +1,439 @@
#
# 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
# force 32 bit build for some utilities
#CONFIG_M32=y
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
HOST_CC=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
HOST_CC=gcc
CC=$(CROSS_PREFIX)gcc
CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d
CFLAGS += -Wno-array-bounds -Wno-format-truncation
ifdef CONFIG_LTO
AR=$(CROSS_PREFIX)gcc-ar
else
AR=$(CROSS_PREFIX)ar
endif
endif
STRIP=$(CROSS_PREFIX)strip
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) qjsc$(EXE) run-test262
ifneq ($(CROSS_PREFIX),)
QJSC_CC=gcc
QJSC=./host-qjsc
PROGS+=$(QJSC)
else
QJSC_CC=$(CC)
QJSC=./qjsc$(EXE)
endif
ifndef CONFIG_WIN32
PROGS+=qjscalc
endif
ifdef CONFIG_M32
PROGS+=qjs32 qjs32_s
endif
PROGS+=libquickjs.a
ifdef CONFIG_LTO
PROGS+=libquickjs.lto.a
endif
# examples
ifeq ($(CROSS_PREFIX),)
ifdef CONFIG_ASAN
PROGS+=
else
PROGS+=examples/hello examples/hello_module examples/test_fib
ifndef CONFIG_DARWIN
PROGS+=examples/fib.so examples/point.so
endif
endif
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)/libbf.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o
QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(OBJDIR)/qjscalc.o $(QJS_LIB_OBJS)
LIBS=-lm
ifndef CONFIG_WIN32
LIBS+=-ldl
endif
$(OBJDIR):
mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests
qjs$(EXE): $(QJS_OBJS)
$(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
ifneq ($(CROSS_PREFIX),)
$(QJSC): $(OBJDIR)/qjsc.host.o \
$(patsubst %.o, %.host.o, $(QJS_LIB_OBJS))
$(HOST_CC) $(LDFLAGS) -o $@ $^ $(LIBS)
endif #CROSS_PREFIX
QJSC_DEFINES:=-DCONFIG_CC=\"$(QJSC_CC)\" -DCONFIG_PREFIX=\"$(prefix)\"
ifdef CONFIG_LTO
QJSC_DEFINES+=-DCONFIG_LTO
endif
QJSC_HOST_DEFINES:=-DCONFIG_CC=\"$(HOST_CC)\" -DCONFIG_PREFIX=\"$(prefix)\"
$(OBJDIR)/qjsc.o: CFLAGS+=$(QJSC_DEFINES)
$(OBJDIR)/qjsc.host.o: CFLAGS+=$(QJSC_HOST_DEFINES)
qjs32: $(patsubst %.o, %.m32.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)
qjs32_s: $(patsubst %.o, %.m32s.o, $(QJS_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
@size $@
qjscalc: qjs
ln -sf $< $@
ifdef CONFIG_LTO
LTOEXT=.lto
else
LTOEXT=
endif
libquickjs$(LTOEXT).a: $(QJS_LIB_OBJS)
$(AR) rcs $@ $^
ifdef CONFIG_LTO
libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^
endif # CONFIG_LTO
repl.c: $(QJSC) repl.js
$(QJSC) -c -o $@ -m repl.js
qjscalc.c: $(QJSC) qjscalc.js
$(QJSC) -fbignum -c -o $@ qjscalc.js
ifneq ($(wildcard unicode/UnicodeData.txt),)
$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.m32.o $(OBJDIR)/libunicode.m32s.o \
$(OBJDIR)/libunicode.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-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
# object suffix order: nolto, [m32|m32s]
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.host.o: %.c | $(OBJDIR)
$(HOST_CC) $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.pic.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_OPT) -fPIC -DJS_SHARED_LIBRARY -c -o $@ $<
$(OBJDIR)/%.nolto.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_NOLTO) -c -o $@ $<
$(OBJDIR)/%.m32.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_OPT) -c -o $@ $<
$(OBJDIR)/%.m32s.o: %.c | $(OBJDIR)
$(CC) -m32 $(CFLAGS_SMALL) -c -o $@ $<
$(OBJDIR)/%.debug.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS_DEBUG) -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: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c unicode_gen_def.h
$(HOST_CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o
clean:
rm -f repl.c qjscalc.c out.c
rm -f *.a *.o *.d *~ jscompress unicode_gen regexp_test $(PROGS)
rm -f hello.c hello_module.c test_fib.c
rm -f examples/*.so tests/*.so
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug
rm -rf run-test262-debug run-test262-32
install: all
mkdir -p "$(DESTDIR)$(prefix)/bin"
$(STRIP) qjs qjsc
install -m755 qjs qjsc "$(DESTDIR)$(prefix)/bin"
ln -sf qjs "$(DESTDIR)$(prefix)/bin/qjscalc"
mkdir -p "$(DESTDIR)$(prefix)/lib/quickjs"
install -m644 libquickjs.a "$(DESTDIR)$(prefix)/lib/quickjs"
ifdef CONFIG_LTO
install -m644 libquickjs.lto.a "$(DESTDIR)$(prefix)/lib/quickjs"
endif
mkdir -p "$(DESTDIR)$(prefix)/include/quickjs"
install -m644 quickjs.h quickjs-libc.h "$(DESTDIR)$(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 \
-fno-date -fno-module-loader -fno-bigint
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 \
-fno-date -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)
test_fib.c: $(QJSC) examples/test_fib.js
$(QJSC) -e -M examples/fib.so,fib -m -o $@ examples/test_fib.js
examples/test_fib: $(OBJDIR)/test_fib.o $(OBJDIR)/examples/fib.o libquickjs$(LTOEXT).a
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
examples/fib.so: $(OBJDIR)/examples/fib.pic.o
$(CC) $(LDFLAGS) -shared -o $@ $^
examples/point.so: $(OBJDIR)/examples/point.pic.o
$(CC) $(LDFLAGS) -shared -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.pre: doc/%.texi
makeinfo --html --no-headers --no-split --number-sections -o $@ $<
doc/%.html: doc/%.html.pre
sed -e 's|</style>|</style>\n<meta name="viewport" content="width=device-width, initial-scale=1.0">|' < $< > $@
###############################################################################
# tests
ifndef CONFIG_DARWIN
test: tests/bjson.so examples/point.so
endif
ifdef CONFIG_M32
test: qjs32
endif
test: qjs
./qjs tests/test_closure.js
./qjs tests/test_op.js
./qjs tests/test_builtin.js
./qjs tests/test_loop.js
./qjs tests/test_std.js
ifndef CONFIG_DARWIN
./qjs --bignum tests/test_bjson.js
./qjs examples/test_point.js
endif
./qjs --bignum tests/test_bignum.js
./qjs --qjscalc tests/test_qjscalc.js
ifdef CONFIG_M32
./qjs32 tests/test_closure.js
./qjs32 tests/test_op.js
./qjs32 tests/test_builtin.js
./qjs32 tests/test_loop.js
./qjs32 tests/test_std.js
./qjs32 --bignum tests/test_bignum.js
./qjs32 --qjscalc tests/test_qjscalc.js
endif
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
testall: all test microbench test2o test2
testall-32: all test-32 microbench-32 test2o-32 test2-32
testall-complete: testall testall-32
bench-v8: qjs
make -C tests/bench-v8
./qjs -d tests/bench-v8/combined.js
tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o
$(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS)
-include $(wildcard $(OBJDIR)/*.d)

78
TODO Normal file
View File

@ -0,0 +1,78 @@
Misc:
- use custom printf to avoid C library compatibility issues
- 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
- use custom timezone support to avoid C library compatibility issues
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:
- 64-bit atoms in 64-bit mode ?
- 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
- optimize OP_apply
- optimize f(...b)
Extensions:
- support more features in [features] section
- add built-in preprocessor in compiler, get rid of jscompress
handle #if, #ifdef, #line, limited support for #define
- get rid of __loadScript, use more common name
- BSD sockets
- Workers
REPL:
- debugger
- readline: support MS Windows terminal
- readline: handle dynamic terminal resizing
- multiline editing
- 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/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Test262: 4/67619 errors, 913 excluded, 1660 skipped
Test262bn: 4/69722 errors, 846 excluded, 672 skipped
test262 commit: 19fd4bea797646ae9bbfc9d325f14052ca370b54

1
VERSION Normal file
View File

@ -0,0 +1 @@
2020-01-05

620
cutils.c Normal file
View File

@ -0,0 +1,620 @@
/*
* 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;
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

293
cutils.h Normal file
View File

@ -0,0 +1,293 @@
/*
* 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 __maybe_unused __attribute__((unused))
#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 */

1045
doc/jsbignum.html Normal file

File diff suppressed because it is too large Load Diff

BIN
doc/jsbignum.pdf Normal file

Binary file not shown.

835
doc/jsbignum.texi Normal file
View File

@ -0,0 +1,835 @@
\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 matrices.
@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.
@end itemize
The operator is looked up with the following name:
@table @code
@item +
@code{Symbol.operatorAdd}
@item -
@code{Symbol.operatorSub}
@item *
@code{Symbol.operatorMul}
@item /
@code{Symbol.operatorDiv}
@item %
@code{Symbol.operatorMod}
@item % (math mode)
@code{Symbol.operatorMathMod}
@item **
@code{Symbol.operatorPow}
@item |
@code{Symbol.operatorOr}
@item ^
@code{Symbol.operatorXor}
@item &
@code{Symbol.operatorAnd}
@item <<
@code{Symbol.operatorShl}
@item >>
@code{Symbol.operatorShr}
@item <
@code{Symbol.operatorCmpLT}
@item >
@code{Symbol.operatorCmpLT}, operands swapped
@item <=
@code{Symbol.operatorCmpLE}
@item >=
@code{Symbol.operatorCmpLE}, operands swapped
@item ==, !=
@code{Symbol.operatorCmpEQ}
@end table
The return value of @code{Symbol.operatorCmpLT}, @code{Symbol.operatorCmpLE} and
@code{Symbol.operatorCmpEQ} is converted to @code{Boolean}.
@section Builtin Object changes
@subsection @code{Symbol} constructor
The following global symbols are added for the operator overloading:
@table @code
@item operatorOrder
@item operatorAdd
@item operatorSub
@item operatorMul
@item operatorDiv
@item operatorMod
@item operatorPow
@item operatorShl
@item operatorShr
@item operatorAnd
@item operatorOr
@item operatorXor
@item operatorCmpLT
@item operatorCmpLE
@item operatorCmpEQ
@item operatorPlus
@item operatorNeg
@item operatorNot
@item operatorInc
@item operatorDec
@end table
@chapter The BigInt Mode
@section Introduction
The bigint mode is enabled with the @code{"use bigint"} directive. It
propagates the same way as the strict mode. In bigint mode, all
integers are considered as @code{bigint} (arbitrarily large integer,
similar to the TC39 BigInt
proposal@footnote{@url{https://tc39.github.io/proposal-bigint/}})
instead of @code{number} (floating point number). In order to be able
to exchange data between standard and bigint modes, numbers are
internally represented as 3 different types:
@itemize
@item Small integer (SmallInt): 32 bit integer@footnote{Could be extended to 53 bits without changing the principle.}.
@item Big integer (BigInt): arbitrarily large integer.
@item Floating point number (Float).
@end itemize
In standard mode, the semantics of each operation is modified so that
when it returns a @code{number}, it is either of SmallInt or
Float. But the difference between SmallInt and Float is not observable
in standard mode.
In bigint mode, each operation behaves differently whether its
operands are integer or float. The difference between SmallInt and
BigInt is not observable (i.e. they are both integers).
The following table summarizes the observable types:
@multitable @columnfractions .3 .3 .3
@headitem Internal type @tab Observable type@* (standard mode) @tab Observable type@* (bigint mode)
@item SmallInt @tab number @tab bigint
@item BigInt @tab bigint @tab bigint
@item Float @tab number @tab number
@end multitable
@section Changes that introduce incompatibilities with Javascript
@subsection Standard mode
There is no incompatibility with Javascript.
@subsection Bigint mode
The following changes are visible:
@itemize
@item Integer and Float are different types. Constants are typed. For example: @code{typeof 1.0 === "number"} and @code{typeof 1 === "bigint"}. Another consequence is that @code{1.0 === 1} is false.
@item The range of integers is unlimited. In standard mode: @code{2**53 + 1 === 2**53}. This is no longer true with the bignum extensions.
@item Binary bitwise operators do not truncate to 32 bits i.e. @code{0x800000000 | 1 === 0x800000001} while it gives @code{1} in standard mode.
@item Bitwise shift operators do not truncate to 32 bits and do not mask the shift count with @code{0x1f} i.e. @code{1 << 32 === 4294967296} while it gives @code{1} in standard mode. However, the @code{>>>} operator (unsigned right shift) which is useless with bignums keeps its standard mode behavior@footnote{The unsigned right right operator could be removed in bigint mode.}.
@item Operators with integer operands never return the minus zero floating point value as result. Hence @code{Object.is(0, -0) === true}. Use @code{-0.0} to create a minus zero floating point value.
@item The @code{ToPrimitive} abstract operation is called with the @code{"integer"} preferred type when an integer is required (e.g. for bitwise binary or shift operations).
@item The prototype of integers is no longer @code{Number.prototype}. Instead@* @code{Object.getPrototypeOf(1) === BigInt.prototype}. The prototype of floats remains Number.prototype.
@item If the TC39 BigInt proposal is supported, there is no observable difference between integers and @code{bigint}s.
@end itemize
@section Operators
@subsection Arithmetic operators
The operands are converted to number values as in normal
Javascript. Then the general case is that an Integer is returned if
both operands are Integer. Otherwise, a float is returned.
The @code{+} operator also accepts strings as input and behaves like
standard Javascript in this case.
The binary operator @code{%} returns the truncated remainder of the
division. When the result is an Integer type, a dividend of zero yields a
RangeError exception.
The binary operator @code{%} in math mode returns the Euclidian
remainder of the division i.e. it is always positive.
The binary operator @code{/} returns a float.
The binary operator @code{/} in math mode returns a float if one of
the operands is float. Otherwise, @code{BigInt[Symbol.operatorDiv]} is
invoked.
The returned type of @code{a ** b} is Float if @math{a} or @math{b}
are Float. If @math{a} and @math{b} are integers:
@itemize
@item @math{b < 0} returns a Float in bigint mode. In math mode, @code{BigInt[Symbol.operatorPow]} is invoked.
@item @math{b >= 0} returns an integer.
@end itemize
The unary @code{-} and unary @code{+} return the same type as their
operand. They performs no floating point rounding when the result is a
float.
The unary operators @code{++} and @code{--} return the same type as
their operand.
In standard mode:
If the operator returns an Integer and that the result fits a
SmallInt, it is converted to SmallInt. Otherwise, the Integer is
converted to a Float.
In bigint mode:
If the operator returns an Integer and that the result fits a
SmallInt, it is converted to SmallInt. Otherwise it is a BigInt.
@subsection Logical operators
In standard mode:
The operands have their standard behavior. If the result fits a
SmallInt it is converted to a SmallInt. Otherwise it is a Float.
In bigint mode:
The operands are converted to integer values. The floating point
values are converted to integer by rounding them to zero.
The logical operators are defined assuming the integers are
represented in two complement notation.
For @code{<<} and @code{<<}, the shift can be positive or negative. So
@code{a << b} is defined as @math{\lfloor a/2^{-b} \rfloor} and
@code{a >> b} is defined as @math{\lfloor a/2^{b} \rfloor}.
The operator @code{>>>} is supported for backward compatibility and
behaves the same way as Javascript i.e. implicit conversion to @code{Uint32}.
If the result fits a SmallInt it is converted to a SmallInt. Otherwise
it is a BigInt.
@subsection Relational operators
The relational operators <, <=, >, >=, ==, != work as expected with
integers and floating point numbers (e.g. @code{1.0 == 1} is true).
The strict equality operators === and !== have the usual Javascript
semantics. In particular, different types never equal, so @code{1.0
=== 1} is false.
@section Number literals
Number literals in bigint mode have a slightly different behavior than
in standard Javascript:
@enumerate
@item
A number literal without a decimal point or an exponent is considered
as an Integer. Otherwise it is a Float.
@item
Hexadecimal, octal or binary floating point literals are accepted with
a decimal point or an exponent. The exponent is specified with the
@code{p} letter assuming a base 2. The same convention is used by
C99. Example: @code{0x1p3} is the same as @code{8.0}.
@end enumerate
@section Builtin Object changes
@subsection @code{BigInt} function
The @code{BigInt} function cannot be invoked as a constructor. When
invoked as a function, it converts its first parameter to an
integer. When a floating point number is given as parameter, it is
truncated to an integer with infinite precision.
@code{BigInt} properties:
@table @code
@item asIntN(bits, a)
Set @math{b=a \pmod{2^{bits}}}. Return @math{b} if @math{b < 2^{bits-1}}
otherwise @math{b-2^{bits}}.
@item asUintN(bits, a)
Return @math{a \pmod{2^{bits}}}.
@item tdiv(a, b)
Return @math{trunc(a/b)}. @code{b = 0} raises a RangeError
exception.
@item fdiv(a, b)
Return @math{\lfloor a/b \rfloor}. @code{b = 0} raises a RangeError
exception.
@item cdiv(a, b)
Return @math{\lceil a/b \rceil}. @code{b = 0} raises a RangeError
exception.
@item ediv(a, b)
Return @math{sgn(b) \lfloor a/{|b|} \rfloor} (Euclidian
division). @code{b = 0} raises a RangeError exception.
@item tdivrem(a, b)
@item fdivrem(a, b)
@item cdivrem(a, b)
@item edivrem(a, b)
Return an array of two elements. The first element is the quotient,
the second is the remainder. The same rounding is done as the
corresponding division operation.
@item sqrt(a)
Return @math{\lfloor \sqrt(a) \rfloor}. A RangeError exception is
raised if @math{a < 0}.
@item sqrtrem(a)
Return an array of two elements. The first element is @math{\lfloor
\sqrt{a} \rfloor}. The second element is @math{a-\lfloor \sqrt{a}
\rfloor^2}. A RangeError exception is raised if @math{a < 0}.
@item floorLog2(a)
Return -1 if @math{a \leq 0} otherwise return @math{\lfloor \log2(a) \rfloor}.
@item ctz(a)
Return the number of trailing zeros in the two's complement binary representation of a. Return -1 if @math{a=0}.
@end table
@subsection @code{BigInt.prototype}
It is a normal object.
@subsection @code{Number} constructor
The number constructor returns its argument rounded to a Float using
the global floating point environment. In bigint mode, the Number
constructor returns a Float. In standard mode, it returns a SmallInt
if the value fits it, otherwise a Float.
@subsection @code{Number.prototype}
The following properties are modified:
@table @code
@item toString(radix)
In bigint mode, integers are converted to the specified radix with
infinite precision.
@item toPrecision(p)
@item toFixed(p)
@item toExponential(p)
In bigint mode, integers are accepted and converted to string with
infinite precision.
@item parseInt(string, radix)
In bigint mode, an integer is returned and the conversion is done with
infinite precision.
@end table
@subsection @code{Math} object
The following properties are modified:
@table @code
@item abs(x)
Absolute value. Return an integer if @code{x} is an Integer. Otherwise
return a Float. No rounding is performed.
@item min(a, b)
@item max(a, b)
No rounding is performed. The returned type is the same one as the
minimum (resp. maximum) value.
@end table
@chapter Arbitrarily large floating point numbers
@section Introduction
This extension adds the @code{BigFloat} primitive type. The
@code{BigFloat} type represents floating point numbers are in base 2
with the IEEE 754 semantics. A floating
point number is represented as a sign, mantissa and exponent. The
special values @code{NaN}, @code{+/-Infinity}, @code{+0} and @code{-0}
are supported. The mantissa and exponent can have any bit length with
an implementation specific minimum and maximum.
@section Floating point rounding
Each floating point operation operates with infinite precision and
then rounds the result according to the specified floating point
environment (@code{BigFloatEnv} object). The status flags of the
environment are also set according to the result of the operation.
If no floating point environment is provided, the global floating
point environment is used.
The rounding mode of the global floating point environment is always
@code{RNDN} (``round to nearest with ties to even'')@footnote{The
rationale is that the rounding mode changes must always be
explicit.}. The status flags of the global environment cannot be
read@footnote{The rationale is to avoid side effects for the built-in
operators.}. The precision of the global environment is
@code{BigFloatEnv.prec}. The number of exponent bits of the global
environment is @code{BigFloatEnv.expBits}. If @code{BigFloatEnv.expBits} is
strictly smaller than the maximum allowed number of exponent bits
(@code{BigFloatEnv.expBitsMax}), then the global environment subnormal
flag is set to @code{true}. Otherwise it is set to @code{false};
For example, @code{prec = 53} and @code{ expBits = 11} give exactly
the same precision as the IEEE 754 64 bit floating point. The
default precision is @code{prec = 113} and @code{ expBits = 15} (IEEE
754 128 bit floating point).
The global floating point environment can only be modified temporarily
when calling a function (see @code{BigFloatEnv.setPrec}). Hence a
function can change the global floating point environment for its
callees but not for its caller.
@section Operators
The builtin operators are extended so that a BigFloat is returned if
at least one operand is a BigFloat. The computations are always done
with infinite precision and rounded according to the global floating
point environment.
@code{typeof} applied on a @code{BigFloat} returns @code{bigfloat}.
BigFloat can be compared with all the other numeric types and the
result follows the expected mathematical relations.
However, since BigFloat and Number are different types they are never
equal when using the strict comparison operators (e.g. @code{0.0 ===
0.0l} is false).
@section BigFloat literals
BigFloat literals are floating point numbers with a trailing @code{l}
suffix. BigFloat literals have an infinite precision. They are rounded
according to the global floating point environment when they are
evaluated.@footnote{Base 10 floating point literals cannot usually be
exactly represented as base 2 floating point number. In order to
ensure that the literal is represented accurately with the current
precision, it must be evaluated at runtime.}
@section Builtin Object changes
@subsection @code{BigFloat} function
The @code{BigFloat} function cannot be invoked as a constructor. When
invoked as a function: the parameter is converted to a primitive
type. If the result is a numeric type, it is converted to BigFloat
without rounding. If the result is a string, it is converted to
BigFloat using the precision of the global floating point environment.
@code{BigFloat} properties:
@table @code
@item LN2
@item PI
Getter. Return the value of the corresponding mathematical constant
rounded to nearest, ties to even with the current global
precision. The constant values are cached for small precisions.
@item MIN_VALUE
@item MAX_VALUE
@item EPSILON
Getter. Return the minimum, maximum and epsilon @code{BigFloat} values
(same definition as the corresponding @code{Number} constants).
@item fpRound(a[, e])
Round the floating point number @code{a} according to the floating
point environment @code{e} or the global environment if @code{e} is
undefined.
@item parseFloat(a[, radix[, e]])
Parse the string @code{a} as a floating point number in radix
@code{radix}. The radix is 0 (default) or from 2 to 36. The radix 0
means radix 10 unless there is a hexadecimal or binary prefix. The
result is rounded according to the floating point environment @code{e}
or the global environment if @code{e} is undefined.
@item isFinite(a)
Return true if @code{a} is a finite bigfloat.
@item isNaN(a)
Return true if @code{a} is a NaN bigfloat.
@item add(a, b[, e])
@item sub(a, b[, e])
@item mul(a, b[, e])
@item div(a, b[, e])
Perform the specified floating point operation and round the floating
point number @code{a} according to the floating point environment
@code{e} or the global environment if @code{e} is undefined. If
@code{e} is specified, the floating point status flags are updated.
@item floor(x)
@item ceil(x)
@item round(x)
@item trunc(x)
Round to an integer. No additional rounding is performed.
@item fmod(x, y[, e])
@item remainder(x, y[, e])
Floating point remainder. The quotient is truncated to zero (fmod) or
to the nearest integer with ties to even (remainder). @code{e} is an
optional floating point environment.
@item sqrt(x[, e])
Square root. Return a rounded floating point number. @code{e} is an
optional floating point environment.
@item sin(x[, e])
@item cos(x[, e])
@item tan(x[, e])
@item asin(x[, e])
@item acos(x[, e])
@item atan(x[, e])
@item atan2(x, y[, e])
@item exp(x[, e])
@item log(x[, e])
@item pow(x, y[, e])
Transcendental operations. Return a rounded floating point
number. @code{e} is an optional floating point environment.
@end table
@subsection @code{BigFloat.prototype}
The following properties are modified:
@table @code
@item toString(radix)
For floating point numbers:
@itemize
@item
If the radix is a power of two, the conversion is done with infinite
precision.
@item
Otherwise, the number is rounded to nearest with ties to even using
the global precision. It is then converted to string using the minimum
number of digits so that its conversion back to a floating point using
the global precision and round to nearest gives the same number.
@end itemize
@item toPrecision(p[, rnd_mode])
@item toFixed(p[, rnd_mode])
@item toExponential(p[, rnd_mode])
Same semantics as the corresponding @code{Number} functions with
BigFloats. There is no limit on the accepted precision @code{p}. The
rounding mode can be optionally specified. It is set by default to
@code{BigFloatEnv.RNDNA}.
@end table
@subsection @code{BigFloatEnv} constructor
The @code{BigFloatEnv([p, [,rndMode]]} constructor cannot be invoked as a
function. The floating point environment contains:
@itemize
@item the mantissa precision in bits
@item the exponent size in bits assuming an IEEE 754 representation;
@item the subnormal flag (if true, subnormal floating point numbers can
be generated by the floating point operations).
@item the rounding mode
@item the floating point status. The status flags can only be set by the floating point operations. They can be reset with @code{BigFloatEnv.prototype.clearStatus()} or with the various status flag setters.
@end itemize
@code{new BigFloatEnv([p, [,rndMode]]} creates a new floating point
environment. The status flags are reset. If no parameter is given the
precision, exponent bits and subnormal flags are copied from the
global floating point environment. Otherwise, the precision is set to
@code{p}, the number of exponent bits is set to @code{expBitsMax} and the
subnormal flags is set to @code{false}. If @code{rndMode} is
@code{undefined}, the rounding mode is set to @code{RNDN}.
@code{BigFloatEnv} properties:
@table @code
@item prec
Getter. Return the precision in bits of the global floating point
environment. The initial value is @code{53}.
@item expBits
Getter. Return the exponent size in bits of the global floating point
environment assuming an IEEE 754 representation. If @code{expBits <
expBitsMax}, then subnormal numbers are supported. The initial value
is @code{11}.
@item setPrec(f, p[, e])
Set the precision of the global floating point environment to @code{p}
and the exponent size to @code{e} then call the function
@code{f}. Then the Float precision and exponent size are reset to
their precious value and the return value of @code{f} is returned (or
an exception is raised if @code{f} raised an exception). If @code{e}
is @code{undefined} it is set to @code{BigFloatEnv.expBitsMax}. @code{p}
must be >= 53 and @code{e} must be >= 11 so that the global precision
is at least equivalent to the IEEE 754 64 bit doubles.
@item precMin
Read-only integer. Return the minimum allowed precision. Must be at least 2.
@item precMax
Read-only integer. Return the maximum allowed precision. Must be at least 53.
@item expBitsMin
Read-only integer. Return the minimum allowed exponent size in
bits. Must be at least 3.
@item expBitsMax
Read-only integer. Return the maximum allowed exponent size in
bits. Must be at least 11.
@item RNDN
Read-only integer. Round to nearest, with ties to even rounding mode.
@item RNDZ
Read-only integer. Round to zero rounding mode.
@item RNDD
Read-only integer. Round to -Infinity rounding mode.
@item RNDU
Read-only integer. Round to +Infinity rounding mode.
@item RNDNA
Read-only integer. Round to nearest, with ties away from zero rounding mode.
@item RNDNU
Read-only integer. Round to nearest, with ties to +Infinity rounding mode.
@item RNDF@footnote{Could be removed in case a deterministic behavior for floating point operations is required.}
Read-only integer. Faithful rounding mode. The result is
non-deterministically rounded to -Infinity or +Infinity. This rounding
mode usually gives a faster and deterministic running time for the
floating point operations.
@end table
@code{BigFloatEnv.prototype} properties:
@table @code
@item prec
Getter and setter (Integer). Return or set the precision in bits.
@item expBits
Getter and setter (Integer). Return or set the exponent size in bits
assuming an IEEE 754 representation.
@item rndMode
Getter and setter (Integer). Return or set the rounding mode.
@item subnormal
Getter and setter (Boolean). subnormal flag. It is false when
@code{expBits = expBitsMax}.
@item clearStatus()
Clear the status flags.
@item invalidOperation
@item divideByZero
@item overflow
@item underflow
@item inexact
Getter and setter (Boolean). Status flags.
@end table
@subsection @code{Math} object
The following properties are modified:
@table @code
@item abs(x)
Absolute value. If @code{x} is a BigFloat, its absolute value is
returned as a BigFloat. No rounding is performed.
@item min(a, b)
@item max(a, b)
The returned type is the same one as the minimum (resp. maximum)
value, so @code{BigFloat} values are accepted. When a @code{BigFloat}
is returned, no rounding is performed.
@end table
@chapter Math mode
@section Introduction
A new @emph{math mode} is enabled with the @code{"use math"}
directive. @code{"use bigint"} is implied in math mode. With this
mode, writing mathematical expressions is more intuitive, exact
results (e.g. fractions) can be computed for all operators and floating
point literals have the @code{BigFloat} type by default.
It propagates the same way as the @emph{strict mode}. In
this mode:
@itemize
@item The @code{^} operator is a similar to the power operator (@code{**}).
@item The power operator (both @code{^} and @code{**}) grammar is modified so that @code{-2^2} is allowed and yields @code{-4}.
@item The logical xor operator is still available with the @code{^^} operator.
@item The division operator invokes @code{BigInt[Symbol.operatorDiv]} in case both operands are integers.
@item The power operator invokes @code{BigInt[Symbol.operatorPow]} in case both operands are integers and the exponent is strictly negative.
@item The modulo operator returns the Euclidian remainder (always positive) instead of the truncated remainder.
@item Floating point literals are @code{BigFloat} by default (i.e. a @code{l} suffix is implied).
@end itemize
@section Builtin Object changes
@subsection @code{Symbol} constructor
The following global symbol is added for the operator overloading:
@table @code
@item operatorMathMod
@end table
@section Remaining issues
@enumerate
@item A new floating point literal suffix could be added for @code{Number} literals.
@end enumerate
@bye

1254
doc/quickjs.html Normal file

File diff suppressed because it is too large Load Diff

BIN
doc/quickjs.pdf Normal file

Binary file not shown.

983
doc/quickjs.texi Normal file
View File

@ -0,0 +1,983 @@
\input texinfo
@iftex
@afourpaper
@headings double
@end iftex
@titlepage
@afourpaper
@sp 7
@center @titlefont{QuickJS Javascript Engine}
@sp 3
@end titlepage
@setfilename spec.info
@settitle QuickJS Javascript Engine
@contents
@chapter Introduction
QuickJS is a small and embeddable Javascript engine. It supports the
upcoming ES2020 specification
@footnote{@url{https://www.ecma-international.org/ecma-262/10.0}}
including modules, asynchronous generators, proxies and BigInt.
It supports mathematical extensions such as big decimal float float
numbers (BigDecimal), big binary floating point numbers (BigFloat),
and operator overloading.
@section Main Features
@itemize
@item Small and easily embeddable: just a few C files, no external dependency, 210 KiB of x86 code for a simple ``hello world'' program.
@item Fast interpreter with very low startup time: runs the 69000 tests of the ECMAScript Test Suite@footnote{@url{https://github.com/tc39/test262}} in about 95 seconds on a single core of a desktop PC. The complete life cycle of a runtime instance completes in less than 300 microseconds.
@item Almost complete ES2019 support including modules, asynchronous
generators and full Annex B support (legacy web compatibility). Many
features from the upcoming ES2020 specification
@footnote{@url{https://tc39.github.io/ecma262/}} are also supported.
@item Passes nearly 100% of the ECMAScript Test Suite tests when selecting the ES2019 features.
@item Compile Javascript sources to executables with no external dependency.
@item Garbage collection using reference counting (to reduce memory usage and have deterministic behavior) with cycle removal.
@item Mathematical extensions: BigDecimal, BigFloat, operator overloading, bigint mode, math mode.
@item Command line interpreter with contextual colorization and completion implemented in Javascript.
@item Small built-in standard library with C library wrappers.
@end itemize
@chapter Usage
@section Installation
A Makefile is provided to compile the engine on Linux or MacOS/X. A
preliminary Windows support is available thru cross compilation on a
Linux host with the MingGW tools.
Edit the top of the @code{Makefile} if you wish to select specific
options then run @code{make}.
You can type @code{make install} as root if you wish to install the binaries and support files to
@code{/usr/local} (this is not necessary to use QuickJS).
@section Quick start
@code{qjs} is the command line interpreter (Read-Eval-Print Loop). You can pass
Javascript files and/or expressions as arguments to execute them:
@example
./qjs examples/hello.js
@end example
@code{qjsc} is the command line compiler:
@example
./qjsc -o hello examples/hello.js
./hello
@end example
generates a @code{hello} executable with no external dependency.
@section Command line options
@subsection @code{qjs} interpreter
@verbatim
usage: qjs [options] [files]
@end verbatim
Options are:
@table @code
@item -h
@item --help
List options.
@item -e @code{EXPR}
@item --eval @code{EXPR}
Evaluate EXPR.
@item -i
@item --interactive
Go to interactive mode (it is not the default when files are provided on the command line).
@item -m
@item --module
Load as ES6 module (default=autodetect). A module is autodetected if
the filename extension is @code{.mjs} or if the first keyword of the
source is @code{import}.
@item --script
Load as ES6 script (default=autodetect).
@item --bignum
Enable the bignum extensions: BigDecimal object, BigFloat object and
the @code{"use bigint"} and @code{"use math"} directives.
@end table
Advanced options are:
@table @code
@item --std
Make the @code{std} and @code{os} modules available to the loaded
script even if it is not a module.
@item -d
@item --dump
Dump the memory usage stats.
@item -q
@item --quit
just instantiate the interpreter and quit.
@end table
@subsection @code{qjsc} compiler
@verbatim
usage: qjsc [options] [files]
@end verbatim
Options are:
@table @code
@item -c
Only output bytecode in a C file. The default is to output an executable file.
@item -e
Output @code{main()} and bytecode in a C file. The default is to output an
executable file.
@item -o output
Set the output filename (default = @file{out.c} or @file{a.out}).
@item -N cname
Set the C name of the generated data.
@item -m
Compile as Javascript module (default=autodetect).
@item -M module_name[,cname]
Add initialization code for an external C module. See the
@code{c_module} example.
@item -x
Byte swapped output (only used for cross compilation).
@item -flto
Use link time optimization. The compilation is slower but the
executable is smaller and faster. This option is automatically set
when the @code{-fno-x} options are used.
@item -fno-[eval|string-normalize|regexp|json|proxy|map|typedarray|promise|bigint]
Disable selected language features to produce a smaller executable file.
@item -fbignum
Enable the bignum extensions: BigDecimal object, BigFloat object and
the @code{"use bigint"} and @code{"use math"} directives.
@end table
@section @code{qjscalc} application
The @code{qjscalc} application is a superset of the @code{qjs}
command line interpreter implementing a Javascript calculator with
arbitrarily large integer and floating point numbers, fractions,
complex numbers, polynomials and matrices. The source code is in
@file{qjscalc.js}. More documentation and a web version are available at
@url{http://numcalc.com}.
@section Built-in tests
Run @code{make test} to run the few built-in tests included in the
QuickJS archive.
@section Test262 (ECMAScript Test Suite)
A test262 runner is included in the QuickJS archive. The test262 tests
can be installed in the QuickJS source directory with:
@example
git clone https://github.com/tc39/test262.git test262
cd test262
patch -p1 < ../tests/test262.patch
cd ..
@end example
The patch adds the implementation specific @code{harness} functions
and optimizes the inefficient RegExp character classes and Unicode
property escapes tests (the tests themselves are not modified, only a
slow string initialization function is optimized).
The tests can be run with
@example
make test2
@end example
The configuration files @code{test262.conf}
(resp. @code{test262o.conf} for the old ES5.1 tests@footnote{The old
ES5.1 tests can be extracted with @code{git clone --single-branch
--branch es5-tests https://github.com/tc39/test262.git test262o}}))
contain the options to run the various tests. Tests can be excluded
based on features or filename.
The file @code{test262_errors.txt} contains the current list of
errors. The runner displays a message when a new error appears or when
an existing error is corrected or modified. Use the @code{-u} option
to update the current list of errors (or @code{make test2-update}).
The file @code{test262_report.txt} contains the logs of all the
tests. It is useful to have a clearer analysis of a particular
error. In case of crash, the last line corresponds to the failing
test.
Use the syntax @code{./run-test262 -c test262.conf -f filename.js} to
run a single test. Use the syntax @code{./run-test262 -c test262.conf
N} to start testing at test number @code{N}.
For more information, run @code{./run-test262} to see the command line
options of the test262 runner.
@code{run-test262} accepts the @code{-N} option to be invoked from
@code{test262-harness}@footnote{@url{https://github.com/bterlson/test262-harness}}
thru @code{eshost}. Unless you want to compare QuickJS with other
engines under the same conditions, we do not recommend to run the
tests this way as it is much slower (typically half an hour instead of
about 100 seconds).
@chapter Specifications
@section Language support
@subsection ES2019 support
The ES2019 specification is almost fully supported including the Annex
B (legacy web compatibility) and the Unicode related features.
The following features are not supported yet:
@itemize
@item Realms (although the C API supports different runtimes and contexts)
@item Tail calls@footnote{We believe the current specification of tails calls is too complicated and presents limited practical interests.}
@end itemize
@subsection JSON
The JSON parser is currently more tolerant than the specification.
@subsection ECMA402
ECMA402 (Internationalization API) is not supported.
@subsection Extensions
@itemize
@item The directive @code{"use strip"} indicates that the debug information (including the source code of the functions) should not be retained to save memory. As @code{"use strict"}, the directive can be global to a script or local to a function.
@item The first line of a script beginning with @code{#!} is ignored.
@end itemize
@subsection Mathematical extensions
The mathematical extensions are fully backward compatible with
standard Javascript. See @code{jsbignum.pdf} for more information.
@itemize
@item @code{BigDecimal} support: arbitrary large floating point numbers in base 10.
@item @code{BigFloat} support: arbitrary large floating point numbers in base 2.
@item Operator overloading.
@item The directive @code{"use bigint"} enables the bigint mode where integers are @code{BigInt} by default.
@item The directive @code{"use math"} enables the math mode where the division and power operators on integers produce fractions. Floating point literals are @code{BigFloat} by default and integers are @code{BigInt} by default.
@end itemize
@section Modules
ES6 modules are fully supported. The default name resolution is the
following:
@itemize
@item Module names with a leading @code{.} or @code{..} are relative
to the current module path.
@item Module names without a leading @code{.} or @code{..} are system
modules, such as @code{std} or @code{os}.
@item Module names ending with @code{.so} are native modules using the
QuickJS C API.
@end itemize
@section Standard library
The standard library is included by default in the command line
interpreter. It contains the two modules @code{std} and @code{os} and
a few global objects.
@subsection Global objects
@table @code
@item scriptArgs
Provides the command line arguments. The first argument is the script name.
@item print(...args)
Print the arguments separated by spaces and a trailing newline.
@item console.log(...args)
Same as print().
@end table
@subsection @code{std} module
The @code{std} module provides wrappers to the libc @file{stdlib.h}
and @file{stdio.h} and a few other utilities.
Available exports:
@table @code
@item exit(n)
Exit the process.
@item evalScript(str, options = undefined)
Evaluate the string @code{str} as a script (global
eval). @code{options} is an optional object containing the following
optional properties:
@table @code
@item backtrace_barrier
Boolean (default = false). If true, error backtraces do not list the
stack frames below the evalScript.
@end table
@item loadScript(filename)
Evaluate the file @code{filename} as a script (global eval).
@item Error(errno)
@code{std.Error} constructor. Error instances contain the field
@code{errno} (error code) and @code{message} (result of
@code{std.Error.strerror(errno)}).
The constructor contains the following fields:
@table @code
@item EINVAL
@item EIO
@item EACCES
@item EEXIST
@item ENOSPC
@item ENOSYS
@item EBUSY
@item ENOENT
@item EPERM
@item EPIPE
Integer value of common errors (additional error codes may be defined).
@item strerror(errno)
Return a string that describes the error @code{errno}.
@end table
@item open(filename, flags)
Open a file (wrapper to the libc @code{fopen()}). Throws
@code{std.Error} in case of I/O error.
@item popen(command, flags)
Open a process by creating a pipe (wrapper to the libc @code{popen()}). Throws
@code{std.Error} in case of I/O error.
@item fdopen(fd, flags)
Open a file from a file handle (wrapper to the libc
@code{fdopen()}). Throws @code{std.Error} in case of I/O error.
@item tmpfile()
Open a temporary file. Throws @code{std.Error} in case of I/O error.
@item puts(str)
Equivalent to @code{std.out.puts(str)}.
@item printf(fmt, ...args)
Equivalent to @code{std.out.printf(fmt, ...args)}
@item sprintf(fmt, ...args)
Equivalent to the libc sprintf().
@item in
@item out
@item err
Wrappers to the libc file @code{stdin}, @code{stdout}, @code{stderr}.
@item SEEK_SET
@item SEEK_CUR
@item SEEK_END
Constants for seek().
@item gc()
Manually invoke the cycle removal algorithm. The cycle removal
algorithm is automatically started when needed, so this function is
useful in case of specific memory constraints or for testing.
@item getenv(name)
Return the value of the environment variable @code{name} or
@code{undefined} if it is not defined.
@item urlGet(url, options = undefined)
Download @code{url} using the @file{curl} command line
utility. @code{options} is an optional object containing the following
optional properties:
@table @code
@item binary
Boolean (default = false). If true, the response is an ArrayBuffer
instead of a string. When a string is returned, the data is assumed
to be UTF-8 encoded.
@item full
Boolean (default = false). If true, return the an object contains
the properties @code{response} (response content),
@code{responseHeaders} (headers separated by CRLF), @code{status}
(status code). If @code{full} is false, only the response is
returned if the status is between 200 and 299. Otherwise an
@code{std.Error} exception is raised.
@end table
@end table
FILE prototype:
@table @code
@item close()
Close the file.
@item puts(str)
Outputs the string with the UTF-8 encoding.
@item printf(fmt, ...args)
Formatted printf, same formats as the libc printf.
@item flush()
Flush the buffered file.
@item seek(offset, whence)
Seek to a give file position (whence is @code{std.SEEK_*}). Throws a
@code{std.Error} in case of I/O error.
@item tell()
Return the current file position.
@item eof()
Return true if end of file.
@item fileno()
Return the associated OS handle.
@item read(buffer, position, length)
Read @code{length} bytes from the file to the ArrayBuffer @code{buffer} at byte
position @code{position} (wrapper to the libc @code{fread}).
@item write(buffer, position, length)
Write @code{length} bytes to the file from the ArrayBuffer @code{buffer} at byte
position @code{position} (wrapper to the libc @code{fread}).
@item getline()
Return the next line from the file, assuming UTF-8 encoding, excluding
the trailing line feed.
@item readAsString(max_size = undefined)
Read @code{max_size} bytes from the file and return them as a string
assuming UTF-8 encoding. If @code{max_size} is not present, the file
is read up its end.
@item getByte()
Return the next byte from the file. Return -1 if the end of file is reached.
@item putByte(c)
Write one byte to the file.
@end table
@subsection @code{os} module
The @code{os} module provides Operating System specific functions:
@itemize
@item low level file access
@item signals
@item timers
@item asynchronous I/O
@end itemize
The OS functions usually return 0 if OK or an OS specific negative
error code.
Available exports:
@table @code
@item open(filename, flags, mode = 0o666)
Open a file. Return a handle or < 0 if error.
@item O_RDONLY
@item O_WRONLY
@item O_RDWR
@item O_APPEND
@item O_CREAT
@item O_EXCL
@item O_TRUNC
POSIX open flags.
@item O_TEXT
(Windows specific). Open the file in text mode. The default is binary mode.
@item close(fd)
Close the file handle @code{fd}.
@item seek(fd, offset, whence)
Seek in the file. Use @code{std.SEEK_*} for @code{whence}.
@item read(fd, buffer, offset, length)
Read @code{length} bytes from the file handle @code{fd} to the
ArrayBuffer @code{buffer} at byte position @code{offset}.
Return the number of read bytes or < 0 if error.
@item write(fd, buffer, offset, length)
Write @code{length} bytes to the file handle @code{fd} from the
ArrayBuffer @code{buffer} at byte position @code{offset}.
Return the number of written bytes or < 0 if error.
@item isatty(fd)
Return @code{true} is @code{fd} is a TTY (terminal) handle.
@item ttyGetWinSize(fd)
Return the TTY size as @code{[width, height]} or @code{null} if not available.
@item ttySetRaw(fd)
Set the TTY in raw mode.
@item remove(filename)
Remove a file. Return 0 if OK or < 0 if error.
@item rename(oldname, newname)
Rename a file. Return 0 if OK or < 0 if error.
@item realpath(path)
Return @code{[str, err]} where @code{str} is the canonicalized absolute
pathname of @code{path} and @code{err} the error code.
@item getcwd()
Return @code{[str, err]} where @code{str} is the current working directory
and @code{err} the error code.
@item mkdir(path, mode = 0o777)
Create a directory at @code{path}. Return the error code.
@item stat(path)
@item lstat(path)
Return @code{[obj, err]} where @code{obj} is an object containing the
file status of @code{path}. @code{err} is the error code. The
following fields are defined in @code{obj}: dev, ino, mode, nlink,
uid, gid, rdev, size, blocks, atime, mtime, ctime. The times are
specified in milliseconds since 1970. @code{lstat()} is the same as
@code{stat()} excepts that it returns information about the link
itself.
@item S_IFMT
@item S_IFIFO
@item S_IFCHR
@item S_IFDIR
@item S_IFBLK
@item S_IFREG
@item S_IFSOCK
@item S_IFLNK
@item S_ISGID
@item S_ISUID
Constants to interpret the @code{mode} property returned by
@code{stat()}. They have the same value as in the C system header
@file{sys/stat.h}.
@item utimes(path, atime, mtime)
Change the access and modification times of the file @code{path}. The
times are specified in milliseconds since 1970.
@item symlink(target, linkpath)
Create a link at @code{linkpath} containing the string @code{target}.
@item readlink(path)
Return @code{[str, err]} where @code{str} is the link target and @code{err}
the error code.
@item readdir(path)
Return @code{[array, err]} where @code{array} is an array of strings
containing the filenames of the directory @code{path}. @code{err} is
the error code.
@item setReadHandler(fd, func)
Add a read handler to the file handle @code{fd}. @code{func} is called
each time there is data pending for @code{fd}. A single read handler
per file handle is supported. Use @code{func = null} to remove the
handler.
@item setWriteHandler(fd, func)
Add a write handler to the file handle @code{fd}. @code{func} is
called each time data can be written to @code{fd}. A single write
handler per file handle is supported. Use @code{func = null} to remove
the handler.
@item signal(signal, func)
Call the function @code{func} when the signal @code{signal}
happens. Only a single handler per signal number is supported. Use
@code{null} to set the default handler or @code{undefined} to ignore
the signal.
@item SIGINT
@item SIGABRT
@item SIGFPE
@item SIGILL
@item SIGSEGV
@item SIGTERM
POSIX signal numbers.
@item kill(pid, sig)
Send the signal @code{sig} to the process @code{pid}.
@item exec(args[, options])
Execute a process with the arguments @code{args}. @code{options} is an
object containing optional parameters:
@table @code
@item block
Boolean (default = true). If true, wait until the process is
terminated. In this case, @code{exec} return the exit code if positive
or the negated signal number if the process was interrupted by a
signal. If false, do not block and return the process id of the child.
@item usePath
Boolean (default = true). If true, the file is searched in the
@code{PATH} environment variable.
@item file
String (default = @code{args[0]}). Set the file to be executed.
@item cwd
String. If present, set the working directory of the new process.
@item stdin
@item stdout
@item stderr
If present, set the handle in the child for stdin, stdout or stderr.
@end table
@item waitpid(pid, options)
@code{waitpid} Unix system call. Return the array @code{[ret, status]}.
@item WNOHANG
Constant for the @code{options} argument of @code{waitpid}.
@item dup(fd)
@code{dup} Unix system call.
@item dup2(oldfd, newfd)
@code{dup2} Unix system call.
@item pipe()
@code{pipe} Unix system call. Return two handles as @code{[read_fd,
write_fd]} or null in case of error.
@item sleep(delay_ms)
Sleep during @code{delay_ms} milliseconds.
@item setTimeout(func, delay)
Call the function @code{func} after @code{delay} ms. Return a handle
to the timer.
@item clearTimeout(handle)
Cancel a timer.
@item platform
Return a string representing the platform: @code{"linux"}, @code{"darwin"},
@code{"win32"} or @code{"js"}.
@end table
@section QuickJS C API
The C API was designed to be simple and efficient. The C API is
defined in the header @code{quickjs.h}.
@subsection Runtime and contexts
@code{JSRuntime} represents a Javascript runtime corresponding to an
object heap. Several runtimes can exist at the same time but they
cannot exchange objects. Inside a given runtime, no multi-threading is
supported.
@code{JSContext} represents a Javascript context (or Realm). Each
JSContext has its own global objects and system objects. There can be
several JSContexts per JSRuntime and they can share objects, similar
to frames of the same origin sharing Javascript objects in a
web browser.
@subsection JSValue
@code{JSValue} represents a Javascript value which can be a primitive
type or an object. Reference counting is used, so it is important to
explicitly duplicate (@code{JS_DupValue()}, increment the reference
count) or free (@code{JS_FreeValue()}, decrement the reference count)
JSValues.
@subsection C functions
C functions can be created with
@code{JS_NewCFunction()}. @code{JS_SetPropertyFunctionList()} is a
shortcut to easily add functions, setters and getters properties to a
given object.
Unlike other embedded Javascript engines, there is no implicit stack,
so C functions get their parameters as normal C parameters. As a
general rule, C functions take constant @code{JSValue}s as parameters
(so they don't need to free them) and return a newly allocated (=live)
@code{JSValue}.
@subsection Exceptions
Exceptions: most C functions can return a Javascript exception. It
must be explicitly tested and handled by the C code. The specific
@code{JSValue} @code{JS_EXCEPTION} indicates that an exception
occurred. The actual exception object is stored in the
@code{JSContext} and can be retrieved with @code{JS_GetException()}.
@subsection Script evaluation
Use @code{JS_Eval()} to evaluate a script or module source.
If the script or module was compiled to bytecode with @code{qjsc}, it
can be evaluated by calling @code{js_std_eval_binary()}. The advantage
is that no compilation is needed so it is faster and smaller because
the compiler can be removed from the executable if no @code{eval} is
required.
Note: the bytecode format is linked to a given QuickJS
version. Moreover, no security check is done before its
execution. Hence the bytecode should not be loaded from untrusted
sources. That's why there is no option to output the bytecode to a
binary file in @code{qjsc}.
@subsection JS Classes
C opaque data can be attached to a Javascript object. The type of the
C opaque data is determined with the class ID (@code{JSClassID}) of
the object. Hence the first step is to register a new class ID and JS
class (@code{JS_NewClassID()}, @code{JS_NewClass()}). Then you can
create objects of this class with @code{JS_NewObjectClass()} and get or
set the C opaque point with
@code{JS_GetOpaque()}/@code{JS_SetOpaque()}.
When defining a new JS class, it is possible to declare a finalizer
which is called when the object is destroyed. A @code{gc_mark} method
can be provided so that the cycle removal algorithm can find the other
objects referenced by this object. Other methods are available to
define exotic object behaviors.
The Class ID are globally allocated (i.e. for all runtimes). The
JSClass are allocated per @code{JSRuntime}. @code{JS_SetClassProto()}
is used to define a prototype for a given class in a given
JSContext. @code{JS_NewObjectClass()} sets this prototype in the
created object.
Examples are available in @file{quickjs-libc.c}.
@subsection C Modules
Native ES6 modules are supported and can be dynamically or statically
linked. Look at the @file{test_bjson} and @file{bjson.so}
examples. The standard library @file{quickjs-libc.c} is also a good example
of a native module.
@subsection Memory handling
Use @code{JS_SetMemoryLimit()} to set a global memory allocation limit
to a given JSRuntime.
Custom memory allocation functions can be provided with
@code{JS_NewRuntime2()}.
The maximum system stack size can be set with @code{JS_SetMaxStackSize()}.
@subsection Execution timeout and interrupts
Use @code{JS_SetInterruptHandler()} to set a callback which is
regularly called by the engine when it is executing code. This
callback can be used to implement an execution timeout.
It is used by the command line interpreter to implement a
@code{Ctrl-C} handler.
@chapter Internals
@section Bytecode
The compiler generates bytecode directly with no intermediate
representation such as a parse tree, hence it is very fast. Several
optimizations passes are done over the generated bytecode.
A stack-based bytecode was chosen because it is simple and generates
compact code.
For each function, the maximum stack size is computed at compile time so that
no runtime stack overflow tests are needed.
A separate compressed line number table is maintained for the debug
information.
Access to closure variables is optimized and is almost as fast as local
variables.
Direct @code{eval} in strict mode is optimized.
@section Executable generation
@subsection @code{qjsc} compiler
The @code{qjsc} compiler generates C sources from Javascript files. By
default the C sources are compiled with the system compiler
(@code{gcc} or @code{clang}).
The generated C source contains the bytecode of the compiled functions
or modules. If a full complete executable is needed, it also
contains a @code{main()} function with the necessary C code to initialize the
Javascript engine and to load and execute the compiled functions and
modules.
Javascript code can be mixed with C modules.
In order to have smaller executables, specific Javascript features can
be disabled, in particular @code{eval} or the regular expressions. The
code removal relies on the Link Time Optimization of the system
compiler.
@subsection Binary JSON
@code{qjsc} works by compiling scripts or modules and then serializing
them to a binary format. A subset of this format (without functions or
modules) can be used as binary JSON. The example @file{test_bjson.js}
shows how to use it.
Warning: the binary JSON format may change without notice, so it
should not be used to store persistent data. The @file{test_bjson.js}
example is only used to test the binary object format functions.
@section Runtime
@subsection Strings
Strings are stored either as an 8 bit or a 16 bit array of
characters. Hence random access to characters is always fast.
The C API provides functions to convert Javascript Strings to C UTF-8 encoded
strings. The most common case where the Javascript string contains
only ASCII characters involves no copying.
@subsection Objects
The object shapes (object prototype, property names and flags) are shared
between objects to save memory.
Arrays with no holes (except at the end of the array) are optimized.
TypedArray accesses are optimized.
@subsection Atoms
Object property names and some strings are stored as Atoms (unique
strings) to save memory and allow fast comparison. Atoms are
represented as a 32 bit integer. Half of the atom range is reserved for
immediate integer literals from @math{0} to @math{2^{31}-1}.
@subsection Numbers
Numbers are represented either as 32-bit signed integers or 64-bit IEEE-754
floating point values. Most operations have fast paths for the 32-bit
integer case.
@subsection Garbage collection
Reference counting is used to free objects automatically and
deterministically. A separate cycle removal pass is done when the allocated
memory becomes too large. The cycle removal algorithm only uses the
reference counts and the object content, so no explicit garbage
collection roots need to be manipulated in the C code.
@subsection JSValue
It is a Javascript value which can be a primitive type (such as
Number, String, ...) or an Object. NaN boxing is used in the 32-bit version
to store 64-bit floating point numbers. The representation is
optimized so that 32-bit integers and reference counted values can be
efficiently tested.
In 64-bit code, JSValue are 128-bit large and no NaN boxing is used. The
rationale is that in 64-bit code memory usage is less critical.
In both cases (32 or 64 bits), JSValue exactly fits two CPU registers,
so it can be efficiently returned by C functions.
@subsection Function call
The engine is optimized so that function calls are fast. The system
stack holds the Javascript parameters and local variables.
@section RegExp
A specific regular expression engine was developed. It is both small
and efficient and supports all the ES2020 features including the
Unicode properties. As the Javascript compiler, it directly generates
bytecode without a parse tree.
Backtracking with an explicit stack is used so that there is no
recursion on the system stack. Simple quantifiers are specifically
optimized to avoid recursions.
Infinite recursions coming from quantifiers with empty terms are
avoided.
The full regexp library weights about 15 KiB (x86 code), excluding the
Unicode library.
@section Unicode
A specific Unicode library was developed so that there is no
dependency on an external large Unicode library such as ICU. All the
Unicode tables are compressed while keeping a reasonable access
speed.
The library supports case conversion, Unicode normalization, Unicode
script queries, Unicode general category queries and all Unicode
binary properties.
The full Unicode library weights about 45 KiB (x86 code).
@section BigInt, BigFloat, BigDecimal
BigInt, BigFloat and BigDecimal are implemented with the @code{libbf}
library@footnote{@url{https://bellard.org/libbf}}. It weights about 90
KiB (x86 code) and provides arbitrary precision IEEE 754 floating
point operations and transcendental functions with exact rounding.
@chapter License
QuickJS is released under the MIT license.
Unless otherwise specified, the QuickJS sources are copyright Fabrice
Bellard and Charlie Gordon.
@bye

72
examples/fib.c Normal file
View File

@ -0,0 +1,72 @@
/*
* QuickJS: Example of C module
*
* Copyright (c) 2017-2018 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.h"
#define countof(x) (sizeof(x) / sizeof((x)[0]))
static int fib(int n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
static JSValue js_fib(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int n, res;
if (JS_ToInt32(ctx, &n, argv[0]))
return JS_EXCEPTION;
res = fib(n);
return JS_NewInt32(ctx, res);
}
static const JSCFunctionListEntry js_fib_funcs[] = {
JS_CFUNC_DEF("fib", 1, js_fib ),
};
static int js_fib_init(JSContext *ctx, JSModuleDef *m)
{
return JS_SetModuleExportList(ctx, m, js_fib_funcs,
countof(js_fib_funcs));
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_fib
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_fib_init);
if (!m)
return NULL;
JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs));
return m;
}

10
examples/fib_module.js Normal file
View File

@ -0,0 +1,10 @@
/* fib module */
export function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}

1
examples/hello.js Normal file
View File

@ -0,0 +1 @@
console.log("Hello World");

6
examples/hello_module.js Normal file
View File

@ -0,0 +1,6 @@
/* example of JS module */
import { fib } from "./fib_module.js";
console.log("Hello World");
console.log("fib(10)=", fib(10));

70
examples/pi_bigdecimal.js Normal file
View File

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

66
examples/pi_bigfloat.js Normal file
View File

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

118
examples/pi_bigint.js Normal file
View File

@ -0,0 +1,118 @@
/*
* PI computation in Javascript using the BigInt type
*/
"use strict";
/* return floor(log2(a)) for a > 0 and 0 for a = 0 */
function floor_log2(a)
{
var k_max, a1, k, i;
k_max = 0n;
while ((a >> (2n ** k_max)) != 0n) {
k_max++;
}
k = 0n;
a1 = a;
for(i = k_max - 1n; i >= 0n; i--) {
a1 = a >> (2n ** i);
if (a1 != 0n) {
a = a1;
k |= (1n << i);
}
}
return k;
}
/* return ceil(log2(a)) for a > 0 */
function ceil_log2(a)
{
return floor_log2(a - 1n) + 1n;
}
/* return floor(sqrt(a)) (not efficient but simple) */
function int_sqrt(a)
{
var l, u, s;
if (a == 0n)
return a;
l = ceil_log2(a);
u = 1n << ((l + 1n) / 2n);
/* u >= floor(sqrt(a)) */
for(;;) {
s = u;
u = ((a / s) + s) / 2n;
if (u >= s)
break;
}
return s;
}
/* return pi * 2**prec */
function calc_pi(prec) {
const CHUD_A = 13591409n;
const CHUD_B = 545140134n;
const CHUD_C = 640320n;
const CHUD_C3 = 10939058860032000n; /* C^3/24 */
const CHUD_BITS_PER_TERM = 47.11041313821584202247; /* log2(C/12)*3 */
/* return [P, Q, G] */
function chud_bs(a, b, need_G) {
var c, P, Q, G, P1, Q1, G1, P2, Q2, G2;
if (a == (b - 1n)) {
G = (2n * b - 1n) * (6n * b - 1n) * (6n * b - 5n);
P = G * (CHUD_B * b + CHUD_A);
if (b & 1n)
P = -P;
Q = b * b * b * CHUD_C3;
} else {
c = (a + b) >> 1n;
[P1, Q1, G1] = chud_bs(a, c, true);
[P2, Q2, G2] = chud_bs(c, b, need_G);
P = P1 * Q2 + P2 * G1;
Q = Q1 * Q2;
if (need_G)
G = G1 * G2;
else
G = 0n;
}
return [P, Q, G];
}
var n, P, Q, G;
/* number of serie terms */
n = BigInt(Math.ceil(Number(prec) / CHUD_BITS_PER_TERM)) + 10n;
[P, Q, G] = chud_bs(0n, n, false);
Q = (CHUD_C / 12n) * (Q << prec) / (P + Q * CHUD_A);
G = int_sqrt(CHUD_C << (2n * prec));
return (Q * G) >> prec;
}
function main(args) {
var r, n_digits, n_bits, out;
if (args.length < 1) {
print("usage: pi n_digits");
return;
}
n_digits = args[0] | 0;
/* we add more bits to reduce the probability of bad rounding for
the last digits */
n_bits = BigInt(Math.ceil(n_digits * Math.log2(10))) + 32n;
r = calc_pi(n_bits);
r = ((10n ** BigInt(n_digits)) * r) >> n_bits;
out = r.toString();
print(out[0] + "." + out.slice(1));
}
var args;
if (typeof scriptArgs != "undefined") {
args = scriptArgs;
args.shift();
} else if (typeof arguments != "undefined") {
args = arguments;
} else {
/* default: 1000 digits */
args=[1000];
}
main(args);

151
examples/point.c Normal file
View File

@ -0,0 +1,151 @@
/*
* QuickJS: Example of C module with a class
*
* Copyright (c) 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.h"
#include <math.h>
#define countof(x) (sizeof(x) / sizeof((x)[0]))
/* Point Class */
typedef struct {
int x;
int y;
} JSPointData;
static JSClassID js_point_class_id;
static void js_point_finalizer(JSRuntime *rt, JSValue val)
{
JSPointData *s = JS_GetOpaque(val, js_point_class_id);
/* Note: 's' can be NULL in case JS_SetOpaque() was not called */
js_free_rt(rt, s);
}
static JSValue js_point_ctor(JSContext *ctx,
JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSPointData *s;
JSValue obj = JS_UNDEFINED;
JSValue proto;
s = js_mallocz(ctx, sizeof(*s));
if (!s)
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &s->x, argv[0]))
goto fail;
if (JS_ToInt32(ctx, &s->y, argv[1]))
goto fail;
/* using new_target to get the prototype is necessary when the
class is extended. */
proto = JS_GetPropertyStr(ctx, new_target, "prototype");
if (JS_IsException(proto))
goto fail;
obj = JS_NewObjectProtoClass(ctx, proto, js_point_class_id);
JS_FreeValue(ctx, proto);
if (JS_IsException(obj))
goto fail;
JS_SetOpaque(obj, s);
return obj;
fail:
js_free(ctx, s);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
static JSValue js_point_get_xy(JSContext *ctx, JSValueConst this_val, int magic)
{
JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
if (!s)
return JS_EXCEPTION;
if (magic == 0)
return JS_NewInt32(ctx, s->x);
else
return JS_NewInt32(ctx, s->y);
}
static JSValue js_point_set_xy(JSContext *ctx, JSValueConst this_val, JSValue val, int magic)
{
JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
int v;
if (!s)
return JS_EXCEPTION;
if (JS_ToInt32(ctx, &v, val))
return JS_EXCEPTION;
if (magic == 0)
s->x = v;
else
s->y = v;
return JS_UNDEFINED;
}
static JSValue js_point_norm(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSPointData *s = JS_GetOpaque2(ctx, this_val, js_point_class_id);
if (!s)
return JS_EXCEPTION;
return JS_NewFloat64(ctx, sqrt((double)s->x * s->x + (double)s->y * s->y));
}
static JSClassDef js_point_class = {
"Point",
.finalizer = js_point_finalizer,
};
static const JSCFunctionListEntry js_point_proto_funcs[] = {
JS_CGETSET_MAGIC_DEF("x", js_point_get_xy, js_point_set_xy, 0),
JS_CGETSET_MAGIC_DEF("y", js_point_get_xy, js_point_set_xy, 1),
JS_CFUNC_DEF("norm", 0, js_point_norm),
};
static int js_point_init(JSContext *ctx, JSModuleDef *m)
{
JSValue point_proto, point_class;
/* create the Point class */
JS_NewClassID(&js_point_class_id);
JS_NewClass(JS_GetRuntime(ctx), js_point_class_id, &js_point_class);
point_proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, point_proto, js_point_proto_funcs, countof(js_point_proto_funcs));
JS_SetClassProto(ctx, js_point_class_id, point_proto);
point_class = JS_NewCFunction2(ctx, js_point_ctor, "Point", 2, JS_CFUNC_constructor, 0);
/* set proto.constructor and ctor.prototype */
JS_SetConstructor(ctx, point_class, point_proto);
JS_SetModuleExport(ctx, m, "Point", point_class);
return 0;
}
JSModuleDef *js_init_module(JSContext *ctx, const char *module_name)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_point_init);
if (!m)
return NULL;
JS_AddModuleExport(ctx, m, "Point");
return m;
}

6
examples/test_fib.js Normal file
View File

@ -0,0 +1,6 @@
/* example of JS module importing a C module */
import { fib } from "./fib.so";
console.log("Hello World");
console.log("fib(10)=", fib(10));

40
examples/test_point.js Normal file
View File

@ -0,0 +1,40 @@
/* example of JS module importing a C module */
import { Point } from "./point.so";
function assert(b, str)
{
if (b) {
return;
} else {
throw Error("assertion failed: " + str);
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
get_color() {
return this.color;
}
};
function main()
{
var pt, pt2;
pt = new Point(2, 3);
assert(pt.x === 2);
assert(pt.y === 3);
pt.x = 4;
assert(pt.x === 4);
assert(pt.norm() == 5);
pt2 = new ColorPoint(2, 3, 0xffffff);
assert(pt2.x === 2);
assert(pt2.color === 0xffffff);
assert(pt2.get_color() === 0xffffff);
}
main();

918
jscompress.c Normal file
View File

@ -0,0 +1,918 @@
/*
* Javascript Compressor
*
* Copyright (c) 2008-2018 Fabrice Bellard
* Copyright (c) 2017-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 <getopt.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include "cutils.h"
typedef struct JSToken {
int tok;
char buf[20];
char *str;
int len;
int size;
int line_num; /* line number for start of token */
int lines; /* number of embedded linefeeds in token */
} JSToken;
enum {
TOK_EOF = 256,
TOK_IDENT,
TOK_STR1,
TOK_STR2,
TOK_STR3,
TOK_NUM,
TOK_COM,
TOK_LCOM,
};
void tok_reset(JSToken *tt)
{
if (tt->str != tt->buf) {
free(tt->str);
tt->str = tt->buf;
tt->size = sizeof(tt->buf);
}
tt->len = 0;
}
void tok_add_ch(JSToken *tt, int c)
{
if (tt->len + 1 > tt->size) {
tt->size *= 2;
if (tt->str == tt->buf) {
tt->str = malloc(tt->size);
memcpy(tt->str, tt->buf, tt->len);
} else {
tt->str = realloc(tt->str, tt->size);
}
}
tt->str[tt->len++] = c;
}
FILE *infile;
const char *filename;
int output_line_num;
int line_num;
int ch;
JSToken tokc;
int skip_mask;
#define DEFINE_MAX 20
char *define_tab[DEFINE_MAX];
int define_len;
void error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (filename) {
fprintf(stderr, "%s:%d: ", filename, line_num);
} else {
fprintf(stderr, "jscompress: ");
}
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
void define_symbol(const char *def)
{
int i;
for (i = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i]))
return;
}
if (define_len >= DEFINE_MAX)
error("too many defines");
define_tab[define_len++] = strdup(def);
}
void undefine_symbol(const char *def)
{
int i, j;
for (i = j = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i])) {
free(define_tab[i]);
} else {
define_tab[j++] = define_tab[i];
}
}
define_len = j;
}
const char *find_symbol(const char *def)
{
int i;
for (i = 0; i < define_len; i++) {
if (!strcmp(tokc.str, define_tab[i]))
return "1";
}
return NULL;
}
void next(void);
void nextch(void)
{
ch = fgetc(infile);
if (ch == '\n')
line_num++;
}
int skip_blanks(void)
{
for (;;) {
next();
if (tokc.tok != ' ' && tokc.tok != '\t' &&
tokc.tok != TOK_COM && tokc.tok != TOK_LCOM)
return tokc.tok;
}
}
void parse_directive(void)
{
int ifdef, mask = skip_mask;
/* simplistic preprocessor:
#define / #undef / #ifdef / #ifndef / #else / #endif
no symbol substitution.
*/
skip_mask = 0; /* disable skipping to parse preprocessor line */
nextch();
if (skip_blanks() != TOK_IDENT)
error("expected preprocessing directive after #");
if (!strcmp(tokc.str, "define")) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #define");
define_symbol(tokc.str);
} else if (!strcmp(tokc.str, "undef")) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #undef");
undefine_symbol(tokc.str);
} else if ((ifdef = 1, !strcmp(tokc.str, "ifdef")) ||
(ifdef = 0, !strcmp(tokc.str, "ifndef"))) {
if (skip_blanks() != TOK_IDENT)
error("expected identifier after #ifdef/#ifndef");
mask = (mask << 2) | 2 | ifdef;
if (find_symbol(tokc.str))
mask ^= 1;
} else if (!strcmp(tokc.str, "else")) {
if (!(mask & 2))
error("#else without a #if");
mask ^= 1;
} else if (!strcmp(tokc.str, "endif")) {
if (!(mask & 2))
error("#endif without a #if");
mask >>= 2;
} else {
error("unsupported preprocessing directive");
}
if (skip_blanks() != '\n')
error("extra characters on preprocessing line");
skip_mask = mask;
}
/* return -1 if invalid char */
static int hex_to_num(int ch)
{
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else if (ch >= '0' && ch <= '9')
return ch - '0';
else
return -1;
}
void next(void)
{
again:
tok_reset(&tokc);
tokc.line_num = line_num;
tokc.lines = 0;
switch(ch) {
case EOF:
tokc.tok = TOK_EOF;
if (skip_mask)
error("missing #endif");
break;
case 'a' ... 'z':
case 'A' ... 'Z':
case '_':
case '$':
tok_add_ch(&tokc, ch);
nextch();
while ((ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
(ch == '_' || ch == '$')) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_IDENT;
break;
case '.':
nextch();
if (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, '.');
goto has_dot;
}
tokc.tok = '.';
break;
case '0':
tok_add_ch(&tokc, ch);
nextch();
if (ch == 'x' || ch == 'X') {
/* hexa */
tok_add_ch(&tokc, ch);
nextch();
while ((ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F') ||
(ch >= '0' && ch <= '9')) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_NUM;
break;
}
goto has_digit;
case '1' ... '9':
tok_add_ch(&tokc, ch);
nextch();
has_digit:
/* decimal */
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
if (ch == '.') {
tok_add_ch(&tokc, ch);
nextch();
has_dot:
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
}
if (ch == 'e' || ch == 'E') {
tok_add_ch(&tokc, ch);
nextch();
if (ch == '+' || ch == '-') {
tok_add_ch(&tokc, ch);
nextch();
}
while (ch >= '0' && ch <= '9') {
tok_add_ch(&tokc, ch);
nextch();
}
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_NUM;
break;
case '`':
{
nextch();
while (ch != '`' && ch != EOF) {
if (ch == '\\') {
tok_add_ch(&tokc, ch);
nextch();
if (ch == EOF) {
error("unexpected char after '\\'");
}
tok_add_ch(&tokc, ch);
} else {
tok_add_ch(&tokc, ch);
nextch();
}
}
nextch();
tok_add_ch(&tokc, 0);
tokc.tok = TOK_STR3;
}
break;
case '\"':
case '\'':
{
int n, i, c, hex_digit_count;
int quote_ch;
quote_ch = ch;
nextch();
while (ch != quote_ch && ch != EOF) {
if (ch == '\\') {
nextch();
switch(ch) {
case 'n':
tok_add_ch(&tokc, '\n');
nextch();
break;
case 'r':
tok_add_ch(&tokc, '\r');
nextch();
break;
case 't':
tok_add_ch(&tokc, '\t');
nextch();
break;
case 'v':
tok_add_ch(&tokc, '\v');
nextch();
break;
case '\"':
case '\'':
case '\\':
tok_add_ch(&tokc, ch);
nextch();
break;
case '0' ... '7':
n = 0;
while (ch >= '0' && ch <= '7') {
n = n * 8 + (ch - '0');
nextch();
}
tok_add_ch(&tokc, n);
break;
case 'x':
case 'u':
if (ch == 'x')
hex_digit_count = 2;
else
hex_digit_count = 4;
nextch();
n = 0;
for(i = 0; i < hex_digit_count; i++) {
c = hex_to_num(ch);
if (c < 0)
error("unexpected char after '\\x'");
n = n * 16 + c;
nextch();
}
if (n >= 256)
error("unicode is currently unsupported");
tok_add_ch(&tokc, n);
break;
default:
error("unexpected char after '\\'");
}
} else {
/* XXX: should refuse embedded newlines */
tok_add_ch(&tokc, ch);
nextch();
}
}
nextch();
tok_add_ch(&tokc, 0);
if (quote_ch == '\'')
tokc.tok = TOK_STR1;
else
tokc.tok = TOK_STR2;
}
break;
case '/':
nextch();
if (ch == '/') {
tok_add_ch(&tokc, '/');
tok_add_ch(&tokc, ch);
nextch();
while (ch != '\n' && ch != EOF) {
tok_add_ch(&tokc, ch);
nextch();
}
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_LCOM;
} else if (ch == '*') {
int last;
tok_add_ch(&tokc, '/');
tok_add_ch(&tokc, ch);
last = 0;
for(;;) {
nextch();
if (ch == EOF)
error("unterminated comment");
if (ch == '\n')
tokc.lines++;
tok_add_ch(&tokc, ch);
if (last == '*' && ch == '/')
break;
last = ch;
}
nextch();
tok_add_ch(&tokc, '\0');
tokc.tok = TOK_COM;
} else {
tokc.tok = '/';
}
break;
case '#':
parse_directive();
goto again;
case '\n':
/* adjust line number */
tokc.line_num--;
tokc.lines++;
/* fall thru */
default:
tokc.tok = ch;
nextch();
break;
}
if (skip_mask & 1)
goto again;
}
void print_tok(FILE *f, JSToken *tt)
{
/* keep output lines in sync with input lines */
while (output_line_num < tt->line_num) {
putc('\n', f);
output_line_num++;
}
switch(tt->tok) {
case TOK_IDENT:
case TOK_COM:
case TOK_LCOM:
fprintf(f, "%s", tt->str);
break;
case TOK_NUM:
{
unsigned long a;
char *p;
a = strtoul(tt->str, &p, 0);
if (*p == '\0' && a <= 0x7fffffff) {
/* must be an integer */
fprintf(f, "%d", (int)a);
} else {
fprintf(f, "%s", tt->str);
}
}
break;
case TOK_STR3:
fprintf(f, "`%s`", tt->str);
break;
case TOK_STR1:
case TOK_STR2:
{
int i, c, quote_ch;
if (tt->tok == TOK_STR1)
quote_ch = '\'';
else
quote_ch = '\"';
fprintf(f, "%c", quote_ch);
for(i = 0; i < tt->len - 1; i++) {
c = (uint8_t)tt->str[i];
switch(c) {
case '\r':
fprintf(f, "\\r");
break;
case '\n':
fprintf(f, "\\n");
break;
case '\t':
fprintf(f, "\\t");
break;
case '\v':
fprintf(f, "\\v");
break;
case '\"':
case '\'':
if (c == quote_ch)
fprintf(f, "\\%c", c);
else
fprintf(f, "%c", c);
break;
case '\\':
fprintf(f, "\\\\");
break;
default:
/* XXX: no utf-8 support! */
if (c >= 32 && c <= 255) {
fprintf(f, "%c", c);
} else if (c <= 255)
fprintf(f, "\\x%02x", c);
else
fprintf(f, "\\u%04x", c);
break;
}
}
fprintf(f, "%c", quote_ch);
}
break;
default:
if (tokc.tok >= 256)
error("unsupported token in print_tok: %d", tt->tok);
fprintf(f, "%c", tt->tok);
break;
}
output_line_num += tt->lines;
}
/* check if token pasting could occur */
static BOOL compat_token(int c1, int c2)
{
if ((c1 == TOK_IDENT || c1 == TOK_NUM) &&
(c2 == TOK_IDENT || c2 == TOK_NUM))
return FALSE;
if ((c1 == c2 && strchr("+-<>&|=*/.", c1))
|| (c2 == '=' && strchr("+-<>&|!*/^%", c1))
|| (c1 == '=' && c2 == '>')
|| (c1 == '/' && c2 == '*')
|| (c1 == '.' && c2 == TOK_NUM)
|| (c1 == TOK_NUM && c2 == '.'))
return FALSE;
return TRUE;
}
void js_compress(const char *filename, const char *outfilename,
BOOL do_strip, BOOL keep_header)
{
FILE *outfile;
int ltok, seen_space;
line_num = 1;
infile = fopen(filename, "rb");
if (!infile) {
perror(filename);
exit(1);
}
output_line_num = 1;
outfile = fopen(outfilename, "wb");
if (!outfile) {
perror(outfilename);
exit(1);
}
nextch();
next();
ltok = 0;
seen_space = 0;
if (do_strip) {
if (keep_header) {
while (tokc.tok == ' ' ||
tokc.tok == '\n' ||
tokc.tok == '\t' ||
tokc.tok == '\v' ||
tokc.tok == '\b' ||
tokc.tok == '\f') {
seen_space = 1;
next();
}
if (tokc.tok == TOK_COM) {
print_tok(outfile, &tokc);
//fprintf(outfile, "\n");
ltok = tokc.tok;
seen_space = 0;
next();
}
}
for(;;) {
if (tokc.tok == TOK_EOF)
break;
if (tokc.tok == ' ' ||
tokc.tok == '\r' ||
tokc.tok == '\t' ||
tokc.tok == '\v' ||
tokc.tok == '\b' ||
tokc.tok == '\f' ||
tokc.tok == TOK_LCOM ||
tokc.tok == TOK_COM) {
/* don't print spaces or comments */
seen_space = 1;
} else if (tokc.tok == TOK_STR3) {
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
} else if (tokc.tok == TOK_STR1 || tokc.tok == TOK_STR2) {
int count, i;
/* find the optimal quote char */
count = 0;
for(i = 0; i < tokc.len; i++) {
if (tokc.str[i] == '\'')
count++;
else if (tokc.str[i] == '\"')
count--;
}
if (count > 0)
tokc.tok = TOK_STR2;
else if (count < 0)
tokc.tok = TOK_STR1;
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
} else {
if (seen_space && !compat_token(ltok, tokc.tok)) {
fprintf(outfile, " ");
}
print_tok(outfile, &tokc);
ltok = tokc.tok;
seen_space = 0;
}
next();
}
} else {
/* just handle preprocessing */
while (tokc.tok != TOK_EOF) {
print_tok(outfile, &tokc);
next();
}
}
fclose(outfile);
fclose(infile);
}
#define HASH_SIZE 30011
#define MATCH_LEN_MIN 3
#define MATCH_LEN_MAX (4 + 63)
#define DIST_MAX 65535
static int find_longest_match(int *pdist, const uint8_t *src, int src_len,
const int *hash_next, int cur_pos)
{
int pos, i, match_len, match_pos, pos_min, len_max;
len_max = min_int(src_len - cur_pos, MATCH_LEN_MAX);
match_len = 0;
match_pos = 0;
pos_min = max_int(cur_pos - DIST_MAX - 1, 0);
pos = hash_next[cur_pos];
while (pos >= pos_min) {
for(i = 0; i < len_max; i++) {
if (src[cur_pos + i] != src[pos + i])
break;
}
if (i > match_len) {
match_len = i;
match_pos = pos;
}
pos = hash_next[pos];
}
*pdist = cur_pos - match_pos - 1;
return match_len;
}
int lz_compress(uint8_t **pdst, const uint8_t *src, int src_len)
{
int *hash_table, *hash_next;
uint32_t h, v;
int i, dist, len, len1, dist1;
uint8_t *dst, *q;
/* build the hash table */
hash_table = malloc(sizeof(hash_table[0]) * HASH_SIZE);
for(i = 0; i < HASH_SIZE; i++)
hash_table[i] = -1;
hash_next = malloc(sizeof(hash_next[0]) * src_len);
for(i = 0; i < src_len; i++)
hash_next[i] = -1;
for(i = 0; i < src_len - MATCH_LEN_MIN + 1; i++) {
h = ((src[i] << 16) | (src[i + 1] << 8) | src[i + 2]) % HASH_SIZE;
hash_next[i] = hash_table[h];
hash_table[h] = i;
}
for(;i < src_len; i++) {
hash_next[i] = -1;
}
free(hash_table);
dst = malloc(src_len + 4); /* never larger than the source */
q = dst;
*q++ = src_len >> 24;
*q++ = src_len >> 16;
*q++ = src_len >> 8;
*q++ = src_len >> 0;
/* compress */
i = 0;
while (i < src_len) {
if (src[i] >= 128)
return -1;
len = find_longest_match(&dist, src, src_len, hash_next, i);
if (len >= MATCH_LEN_MIN) {
/* heuristic: see if better length just after */
len1 = find_longest_match(&dist1, src, src_len, hash_next, i + 1);
if (len1 > len)
goto no_match;
}
if (len < MATCH_LEN_MIN) {
no_match:
*q++ = src[i];
i++;
} else if (len <= (3 + 15) && dist < (1 << 10)) {
v = 0x8000 | ((len - 3) << 10) | dist;
*q++ = v >> 8;
*q++ = v;
i += len;
} else if (len >= 4 && len <= (4 + 63) && dist < (1 << 16)) {
v = 0xc00000 | ((len - 4) << 16) | dist;
*q++ = v >> 16;
*q++ = v >> 8;
*q++ = v;
i += len;
} else {
goto no_match;
}
}
free(hash_next);
*pdst = dst;
return q - dst;
}
static int load_file(uint8_t **pbuf, const char *filename)
{
FILE *f;
uint8_t *buf;
int buf_len;
f = fopen(filename, "rb");
if (!f) {
perror(filename);
exit(1);
}
fseek(f, 0, SEEK_END);
buf_len = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(buf_len + 1);
fread(buf, 1, buf_len, f);
buf[buf_len] = '\0';
fclose(f);
*pbuf = buf;
return buf_len;
}
static void save_file(const char *filename, const uint8_t *buf, int buf_len)
{
FILE *f;
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fwrite(buf, 1, buf_len, f);
fclose(f);
}
static void save_c_source(const char *filename, const uint8_t *buf, int buf_len,
const char *var_name)
{
FILE *f;
int i;
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fprintf(f, "/* This file is automatically generated - do not edit */\n\n");
fprintf(f, "const uint8_t %s[] = {\n", var_name);
for(i = 0; i < buf_len; i++) {
fprintf(f, " 0x%02x,", buf[i]);
if ((i % 8) == 7 || (i == buf_len - 1))
fprintf(f, "\n");
}
fprintf(f, "};\n");
fclose(f);
}
#define DEFAULT_OUTPUT_FILENAME "out.js"
void help(void)
{
printf("jscompress version 1.0 Copyright (c) 2008-2018 Fabrice Bellard\n"
"usage: jscompress [options] filename\n"
"Javascript compressor\n"
"\n"
"-h print this help\n"
"-n do not compress spaces\n"
"-H keep the first comment\n"
"-c compress to file\n"
"-C name compress to C source ('name' is the variable name)\n"
"-D symbol define preprocessor symbol\n"
"-U symbol undefine preprocessor symbol\n"
"-o outfile set the output filename (default=%s)\n",
DEFAULT_OUTPUT_FILENAME);
exit(1);
}
int main(int argc, char **argv)
{
int c, do_strip, keep_header, compress;
const char *out_filename, *c_var, *fname;
char tmpfilename[1024];
do_strip = 1;
keep_header = 0;
out_filename = DEFAULT_OUTPUT_FILENAME;
compress = 0;
c_var = NULL;
for(;;) {
c = getopt(argc, argv, "hno:HcC:D:U:");
if (c == -1)
break;
switch(c) {
case 'h':
help();
break;
case 'n':
do_strip = 0;
break;
case 'o':
out_filename = optarg;
break;
case 'H':
keep_header = 1;
break;
case 'c':
compress = 1;
break;
case 'C':
c_var = optarg;
compress = 1;
break;
case 'D':
define_symbol(optarg);
break;
case 'U':
undefine_symbol(optarg);
break;
}
}
if (optind >= argc)
help();
filename = argv[optind++];
if (compress) {
#if defined(__ANDROID__)
/* XXX: use another directory ? */
snprintf(tmpfilename, sizeof(tmpfilename), "out.%d", getpid());
#else
snprintf(tmpfilename, sizeof(tmpfilename), "/tmp/out.%d", getpid());
#endif
fname = tmpfilename;
} else {
fname = out_filename;
}
js_compress(filename, fname, do_strip, keep_header);
if (compress) {
uint8_t *buf1, *buf2;
int buf1_len, buf2_len;
buf1_len = load_file(&buf1, fname);
unlink(fname);
buf2_len = lz_compress(&buf2, buf1, buf1_len);
if (buf2_len < 0) {
fprintf(stderr, "Could not compress file (UTF8 chars are forbidden)\n");
exit(1);
}
if (c_var) {
save_c_source(out_filename, buf2, buf2_len, c_var);
} else {
save_file(out_filename, buf2, buf2_len);
}
free(buf1);
free(buf2);
}
return 0;
}

8166
libbf.c Normal file

File diff suppressed because it is too large Load Diff

499
libbf.h Normal file
View File

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

58
libregexp-opcode.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 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.
*/
#ifdef DEF
DEF(invalid, 1) /* never used */
DEF(char, 3)
DEF(char32, 5)
DEF(dot, 1)
DEF(any, 1) /* same as dot but match any character including line terminator */
DEF(line_start, 1)
DEF(line_end, 1)
DEF(goto, 5)
DEF(split_goto_first, 5)
DEF(split_next_first, 5)
DEF(match, 1)
DEF(save_start, 2) /* save start position */
DEF(save_end, 2) /* save end position, must come after saved_start */
DEF(save_reset, 3) /* reset save positions */
DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
DEF(push_i32, 5) /* push integer on the stack */
DEF(drop, 1)
DEF(word_boundary, 1)
DEF(not_word_boundary, 1)
DEF(back_reference, 2)
DEF(backward_back_reference, 2) /* must come after back_reference */
DEF(range, 3) /* variable length */
DEF(range32, 3) /* variable length */
DEF(lookahead, 5)
DEF(negative_lookahead, 5)
DEF(push_char_pos, 1) /* push the character position on the stack */
DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character
position */
DEF(prev, 1) /* go to the previous char */
DEF(simple_greedy_quant, 17)
#endif /* DEF */

2541
libregexp.c Normal file

File diff suppressed because it is too large Load Diff

91
libregexp.h Normal file
View File

@ -0,0 +1,91 @@
/*
* Regular Expression Engine
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBREGEXP_H
#define LIBREGEXP_H
#include <stddef.h>
#include "libunicode.h"
#define LRE_BOOL int /* for documentation purposes */
#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_STICKY (1 << 5)
#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,
const char *buf, size_t buf_len, int re_flags,
void *opaque);
int lre_get_capture_count(const uint8_t *bc_buf);
int lre_get_flags(const uint8_t *bc_buf);
int lre_exec(uint8_t **capture,
const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
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);
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 */

4313
libunicode-table.h Normal file

File diff suppressed because it is too large Load Diff

1538
libunicode.c Normal file

File diff suppressed because it is too large Load Diff

124
libunicode.h Normal file
View File

@ -0,0 +1,124 @@
/*
* Unicode utilities
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBUNICODE_H
#define LIBUNICODE_H
#include <inttypes.h>
#define LRE_BOOL int /* for documentation purposes */
/* 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);
LRE_BOOL lre_is_cased(uint32_t c);
LRE_BOOL lre_is_case_ignorable(uint32_t c);
/* char ranges */
typedef struct {
int len; /* in points, always even */
int size;
uint32_t *points; /* points sorted by increasing value */
void *mem_opaque;
void *(*realloc_func)(void *opaque, void *ptr, size_t size);
} CharRange;
typedef enum {
CR_OP_UNION,
CR_OP_INTER,
CR_OP_XOR,
} CharRangeOpEnum;
void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
void cr_free(CharRange *cr);
int cr_realloc(CharRange *cr, int size);
int cr_copy(CharRange *cr, const CharRange *cr1);
static inline int cr_add_point(CharRange *cr, uint32_t v)
{
if (cr->len >= cr->size) {
if (cr_realloc(cr, cr->len + 1))
return -1;
}
cr->points[cr->len++] = v;
return 0;
}
static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
if ((cr->len + 2) > cr->size) {
if (cr_realloc(cr, cr->len + 2))
return -1;
}
cr->points[cr->len++] = c1;
cr->points[cr->len++] = c2;
return 0;
}
int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len);
static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
{
uint32_t b_pt[2];
b_pt[0] = c1;
b_pt[1] = c2 + 1;
return cr_union1(cr, b_pt, 2);
}
int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
const uint32_t *b_pt, int b_len, int op);
int cr_invert(CharRange *cr);
#ifdef CONFIG_ALL_UNICODE
LRE_BOOL lre_is_id_start(uint32_t c);
LRE_BOOL lre_is_id_continue(uint32_t c);
int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
UnicodeNormalizationEnum n_type,
void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
/* Unicode character range functions */
int unicode_script(CharRange *cr,
const char *script_name, LRE_BOOL is_ext);
int unicode_general_category(CharRange *cr, const char *gc_name);
int unicode_prop(CharRange *cr, const char *prop_name);
#endif /* CONFIG_ALL_UNICODE */
#undef LRE_BOOL
#endif /* LIBUNICODE_H */

100
list.h Normal file
View File

@ -0,0 +1,100 @@
/*
* Linux klist like system
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIST_H
#define LIST_H
#ifndef NULL
#include <stddef.h>
#endif
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_HEAD_INIT(el) { &(el), &(el) }
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) \
((type *)((uint8_t *)(el) - offsetof(type, member)))
static inline void init_list_head(struct list_head *head)
{
head->prev = head;
head->next = head;
}
/* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next)
{
prev->next = el;
el->prev = prev;
el->next = next;
next->prev = el;
}
/* add 'el' at the head of the list 'head' (= after element head) */
static inline void list_add(struct list_head *el, struct list_head *head)
{
__list_add(el, head, head->next);
}
/* add 'el' at the end of the list 'head' (= before element head) */
static inline void list_add_tail(struct list_head *el, struct list_head *head)
{
__list_add(el, head->prev, head);
}
static inline void list_del(struct list_head *el)
{
struct list_head *prev, *next;
prev = el->prev;
next = el->next;
prev->next = next;
next->prev = prev;
el->prev = NULL; /* fail safe */
el->next = NULL; /* fail safe */
}
static inline int list_empty(struct list_head *el)
{
return el->next == el;
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)
#define list_for_each_safe(el, el1, head) \
for(el = (head)->next, el1 = el->next; el != (head); \
el = el1, el1 = el->next)
#define list_for_each_prev(el, head) \
for(el = (head)->prev; el != (head); el = el->prev)
#define list_for_each_prev_safe(el, el1, head) \
for(el = (head)->prev, el1 = el->prev; el != (head); \
el = el1, el1 = el->prev)
#endif /* LIST_H */

520
qjs.c Normal file
View File

@ -0,0 +1,520 @@
/*
* QuickJS stand alone interpreter
*
* Copyright (c) 2017-2020 Fabrice Bellard
* Copyright (c) 2017-2020 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 <inttypes.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#if defined(__APPLE__)
#include <malloc/malloc.h>
#elif defined(__linux__)
#include <malloc.h>
#endif
#include "cutils.h"
#include "quickjs-libc.h"
/* enable bignums */
#define CONFIG_BIGNUM
extern const uint8_t qjsc_repl[];
extern const uint32_t qjsc_repl_size;
#ifdef CONFIG_BIGNUM
extern const uint8_t qjsc_qjscalc[];
extern const uint32_t qjsc_qjscalc_size;
#endif
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
const char *filename, int eval_flags)
{
JSValue val;
int ret;
if ((eval_flags & JS_EVAL_TYPE_MASK) == JS_EVAL_TYPE_MODULE) {
/* for the modules, we compile then run to be able to set
import.meta */
val = JS_Eval(ctx, buf, buf_len, filename,
eval_flags | JS_EVAL_FLAG_COMPILE_ONLY);
if (!JS_IsException(val)) {
js_module_set_import_meta(ctx, val, TRUE, TRUE);
val = JS_EvalFunction(ctx, val);
}
} else {
val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
}
if (JS_IsException(val)) {
js_std_dump_error(ctx);
ret = -1;
} else {
ret = 0;
}
JS_FreeValue(ctx, val);
return ret;
}
static int eval_file(JSContext *ctx, const char *filename, int module)
{
uint8_t *buf;
int ret, eval_flags;
size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename);
if (!buf) {
perror(filename);
exit(1);
}
if (module < 0) {
module = (has_suffix(filename, ".mjs") ||
JS_DetectModule((const char *)buf, buf_len));
}
if (module)
eval_flags = JS_EVAL_TYPE_MODULE;
else
eval_flags = JS_EVAL_TYPE_GLOBAL;
ret = eval_buf(ctx, buf, buf_len, filename, eval_flags);
js_free(ctx, buf);
return ret;
}
#if defined(__APPLE__)
#define MALLOC_OVERHEAD 0
#else
#define MALLOC_OVERHEAD 8
#endif
struct trace_malloc_data {
uint8_t *base;
};
static inline unsigned long long js_trace_malloc_ptr_offset(uint8_t *ptr,
struct trace_malloc_data *dp)
{
return ptr - dp->base;
}
/* default memory allocation functions with memory limitation */
static inline size_t js_trace_malloc_usable_size(void *ptr)
{
#if defined(__APPLE__)
return malloc_size(ptr);
#elif defined(_WIN32)
return _msize(ptr);
#elif defined(EMSCRIPTEN)
return 0;
#elif defined(__linux__)
return malloc_usable_size(ptr);
#else
/* change this to `return 0;` if compilation fails */
return malloc_usable_size(ptr);
#endif
}
static void __attribute__((format(printf, 2, 3)))
js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
{
va_list ap;
int c;
va_start(ap, fmt);
while ((c = *fmt++) != '\0') {
if (c == '%') {
/* only handle %p and %zd */
if (*fmt == 'p') {
uint8_t *ptr = va_arg(ap, void *);
if (ptr == NULL) {
printf("NULL");
} else {
printf("H%+06lld.%zd",
js_trace_malloc_ptr_offset(ptr, s->opaque),
js_trace_malloc_usable_size(ptr));
}
fmt++;
continue;
}
if (fmt[0] == 'z' && fmt[1] == 'd') {
size_t sz = va_arg(ap, size_t);
printf("%zd", sz);
fmt += 2;
continue;
}
}
putc(c, stdout);
}
va_end(ap);
}
static void js_trace_malloc_init(struct trace_malloc_data *s)
{
free(s->base = malloc(8));
}
static void *js_trace_malloc(JSMallocState *s, size_t size)
{
void *ptr;
/* Do not allocate zero bytes: behavior is platform dependent */
assert(size != 0);
if (unlikely(s->malloc_size + size > s->malloc_limit))
return NULL;
ptr = malloc(size);
js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
if (ptr) {
s->malloc_count++;
s->malloc_size += js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
}
return ptr;
}
static void js_trace_free(JSMallocState *s, void *ptr)
{
if (!ptr)
return;
js_trace_malloc_printf(s, "F %p\n", ptr);
s->malloc_count--;
s->malloc_size -= js_trace_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
free(ptr);
}
static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
{
size_t old_size;
if (!ptr) {
if (size == 0)
return NULL;
return js_trace_malloc(s, size);
}
old_size = js_trace_malloc_usable_size(ptr);
if (size == 0) {
js_trace_malloc_printf(s, "R %zd %p\n", size, ptr);
s->malloc_count--;
s->malloc_size -= old_size + MALLOC_OVERHEAD;
free(ptr);
return NULL;
}
if (s->malloc_size + size - old_size > s->malloc_limit)
return NULL;
js_trace_malloc_printf(s, "R %zd %p", size, ptr);
ptr = realloc(ptr, size);
js_trace_malloc_printf(s, " -> %p\n", ptr);
if (ptr) {
s->malloc_size += js_trace_malloc_usable_size(ptr) - old_size;
}
return ptr;
}
static const JSMallocFunctions trace_mf = {
js_trace_malloc,
js_trace_free,
js_trace_realloc,
#if defined(__APPLE__)
malloc_size,
#elif defined(_WIN32)
(size_t (*)(const void *))_msize,
#elif defined(EMSCRIPTEN)
NULL,
#elif defined(__linux__)
(size_t (*)(const void *))malloc_usable_size,
#else
/* change this to `NULL,` if compilation fails */
malloc_usable_size,
#endif
};
#define PROG_NAME "qjs"
void help(void)
{
printf("QuickJS version " CONFIG_VERSION "\n"
"usage: " PROG_NAME " [options] [file]\n"
"-h --help list options\n"
"-e --eval EXPR evaluate EXPR\n"
"-i --interactive go to interactive mode\n"
"-m --module load as ES6 module (default=autodetect)\n"
" --script load as ES6 script (default=autodetect)\n"
" --std make 'std' and 'os' available to the loaded script\n"
#ifdef CONFIG_BIGNUM
" --bignum enable the bignum extensions (BigFloat, BigDecimal)\n"
" --qjscalc load the QJSCalc runtime (default if invoked as qjscalc)\n"
#endif
"-T --trace trace memory allocation\n"
"-d --dump dump the memory usage stats\n"
" --memory-limit n limit the memory usage to 'n' bytes\n"
" --unhandled-rejection dump unhandled promise rejections\n"
"-q --quit just instantiate the interpreter and quit\n");
exit(1);
}
int main(int argc, char **argv)
{
JSRuntime *rt;
JSContext *ctx;
struct trace_malloc_data trace_data = { NULL };
int optind;
char *expr = NULL;
int interactive = 0;
int dump_memory = 0;
int trace_memory = 0;
int empty_run = 0;
int module = -1;
int load_std = 0;
int dump_unhandled_promise_rejection = 0;
size_t memory_limit = 0;
#ifdef CONFIG_BIGNUM
int load_jscalc, bignum_ext = 0;
#endif
#ifdef CONFIG_BIGNUM
/* load jscalc runtime if invoked as 'qjscalc' */
{
const char *p, *exename;
exename = argv[0];
p = strrchr(exename, '/');
if (p)
exename = p + 1;
load_jscalc = !strcmp(exename, "qjscalc");
}
#endif
/* cannot use getopt because we want to pass the command line to
the script */
optind = 1;
while (optind < argc && *argv[optind] == '-') {
char *arg = argv[optind] + 1;
const char *longopt = "";
/* a single - is not an option, it also stops argument scanning */
if (!*arg)
break;
optind++;
if (*arg == '-') {
longopt = arg + 1;
arg += strlen(arg);
/* -- stops argument scanning */
if (!*longopt)
break;
}
for (; *arg || *longopt; longopt = "") {
char opt = *arg;
if (opt)
arg++;
if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) {
help();
continue;
}
if (opt == 'e' || !strcmp(longopt, "eval")) {
if (*arg) {
expr = arg;
break;
}
if (optind < argc) {
expr = argv[optind++];
break;
}
fprintf(stderr, "qjs: missing expression for -e\n");
exit(2);
}
if (opt == 'i' || !strcmp(longopt, "interactive")) {
interactive++;
continue;
}
if (opt == 'm' || !strcmp(longopt, "module")) {
module = 1;
continue;
}
if (!strcmp(longopt, "script")) {
module = 0;
continue;
}
if (opt == 'd' || !strcmp(longopt, "dump")) {
dump_memory++;
continue;
}
if (opt == 'T' || !strcmp(longopt, "trace")) {
trace_memory++;
continue;
}
if (!strcmp(longopt, "std")) {
load_std = 1;
continue;
}
if (!strcmp(longopt, "unhandled-rejection")) {
dump_unhandled_promise_rejection = 1;
continue;
}
#ifdef CONFIG_BIGNUM
if (!strcmp(longopt, "bignum")) {
bignum_ext = 1;
continue;
}
if (!strcmp(longopt, "qjscalc")) {
load_jscalc = 1;
continue;
}
#endif
if (opt == 'q' || !strcmp(longopt, "quit")) {
empty_run++;
continue;
}
if (!strcmp(longopt, "memory-limit")) {
if (optind >= argc) {
fprintf(stderr, "expecting memory limit");
exit(1);
}
memory_limit = (size_t)strtod(argv[optind++], NULL);
continue;
}
if (opt) {
fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
} else {
fprintf(stderr, "qjs: unknown option '--%s'\n", longopt);
}
help();
}
}
if (trace_memory) {
js_trace_malloc_init(&trace_data);
rt = JS_NewRuntime2(&trace_mf, &trace_data);
} else {
rt = JS_NewRuntime();
}
if (!rt) {
fprintf(stderr, "qjs: cannot allocate JS runtime\n");
exit(2);
}
if (memory_limit != 0)
JS_SetMemoryLimit(rt, memory_limit);
ctx = JS_NewContext(rt);
if (!ctx) {
fprintf(stderr, "qjs: cannot allocate JS context\n");
exit(2);
}
#ifdef CONFIG_BIGNUM
if (bignum_ext || load_jscalc) {
JS_AddIntrinsicBigFloat(ctx);
JS_AddIntrinsicBigDecimal(ctx);
JS_EnableBignumExt(ctx, TRUE);
}
#endif
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
if (dump_unhandled_promise_rejection) {
JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker,
NULL);
}
if (!empty_run) {
#ifdef CONFIG_BIGNUM
if (load_jscalc) {
js_std_eval_binary(ctx, qjsc_qjscalc, qjsc_qjscalc_size, 0);
}
#endif
js_std_add_helpers(ctx, argc - optind, argv + optind);
/* system modules */
js_init_module_std(ctx, "std");
js_init_module_os(ctx, "os");
/* 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);
}
if (expr) {
if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", 0))
goto fail;
} else
if (optind >= argc) {
/* interactive mode */
interactive = 1;
} else {
const char *filename;
filename = argv[optind];
if (eval_file(ctx, filename, module))
goto fail;
}
if (interactive) {
js_std_eval_binary(ctx, qjsc_repl, qjsc_repl_size, 0);
}
js_std_loop(ctx);
}
if (dump_memory) {
JSMemoryUsage stats;
JS_ComputeMemoryUsage(rt, &stats);
JS_DumpMemoryUsage(stdout, &stats, rt);
}
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
if (empty_run && dump_memory) {
clock_t t[5];
double best[5];
int i, j;
for (i = 0; i < 100; i++) {
t[0] = clock();
rt = JS_NewRuntime();
t[1] = clock();
ctx = JS_NewContext(rt);
t[2] = clock();
JS_FreeContext(ctx);
t[3] = clock();
JS_FreeRuntime(rt);
t[4] = clock();
for (j = 4; j > 0; j--) {
double ms = 1000.0 * (t[j] - t[j - 1]) / CLOCKS_PER_SEC;
if (i == 0 || best[j] > ms)
best[j] = ms;
}
}
printf("\nInstantiation times (ms): %.3f = %.3f+%.3f+%.3f+%.3f\n",
best[1] + best[2] + best[3] + best[4],
best[1], best[2], best[3], best[4]);
}
return 0;
fail:
js_std_free_handlers(rt);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return 1;
}

707
qjsc.c Normal file
View File

@ -0,0 +1,707 @@
/*
* QuickJS command line compiler
*
* Copyright (c) 2018-2020 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 <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#if !defined(_WIN32)
#include <sys/wait.h>
#endif
#include "cutils.h"
#include "quickjs-libc.h"
/* enable bignums */
#define CONFIG_BIGNUM
typedef struct {
char *name;
char *short_name;
int flags;
} namelist_entry_t;
typedef struct namelist_t {
namelist_entry_t *array;
int count;
int size;
} namelist_t;
typedef struct {
const char *option_name;
const char *init_name;
} FeatureEntry;
static namelist_t cname_list;
static namelist_t cmodule_list;
static namelist_t init_module_list;
static uint64_t feature_bitmap;
static FILE *outfile;
static BOOL byte_swap;
static BOOL dynamic_export;
static const char *c_ident_prefix = "qjsc_";
#define FE_ALL (-1)
static const FeatureEntry feature_list[] = {
{ "date", "Date" },
{ "eval", "Eval" },
{ "string-normalize", "StringNormalize" },
{ "regexp", "RegExp" },
{ "json", "JSON" },
{ "proxy", "Proxy" },
{ "map", "MapSet" },
{ "typedarray", "TypedArrays" },
{ "promise", "Promise" },
#define FE_MODULE_LOADER 9
{ "module-loader", NULL },
{ "bigint", "BigInt" },
};
void namelist_add(namelist_t *lp, const char *name, const char *short_name,
int flags)
{
namelist_entry_t *e;
if (lp->count == lp->size) {
size_t newsize = lp->size + (lp->size >> 1) + 4;
namelist_entry_t *a =
realloc(lp->array, sizeof(lp->array[0]) * newsize);
/* XXX: check for realloc failure */
lp->array = a;
lp->size = newsize;
}
e = &lp->array[lp->count++];
e->name = strdup(name);
if (short_name)
e->short_name = strdup(short_name);
else
e->short_name = NULL;
e->flags = flags;
}
void namelist_free(namelist_t *lp)
{
while (lp->count > 0) {
namelist_entry_t *e = &lp->array[--lp->count];
free(e->name);
free(e->short_name);
}
free(lp->array);
lp->array = NULL;
lp->size = 0;
}
namelist_entry_t *namelist_find(namelist_t *lp, const char *name)
{
int i;
for(i = 0; i < lp->count; i++) {
namelist_entry_t *e = &lp->array[i];
if (!strcmp(e->name, name))
return e;
}
return NULL;
}
static void get_c_name(char *buf, size_t buf_size, const char *file)
{
const char *p, *r;
size_t len, i;
int c;
char *q;
p = strrchr(file, '/');
if (!p)
p = file;
else
p++;
r = strrchr(p, '.');
if (!r)
len = strlen(p);
else
len = r - p;
pstrcpy(buf, buf_size, c_ident_prefix);
q = buf + strlen(buf);
for(i = 0; i < len; i++) {
c = p[i];
if (!((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z'))) {
c = '_';
}
if ((q - buf) < buf_size - 1)
*q++ = c;
}
*q = '\0';
}
static void dump_hex(FILE *f, const uint8_t *buf, size_t len)
{
size_t i, col;
col = 0;
for(i = 0; i < len; i++) {
fprintf(f, " 0x%02x,", buf[i]);
if (++col == 8) {
fprintf(f, "\n");
col = 0;
}
}
if (col != 0)
fprintf(f, "\n");
}
static void output_object_code(JSContext *ctx,
FILE *fo, JSValueConst obj, const char *c_name,
BOOL load_only)
{
uint8_t *out_buf;
size_t out_buf_len;
int flags;
flags = JS_WRITE_OBJ_BYTECODE;
if (byte_swap)
flags |= JS_WRITE_OBJ_BSWAP;
out_buf = JS_WriteObject(ctx, &out_buf_len, obj, flags);
if (!out_buf) {
js_std_dump_error(ctx);
exit(1);
}
namelist_add(&cname_list, c_name, NULL, load_only);
fprintf(fo, "const uint32_t %s_size = %u;\n\n",
c_name, (unsigned int)out_buf_len);
fprintf(fo, "const uint8_t %s[%u] = {\n",
c_name, (unsigned int)out_buf_len);
dump_hex(fo, out_buf, out_buf_len);
fprintf(fo, "};\n\n");
js_free(ctx, out_buf);
}
static int js_module_dummy_init(JSContext *ctx, JSModuleDef *m)
{
/* should never be called when compiling JS code */
abort();
}
static void find_unique_cname(char *cname, size_t cname_size)
{
char cname1[1024];
int suffix_num;
size_t len, max_len;
assert(cname_size >= 32);
/* find a C name not matching an existing module C name by
adding a numeric suffix */
len = strlen(cname);
max_len = cname_size - 16;
if (len > max_len)
cname[max_len] = '\0';
suffix_num = 1;
for(;;) {
snprintf(cname1, sizeof(cname1), "%s_%d", cname, suffix_num);
if (!namelist_find(&cname_list, cname1))
break;
suffix_num++;
}
pstrcpy(cname, cname_size, cname1);
}
JSModuleDef *jsc_module_loader(JSContext *ctx,
const char *module_name, void *opaque)
{
JSModuleDef *m;
namelist_entry_t *e;
/* check if it is a declared C or system module */
e = namelist_find(&cmodule_list, module_name);
if (e) {
/* add in the static init module list */
namelist_add(&init_module_list, e->name, e->short_name, 0);
/* create a dummy module */
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
} else if (has_suffix(module_name, ".so")) {
fprintf(stderr, "Warning: binary module '%s' will be dynamically loaded\n", module_name);
/* create a dummy module */
m = JS_NewCModule(ctx, module_name, js_module_dummy_init);
/* the resulting executable will export its symbols for the
dynamic library */
dynamic_export = TRUE;
} else {
size_t buf_len;
uint8_t *buf;
JSValue func_val;
char cname[1024];
buf = js_load_file(ctx, &buf_len, module_name);
if (!buf) {
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
module_name);
return NULL;
}
/* compile the module */
func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name,
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
js_free(ctx, buf);
if (JS_IsException(func_val))
return NULL;
get_c_name(cname, sizeof(cname), module_name);
if (namelist_find(&cname_list, cname)) {
find_unique_cname(cname, sizeof(cname));
}
output_object_code(ctx, outfile, func_val, cname, TRUE);
/* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
}
return m;
}
static void compile_file(JSContext *ctx, FILE *fo,
const char *filename,
const char *c_name1,
int module)
{
uint8_t *buf;
char c_name[1024];
int eval_flags;
JSValue obj;
size_t buf_len;
buf = js_load_file(ctx, &buf_len, filename);
if (!buf) {
fprintf(stderr, "Could not load '%s'\n", filename);
exit(1);
}
eval_flags = JS_EVAL_FLAG_COMPILE_ONLY;
if (module < 0) {
module = (has_suffix(filename, ".mjs") ||
JS_DetectModule((const char *)buf, buf_len));
}
if (module)
eval_flags |= JS_EVAL_TYPE_MODULE;
else
eval_flags |= JS_EVAL_TYPE_GLOBAL;
obj = JS_Eval(ctx, (const char *)buf, buf_len, filename, eval_flags);
if (JS_IsException(obj)) {
js_std_dump_error(ctx);
exit(1);
}
js_free(ctx, buf);
if (c_name1) {
pstrcpy(c_name, sizeof(c_name), c_name1);
} else {
get_c_name(c_name, sizeof(c_name), filename);
}
output_object_code(ctx, fo, obj, c_name, FALSE);
JS_FreeValue(ctx, obj);
}
static const char main_c_template1[] =
"int main(int argc, char **argv)\n"
"{\n"
" JSRuntime *rt;\n"
" JSContext *ctx;\n"
" rt = JS_NewRuntime();\n"
;
static const char main_c_template2[] =
" js_std_loop(ctx);\n"
" JS_FreeContext(ctx);\n"
" JS_FreeRuntime(rt);\n"
" return 0;\n"
"}\n";
#define PROG_NAME "qjsc"
void help(void)
{
printf("QuickJS Compiler version " CONFIG_VERSION "\n"
"usage: " PROG_NAME " [options] [files]\n"
"\n"
"options are:\n"
"-c only output bytecode in a C file\n"
"-e output main() and bytecode in a C file (default = executable output)\n"
"-o output set the output filename\n"
"-N cname set the C name of the generated data\n"
"-m compile as Javascript module (default=autodetect)\n"
"-M module_name[,cname] add initialization code for an external C module\n"
"-x byte swapped output\n"
"-p prefix set the prefix of the generated C names\n"
);
#ifdef CONFIG_LTO
{
int i;
printf("-flto use link time optimization\n");
printf("-fbignum enable bignum extensions\n");
printf("-fno-[");
for(i = 0; i < countof(feature_list); i++) {
if (i != 0)
printf("|");
printf("%s", feature_list[i].option_name);
}
printf("]\n"
" disable selected language features (smaller code size)\n");
}
#endif
exit(1);
}
#if defined(CONFIG_CC) && !defined(_WIN32)
int exec_cmd(char **argv)
{
int pid, status, ret;
pid = fork();
if (pid == 0) {
execvp(argv[0], argv);
exit(1);
}
for(;;) {
ret = waitpid(pid, &status, 0);
if (ret == pid && WIFEXITED(status))
break;
}
return WEXITSTATUS(status);
}
static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
const char *argv[64];
const char **arg, *bn_suffix, *lto_suffix;
char libjsname[1024];
char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p;
int ret;
/* get the directory of the executable */
pstrcpy(exe_dir, sizeof(exe_dir), exename);
p = strrchr(exe_dir, '/');
if (p) {
*p = '\0';
} else {
pstrcpy(exe_dir, sizeof(exe_dir), ".");
}
/* if 'quickjs.h' is present at the same path as the executable, we
use it as include and lib directory */
snprintf(buf, sizeof(buf), "%s/quickjs.h", exe_dir);
if (access(buf, R_OK) == 0) {
pstrcpy(inc_dir, sizeof(inc_dir), exe_dir);
pstrcpy(lib_dir, sizeof(lib_dir), exe_dir);
} else {
snprintf(inc_dir, sizeof(inc_dir), "%s/include/quickjs", CONFIG_PREFIX);
snprintf(lib_dir, sizeof(lib_dir), "%s/lib/quickjs", CONFIG_PREFIX);
}
lto_suffix = "";
bn_suffix = "";
arg = argv;
*arg++ = CONFIG_CC;
*arg++ = "-O2";
#ifdef CONFIG_LTO
if (use_lto) {
*arg++ = "-flto";
lto_suffix = ".lto";
}
#endif
/* XXX: use the executable path to find the includes files and
libraries */
*arg++ = "-D";
*arg++ = "_GNU_SOURCE";
*arg++ = "-I";
*arg++ = inc_dir;
*arg++ = "-o";
*arg++ = out_filename;
if (dynamic_export)
*arg++ = "-rdynamic";
*arg++ = cfilename;
snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a",
lib_dir, bn_suffix, lto_suffix);
*arg++ = libjsname;
*arg++ = "-lm";
*arg++ = "-ldl";
*arg = NULL;
if (verbose) {
for(arg = argv; *arg != NULL; arg++)
printf("%s ", *arg);
printf("\n");
}
ret = exec_cmd((char **)argv);
unlink(cfilename);
return ret;
}
#else
static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
fprintf(stderr, "Executable output is not supported for this target\n");
exit(1);
return 0;
}
#endif
typedef enum {
OUTPUT_C,
OUTPUT_C_MAIN,
OUTPUT_EXECUTABLE,
} OutputTypeEnum;
int main(int argc, char **argv)
{
int c, i, verbose;
const char *out_filename, *cname;
char cfilename[1024];
FILE *fo;
JSRuntime *rt;
JSContext *ctx;
BOOL use_lto, bignum_ext;
int module;
OutputTypeEnum output_type;
out_filename = NULL;
output_type = OUTPUT_EXECUTABLE;
cname = NULL;
feature_bitmap = FE_ALL;
module = -1;
byte_swap = FALSE;
verbose = 0;
use_lto = FALSE;
bignum_ext = FALSE;
/* add system modules */
namelist_add(&cmodule_list, "std", "std", 0);
namelist_add(&cmodule_list, "os", "os", 0);
for(;;) {
c = getopt(argc, argv, "ho:cN:f:mxevM:p:");
if (c == -1)
break;
switch(c) {
case 'h':
help();
case 'o':
out_filename = optarg;
break;
case 'c':
output_type = OUTPUT_C;
break;
case 'e':
output_type = OUTPUT_C_MAIN;
break;
case 'N':
cname = optarg;
break;
case 'f':
{
const char *p;
p = optarg;
if (!strcmp(optarg, "lto")) {
use_lto = TRUE;
} else if (strstart(p, "no-", &p)) {
use_lto = TRUE;
for(i = 0; i < countof(feature_list); i++) {
if (!strcmp(p, feature_list[i].option_name)) {
feature_bitmap &= ~((uint64_t)1 << i);
break;
}
}
if (i == countof(feature_list))
goto bad_feature;
} else if (!strcmp(optarg, "bignum")) {
bignum_ext = TRUE;
} else {
bad_feature:
fprintf(stderr, "unsupported feature: %s\n", optarg);
exit(1);
}
}
break;
case 'm':
module = 1;
break;
case 'M':
{
char *p;
char path[1024];
char cname[1024];
pstrcpy(path, sizeof(path), optarg);
p = strchr(path, ',');
if (p) {
*p = '\0';
pstrcpy(cname, sizeof(cname), p + 1);
} else {
get_c_name(cname, sizeof(cname), path);
}
namelist_add(&cmodule_list, path, cname, 0);
}
break;
case 'x':
byte_swap = TRUE;
break;
case 'v':
verbose++;
break;
case 'p':
c_ident_prefix = optarg;
break;
default:
break;
}
}
if (optind >= argc)
help();
if (!out_filename) {
if (output_type == OUTPUT_EXECUTABLE) {
out_filename = "a.out";
} else {
out_filename = "out.c";
}
}
if (output_type == OUTPUT_EXECUTABLE) {
#if defined(_WIN32) || defined(__ANDROID__)
/* XXX: find a /tmp directory ? */
snprintf(cfilename, sizeof(cfilename), "out%d.c", getpid());
#else
snprintf(cfilename, sizeof(cfilename), "/tmp/out%d.c", getpid());
#endif
} else {
pstrcpy(cfilename, sizeof(cfilename), out_filename);
}
fo = fopen(cfilename, "w");
if (!fo) {
perror(cfilename);
exit(1);
}
outfile = fo;
rt = JS_NewRuntime();
ctx = JS_NewContext(rt);
#ifdef CONFIG_BIGNUM
if (bignum_ext) {
JS_AddIntrinsicBigFloat(ctx);
JS_AddIntrinsicBigDecimal(ctx);
JS_EnableBignumExt(ctx, TRUE);
}
#endif
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
"\n"
);
if (output_type != OUTPUT_C) {
fprintf(fo, "#include \"quickjs-libc.h\"\n"
"\n"
);
} else {
fprintf(fo, "#include <inttypes.h>\n"
"\n"
);
}
for(i = optind; i < argc; i++) {
const char *filename = argv[i];
compile_file(ctx, fo, filename, cname, module);
cname = NULL;
}
if (output_type != OUTPUT_C) {
fputs(main_c_template1, fo);
fprintf(fo, " ctx = JS_NewContextRaw(rt);\n");
/* add the module loader if necessary */
if (feature_bitmap & (1 << FE_MODULE_LOADER)) {
fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n");
}
/* add the basic objects */
fprintf(fo, " JS_AddIntrinsicBaseObjects(ctx);\n");
for(i = 0; i < countof(feature_list); i++) {
if ((feature_bitmap & ((uint64_t)1 << i)) &&
feature_list[i].init_name) {
fprintf(fo, " JS_AddIntrinsic%s(ctx);\n",
feature_list[i].init_name);
}
}
if (bignum_ext) {
fprintf(fo,
" JS_AddIntrinsicBigFloat(ctx);\n"
" JS_AddIntrinsicBigDecimal(ctx);\n"
" JS_EnableBignumExt(ctx, 1);\n");
}
fprintf(fo, " js_std_add_helpers(ctx, argc, argv);\n");
for(i = 0; i < init_module_list.count; i++) {
namelist_entry_t *e = &init_module_list.array[i];
/* initialize the static C modules */
fprintf(fo,
" {\n"
" extern JSModuleDef *js_init_module_%s(JSContext *ctx, const char *name);\n"
" js_init_module_%s(ctx, \"%s\");\n"
" }\n",
e->short_name, e->short_name, e->name);
}
for(i = 0; i < cname_list.count; i++) {
namelist_entry_t *e = &cname_list.array[i];
fprintf(fo, " js_std_eval_binary(ctx, %s, %s_size, %s);\n",
e->name, e->name,
e->flags ? "1" : "0");
}
fputs(main_c_template2, fo);
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
fclose(fo);
if (output_type == OUTPUT_EXECUTABLE) {
return output_executable(out_filename, cfilename, use_lto, verbose,
argv[0]);
}
namelist_free(&cname_list);
namelist_free(&cmodule_list);
namelist_free(&init_module_list);
return 0;
}

2442
qjscalc.js Normal file

File diff suppressed because it is too large Load Diff

289
quickjs-atom.h Normal file
View File

@ -0,0 +1,289 @@
/*
* QuickJS atom definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-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.
*/
#ifdef DEF
/* Note: first atoms are considered as keywords in the parser */
DEF(null, "null") /* must be first */
DEF(false, "false")
DEF(true, "true")
DEF(if, "if")
DEF(else, "else")
DEF(return, "return")
DEF(var, "var")
DEF(this, "this")
DEF(delete, "delete")
DEF(void, "void")
DEF(typeof, "typeof")
DEF(new, "new")
DEF(in, "in")
DEF(instanceof, "instanceof")
DEF(do, "do")
DEF(while, "while")
DEF(for, "for")
DEF(break, "break")
DEF(continue, "continue")
DEF(switch, "switch")
DEF(case, "case")
DEF(default, "default")
DEF(throw, "throw")
DEF(try, "try")
DEF(catch, "catch")
DEF(finally, "finally")
DEF(function, "function")
DEF(debugger, "debugger")
DEF(with, "with")
/* FutureReservedWord */
DEF(class, "class")
DEF(const, "const")
DEF(enum, "enum")
DEF(export, "export")
DEF(extends, "extends")
DEF(import, "import")
DEF(super, "super")
/* FutureReservedWords when parsing strict mode code */
DEF(implements, "implements")
DEF(interface, "interface")
DEF(let, "let")
DEF(package, "package")
DEF(private, "private")
DEF(protected, "protected")
DEF(public, "public")
DEF(static, "static")
DEF(yield, "yield")
DEF(await, "await")
/* empty string */
DEF(empty_string, "")
/* identifiers */
DEF(length, "length")
DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber")
DEF(message, "message")
DEF(stack, "stack")
DEF(name, "name")
DEF(toString, "toString")
DEF(toLocaleString, "toLocaleString")
DEF(valueOf, "valueOf")
DEF(eval, "eval")
DEF(prototype, "prototype")
DEF(constructor, "constructor")
DEF(configurable, "configurable")
DEF(writable, "writable")
DEF(enumerable, "enumerable")
DEF(value, "value")
DEF(get, "get")
DEF(set, "set")
DEF(of, "of")
DEF(__proto__, "__proto__")
DEF(undefined, "undefined")
DEF(number, "number")
DEF(boolean, "boolean")
DEF(string, "string")
DEF(object, "object")
DEF(symbol, "symbol")
DEF(integer, "integer")
DEF(unknown, "unknown")
DEF(arguments, "arguments")
DEF(callee, "callee")
DEF(caller, "caller")
DEF(_eval_, "<eval>")
DEF(_ret_, "<ret>")
DEF(_var_, "<var>")
DEF(_with_, "<with>")
DEF(lastIndex, "lastIndex")
DEF(target, "target")
DEF(index, "index")
DEF(input, "input")
DEF(defineProperties, "defineProperties")
DEF(apply, "apply")
DEF(join, "join")
DEF(concat, "concat")
DEF(split, "split")
DEF(construct, "construct")
DEF(getPrototypeOf, "getPrototypeOf")
DEF(setPrototypeOf, "setPrototypeOf")
DEF(isExtensible, "isExtensible")
DEF(preventExtensions, "preventExtensions")
DEF(has, "has")
DEF(deleteProperty, "deleteProperty")
DEF(defineProperty, "defineProperty")
DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor")
DEF(ownKeys, "ownKeys")
DEF(add, "add")
DEF(done, "done")
DEF(next, "next")
DEF(values, "values")
DEF(source, "source")
DEF(flags, "flags")
DEF(global, "global")
DEF(unicode, "unicode")
DEF(raw, "raw")
DEF(new_target, "new.target")
DEF(this_active_func, "this.active_func")
DEF(home_object, "<home_object>")
DEF(computed_field, "<computed_field>")
DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */
DEF(class_fields_init, "<class_fields_init>")
DEF(brand, "<brand>")
DEF(hash_constructor, "#constructor")
DEF(as, "as")
DEF(from, "from")
DEF(meta, "meta")
DEF(_default_, "*default*")
DEF(_star_, "*")
DEF(Module, "Module")
DEF(then, "then")
DEF(resolve, "resolve")
DEF(reject, "reject")
DEF(promise, "promise")
DEF(proxy, "proxy")
DEF(revoke, "revoke")
DEF(async, "async")
DEF(exec, "exec")
DEF(groups, "groups")
DEF(status, "status")
DEF(reason, "reason")
DEF(globalThis, "globalThis")
#ifdef CONFIG_BIGNUM
DEF(bigint, "bigint")
DEF(bigfloat, "bigfloat")
DEF(bigdecimal, "bigdecimal")
DEF(roundingMode, "roundingMode")
DEF(maximumSignificantDigits, "maximumSignificantDigits")
DEF(maximumFractionDigits, "maximumFractionDigits")
#endif
#ifdef CONFIG_ATOMICS
DEF(not_equal, "not-equal")
DEF(timed_out, "timed-out")
DEF(ok, "ok")
#endif
DEF(toJSON, "toJSON")
/* class names */
DEF(Object, "Object")
DEF(Array, "Array")
DEF(Error, "Error")
DEF(Number, "Number")
DEF(String, "String")
DEF(Boolean, "Boolean")
DEF(Symbol, "Symbol")
DEF(Arguments, "Arguments")
DEF(Math, "Math")
DEF(JSON, "JSON")
DEF(Date, "Date")
DEF(Function, "Function")
DEF(GeneratorFunction, "GeneratorFunction")
DEF(ForInIterator, "ForInIterator")
DEF(RegExp, "RegExp")
DEF(ArrayBuffer, "ArrayBuffer")
DEF(SharedArrayBuffer, "SharedArrayBuffer")
/* must keep same order as class IDs for typed arrays */
DEF(Uint8ClampedArray, "Uint8ClampedArray")
DEF(Int8Array, "Int8Array")
DEF(Uint8Array, "Uint8Array")
DEF(Int16Array, "Int16Array")
DEF(Uint16Array, "Uint16Array")
DEF(Int32Array, "Int32Array")
DEF(Uint32Array, "Uint32Array")
#ifdef CONFIG_BIGNUM
DEF(BigInt64Array, "BigInt64Array")
DEF(BigUint64Array, "BigUint64Array")
#endif
DEF(Float32Array, "Float32Array")
DEF(Float64Array, "Float64Array")
DEF(DataView, "DataView")
#ifdef CONFIG_BIGNUM
DEF(BigInt, "BigInt")
DEF(BigFloat, "BigFloat")
DEF(BigFloatEnv, "BigFloatEnv")
DEF(BigDecimal, "BigDecimal")
#endif
DEF(Map, "Map")
DEF(Set, "Set") /* Map + 1 */
DEF(WeakMap, "WeakMap") /* Map + 2 */
DEF(WeakSet, "WeakSet") /* Map + 3 */
DEF(Map_Iterator, "Map Iterator")
DEF(Set_Iterator, "Set Iterator")
DEF(Array_Iterator, "Array Iterator")
DEF(String_Iterator, "String Iterator")
DEF(RegExp_String_Iterator, "RegExp String Iterator")
DEF(Generator, "Generator")
DEF(Proxy, "Proxy")
DEF(Promise, "Promise")
DEF(PromiseResolveFunction, "PromiseResolveFunction")
DEF(PromiseRejectFunction, "PromiseRejectFunction")
DEF(AsyncFunction, "AsyncFunction")
DEF(AsyncFunctionResolve, "AsyncFunctionResolve")
DEF(AsyncFunctionReject, "AsyncFunctionReject")
DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction")
DEF(AsyncGenerator, "AsyncGenerator")
DEF(EvalError, "EvalError")
DEF(RangeError, "RangeError")
DEF(ReferenceError, "ReferenceError")
DEF(SyntaxError, "SyntaxError")
DEF(TypeError, "TypeError")
DEF(URIError, "URIError")
DEF(InternalError, "InternalError")
/* private symbols */
DEF(Private_brand, "<brand>")
/* symbols */
DEF(Symbol_toPrimitive, "Symbol.toPrimitive")
DEF(Symbol_iterator, "Symbol.iterator")
DEF(Symbol_match, "Symbol.match")
DEF(Symbol_matchAll, "Symbol.matchAll")
DEF(Symbol_replace, "Symbol.replace")
DEF(Symbol_search, "Symbol.search")
DEF(Symbol_split, "Symbol.split")
DEF(Symbol_toStringTag, "Symbol.toStringTag")
DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable")
DEF(Symbol_hasInstance, "Symbol.hasInstance")
DEF(Symbol_species, "Symbol.species")
DEF(Symbol_unscopables, "Symbol.unscopables")
DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
#ifdef CONFIG_BIGNUM
DEF(Symbol_operatorOrder, "Symbol.operatorOrder")
DEF(Symbol_operatorAdd, "Symbol.operatorAdd")
DEF(Symbol_operatorSub, "Symbol.operatorSub")
DEF(Symbol_operatorMul, "Symbol.operatorMul")
DEF(Symbol_operatorDiv, "Symbol.operatorDiv")
DEF(Symbol_operatorMod, "Symbol.operatorMod")
DEF(Symbol_operatorPow, "Symbol.operatorPow")
DEF(Symbol_operatorShl, "Symbol.operatorShl")
DEF(Symbol_operatorShr, "Symbol.operatorShr")
DEF(Symbol_operatorAnd, "Symbol.operatorAnd")
DEF(Symbol_operatorOr, "Symbol.operatorOr")
DEF(Symbol_operatorXor, "Symbol.operatorXor")
DEF(Symbol_operatorCmpLT, "Symbol.operatorCmpLT")
DEF(Symbol_operatorCmpLE, "Symbol.operatorCmpLE")
DEF(Symbol_operatorCmpEQ, "Symbol.operatorCmpEQ")
DEF(Symbol_operatorPlus, "Symbol.operatorPlus")
DEF(Symbol_operatorNeg, "Symbol.operatorNeg")
DEF(Symbol_operatorNot, "Symbol.operatorNot")
DEF(Symbol_operatorInc, "Symbol.operatorInc")
DEF(Symbol_operatorDec, "Symbol.operatorDec")
DEF(Symbol_operatorMathMod, "Symbol.operatorMathMod")
#endif
#endif /* DEF */

2804
quickjs-libc.c Normal file

File diff suppressed because it is too large Load Diff

49
quickjs-libc.h Normal file
View File

@ -0,0 +1,49 @@
/*
* QuickJS C library
*
* Copyright (c) 2017-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef QUICKJS_LIBC_H
#define QUICKJS_LIBC_H
#include <stdio.h>
#include <stdlib.h>
#include "quickjs.h"
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);
void js_std_free_handlers(JSRuntime *rt);
void js_std_dump_error(JSContext *ctx);
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
JS_BOOL use_realpath, JS_BOOL is_main);
JSModuleDef *js_module_loader(JSContext *ctx,
const char *module_name, void *opaque);
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags);
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason,
JS_BOOL is_handled, void *opaque);
#endif /* QUICKJS_LIBC_H */

371
quickjs-opcode.h Normal file
View File

@ -0,0 +1,371 @@
/*
* QuickJS opcode definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-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.
*/
#ifdef FMT
FMT(none)
FMT(none_int)
FMT(none_loc)
FMT(none_arg)
FMT(none_var_ref)
FMT(u8)
FMT(i8)
FMT(loc8)
FMT(const8)
FMT(label8)
FMT(u16)
FMT(i16)
FMT(label16)
FMT(npop)
FMT(npopx)
FMT(npop_u16)
FMT(loc)
FMT(arg)
FMT(var_ref)
FMT(u32)
FMT(i32)
FMT(const)
FMT(label)
FMT(atom)
FMT(atom_u8)
FMT(atom_u16)
FMT(atom_label_u8)
FMT(atom_label_u16)
FMT(label_u16)
#undef FMT
#endif /* FMT */
#ifdef DEF
#ifndef def
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
#endif
DEF(invalid, 1, 0, 0, none) /* never emitted */
/* push values */
DEF( push_i32, 5, 0, 1, i32)
DEF( push_const, 5, 0, 1, const)
DEF( fclosure, 5, 0, 1, const) /* must follow push_const */
DEF(push_atom_value, 5, 0, 1, atom)
DEF( private_symbol, 5, 0, 1, atom)
DEF( undefined, 1, 0, 1, none)
DEF( null, 1, 0, 1, none)
DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
DEF( push_false, 1, 0, 1, none)
DEF( push_true, 1, 0, 1, none)
DEF( object, 1, 0, 1, none)
DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */
DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */
DEF( drop, 1, 1, 0, none) /* a -> */
DEF( nip, 1, 2, 1, none) /* a b -> b */
DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
DEF( dup, 1, 1, 2, none) /* a -> a a */
DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
DEF( swap, 1, 2, 2, none) /* a b -> b a */
DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */
DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF( apply, 3, 3, 1, u16)
DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none)
DEF(check_ctor_return, 1, 1, 2, none)
DEF( check_ctor, 1, 0, 0, none)
DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
DEF( return_async, 1, 1, 0, none)
DEF( throw, 1, 1, 0, none)
DEF( throw_var, 6, 0, 0, atom_u8)
DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
bytecode string */
DEF( get_super_ctor, 1, 1, 1, none)
DEF( get_super, 1, 1, 1, none)
DEF( import, 1, 1, 1, none) /* dynamic module import */
DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */
DEF( get_ref_value, 1, 2, 3, none)
DEF( put_ref_value, 1, 3, 0, none)
DEF( define_var, 6, 0, 0, atom_u8)
DEF(check_define_var, 6, 0, 0, atom_u8)
DEF( define_func, 6, 1, 0, atom_u8)
DEF( get_field, 5, 1, 1, atom)
DEF( get_field2, 5, 1, 2, atom)
DEF( put_field, 5, 2, 0, atom)
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
DEF( get_array_el, 1, 2, 1, none)
DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
DEF( put_array_el, 1, 3, 0, none)
DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */
DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */
DEF( define_field, 5, 2, 1, atom)
DEF( set_name, 5, 1, 1, atom)
DEF(set_name_computed, 1, 2, 2, none)
DEF( set_proto, 1, 2, 1, none)
DEF(set_home_object, 1, 2, 2, none)
DEF(define_array_el, 1, 3, 2, none)
DEF( append, 1, 3, 2, none) /* append enumerated object, update length */
DEF(copy_data_properties, 2, 3, 3, u8)
DEF( define_method, 6, 2, 1, atom_u8)
DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */
DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */
DEF( get_loc, 3, 0, 1, loc)
DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
DEF( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
DEF( get_var_ref, 3, 0, 1, var_ref)
DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
DEF(set_loc_uninitialized, 3, 0, 0, loc)
DEF( get_loc_check, 3, 0, 1, loc)
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_var_ref_check, 3, 0, 1, var_ref)
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
DEF( close_loc, 3, 0, 0, loc)
DEF( if_false, 5, 1, 0, label)
DEF( if_true, 5, 1, 0, label) /* must come after if_false */
DEF( goto, 5, 0, 0, label) /* must come after if_true */
DEF( catch, 5, 0, 1, label)
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
DEF( to_object, 1, 1, 1, none)
//DEF( to_string, 1, 1, 1, none)
DEF( to_propkey, 1, 1, 1, none)
DEF( to_propkey2, 1, 2, 2, none)
DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8)
DEF( make_loc_ref, 7, 0, 2, atom_u16)
DEF( make_arg_ref, 7, 0, 2, atom_u16)
DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
DEF( make_var_ref, 5, 0, 2, atom)
DEF( for_in_start, 1, 1, 1, none)
DEF( for_of_start, 1, 1, 3, none)
DEF(for_await_of_start, 1, 1, 3, none)
DEF( for_in_next, 1, 1, 3, none)
DEF( for_of_next, 2, 3, 5, u8)
DEF(for_await_of_next, 1, 3, 4, none)
DEF(iterator_get_value_done, 1, 1, 2, none)
DEF( iterator_close, 1, 3, 0, none)
DEF(iterator_close_return, 1, 4, 4, none)
DEF(async_iterator_close, 1, 3, 2, none)
DEF(async_iterator_next, 1, 4, 4, none)
DEF(async_iterator_get, 2, 4, 5, u8)
DEF( initial_yield, 1, 0, 0, none)
DEF( yield, 1, 1, 2, none)
DEF( yield_star, 1, 2, 2, none)
DEF(async_yield_star, 1, 1, 2, none)
DEF( await, 1, 1, 1, none)
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)
DEF( dec, 1, 1, 1, none)
DEF( inc, 1, 1, 1, none)
DEF( post_dec, 1, 1, 2, none)
DEF( post_inc, 1, 1, 2, none)
DEF( dec_loc, 2, 0, 0, loc8)
DEF( inc_loc, 2, 0, 0, loc8)
DEF( add_loc, 2, 1, 0, loc8)
DEF( not, 1, 1, 1, none)
DEF( lnot, 1, 1, 1, none)
DEF( typeof, 1, 1, 1, none)
DEF( delete, 1, 2, 1, none)
DEF( delete_var, 5, 0, 1, atom)
DEF( mul, 1, 2, 1, none)
DEF( div, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none)
DEF( sar, 1, 2, 1, none)
DEF( shr, 1, 2, 1, none)
DEF( lt, 1, 2, 1, none)
DEF( lte, 1, 2, 1, none)
DEF( gt, 1, 2, 1, none)
DEF( gte, 1, 2, 1, none)
DEF( instanceof, 1, 2, 1, none)
DEF( in, 1, 2, 1, none)
DEF( eq, 1, 2, 1, none)
DEF( neq, 1, 2, 1, none)
DEF( strict_eq, 1, 2, 1, none)
DEF( strict_neq, 1, 2, 1, none)
DEF( and, 1, 2, 1, none)
DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none)
DEF(is_undefined_or_null, 1, 1, 1, none)
#ifdef CONFIG_BIGNUM
DEF( mul_pow10, 1, 2, 1, none)
DEF( math_div, 1, 2, 1, none)
DEF( math_mod, 1, 2, 1, none)
DEF( math_pow, 1, 2, 1, none)
#endif
/* must be the last non short and non temporary opcode */
DEF( nop, 1, 0, 0, none)
/* temporary opcodes: never emitted in the final bytecode */
def(set_arg_valid_upto, 3, 0, 0, arg) /* emitted in phase 1, removed in phase 2 */
def(close_var_object, 1, 0, 0, none) /* emitted in phase 1, removed in phase 2 */
def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
#if SHORT_OPCODES
DEF( push_minus1, 1, 0, 1, none_int)
DEF( push_0, 1, 0, 1, none_int)
DEF( push_1, 1, 0, 1, none_int)
DEF( push_2, 1, 0, 1, none_int)
DEF( push_3, 1, 0, 1, none_int)
DEF( push_4, 1, 0, 1, none_int)
DEF( push_5, 1, 0, 1, none_int)
DEF( push_6, 1, 0, 1, none_int)
DEF( push_7, 1, 0, 1, none_int)
DEF( push_i8, 2, 0, 1, i8)
DEF( push_i16, 3, 0, 1, i16)
DEF( push_const8, 2, 0, 1, const8)
DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
DEF(push_empty_string, 1, 0, 1, none)
DEF( get_loc8, 2, 0, 1, loc8)
DEF( put_loc8, 2, 1, 0, loc8)
DEF( set_loc8, 2, 1, 1, loc8)
DEF( get_loc0, 1, 0, 1, none_loc)
DEF( get_loc1, 1, 0, 1, none_loc)
DEF( get_loc2, 1, 0, 1, none_loc)
DEF( get_loc3, 1, 0, 1, none_loc)
DEF( put_loc0, 1, 1, 0, none_loc)
DEF( put_loc1, 1, 1, 0, none_loc)
DEF( put_loc2, 1, 1, 0, none_loc)
DEF( put_loc3, 1, 1, 0, none_loc)
DEF( set_loc0, 1, 1, 1, none_loc)
DEF( set_loc1, 1, 1, 1, none_loc)
DEF( set_loc2, 1, 1, 1, none_loc)
DEF( set_loc3, 1, 1, 1, none_loc)
DEF( get_arg0, 1, 0, 1, none_arg)
DEF( get_arg1, 1, 0, 1, none_arg)
DEF( get_arg2, 1, 0, 1, none_arg)
DEF( get_arg3, 1, 0, 1, none_arg)
DEF( put_arg0, 1, 1, 0, none_arg)
DEF( put_arg1, 1, 1, 0, none_arg)
DEF( put_arg2, 1, 1, 0, none_arg)
DEF( put_arg3, 1, 1, 0, none_arg)
DEF( set_arg0, 1, 1, 1, none_arg)
DEF( set_arg1, 1, 1, 1, none_arg)
DEF( set_arg2, 1, 1, 1, none_arg)
DEF( set_arg3, 1, 1, 1, none_arg)
DEF( get_var_ref0, 1, 0, 1, none_var_ref)
DEF( get_var_ref1, 1, 0, 1, none_var_ref)
DEF( get_var_ref2, 1, 0, 1, none_var_ref)
DEF( get_var_ref3, 1, 0, 1, none_var_ref)
DEF( put_var_ref0, 1, 1, 0, none_var_ref)
DEF( put_var_ref1, 1, 1, 0, none_var_ref)
DEF( put_var_ref2, 1, 1, 0, none_var_ref)
DEF( put_var_ref3, 1, 1, 0, none_var_ref)
DEF( set_var_ref0, 1, 1, 1, none_var_ref)
DEF( set_var_ref1, 1, 1, 1, none_var_ref)
DEF( set_var_ref2, 1, 1, 1, none_var_ref)
DEF( set_var_ref3, 1, 1, 1, none_var_ref)
DEF( get_length, 1, 1, 1, none)
DEF( if_false8, 2, 1, 0, label8)
DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
DEF( goto16, 3, 0, 0, label16)
DEF( call0, 1, 1, 1, npopx)
DEF( call1, 1, 1, 1, npopx)
DEF( call2, 1, 1, 1, npopx)
DEF( call3, 1, 1, 1, npopx)
DEF( is_undefined, 1, 1, 1, none)
DEF( is_null, 1, 1, 1, none)
DEF( is_function, 1, 1, 1, none)
#endif
#undef DEF
#undef def
#endif /* DEF */

51583
quickjs.c Normal file

File diff suppressed because it is too large Load Diff

976
quickjs.h Normal file
View File

@ -0,0 +1,976 @@
/*
* QuickJS Javascript Engine
*
* Copyright (c) 2017-2020 Fabrice Bellard
* Copyright (c) 2017-2020 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 QUICKJS_H
#define QUICKJS_H
#include <stdio.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) || defined(__clang__)
#define js_likely(x) __builtin_expect(!!(x), 1)
#define js_unlikely(x) __builtin_expect(!!(x), 0)
#define js_force_inline inline __attribute__((always_inline))
#define __js_printf_like(f, a) __attribute__((format(printf, f, a)))
#else
#define js_likely(x) (x)
#define js_unlikely(x) (x)
#define js_force_inline inline
#define __js_printf_like(a, b)
#endif
#define JS_BOOL int
typedef struct JSRuntime JSRuntime;
typedef struct JSContext JSContext;
typedef struct JSObject JSObject;
typedef struct JSClass JSClass;
typedef uint32_t JSClassID;
typedef uint32_t JSAtom;
#if defined(__x86_64__) || defined(__aarch64__)
#define JS_PTR64
#define JS_PTR64_DEF(a) a
#else
#define JS_PTR64_DEF(a)
#endif
#ifndef JS_PTR64
#define JS_NAN_BOXING
#endif
enum {
/* all tags with a reference count are negative */
JS_TAG_FIRST = -11, /* first negative tag */
JS_TAG_BIG_DECIMAL = -11,
JS_TAG_BIG_INT = -10,
JS_TAG_BIG_FLOAT = -9,
JS_TAG_SYMBOL = -8,
JS_TAG_STRING = -7,
JS_TAG_MODULE = -3, /* used internally */
JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */
JS_TAG_OBJECT = -1,
JS_TAG_INT = 0,
JS_TAG_BOOL = 1,
JS_TAG_NULL = 2,
JS_TAG_UNDEFINED = 3,
JS_TAG_UNINITIALIZED = 4,
JS_TAG_CATCH_OFFSET = 5,
JS_TAG_EXCEPTION = 6,
JS_TAG_FLOAT64 = 7,
/* any larger tag is FLOAT64 if JS_NAN_BOXING */
};
typedef struct JSRefCountHeader {
int ref_count;
} JSRefCountHeader;
#define JS_FLOAT64_NAN NAN
#ifdef CONFIG_CHECK_JSVALUE
/* JSValue consistency : it is not possible to run the code in this
mode, but it is useful to detect simple reference counting
errors. It would be interesting to modify a static C analyzer to
handle specific annotations (clang has such annotations but only
for objective C) */
typedef struct __JSValue *JSValue;
typedef const struct __JSValue *JSValueConst;
#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf)
/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)
#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)
#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)
#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)
#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))
#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag))
#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1)
static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
{
return JS_MKVAL(JS_TAG_FLOAT64, (int)d);
}
static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
{
return 0;
}
#elif defined(JS_NAN_BOXING)
typedef uint64_t JSValue;
#define JSValueConst JSValue
#define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
#define JS_VALUE_GET_INT(v) (int)(v)
#define JS_VALUE_GET_BOOL(v) (int)(v)
#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr))
#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */
static inline double JS_VALUE_GET_FLOAT64(JSValue v)
{
union {
JSValue v;
double d;
} u;
u.v = v;
u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32;
return u.d;
}
#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32))
static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
{
union {
double d;
uint64_t u64;
} u;
JSValue v;
u.d = d;
/* normalize NaN */
if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000))
v = JS_NAN;
else
v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32);
return v;
}
#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST))
/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
static inline int JS_VALUE_GET_NORM_TAG(JSValue v)
{
uint32_t tag;
tag = JS_VALUE_GET_TAG(v);
if (JS_TAG_IS_FLOAT64(tag))
return JS_TAG_FLOAT64;
else
return tag;
}
static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
{
uint32_t tag;
tag = JS_VALUE_GET_TAG(v);
return tag == (JS_NAN >> 32);
}
#else /* !JS_NAN_BOXING */
typedef union JSValueUnion {
int32_t int32;
double float64;
void *ptr;
} JSValueUnion;
typedef struct JSValue {
JSValueUnion u;
int64_t tag;
} JSValue;
#define JSValueConst JSValue
#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag)
/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)
#define JS_VALUE_GET_INT(v) ((v).u.int32)
#define JS_VALUE_GET_BOOL(v) ((v).u.int32)
#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
#define JS_VALUE_GET_PTR(v) ((v).u.ptr)
#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
{
JSValue v;
v.tag = JS_TAG_FLOAT64;
v.u.float64 = d;
return v;
}
static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
{
union {
double d;
uint64_t u64;
} u;
if (v.tag != JS_TAG_FLOAT64)
return 0;
u.d = v.u.float64;
return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000;
}
#endif /* !JS_NAN_BOXING */
#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)
#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2)))
#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))
#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))
#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST)
/* special values */
#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0)
#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0)
#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0)
#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1)
#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0)
#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0)
/* flags for object properties */
#define JS_PROP_CONFIGURABLE (1 << 0)
#define JS_PROP_WRITABLE (1 << 1)
#define JS_PROP_ENUMERABLE (1 << 2)
#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)
#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */
#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */
#define JS_PROP_NORMAL (0 << 4)
#define JS_PROP_GETSET (1 << 4)
#define JS_PROP_VARREF (2 << 4) /* used internally */
#define JS_PROP_AUTOINIT (3 << 4) /* used internally */
/* flags for JS_DefineProperty */
#define JS_PROP_HAS_SHIFT 8
#define JS_PROP_HAS_CONFIGURABLE (1 << 8)
#define JS_PROP_HAS_WRITABLE (1 << 9)
#define JS_PROP_HAS_ENUMERABLE (1 << 10)
#define JS_PROP_HAS_GET (1 << 11)
#define JS_PROP_HAS_SET (1 << 12)
#define JS_PROP_HAS_VALUE (1 << 13)
/* throw an exception if false would be returned
(JS_DefineProperty/JS_SetProperty) */
#define JS_PROP_THROW (1 << 14)
/* throw an exception if false would be returned in strict mode
(JS_SetProperty) */
#define JS_PROP_THROW_STRICT (1 << 15)
#define JS_PROP_NO_ADD (1 << 16) /* internal use */
#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */
#define JS_DEFAULT_STACK_SIZE (256 * 1024)
/* JS_Eval() flags */
#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */
#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */
#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */
#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */
#define JS_EVAL_TYPE_MASK (3 << 0)
#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */
#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */
/* compile but do not run. The result is an object with a
JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
with JS_EvalFunction(). */
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
/* don't include the stack frames before this eval in the Error() backtraces */
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data);
typedef struct JSMallocState {
size_t malloc_count;
size_t malloc_size;
size_t malloc_limit;
void *opaque; /* user opaque */
} JSMallocState;
typedef struct JSMallocFunctions {
void *(*js_malloc)(JSMallocState *s, size_t size);
void (*js_free)(JSMallocState *s, void *ptr);
void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);
size_t (*js_malloc_usable_size)(const void *ptr);
} JSMallocFunctions;
typedef struct JSGCObjectHeader JSGCObjectHeader;
JSRuntime *JS_NewRuntime(void);
/* info lifetime must exceed that of rt */
void JS_SetRuntimeInfo(JSRuntime *rt, const char *info);
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit);
void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold);
JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque);
void JS_FreeRuntime(JSRuntime *rt);
typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp);
void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
void JS_RunGC(JSRuntime *rt);
JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj);
JSContext *JS_NewContext(JSRuntime *rt);
void JS_FreeContext(JSContext *s);
void *JS_GetContextOpaque(JSContext *ctx);
void JS_SetContextOpaque(JSContext *ctx, void *opaque);
JSRuntime *JS_GetRuntime(JSContext *ctx);
void JS_SetMaxStackSize(JSContext *ctx, size_t stack_size);
void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj);
JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id);
/* the following functions are used to select the intrinsic object to
save memory */
JSContext *JS_NewContextRaw(JSRuntime *rt);
void JS_AddIntrinsicBaseObjects(JSContext *ctx);
void JS_AddIntrinsicDate(JSContext *ctx);
void JS_AddIntrinsicEval(JSContext *ctx);
void JS_AddIntrinsicStringNormalize(JSContext *ctx);
void JS_AddIntrinsicRegExpCompiler(JSContext *ctx);
void JS_AddIntrinsicRegExp(JSContext *ctx);
void JS_AddIntrinsicJSON(JSContext *ctx);
void JS_AddIntrinsicProxy(JSContext *ctx);
void JS_AddIntrinsicMapSet(JSContext *ctx);
void JS_AddIntrinsicTypedArrays(JSContext *ctx);
void JS_AddIntrinsicPromise(JSContext *ctx);
void JS_AddIntrinsicBigInt(JSContext *ctx);
void JS_AddIntrinsicBigFloat(JSContext *ctx);
void JS_AddIntrinsicBigDecimal(JSContext *ctx);
/* enable "use bigint", "use math" and operator overloading */
void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable);
JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv);
void *js_malloc_rt(JSRuntime *rt, size_t size);
void js_free_rt(JSRuntime *rt, void *ptr);
void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size);
size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr);
void *js_mallocz_rt(JSRuntime *rt, size_t size);
void *js_malloc(JSContext *ctx, size_t size);
void js_free(JSContext *ctx, void *ptr);
void *js_realloc(JSContext *ctx, void *ptr, size_t size);
size_t js_malloc_usable_size(JSContext *ctx, const void *ptr);
void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack);
void *js_mallocz(JSContext *ctx, size_t size);
char *js_strdup(JSContext *ctx, const char *str);
char *js_strndup(JSContext *ctx, const char *s, size_t n);
typedef struct JSMemoryUsage {
int64_t malloc_size, malloc_limit, memory_used_size;
int64_t malloc_count;
int64_t memory_used_count;
int64_t atom_count, atom_size;
int64_t str_count, str_size;
int64_t obj_count, obj_size;
int64_t prop_count, prop_size;
int64_t shape_count, shape_size;
int64_t js_func_count, js_func_size, js_func_code_size;
int64_t js_func_pc2line_count, js_func_pc2line_size;
int64_t c_func_count, array_count;
int64_t fast_array_count, fast_array_elements;
int64_t binary_object_count, binary_object_size;
} JSMemoryUsage;
void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s);
void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);
/* atom support */
JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len);
JSAtom JS_NewAtom(JSContext *ctx, const char *str);
JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n);
JSAtom JS_DupAtom(JSContext *ctx, JSAtom v);
void JS_FreeAtom(JSContext *ctx, JSAtom v);
void JS_FreeAtomRT(JSRuntime *rt, JSAtom v);
JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom);
JSValue JS_AtomToString(JSContext *ctx, JSAtom atom);
const char *JS_AtomToCString(JSContext *ctx, JSAtom atom);
JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val);
/* object class support */
typedef struct JSPropertyEnum {
JS_BOOL is_enumerable;
JSAtom atom;
} JSPropertyEnum;
typedef struct JSPropertyDescriptor {
int flags;
JSValue value;
JSValue getter;
JSValue setter;
} JSPropertyDescriptor;
typedef struct JSClassExoticMethods {
/* Return -1 if exception (can only happen in case of Proxy object),
FALSE if the property does not exists, TRUE if it exists. If 1 is
returned, the property descriptor 'desc' is filled if != NULL. */
int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc,
JSValueConst obj, JSAtom prop);
/* '*ptab' should hold the '*plen' property keys. Return 0 if OK,
-1 if exception. The 'is_enumerable' field is ignored.
*/
int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab,
uint32_t *plen,
JSValueConst obj);
/* return < 0 if exception, or TRUE/FALSE */
int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop);
/* return < 0 if exception or TRUE/FALSE */
int (*define_own_property)(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValueConst val,
JSValueConst getter, JSValueConst setter,
int flags);
/* The following methods can be emulated with the previous ones,
so they are usually not needed */
/* return < 0 if exception or TRUE/FALSE */
int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom);
JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
JSValueConst receiver);
/* return < 0 if exception or TRUE/FALSE */
int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
JSValueConst value, JSValueConst receiver, int flags);
} JSClassExoticMethods;
typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);
typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0)
typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_val, int argc, JSValueConst *argv,
int flags);
typedef struct JSClassDef {
const char *class_name;
JSClassFinalizer *finalizer;
JSClassGCMark *gc_mark;
/* if call != NULL, the object is a function. If (flags &
JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a
constructor. In this case, 'this_val' is new.target. A
constructor call only happens if the object constructor bit is
set (see JS_SetConstructorBit()). */
JSClassCall *call;
/* XXX: suppress this indirection ? It is here only to save memory
because only a few classes need these methods */
JSClassExoticMethods *exotic;
} JSClassDef;
JSClassID JS_NewClassID(JSClassID *pclass_id);
int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);
int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
/* value handling */
static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)
{
return JS_MKVAL(JS_TAG_BOOL, val);
}
static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val)
{
return JS_MKVAL(JS_TAG_INT, val);
}
static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val)
{
return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);
}
JSValue JS_NewInt64(JSContext *ctx, int64_t v);
JSValue JS_NewBigInt64(JSContext *ctx, int64_t v);
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);
}
return v;
}
JS_BOOL JS_IsNumber(JSValueConst v);
static inline JS_BOOL JS_IsInteger(JSValueConst v)
{
int tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG_INT || tag == JS_TAG_BIG_INT;
}
static inline JS_BOOL JS_IsBigFloat(JSValueConst v)
{
int tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG_BIG_FLOAT;
}
static inline JS_BOOL JS_IsBigDecimal(JSValueConst v)
{
int tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG_BIG_DECIMAL;
}
static inline JS_BOOL JS_IsBool(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL;
}
static inline JS_BOOL JS_IsNull(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_NULL;
}
static inline JS_BOOL JS_IsUndefined(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED;
}
static inline JS_BOOL JS_IsException(JSValueConst v)
{
return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION);
}
static inline JS_BOOL JS_IsUninitialized(JSValueConst v)
{
return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED);
}
static inline JS_BOOL JS_IsString(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING;
}
static inline JS_BOOL JS_IsSymbol(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL;
}
static inline JS_BOOL JS_IsObject(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT;
}
JSValue JS_Throw(JSContext *ctx, JSValue obj);
JSValue JS_GetException(JSContext *ctx);
JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
void JS_ResetUncatchableError(JSContext *ctx);
JSValue JS_NewError(JSContext *ctx);
JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
void __JS_FreeValue(JSContext *ctx, JSValue v);
static inline void JS_FreeValue(JSContext *ctx, JSValue v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
if (--p->ref_count <= 0) {
__JS_FreeValue(ctx, v);
}
}
}
void __JS_FreeValueRT(JSRuntime *rt, JSValue v);
static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
if (--p->ref_count <= 0) {
__JS_FreeValueRT(rt, v);
}
}
}
static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
p->ref_count++;
}
return (JSValue)v;
}
static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
{
if (JS_VALUE_HAS_REF_COUNT(v)) {
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
p->ref_count++;
}
return (JSValue)v;
}
int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
static int inline JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
{
return JS_ToInt32(ctx, (int32_t*)pres, val);
}
int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val);
int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val);
int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1);
JSValue JS_NewString(JSContext *ctx, const char *str);
JSValue JS_NewAtomString(JSContext *ctx, const char *str);
JSValue JS_ToString(JSContext *ctx, JSValueConst val);
JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val);
const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8);
static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1)
{
return JS_ToCStringLen2(ctx, plen, val1, 0);
}
static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1)
{
return JS_ToCStringLen2(ctx, NULL, val1, 0);
}
void JS_FreeCString(JSContext *ctx, const char *ptr);
JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id);
JSValue JS_NewObjectClass(JSContext *ctx, int class_id);
JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);
JSValue JS_NewObject(JSContext *ctx);
JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val);
JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val);
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_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
JSAtom prop, JSValueConst receiver,
JS_BOOL throw_ref_error);
static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj,
JSAtom prop)
{
return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0);
}
JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
const char *prop);
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
uint32_t idx);
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue val,
int flags);
static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue val)
{
return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW);
}
int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
uint32_t idx, JSValue val);
int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
int64_t idx, JSValue val);
int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
const char *prop, JSValue val);
int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop);
int JS_IsExtensible(JSContext *ctx, JSValueConst obj);
int JS_PreventExtensions(JSContext *ctx, JSValueConst obj);
int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags);
int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val);
JSValueConst JS_GetPrototype(JSContext *ctx, JSValueConst val);
#define JS_GPN_STRING_MASK (1 << 0)
#define JS_GPN_SYMBOL_MASK (1 << 1)
#define JS_GPN_PRIVATE_MASK (1 << 2)
/* only include the enumerable properties */
#define JS_GPN_ENUM_ONLY (1 << 4)
/* set theJSPropertyEnum.is_enumerable field */
#define JS_GPN_SET_ENUM (1 << 5)
int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
uint32_t *plen, JSValueConst obj, int flags);
int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
JSValueConst obj, JSAtom prop);
JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
int argc, JSValueConst *argv);
JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
int argc, JSValueConst *argv);
JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
int argc, JSValueConst *argv);
JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
JSValueConst new_target,
int argc, JSValueConst *argv);
JS_BOOL JS_DetectModule(const char *input, size_t input_len);
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
const char *filename, int eval_flags);
JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj);
JSValue JS_GetGlobalObject(JSContext *ctx);
int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj);
int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValueConst val,
JSValueConst getter, JSValueConst setter, int flags);
int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue val, int flags);
int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
uint32_t idx, JSValue val, int flags);
int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
const char *prop, JSValue val, int flags);
int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
JSAtom prop, JSValue getter, JSValue setter,
int flags);
void JS_SetOpaque(JSValue obj, void *opaque);
void *JS_GetOpaque(JSValueConst obj, JSClassID class_id);
void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id);
/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
const char *filename);
JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
JSValueConst replacer, JSValueConst space0);
typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr);
JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
JSFreeArrayBufferDataFunc *free_func, void *opaque,
JS_BOOL is_shared);
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);
JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
size_t *pbyte_offset,
size_t *pbyte_length,
size_t *pbytes_per_element);
JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
/* is_handled = TRUE means that the rejection is handled */
typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason,
JS_BOOL is_handled, void *opaque);
void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque);
/* return != 0 if the JS code needs to be interrupted */
typedef int JSInterruptHandler(JSRuntime *rt, void *opaque);
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
/* if can_block is TRUE, Atomics.wait() can be used */
void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
typedef struct JSModuleDef JSModuleDef;
/* return the module specifier (allocated with js_malloc()) or NULL if
exception */
typedef char *JSModuleNormalizeFunc(JSContext *ctx,
const char *module_base_name,
const char *module_name, void *opaque);
typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx,
const char *module_name, void *opaque);
/* module_normalize = NULL is allowed and invokes the default module
filename normalizer */
void JS_SetModuleLoaderFunc(JSRuntime *rt,
JSModuleNormalizeFunc *module_normalize,
JSModuleLoaderFunc *module_loader, void *opaque);
/* return the import.meta object of a module */
JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);
JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);
/* JS Job support */
typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv);
int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv);
JS_BOOL JS_IsJobPending(JSRuntime *rt);
int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx);
/* Object Writer/Reader (currently only used to handle precompiled code) */
#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */
uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
int flags);
#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */
JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags);
/* load the dependencies of the module 'obj'. Useful when JS_ReadObject()
returns a module. */
int JS_ResolveModule(JSContext *ctx, JSValueConst obj);
/* C function definition */
typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
JS_CFUNC_generic,
JS_CFUNC_generic_magic,
JS_CFUNC_constructor,
JS_CFUNC_constructor_magic,
JS_CFUNC_constructor_or_func,
JS_CFUNC_constructor_or_func_magic,
JS_CFUNC_f_f,
JS_CFUNC_f_f_f,
JS_CFUNC_getter,
JS_CFUNC_setter,
JS_CFUNC_getter_magic,
JS_CFUNC_setter_magic,
JS_CFUNC_iterator_next,
} JSCFunctionEnum;
typedef union JSCFunctionType {
JSCFunction *generic;
JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
JSCFunction *constructor;
JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic);
JSCFunction *constructor_or_func;
double (*f_f)(double);
double (*f_f_f)(double, double);
JSValue (*getter)(JSContext *ctx, JSValueConst this_val);
JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val);
JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic);
JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic);
JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int *pdone, int magic);
} JSCFunctionType;
JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
const char *name,
int length, JSCFunctionEnum cproto, int magic);
JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
int length, int magic, int data_len,
JSValueConst *data);
static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
int length)
{
return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0);
}
static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
const char *name,
int length, JSCFunctionEnum cproto, int magic)
{
return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic);
}
void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
JSValueConst proto);
/* C property definition */
typedef struct JSCFunctionListEntry {
const char *name;
uint8_t prop_flags;
uint8_t def_type;
int16_t magic;
union {
struct {
uint8_t length; /* XXX: should move outside union */
uint8_t cproto; /* XXX: should move outside union */
JSCFunctionType cfunc;
} func;
struct {
JSCFunctionType get;
JSCFunctionType set;
} getset;
struct {
const char *name;
int base;
} alias;
struct {
const struct JSCFunctionListEntry *tab;
int len;
} prop_list;
const char *str;
int32_t i32;
int64_t i64;
double f64;
} u;
} JSCFunctionListEntry;
#define JS_DEF_CFUNC 0
#define JS_DEF_CGETSET 1
#define JS_DEF_CGETSET_MAGIC 2
#define JS_DEF_PROP_STRING 3
#define JS_DEF_PROP_INT32 4
#define JS_DEF_PROP_INT64 5
#define JS_DEF_PROP_DOUBLE 6
#define JS_DEF_PROP_UNDEFINED 7
#define JS_DEF_OBJECT 8
#define JS_DEF_ALIAS 9
/* Note: c++ does not like nested designators */
#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } }
#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } }
#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } }
#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } }
#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } }
#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } }
#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } }
#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } }
#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } }
#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } }
#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } }
#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } }
#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } }
void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
const JSCFunctionListEntry *tab,
int len);
/* C module definition */
typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m);
JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
JSModuleInitFunc *func);
/* can only be called before the module is instantiated */
int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str);
int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
const JSCFunctionListEntry *tab, int len);
/* can only be called after the module is instantiated */
int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
JSValue val);
int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
const JSCFunctionListEntry *tab, int len);
#undef js_unlikely
#undef js_force_inline
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* QUICKJS_H */

1
readme.txt Normal file
View File

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

107
release.sh Executable file
View File

@ -0,0 +1,107 @@
#!/bin/sh
# Release the QuickJS source code
set -e
version=`cat VERSION`
if [ "$1" = "-h" ] ; then
echo "release.sh [all]"
echo ""
echo "all: build all the archives. Otherwise only build the quickjs source archive."
exit 1
fi
extras="no"
binary="no"
quickjs="no"
if [ "$1" = "all" ] ; then
extras="yes"
binary="yes"
quickjs="yes"
elif [ "$1" = "binary" ] ; then
binary="yes"
else
quickjs="yes"
fi
#################################################"
# extras
if [ "$extras" = "yes" ] ; then
d="quickjs-${version}"
name="quickjs-extras-${version}"
outdir="/tmp/${d}"
rm -rf $outdir
mkdir -p $outdir $outdir/unicode $outdir/tests
cp unicode/* $outdir/unicode
cp -a tests/bench-v8 $outdir/tests
( cd /tmp && tar Jcvf /tmp/${name}.tar.xz ${d} )
fi
#################################################"
# binary release
if [ "$binary" = "yes" ] ; then
d="quickjs-linux-x86_64-${version}"
name="quickjs-linux-x86_64-${version}"
outdir="/tmp/${d}"
rm -rf $outdir
mkdir -p $outdir
files="qjs run-test262"
make -j4 $files
strip $files
cp $files $outdir
( cd /tmp/$d && rm -f ../${name}.zip && zip -r ../${name}.zip . )
fi
#################################################"
# quickjs
if [ "$quickjs" = "yes" ] ; then
make build_doc
d="quickjs-${version}"
outdir="/tmp/${d}"
rm -rf $outdir
mkdir -p $outdir $outdir/doc $outdir/tests $outdir/examples
cp Makefile VERSION TODO Changelog readme.txt release.sh unicode_download.sh \
qjs.c qjsc.c qjscalc.js repl.js \
quickjs.c quickjs.h quickjs-atom.h \
quickjs-libc.c quickjs-libc.h quickjs-opcode.h \
cutils.c cutils.h list.h \
libregexp.c libregexp.h libregexp-opcode.h \
libunicode.c libunicode.h libunicode-table.h \
libbf.c libbf.h \
jscompress.c unicode_gen.c unicode_gen_def.h \
run-test262.c test262o.conf test262.conf \
test262o_errors.txt test262_errors.txt \
$outdir
cp tests/*.js tests/*.patch tests/bjson.c $outdir/tests
cp examples/*.js examples/*.c $outdir/examples
cp doc/quickjs.texi doc/quickjs.pdf doc/quickjs.html \
doc/jsbignum.texi doc/jsbignum.html doc/jsbignum.pdf \
$outdir/doc
( cd /tmp && tar Jcvf /tmp/${d}.tar.xz ${d} )
fi

1544
repl.js Normal file

File diff suppressed because it is too large Load Diff

2098
run-test262.c Normal file

File diff suppressed because it is too large Load Diff

182
test262.conf Normal file
View File

@ -0,0 +1,182 @@
[config]
# general settings for test262 ES6 version
# framework style: old, new
style=new
# handle tests tagged as [noStrict]: yes, no, skip
nostrict=yes
# handle tests tagged as [strictOnly]: yes, no, skip
strict=yes
# test mode: default, default-nostrict, default-strict, strict, nostrict, both, all
mode=default
# handle tests flagged as [async]: yes, no, skip
# for these, load 'harness/doneprintHandle.js' prior to test
# and expect `print('Test262:AsyncTestComplete')` to be called for
# successful termination
async=yes
# handle tests flagged as [module]: yes, no, skip
module=yes
# output error messages: yes, no
verbose=yes
# load harness files from this directory
harnessdir=test262/harness
# names of harness include files to skip
#harnessexclude=
# name of the error file for known errors
errorfile=test262_errors.txt
# exclude tests enumerated in this file (see also [exclude] section)
#excludefile=test262_exclude.txt
# report test results to this file
reportfile=test262_report.txt
# enumerate tests from this directory
testdir=test262/test
[features]
# Standard language features and proposed extensions
# list the features that are included
# skipped features are tagged as such to avoid warnings
AggregateError=skip
Array.prototype.flat
Array.prototype.flatMap
Array.prototype.flatten
Array.prototype.values
ArrayBuffer
arrow-function
async-functions
async-iteration
Atomics
BigInt
caller
class
class-fields-private
class-fields-public
class-methods-private
class-static-fields-public
class-static-fields-private
class-static-methods-private
coalesce-expression
computed-property-names
const
cross-realm=skip
DataView
DataView.prototype.getFloat32
DataView.prototype.getFloat64
DataView.prototype.getInt16
DataView.prototype.getInt32
DataView.prototype.getInt8
DataView.prototype.getUint16
DataView.prototype.getUint32
DataView.prototype.setUint8
default-arg
default-parameters
destructuring-assignment
destructuring-binding
dynamic-import
export-star-as-namespace-from-module
FinalizationGroup=skip
Float32Array
Float64Array
for-in-order
for-of
generators
globalThis
hashbang
host-gc-required=skip
import.meta
Int32Array
Int8Array
IsHTMLDDA=skip
json-superset
let
Map
new.target
numeric-separator-literal
object-rest
object-spread
Object.fromEntries
Object.is
optional-catch-binding
optional-chaining
Promise.allSettled
Promise.prototype.finally
Proxy
proxy-missing-checks
Reflect
Reflect.construct
Reflect.set
Reflect.setPrototypeOf
regexp-dotall
regexp-lookbehind
regexp-match-indices=skip
regexp-named-groups
regexp-unicode-property-escapes
rest-parameters
Set
SharedArrayBuffer
string-trimming
String.fromCodePoint
String.prototype.endsWith
String.prototype.includes
String.prototype.matchAll
String.prototype.replaceAll
String.prototype.trimEnd
String.prototype.trimStart
super
Symbol
Symbol.asyncIterator
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.iterator
Symbol.match
Symbol.matchAll
Symbol.prototype.description
Symbol.replace
Symbol.search
Symbol.species
Symbol.split
Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables
tail-call-optimization=skip
template
top-level-await=skip
TypedArray
u180e
Uint16Array
Uint8Array
Uint8ClampedArray
WeakMap
WeakRef=skip
WeakSet
well-formed-json-stringify
[exclude]
# list excluded tests and directories here
# intl not supported
test262/test/intl402/
# incompatible with the "caller" feature
test262/test/built-ins/Function/prototype/restricted-property-caller.js
test262/test/built-ins/Function/prototype/restricted-property-arguments.js
test262/test/built-ins/ThrowTypeError/unique-per-realm-function-proto.js
# slow tests
#test262/test/built-ins/RegExp/CharacterClassEscapes/
#test262/test/built-ins/RegExp/property-escapes/
[tests]
# list test files or use config.testdir

4
test262_errors.txt Normal file
View File

@ -0,0 +1,4 @@
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called
test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: value has no property
test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: value has no property

410
test262o.conf Normal file
View File

@ -0,0 +1,410 @@
[config]
# general settings for test262 ES5 version
# framework style: old, new
style=old
# handle tests tagged as @noStrict: yes, no, skip
nostrict=yes
# handle tests tagged as @strictOnly: yes, no, skip
strict=yes
# test mode: default, default-nostrict, default-strict, strict, nostrict, both, all
mode=default
# output error messages: yes, no
verbose=yes
# load harness files this directory
harnessdir=test262o/test/harness
# name of the error file for known errors
errorfile=test262o_errors.txt
# exclude tests enumerated in this file
#excludefile=test262o_excluded.txt
# report test results to this file
reportfile=test262o_report.txt
# enumerate tests from this directory
testdir=test262o/test/suite
[exclude]
# list excluded tests and directories here
# intl not supported
test262o/test/suite/intl402/
# ES6 != ES5: block scoped function definitions allowed in strict mode
test262o/test/suite/bestPractice/Sbp_A1_T1.js
test262o/test/suite/bestPractice/Sbp_A2_T1.js
test262o/test/suite/bestPractice/Sbp_A2_T2.js
test262o/test/suite/bestPractice/Sbp_A3_T1.js
test262o/test/suite/bestPractice/Sbp_A3_T2.js
test262o/test/suite/bestPractice/Sbp_A4_T1.js
test262o/test/suite/bestPractice/Sbp_A4_T2.js
test262o/test/suite/bestPractice/Sbp_A5_T2.js
# ES6 != ES5: `y={x};` is shorthand for `y={x:x}`
test262o/test/suite/ch12/12.1/S12.1_A4_T2.js
test262o/test/suite/ch12/12.6/12.6.4/S12.6.4_A15.js
# ES6 != ES5: function length property is configurable
test262o/test/suite/ch11/11.4/11.4.1/11.4.1-5-a-28-s.js
test262o/test/suite/ch13/13.2/13.2-15-1.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.1/S15.1.2.1_A4.2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A9.2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A7.2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.4/S15.1.2.4_A2.2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.5/S15.1.2.5_A2.2.js
test262o/test/suite/ch15/15.1/15.1.3/15.1.3.1/S15.1.3.1_A5.2.js
test262o/test/suite/ch15/15.1/15.1.3/15.1.3.2/S15.1.3.2_A5.2.js
test262o/test/suite/ch15/15.1/15.1.3/15.1.3.3/S15.1.3.3_A5.2.js
test262o/test/suite/ch15/15.1/15.1.3/15.1.3.4/S15.1.3.4_A5.2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-186.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-187.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-191.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-194.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-201.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.2/S15.2.4.2_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.3/S15.2.4.3_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.4/S15.2.4.4_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.5/S15.2.4.5_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.6/S15.2.4.6_A9.js
test262o/test/suite/ch15/15.2/15.2.4/15.2.4.7/S15.2.4.7_A9.js
test262o/test/suite/ch15/15.3/15.3.3/15.3.3.2/15.3.3.2-1.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.2/S15.3.4.2_A9.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.3/S15.3.4.3_A9.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.4/S15.3.4.4_A9.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-15-2.js
test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T1.js
test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T2.js
test262o/test/suite/ch15/15.3/15.3.5/S15.3.5.1_A2_T3.js
test262o/test/suite/ch15/15.4/15.4.3/S15.4.3_A2.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.2/S15.4.4.2_A4.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.3/S15.4.4.3_A4.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.4/S15.4.4.4_A4.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.5/S15.4.4.5_A6.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A6.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.8/S15.4.4.8_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.9/S15.4.4.9_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.11/S15.4.4.11_A7.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A5.2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.13/S15.4.4.13_A5.2.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.4/S15.5.4.4_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.5/S15.5.4.5_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.6/S15.5.4.6_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.7/S15.5.4.7_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.8/S15.5.4.8_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.9/S15.5.4.9_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.10/S15.5.4.10_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.11/S15.5.4.11_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.12/S15.5.4.12_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.13/S15.5.4.13_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.14/S15.5.4.14_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.15/S15.5.4.15_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.16/S15.5.4.16_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.17/S15.5.4.17_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.18/S15.5.4.18_A9.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.19/S15.5.4.19_A9.js
test262o/test/suite/ch15/15.9/15.9.4/15.9.4.2/S15.9.4.2_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.4/15.9.4.3/S15.9.4.3_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.1/S15.9.5.1_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.2/S15.9.5.2_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.3/S15.9.5.3_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.4/S15.9.5.4_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.5/S15.9.5.5_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.6/S15.9.5.6_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.7/S15.9.5.7_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.8/S15.9.5.8_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.9/S15.9.5.9_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.10/S15.9.5.10_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.11/S15.9.5.11_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.12/S15.9.5.12_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.13/S15.9.5.13_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.14/S15.9.5.14_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.15/S15.9.5.15_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.16/S15.9.5.16_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.17/S15.9.5.17_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.18/S15.9.5.18_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.19/S15.9.5.19_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.20/S15.9.5.20_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.21/S15.9.5.21_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.22/S15.9.5.22_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.23/S15.9.5.23_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.24/S15.9.5.24_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.25/S15.9.5.25_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.26/S15.9.5.26_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.27/S15.9.5.27_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.28/S15.9.5.28_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.29/S15.9.5.29_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.30/S15.9.5.30_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.31/S15.9.5.31_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.32/S15.9.5.32_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.33/S15.9.5.33_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.34/S15.9.5.34_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.35/S15.9.5.35_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.36/S15.9.5.36_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.37/S15.9.5.37_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.38/S15.9.5.38_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.39/S15.9.5.39_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.40/S15.9.5.40_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.41/S15.9.5.41_A3_T2.js
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.42/S15.9.5.42_A3_T2.js
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.2/S15.10.6.2_A9.js
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.3/S15.10.6.3_A9.js
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.4/S15.10.6.4_A9.js
# ES6 != ES5: object literals may have duplicates
test262o/test/suite/ch11/11.1/11.1.5/11.1.5-4-4-a-1-s.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-b-1.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-b-2.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-c-1.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-c-2.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-1.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-2.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-3.js
test262o/test/suite/ch11/11.1/11.1.5/11.1.5_4-4-d-4.js
# ES6 != ES5: Date.prototype is no longer an instance of Date
test262o/test/suite/ch15/15.9/15.9.5/15.9.5.40/15.9.5.40_1.js
# ES6 != ES5: Object.getPrototypeOf converts argument to object
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-1.js
# ES6 != ES5: Object.getPrototypeOf(NativeError)
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-12.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-13.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-14.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-15.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-16.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.2/15.2.3.2-2-17.js
# ES6 != ES5: Object.getOwnPropertyDescriptor converts argument to object
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-1.js
# ES6 != ES5: Object.getOwnPropertyNames converts argument to object
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1-5.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-1.js
# ES6 != ES5: Object.seal accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.8/15.2.3.8-1.js
# ES6 != ES5: Object.freeze accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.9/15.2.3.9-1.js
# ES6 != ES5: Object.preventExtensions accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.10/15.2.3.10-1.js
# ES6 != ES5: Object.isSealed accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.11/15.2.3.11-1.js
# ES6 != ES5: Object.isFrozen accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.12/15.2.3.12-1.js
# ES6 != ES5: Object.isExtensible accepts all types
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-3.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1-4.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.13/15.2.3.13-1.js
# ES6 != ES5: Object.keys converts argument to object
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-1.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-2.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.14/15.2.3.14-1-3.js
# ES6 != ES5: source and other properties of RegExp.prototype are not own properties
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-212.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-213.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-214.js
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-215.js
# ES6 != ES5: String numeric object properties are enumerated first
test262o/test/suite/ch15/15.2/15.2.3/15.2.3.4/15.2.3.4-4-44.js
# ES6: new RegExp(regex, flags) is valid
test262o/test/suite/ch15/15.10/15.10.3/S15.10.3.1_A2_T1.js
test262o/test/suite/ch15/15.10/15.10.3/S15.10.3.1_A2_T2.js
test262o/test/suite/ch15/15.10/15.10.4/15.10.4.1/15.10.4.1-1.js
test262o/test/suite/ch15/15.10/15.10.4/S15.10.4.1_A2_T1.js
test262o/test/suite/ch15/15.10/15.10.4/S15.10.4.1_A2_T2.js
# ES6 != ES5: RegExp.prototype.test behavior
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.2/S15.10.6.2_A5_T3.js
# ES6 != ES5: source, global, ignoreCase, multiline, lastIndex are not data properties
# of RegExp objects and RegExp.prototype is not a RegExp object
test262o/test/suite/ch15/15.10/15.10.6/15.10.6.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/15.10.7.1-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/15.10.7.1-2.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A8.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A9.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.1/S15.10.7.1_A10.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/15.10.7.2-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/15.10.7.2-2.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A8.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A9.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.2/S15.10.7.2_A10.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/15.10.7.3-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/15.10.7.3-2.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A8.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A9.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.3/S15.10.7.3_A10.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/15.10.7.4-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/15.10.7.4-2.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A8.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A9.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.4/S15.10.7.4_A10.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.5/15.10.7.5-1.js
test262o/test/suite/ch15/15.10/15.10.7/15.10.7.5/15.10.7.5-2.js
# ES6 != ES5: Error.prototype is a normal object
test262o/test/suite/ch15/15.11/15.11.4/S15.11.4_A2.js
# ES6 different ToLength() semantics
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.5/S15.4.4.5_A4_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A2_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T1.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.6/S15.4.4.6_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A2_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A4_T1.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.7/S15.4.4.7_A4_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.8/S15.4.4.8_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.9/S15.4.4.9_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T1.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.10/S15.4.4.10_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.11/S15.4.4.11_A4_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A3_T1.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.12/S15.4.4.12_A3_T3.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.13/S15.4.4.13_A3_T2.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-8.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-28.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.14/15.4.4.14-3-29.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-28.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-8.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.16/15.4.4.16-3-29.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-8.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-28.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.17/15.4.4.17-3-29.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-8.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-28.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.19/15.4.4.19-3-29.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-25.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-7.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-12.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-25.js
# ES6 different ToLength() semantics causes near infinite runtime
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-14.js
test262o/test/suite/ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-14.js
# ES6 arguments/caller changes
test262o/test/suite/ch10/10.6/10.6-13-b-1-s.js
test262o/test/suite/ch10/10.6/10.6-13-b-2-s.js
test262o/test/suite/ch10/10.6/10.6-13-b-3-s.js
test262o/test/suite/ch10/10.6/10.6-14-1-s.js
test262o/test/suite/ch10/10.6/10.6-14-b-1-s.js
test262o/test/suite/ch10/10.6/10.6-14-b-4-s.js
test262o/test/suite/ch13/13.2/13.2-29-s.js
test262o/test/suite/ch13/13.2/13.2-30-s.js
test262o/test/suite/ch13/13.2/13.2-31-s.js
test262o/test/suite/ch13/13.2/13.2-32-s.js
test262o/test/suite/ch13/13.2/13.2-33-s.js
test262o/test/suite/ch13/13.2/13.2-34-s.js
test262o/test/suite/ch13/13.2/13.2-35-s.js
test262o/test/suite/ch13/13.2/13.2-36-s.js
test262o/test/suite/ch13/13.2/S13.2.3_A1.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-1.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-4.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-5.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-1.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-4.js
test262o/test/suite/ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-5.js
# u180e is no longer considered as a space
test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A2.js
test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A3_T1.js
test262o/test/suite/ch09/9.3/9.3.1/S9.3.1_A3_T2.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.2/S15.1.2.2_A2_T10.js
test262o/test/suite/ch15/15.1/15.1.2/15.1.2.3/S15.1.2.3_A2_T10.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-2.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-3.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-4.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-5.js
test262o/test/suite/ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-6.js
test262o/test/suite/ch15/15.10/15.10.2/15.10.2.12/S15.10.2.12_A1_T1.js
test262o/test/suite/ch15/15.10/15.10.2/15.10.2.12/S15.10.2.12_A2_T1.js
# E6 eval return value is different
test262o/test/suite/ch12/12.6/12.6.3/S12.6.3_A9.js
test262o/test/suite/ch12/12.6/12.6.3/S12.6.3_A9.1.js
# ECMA 2019 optional-catch-binding feature allows try{}catch{}
test262o/test/suite/ch12/12.14/S12.14_A16_T4.js
# Syntax error instead of ReferenceError in ES2020
test262o/test/suite/ch11/11.13/11.13.1/11.13.1-1-1.js
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
[tests]
# list test files or use config.testdir

0
test262o_errors.txt Normal file
View File

88
tests/bjson.c Normal file
View File

@ -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;
}

1053
tests/microbench.js Normal file

File diff suppressed because it is too large Load Diff

71
tests/test262.patch Normal file
View File

@ -0,0 +1,71 @@
diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js
index 9c1217351e..3c24755558 100644
--- a/harness/atomicsHelper.js
+++ b/harness/atomicsHelper.js
@@ -227,10 +227,14 @@ $262.agent.waitUntil = function(typedArray, index, expected) {
* }
*/
$262.agent.timeouts = {
- yield: 100,
- small: 200,
- long: 1000,
- huge: 10000,
+// yield: 100,
+// small: 200,
+// long: 1000,
+// huge: 10000,
+ yield: 20,
+ small: 20,
+ long: 100,
+ huge: 1000,
};
/**
diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js
index be7039fda0..7b38abf8df 100644
--- a/harness/regExpUtils.js
+++ b/harness/regExpUtils.js
@@ -6,24 +6,27 @@ description: |
defines: [buildString, testPropertyEscapes, matchValidator]
---*/
+if ($262 && typeof $262.codePointRange === "function") {
+ /* use C function to build the codePointRange (much faster with
+ slow JS engines) */
+ codePointRange = $262.codePointRange;
+} else {
+ codePointRange = function codePointRange(start, end) {
+ const codePoints = [];
+ let length = 0;
+ for (codePoint = start; codePoint < end; codePoint++) {
+ codePoints[length++] = codePoint;
+ }
+ return String.fromCodePoint.apply(null, codePoints);
+ }
+}
+
function buildString({ loneCodePoints, ranges }) {
- const CHUNK_SIZE = 10000;
- let result = Reflect.apply(String.fromCodePoint, null, loneCodePoints);
- for (let i = 0; i < ranges.length; i++) {
- const range = ranges[i];
- const start = range[0];
- const end = range[1];
- const codePoints = [];
- for (let length = 0, codePoint = start; codePoint <= end; codePoint++) {
- codePoints[length++] = codePoint;
- if (length === CHUNK_SIZE) {
- result += Reflect.apply(String.fromCodePoint, null, codePoints);
- codePoints.length = length = 0;
- }
+ let result = String.fromCodePoint.apply(null, loneCodePoints);
+ for (const [start, end] of ranges) {
+ result += codePointRange(start, end + 1);
}
- result += Reflect.apply(String.fromCodePoint, null, codePoints);
- }
- return result;
+ return result;
}
function testPropertyEscapes(regex, string, expression) {

241
tests/test_bignum.js Normal file
View File

@ -0,0 +1,241 @@
"use strict";
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
if (actual !== null && expected !== null
&& typeof actual == 'object' && typeof expected == 'object'
&& actual.toString() === expected.toString())
return;
throw Error("assertion failed: got |" + actual + "|" +
", expected |" + expected + "|" +
(message ? " (" + message + ")" : ""));
}
function assertThrows(err, func)
{
var ex;
ex = false;
try {
func();
} catch(e) {
ex = true;
assert(e instanceof err);
}
assert(ex, true, "exception expected");
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function bigint_pow(a, n)
{
var r, i;
r = 1n;
for(i = 0n; i < n; i++)
r *= a;
return r;
}
/* a must be < b */
function test_less(a, b)
{
assert(a < b);
assert(!(b < a));
assert(a <= b);
assert(!(b <= a));
assert(b > a);
assert(!(a > b));
assert(b >= a);
assert(!(a >= b));
assert(a != b);
assert(!(a == b));
}
/* a must be numerically equal to b */
function test_eq(a, b)
{
assert(a == b);
assert(b == a);
assert(!(a != b));
assert(!(b != a));
assert(a <= b);
assert(b <= a);
assert(!(a < b));
assert(a >= b);
assert(b >= a);
assert(!(a > b));
}
function test_bigint1()
{
var a, r;
test_less(2n, 3n);
test_eq(3n, 3n);
test_less(2, 3n);
test_eq(3, 3n);
test_less(2.1, 3n);
test_eq(Math.sqrt(4), 2n);
a = bigint_pow(3n, 100n);
assert((a - 1n) != a);
assert(a == 515377520732011331036461129765621272702107522001n);
assert(a == 0x5a4653ca673768565b41f775d6947d55cf3813d1n);
r = 1n << 31n;
assert(r, 2147483648n, "1 << 31n === 2147483648n");
r = 1n << 32n;
assert(r, 4294967296n, "1 << 32n === 4294967296n");
}
function test_bigint2()
{
assert(BigInt(""), 0n);
assert(BigInt(" 123"), 123n);
assert(BigInt(" 123 "), 123n);
assertThrows(SyntaxError, () => { BigInt("+") } );
assertThrows(SyntaxError, () => { BigInt("-") } );
assertThrows(SyntaxError, () => { BigInt("\x00a") } );
assertThrows(SyntaxError, () => { BigInt(" 123 r") } );
}
function test_divrem(div1, a, b, q)
{
var div, divrem, t;
div = BigInt[div1];
divrem = BigInt[div1 + "rem"];
assert(div(a, b) == q);
t = divrem(a, b);
assert(t[0] == q);
assert(a == b * q + t[1]);
}
function test_idiv1(div, a, b, r)
{
test_divrem(div, a, b, r[0]);
test_divrem(div, -a, b, r[1]);
test_divrem(div, a, -b, r[2]);
test_divrem(div, -a, -b, r[3]);
}
/* QuickJS BigInt extensions */
function test_bigint_ext()
{
var r;
assert(BigInt.floorLog2(0n) === -1n);
assert(BigInt.floorLog2(7n) === 2n);
assert(BigInt.sqrt(0xffffffc000000000000000n) === 17592185913343n);
r = BigInt.sqrtrem(0xffffffc000000000000000n);
assert(r[0] === 17592185913343n);
assert(r[1] === 35167191957503n);
test_idiv1("tdiv", 3n, 2n, [1n, -1n, -1n, 1n]);
test_idiv1("fdiv", 3n, 2n, [1n, -2n, -2n, 1n]);
test_idiv1("cdiv", 3n, 2n, [2n, -1n, -1n, 2n]);
test_idiv1("ediv", 3n, 2n, [1n, -2n, -1n, 2n]);
}
function test_bigfloat()
{
var e, a, b, sqrt2;
assert(typeof 1n === "bigint");
assert(typeof 1l === "bigfloat");
assert(1 == 1.0l);
assert(1 !== 1.0l);
test_less(2l, 3l);
test_eq(3l, 3l);
test_less(2, 3l);
test_eq(3, 3l);
test_less(2.1, 3l);
test_eq(Math.sqrt(9), 3l);
test_less(2n, 3l);
test_eq(3n, 3l);
e = new BigFloatEnv(128);
assert(e.prec == 128);
a = BigFloat.sqrt(2l, e);
assert(a == BigFloat.parseFloat("0x1.6a09e667f3bcc908b2fb1366ea957d3e", 0, e));
assert(e.inexact === true);
assert(BigFloat.fpRound(a) == 0x1.6a09e667f3bcc908b2fb1366ea95l);
b = BigFloatEnv.setPrec(BigFloat.sqrt.bind(null, 2), 128);
assert(a == b);
}
function test_bigdecimal()
{
assert(1d === 1d);
assert(1d !== 2d);
test_less(1d, 2d);
test_eq(2d, 2d);
test_less(1, 2d);
test_eq(2, 2d);
test_less(1.1, 2d);
test_eq(Math.sqrt(4), 2d);
test_less(2n, 3d);
test_eq(3n, 3d);
assert(BigDecimal("1234.1") === 1234.1d);
assert(BigDecimal(" 1234.1") === 1234.1d);
assert(BigDecimal(" 1234.1 ") === 1234.1d);
assert(BigDecimal(0.1) === 0.1d);
assert(BigDecimal(123) === 123d);
assert(BigDecimal(true) === 1d);
assert(123d + 1d === 124d);
assert(123d - 1d === 122d);
assert(3.2d * 3d === 9.6d);
assert(10d / 2d === 5d);
assertThrows(RangeError, () => { 10d / 3d } );
assert(BigDecimal.div(20d, 3d,
{ roundingMode: "half-even",
maximumSignificantDigits: 3 }) === 6.67d);
assert(BigDecimal.div(20d, 3d,
{ roundingMode: "half-even",
maximumFractionDigits: 3 }) === 6.667d);
assert(10d % 3d === 1d);
assert(-10d % 3d === -1d);
assert(-10d % 3d === -1d);
assert(1234.5d ** 3d === 1881365963.625d);
assertThrows(RangeError, () => { 2d ** 3.1d } );
assertThrows(RangeError, () => { 2d ** -3d } );
assert(BigDecimal.sqrt(2d,
{ roundingMode: "half-even",
maximumSignificantDigits: 4 }) === 1.414d);
assert(BigDecimal.round(3.14159d,
{ roundingMode: "half-even",
maximumFractionDigits: 3 }) === 3.142d);
}
test_bigint1();
test_bigint2();
test_bigint_ext();
test_bigfloat();
test_bigdecimal();

125
tests/test_bjson.js Normal file
View File

@ -0,0 +1,125 @@
import * as bjson from "./bjson.so";
function assert(b, str)
{
if (b) {
return;
} else {
throw Error("assertion failed: " + str);
}
}
function toHex(a)
{
var i, s = "", tab, v;
tab = new Uint8Array(a);
for(i = 0; i < tab.length; i++) {
v = tab[i].toString(16);
if (v.length < 2)
v = "0" + v;
if (i !== 0)
s += " ";
s += v;
}
return s;
}
function toStr(a)
{
var s, i, props, prop;
switch(typeof(a)) {
case "object":
if (a === null)
return "null";
if (Array.isArray(a)) {
s = "[";
for(i = 0; i < a.length; i++) {
if (i != 0)
s += ",";
s += toStr(a[i]);
}
s += "]";
} else {
props = Object.keys(a);
s = "{";
for(i = 0; i < props.length; i++) {
if (i != 0)
s += ",";
prop = props[i];
s += prop + ":" + toStr(a[prop]);
}
s += "}";
}
return s;
case "undefined":
return "undefined";
case "string":
return a.__quote();
case "number":
case "bigfloat":
if (a == 0 && 1 / a < 0)
return "-0";
else
return a.toString();
break;
default:
return a.toString();
}
}
function bjson_test(a)
{
var buf, r, a_str, r_str;
a_str = toStr(a);
buf = bjson.write(a);
if (0) {
print(a_str, "->", toHex(buf));
}
r = bjson.read(buf, 0, buf.byteLength);
r_str = toStr(r);
if (a_str != r_str) {
print(a_str);
print(r_str);
assert(false);
}
}
function bjson_test_all()
{
var obj;
bjson_test({x:1, y:2, if:3});
bjson_test([1, 2, 3]);
bjson_test([1.0, "aa", true, false, undefined, null, NaN, -Infinity, -0.0]);
if (typeof BigInt !== "undefined") {
bjson_test([BigInt("1"), -BigInt("0x123456789"),
BigInt("0x123456789abcdef123456789abcdef")]);
}
if (typeof BigFloat !== "undefined") {
BigFloatEnv.setPrec(function () {
bjson_test([BigFloat("0.1"), BigFloat("-1e30"), BigFloat("0"),
BigFloat("-0"), BigFloat("Infinity"), BigFloat("-Infinity"),
0.0 / BigFloat("0"), BigFloat.MAX_VALUE,
BigFloat.MIN_VALUE]);
}, 113, 15);
}
if (typeof BigDecimal !== "undefined") {
bjson_test([BigDecimal("0"),
BigDecimal("0.8"), BigDecimal("123321312321321e100"),
BigDecimal("-1233213123213214332333223332e100"),
BigDecimal("1.233e-1000")]);
}
/* tested with a circular reference */
obj = {};
obj.x = obj;
try {
bjson.write(obj);
assert(false);
} catch(e) {
assert(e instanceof TypeError);
}
}
bjson_test_all();

647
tests/test_builtin.js Normal file
View File

@ -0,0 +1,647 @@
"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 + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function my_func(a, b)
{
return a + b;
}
function test_function()
{
function f(a, b) {
var i, tab = [];
tab.push(this);
for(i = 0; i < arguments.length; i++)
tab.push(arguments[i]);
return tab;
}
function constructor1(a) {
this.x = a;
}
var r, g;
r = my_func.call(null, 1, 2);
assert(r, 3, "call");
r = my_func.apply(null, [1, 2]);
assert(r, 3, "apply");
r = new Function("a", "b", "return a + b;");
assert(r(2,3), 5, "function");
g = f.bind(1, 2);
assert(g.length, 1);
assert(g.name, "bound f");
assert(g(3), [1,2,3]);
g = constructor1.bind(null, 1);
r = new g();
assert(r.x, 1);
}
function test()
{
var r, a, b, c, err;
r = Error("hello");
assert(r.message, "hello", "Error");
a = new Object();
a.x = 1;
assert(a.x, 1, "Object");
assert(Object.getPrototypeOf(a), Object.prototype, "getPrototypeOf");
Object.defineProperty(a, "y", { value: 3, writable: true, configurable: true, enumerable: true });
assert(a.y, 3, "defineProperty");
Object.defineProperty(a, "z", { get: function () { return 4; }, set: function(val) { this.z_val = val; }, configurable: true, enumerable: true });
assert(a.z, 4, "get");
a.z = 5;
assert(a.z_val, 5, "set");
a = { get z() { return 4; }, set z(val) { this.z_val = val; } };
assert(a.z, 4, "get");
a.z = 5;
assert(a.z_val, 5, "set");
b = Object.create(a);
assert(Object.getPrototypeOf(b), a, "create");
c = {u:2};
/* XXX: refcount bug in 'b' instead of 'a' */
Object.setPrototypeOf(a, c);
assert(Object.getPrototypeOf(a), c, "setPrototypeOf");
a = {};
assert(a.toString(), "[object Object]", "toString");
a = {x:1};
assert(Object.isExtensible(a), true, "extensible");
Object.preventExtensions(a);
err = false;
try {
a.y = 2;
} catch(e) {
err = true;
}
assert(Object.isExtensible(a), false, "extensible");
assert(typeof a.y, "undefined", "extensible");
assert(err, true, "extensible");
}
function test_enum()
{
var a, tab;
a = {x:1,
"18014398509481984": 1,
"9007199254740992": 1,
"9007199254740991": 1,
"4294967296": 1,
"4294967295": 1,
y:1,
"4294967294": 1,
"1": 2};
tab = Object.keys(a);
// console.log("tab=" + tab.toString());
assert(tab, ["1","4294967294","x","18014398509481984","9007199254740992","9007199254740991","4294967296","4294967295","y"], "keys");
}
function test_array()
{
var a, err;
a = [1, 2, 3];
assert(a.length, 3, "array");
assert(a[2], 3, "array1");
a = new Array(10);
assert(a.length, 10, "array2");
a = new Array(1, 2);
assert(a.length === 2 && a[0] === 1 && a[1] === 2, true, "array3");
a = [1, 2, 3];
a.length = 2;
assert(a.length === 2 && a[0] === 1 && a[1] === 2, true, "array4");
a = [];
a[1] = 10;
a[4] = 3;
assert(a.length, 5);
a = [1,2];
a.length = 5;
a[4] = 1;
a.length = 4;
assert(a[4] !== 1, true, "array5");
a = [1,2];
a.push(3,4);
assert(a.join(), "1,2,3,4", "join");
a = [1,2,3,4,5];
Object.defineProperty(a, "3", { configurable: false });
err = false;
try {
a.length = 2;
} catch(e) {
err = true;
}
assert(err && a.toString() === "1,2,3,4");
}
function test_string()
{
var a;
a = String("abc");
assert(a.length, 3, "string");
assert(a[1], "b", "string");
assert(a.charCodeAt(1), 0x62, "string");
assert(String.fromCharCode(65), "A", "string");
assert(String.fromCharCode.apply(null, [65, 66, 67]), "ABC", "string");
assert(a.charAt(1), "b");
assert(a.charAt(-1), "");
assert(a.charAt(3), "");
a = "abcd";
assert(a.substring(1, 3), "bc", "substring");
a = String.fromCharCode(0x20ac);
assert(a.charCodeAt(0), 0x20ac, "unicode");
assert(a, "€", "unicode");
assert(a, "\u20ac", "unicode");
assert(a, "\u{20ac}", "unicode");
assert("a", "\x61", "unicode");
a = "\u{10ffff}";
assert(a.length, 2, "unicode");
assert(a, "\u{dbff}\u{dfff}", "unicode");
assert(a.codePointAt(0), 0x10ffff);
assert(String.fromCodePoint(0x10ffff), a);
assert("a".concat("b", "c"), "abc");
assert("abcabc".indexOf("cab"), 2);
assert("abcabc".indexOf("cab2"), -1);
assert("abc".indexOf("c"), 2);
assert("aaa".indexOf("a"), 0);
assert("aaa".indexOf("a", NaN), 0);
assert("aaa".indexOf("a", -Infinity), 0);
assert("aaa".indexOf("a", -1), 0);
assert("aaa".indexOf("a", -0), 0);
assert("aaa".indexOf("a", 0), 0);
assert("aaa".indexOf("a", 1), 1);
assert("aaa".indexOf("a", 2), 2);
assert("aaa".indexOf("a", 3), -1);
assert("aaa".indexOf("a", 4), -1);
assert("aaa".indexOf("a", Infinity), -1);
assert("aaa".indexOf(""), 0);
assert("aaa".indexOf("", NaN), 0);
assert("aaa".indexOf("", -Infinity), 0);
assert("aaa".indexOf("", -1), 0);
assert("aaa".indexOf("", -0), 0);
assert("aaa".indexOf("", 0), 0);
assert("aaa".indexOf("", 1), 1);
assert("aaa".indexOf("", 2), 2);
assert("aaa".indexOf("", 3), 3);
assert("aaa".indexOf("", 4), 3);
assert("aaa".indexOf("", Infinity), 3);
assert("aaa".lastIndexOf("a"), 2);
assert("aaa".lastIndexOf("a", NaN), 2);
assert("aaa".lastIndexOf("a", -Infinity), 0);
assert("aaa".lastIndexOf("a", -1), 0);
assert("aaa".lastIndexOf("a", -0), 0);
assert("aaa".lastIndexOf("a", 0), 0);
assert("aaa".lastIndexOf("a", 1), 1);
assert("aaa".lastIndexOf("a", 2), 2);
assert("aaa".lastIndexOf("a", 3), 2);
assert("aaa".lastIndexOf("a", 4), 2);
assert("aaa".lastIndexOf("a", Infinity), 2);
assert("aaa".lastIndexOf(""), 3);
assert("aaa".lastIndexOf("", NaN), 3);
assert("aaa".lastIndexOf("", -Infinity), 0);
assert("aaa".lastIndexOf("", -1), 0);
assert("aaa".lastIndexOf("", -0), 0);
assert("aaa".lastIndexOf("", 0), 0);
assert("aaa".lastIndexOf("", 1), 1);
assert("aaa".lastIndexOf("", 2), 2);
assert("aaa".lastIndexOf("", 3), 3);
assert("aaa".lastIndexOf("", 4), 3);
assert("aaa".lastIndexOf("", Infinity), 3);
assert("a,b,c".split(","), ["a","b","c"]);
assert(",b,c".split(","), ["","b","c"]);
assert("a,b,".split(","), ["a","b",""]);
assert("aaaa".split(), [ "aaaa" ]);
assert("aaaa".split(undefined, 0), [ ]);
assert("aaaa".split(""), [ "a", "a", "a", "a" ]);
assert("aaaa".split("", 0), [ ]);
assert("aaaa".split("", 1), [ "a" ]);
assert("aaaa".split("", 2), [ "a", "a" ]);
assert("aaaa".split("a"), [ "", "", "", "", "" ]);
assert("aaaa".split("a", 2), [ "", "" ]);
assert("aaaa".split("aa"), [ "", "", "" ]);
assert("aaaa".split("aa", 0), [ ]);
assert("aaaa".split("aa", 1), [ "" ]);
assert("aaaa".split("aa", 2), [ "", "" ]);
assert("aaaa".split("aaa"), [ "", "a" ]);
assert("aaaa".split("aaaa"), [ "", "" ]);
assert("aaaa".split("aaaaa"), [ "aaaa" ]);
assert("aaaa".split("aaaaa", 0), [ ]);
assert("aaaa".split("aaaaa", 1), [ "aaaa" ]);
assert(eval('"\0"'), "\0");
}
function test_math()
{
var a;
a = 1.4;
assert(Math.floor(a), 1);
assert(Math.ceil(a), 2);
assert(Math.imul(0x12345678, 123), -1088058456);
assert(Math.fround(0.1), 0.10000000149011612);
}
function test_number()
{
assert(parseInt("123"), 123);
assert(parseInt(" 123r"), 123);
assert(parseInt("0x123"), 0x123);
assert(parseInt("0o123"), 0);
assert(+" 123 ", 123);
assert(+"0b111", 7);
assert(+"0o123", 83);
assert(parseFloat("0x1234"), 0);
assert(parseFloat("Infinity"), Infinity);
assert(parseFloat("-Infinity"), -Infinity);
assert(parseFloat("123.2"), 123.2);
assert(parseFloat("123.2e3"), 123200);
assert(Number.isNaN(Number("+")));
assert(Number.isNaN(Number("-")));
assert(Number.isNaN(Number("\x00a")));
assert((25).toExponential(0), "3e+1");
assert((-25).toExponential(0), "-3e+1");
assert((2.5).toPrecision(1), "3");
assert((-2.5).toPrecision(1), "-3");
assert((1.125).toFixed(2), "1.13");
assert((-1.125).toFixed(2), "-1.13");
}
function test_eval2()
{
var g_call_count = 0;
/* force non strict mode for f1 and f2 */
var f1 = new Function("eval", "eval(1, 2)");
var f2 = new Function("eval", "eval(...[1, 2])");
function g(a, b) {
assert(a, 1);
assert(b, 2);
g_call_count++;
}
f1(g);
f2(g);
assert(g_call_count, 2);
}
function test_eval()
{
function f(b) {
var x = 1;
return eval(b);
}
var r, a;
r = eval("1+1;");
assert(r, 2, "eval");
r = eval("var my_var=2; my_var;");
assert(r, 2, "eval");
assert(typeof my_var, "undefined");
assert(eval("if (1) 2; else 3;"), 2);
assert(eval("if (0) 2; else 3;"), 3);
assert(f.call(1, "this"), 1);
a = 2;
assert(eval("a"), 2);
eval("a = 3");
assert(a, 3);
assert(f("arguments.length", 1), 2);
assert(f("arguments[1]", 1), 1);
a = 4;
assert(f("a"), 4);
f("a=3");
assert(a, 3);
test_eval2();
}
function test_typed_array()
{
var buffer, a, i;
a = new Uint8Array(4);
assert(a.length, 4);
for(i = 0; i < a.length; i++)
a[i] = i;
assert(a.join(","), "0,1,2,3");
a[0] = -1;
assert(a[0], 255);
a = new Int8Array(3);
a[0] = 255;
assert(a[0], -1);
a = new Int32Array(3);
a[0] = Math.pow(2, 32) - 1;
assert(a[0], -1);
assert(a.BYTES_PER_ELEMENT, 4);
a = new Uint8ClampedArray(4);
a[0] = -100;
a[1] = 1.5;
a[2] = 0.5;
a[3] = 1233.5;
assert(a.toString(), "0,2,0,255");
buffer = new ArrayBuffer(16);
assert(buffer.byteLength, 16);
a = new Uint32Array(buffer, 12, 1);
assert(a.length, 1);
a[0] = -1;
a = new Uint16Array(buffer, 2);
a[0] = -1;
a = new Float32Array(buffer, 8, 1);
a[0] = 1;
a = new Uint8Array(buffer);
assert(a.toString(), "0,0,255,255,0,0,0,0,0,0,128,63,255,255,255,255");
assert(a.buffer, buffer);
a = new Uint8Array([1, 2, 3, 4]);
assert(a.toString(), "1,2,3,4");
a.set([10, 11], 2);
assert(a.toString(), "1,2,10,11");
}
function test_json()
{
var a, s;
s = '{"x":1,"y":true,"z":null,"a":[1,2,3],"s":"str"}';
a = JSON.parse(s);
assert(a.x, 1);
assert(a.y, true);
assert(a.z, null);
assert(JSON.stringify(a), s);
/* indentation test */
assert(JSON.stringify([[{x:1,y:{},z:[]},2,3]],undefined,1),
`[
[
{
"x": 1,
"y": {},
"z": []
},
2,
3
]
]`);
}
function test_date()
{
var d = new Date(1506098258091), a, s;
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());
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");
}
function test_regexp()
{
var a, str;
str = "abbbbbc";
a = /(b+)c/.exec(str);
assert(a[0], "bbbbbc");
assert(a[1], "bbbbb");
assert(a.index, 1);
assert(a.input, str);
a = /(b+)c/.test(str);
assert(a, true);
assert(/\x61/.exec("a")[0], "a");
assert(/\u0061/.exec("a")[0], "a");
assert(/\ca/.exec("\x01")[0], "\x01");
assert(/\\a/.exec("\\a")[0], "\\a");
assert(/\c0/.exec("\\c0")[0], "\\c0");
a = /(\.(?=com|org)|\/)/.exec("ah.com");
assert(a.index === 2 && a[0] === ".");
a = /(\.(?!com|org)|\/)/.exec("ah.com");
assert(a, null);
a = /(?=(a+))/.exec("baaabac");
assert(a.index === 1 && a[0] === "" && a[1] === "aaa");
a = /(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac");
assert(a, ["zaacbbbcac","z","ac","a",,"c"]);
a = eval("/\0a/");
assert(a.toString(), "/\0a/");
assert(a.exec("\0a")[0], "\0a");
}
function test_symbol()
{
var a, b, obj, c;
a = Symbol("abc");
obj = {};
obj[a] = 2;
assert(obj[a], 2);
assert(typeof obj["abc"], "undefined");
assert(String(a), "Symbol(abc)");
b = Symbol("abc");
assert(a == a);
assert(a === a);
assert(a != b);
assert(a !== b);
b = Symbol.for("abc");
c = Symbol.for("abc");
assert(b === c);
assert(b !== a);
assert(Symbol.keyFor(b), "abc");
assert(Symbol.keyFor(a), undefined);
a = Symbol("aaa");
assert(a.valueOf(), a);
assert(a.toString(), "Symbol(aaa)");
b = Object(a);
assert(b.valueOf(), a);
assert(b.toString(), "Symbol(aaa)");
}
function test_map()
{
var a, i, n, tab, o, v;
n = 1000;
a = new Map();
tab = [];
for(i = 0; i < n; i++) {
v = { };
o = { id: i };
tab[i] = [o, v];
a.set(o, v);
}
assert(a.size, n);
for(i = 0; i < n; i++) {
assert(a.get(tab[i][0]), tab[i][1]);
}
i = 0;
a.forEach(function (v, o) {
assert(o, tab[i++][0]);
assert(a.has(o));
assert(a.delete(o));
assert(!a.has(o));
});
assert(a.size, 0);
}
function test_weak_map()
{
var a, i, n, tab, o, v, n2;
a = new WeakMap();
n = 10;
tab = [];
for(i = 0; i < n; i++) {
v = { };
o = { id: i };
tab[i] = [o, v];
a.set(o, v);
}
o = null;
n2 = n >> 1;
for(i = 0; i < n2; i++) {
a.delete(tab[i][0]);
}
for(i = n2; i < n; i++) {
tab[i][0] = null; /* should remove the object from the WeakMap too */
}
/* the WeakMap should be empty here */
}
function test_generator()
{
function *f() {
var ret;
yield 1;
ret = yield 2;
assert(ret, "next_arg");
return 3;
}
function *f2() {
yield 1;
yield 2;
return "ret_val";
}
function *f1() {
var ret = yield *f2();
assert(ret, "ret_val");
return 3;
}
var g, v;
g = f();
v = g.next();
assert(v.value === 1 && v.done === false);
v = g.next();
assert(v.value === 2 && v.done === false);
v = g.next("next_arg");
assert(v.value === 3 && v.done === true);
v = g.next();
assert(v.value === undefined && v.done === true);
g = f1();
v = g.next();
assert(v.value === 1 && v.done === false);
v = g.next();
assert(v.value === 2 && v.done === false);
v = g.next();
assert(v.value === 3 && v.done === true);
v = g.next();
assert(v.value === undefined && v.done === true);
}
test();
test_function();
test_enum();
test_array();
test_string();
test_math();
test_number();
test_eval();
test_typed_array();
test_json();
test_date();
test_regexp();
test_symbol();
test_map();
test_weak_map();
test_generator();

221
tests/test_closure.js Normal file
View File

@ -0,0 +1,221 @@
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 + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
var log_str = "";
function log(str)
{
log_str += str + ",";
}
function f(a, b, c)
{
var x = 10;
log("a="+a);
function g(d) {
function h() {
log("d=" + d);
log("x=" + x);
}
log("b=" + b);
log("c=" + c);
h();
}
g(4);
return g;
}
var g1 = f(1, 2, 3);
g1(5);
assert(log_str, "a=1,b=2,c=3,d=4,x=10,b=2,c=3,d=5,x=10,", "closure1");
function test_closure1()
{
function f2()
{
var val = 1;
function set(a) {
val = a;
}
function get(a) {
return val;
}
return { "set": set, "get": get };
}
var obj = f2();
obj.set(10);
var r;
r = obj.get();
assert(r, 10, "closure2");
}
function test_closure2()
{
var expr_func = function myfunc1(n) {
function myfunc2(n) {
return myfunc1(n - 1);
}
if (n == 0)
return 0;
else
return myfunc2(n);
};
var r;
r = expr_func(1);
assert(r, 0, "expr_func");
}
function test_closure3()
{
function fib(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
var fib_func = function fib1(n)
{
if (n <= 0)
return 0;
else if (n == 1)
return 1;
else
return fib1(n - 1) + fib1(n - 2);
};
assert(fib(6), 8, "fib");
assert(fib_func(6), 8, "fib_func");
}
function test_arrow_function()
{
"use strict";
function f1() {
return (() => arguments)();
}
function f2() {
return (() => this)();
}
function f3() {
return (() => eval("this"))();
}
function f4() {
return (() => eval("new.target"))();
}
var a;
a = f1(1, 2);
assert(a.length, 2);
assert(a[0] === 1 && a[1] === 2);
assert(f2.call("this_val") === "this_val");
assert(f3.call("this_val") === "this_val");
assert(new f4() === f4);
var o1 = { f() { return this; } };
var o2 = { f() {
return (() => eval("super.f()"))();
} };
o2.__proto__ = o1;
assert(o2.f() === o2);
}
function test_with()
{
var o1 = { x: "o1", y: "o1" };
var x = "local";
eval('var z="var_obj";');
assert(z === "var_obj");
with (o1) {
assert(x === "o1");
assert(eval("x") === "o1");
var f = function () {
o2 = { x: "o2" };
with (o2) {
assert(x === "o2");
assert(y === "o1");
assert(z === "var_obj");
assert(eval("x") === "o2");
assert(eval("y") === "o1");
assert(eval("z") === "var_obj");
assert(eval('eval("x")') === "o2");
}
};
f();
}
}
function test_eval_closure()
{
var tab;
tab = [];
for(let i = 0; i < 3; i++) {
eval("tab.push(function g1() { return i; })");
}
for(let i = 0; i < 3; i++) {
assert(tab[i]() === i);
}
tab = [];
for(let i = 0; i < 3; i++) {
let f = function f() {
eval("tab.push(function g2() { return i; })");
};
f();
}
for(let i = 0; i < 3; i++) {
assert(tab[i]() === i);
}
}
function test_eval_const()
{
const a = 1;
var success = false;
var f = function () {
eval("a = 1");
};
try {
f();
} catch(e) {
success = (e instanceof TypeError);
}
assert(success);
}
test_closure1();
test_closure2();
test_closure3();
test_arrow_function();
test_with();
test_eval_closure();
test_eval_const();

368
tests/test_loop.js Normal file
View File

@ -0,0 +1,368 @@
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 + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function test_while()
{
var i, c;
i = 0;
c = 0;
while (i < 3) {
c++;
i++;
}
assert(c === 3);
}
function test_while_break()
{
var i, c;
i = 0;
c = 0;
while (i < 3) {
c++;
if (i == 1)
break;
i++;
}
assert(c === 2 && i === 1);
}
function test_do_while()
{
var i, c;
i = 0;
c = 0;
do {
c++;
i++;
} while (i < 3);
assert(c === 3 && i === 3);
}
function test_for()
{
var i, c;
c = 0;
for(i = 0; i < 3; i++) {
c++;
}
assert(c === 3 && i === 3);
c = 0;
for(var j = 0; j < 3; j++) {
c++;
}
assert(c === 3 && j === 3);
}
function test_for_in()
{
var i, tab, a, b;
tab = [];
for(i in {x:1, y: 2}) {
tab.push(i);
}
assert(tab.toString(), "x,y", "for_in");
/* prototype chain test */
a = {x:2, y: 2, "1": 3};
b = {"4" : 3 };
Object.setPrototypeOf(a, b);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "1,x,y,4", "for_in");
/* non enumerable properties hide enumerables ones in the
prototype chain */
a = {y: 2, "1": 3};
Object.defineProperty(a, "x", { value: 1 });
b = {"x" : 3 };
Object.setPrototypeOf(a, b);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "1,y", "for_in");
/* array optimization */
a = [];
for(i = 0; i < 10; i++)
a.push(i);
tab = [];
for(i in a) {
tab.push(i);
}
assert(tab.toString(), "0,1,2,3,4,5,6,7,8,9", "for_in");
/* iterate with a field */
a={x:0};
tab = [];
for(a.x in {x:1, y: 2}) {
tab.push(a.x);
}
assert(tab.toString(), "x,y", "for_in");
/* iterate with a variable field */
a=[0];
tab = [];
for(a[0] in {x:1, y: 2}) {
tab.push(a[0]);
}
assert(tab.toString(), "x,y", "for_in");
/* variable definition in the for in */
tab = [];
for(var j in {x:1, y: 2}) {
tab.push(j);
}
assert(tab.toString(), "x,y", "for_in");
/* variable assigment in the for in */
tab = [];
for(var k = 2 in {x:1, y: 2}) {
tab.push(k);
}
assert(tab.toString(), "x,y", "for_in");
}
function test_for_in2()
{
var i;
tab = [];
for(i in {x:1, y: 2, z:3}) {
if (i === "y")
continue;
tab.push(i);
}
assert(tab.toString() == "x,z");
tab = [];
for(i in {x:1, y: 2, z:3}) {
if (i === "z")
break;
tab.push(i);
}
assert(tab.toString() == "x,y");
}
function test_for_break()
{
var i, c;
c = 0;
L1: for(i = 0; i < 3; i++) {
c++;
if (i == 0)
continue;
while (1) {
break L1;
}
}
assert(c === 2 && i === 1);
}
function test_switch1()
{
var i, a, s;
s = "";
for(i = 0; i < 3; i++) {
a = "?";
switch(i) {
case 0:
a = "a";
break;
case 1:
a = "b";
break;
default:
a = "c";
break;
}
s += a;
}
assert(s === "abc" && i === 3);
}
function test_switch2()
{
var i, a, s;
s = "";
for(i = 0; i < 4; i++) {
a = "?";
switch(i) {
case 0:
a = "a";
break;
case 1:
a = "b";
break;
case 2:
continue;
default:
a = "" + i;
break;
}
s += a;
}
assert(s === "ab3" && i === 4);
}
function test_try_catch1()
{
try {
throw "hello";
} catch (e) {
assert(e, "hello", "catch");
return;
}
assert(false, "catch");
}
function test_try_catch2()
{
var a;
try {
a = 1;
} catch (e) {
a = 2;
}
assert(a, 1, "catch");
}
function test_try_catch3()
{
var s;
s = "";
try {
s += "t";
} catch (e) {
s += "c";
} finally {
s += "f";
}
assert(s, "tf", "catch");
}
function test_try_catch4()
{
var s;
s = "";
try {
s += "t";
throw "c";
} catch (e) {
s += e;
} finally {
s += "f";
}
assert(s, "tcf", "catch");
}
function test_try_catch5()
{
var s;
s = "";
for(;;) {
try {
s += "t";
break;
s += "b";
} finally {
s += "f";
}
}
assert(s, "tf", "catch");
}
function test_try_catch6()
{
function f() {
try {
s += 't';
return 1;
} finally {
s += "f";
}
}
var s = "";
assert(f() === 1);
assert(s, "tf", "catch6");
}
function test_try_catch7()
{
var s;
s = "";
try {
try {
s += "t";
throw "a";
} finally {
s += "f";
}
} catch(e) {
s += e;
} finally {
s += "g";
}
assert(s, "tfag", "catch");
}
function test_try_catch8()
{
var i, s;
s = "";
for(var i in {x:1, y:2}) {
try {
s += i;
throw "a";
} catch (e) {
s += e;
} finally {
s += "f";
}
}
assert(s === "xafyaf");
}
test_while();
test_while_break();
test_do_while();
test_for();
test_for_break();
test_switch1();
test_switch2();
test_for_in();
test_for_in2();
test_try_catch1();
test_try_catch2();
test_try_catch3();
test_try_catch4();
test_try_catch5();
test_try_catch6();
test_try_catch7();
test_try_catch8();

363
tests/test_op.js Normal file
View File

@ -0,0 +1,363 @@
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 + ")" : ""));
}
// load more elaborate version of assert if available
try { __loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function test_op1()
{
var r, a;
r = 1 + 2;
assert(r, 3, "1 + 2 === 3");
r = 1 - 2;
assert(r, -1, "1 - 2 === -1");
r = -1;
assert(r, -1, "-1 === -1");
r = +2;
assert(r, 2, "+2 === 2");
r = 2 * 3;
assert(r, 6, "2 * 3 === 6");
r = 4 / 2;
assert(r, 2, "4 / 2 === 2");
r = 4 % 3;
assert(r, 1, "4 % 3 === 3");
r = 4 << 2;
assert(r, 16, "4 << 2 === 16");
r = 1 << 0;
assert(r, 1, "1 << 0 === 1");
r = 1 << 31;
assert(r, -2147483648, "1 << 31 === -2147483648");
r = 1 << 32;
assert(r, 1, "1 << 32 === 1");
r = (1 << 31) < 0;
assert(r, true, "(1 << 31) < 0 === true");
r = -4 >> 1;
assert(r, -2, "-4 >> 1 === -2");
r = -4 >>> 1;
assert(r, 0x7ffffffe, "-4 >>> 1 === 0x7ffffffe");
r = 1 & 1;
assert(r, 1, "1 & 1 === 1");
r = 0 | 1;
assert(r, 1, "0 | 1 === 1");
r = 1 ^ 1;
assert(r, 0, "1 ^ 1 === 0");
r = ~1;
assert(r, -2, "~1 === -2");
r = !1;
assert(r, false, "!1 === false");
assert((1 < 2), true, "(1 < 2) === true");
assert((2 > 1), true, "(2 > 1) === true");
assert(('b' > 'a'), true, "('b' > 'a') === true");
assert(2 ** 8, 256, "2 ** 8 === 256");
}
function test_cvt()
{
assert((NaN | 0) === 0);
assert((Infinity | 0) === 0);
assert(((-Infinity) | 0) === 0);
assert(("12345" | 0) === 12345);
assert(("0x12345" | 0) === 0x12345);
assert(((4294967296 * 3 - 4) | 0) === -4);
assert(("12345" >>> 0) === 12345);
assert(("0x12345" >>> 0) === 0x12345);
assert((NaN >>> 0) === 0);
assert((Infinity >>> 0) === 0);
assert(((-Infinity) >>> 0) === 0);
assert(((4294967296 * 3 - 4) >>> 0) === (4294967296 - 4));
}
function test_eq()
{
assert(null == undefined);
assert(undefined == null);
assert(true == 1);
assert(0 == false);
assert("" == 0);
assert("123" == 123);
assert("122" != 123);
assert((new Number(1)) == 1);
assert(2 == (new Number(2)));
assert((new String("abc")) == "abc");
assert({} != "abc");
}
function test_inc_dec()
{
var a, r;
a = 1;
r = a++;
assert(r === 1 && a === 2, true, "++");
a = 1;
r = ++a;
assert(r === 2 && a === 2, true, "++");
a = 1;
r = a--;
assert(r === 1 && a === 0, true, "--");
a = 1;
r = --a;
assert(r === 0 && a === 0, true, "--");
a = {x:true};
a.x++;
assert(a.x, 2, "++");
a = {x:true};
a.x--;
assert(a.x, 0, "--");
a = [true];
a[0]++;
assert(a[0], 2, "++");
a = {x:true};
r = a.x++;
assert(r === 1 && a.x === 2, true, "++");
a = {x:true};
r = a.x--;
assert(r === 1 && a.x === 0, true, "--");
a = [true];
r = a[0]++;
assert(r === 1 && a[0] === 2, true, "++");
a = [true];
r = a[0]--;
assert(r === 1 && a[0] === 0, true, "--");
}
function F(x)
{
this.x = x;
}
function test_op2()
{
var a, b;
a = new Object;
a.x = 1;
assert(a.x, 1, "new");
b = new F(2);
assert(b.x, 2, "new");
a = {x : 2};
assert(("x" in a), true, "in");
assert(("y" in a), false, "in");
a = {};
assert((a instanceof Object), true, "instanceof");
assert((a instanceof String), false, "instanceof");
assert((typeof 1), "number", "typeof");
assert((typeof Object), "function", "typeof");
assert((typeof null), "object", "typeof");
assert((typeof unknown_var), "undefined", "typeof");
a = {x: 1, if: 2, async: 3};
assert(a.if === 2);
assert(a.async === 3);
}
function test_delete()
{
var a, err;
a = {x: 1, y: 1};
assert((delete a.x), true, "delete");
assert(("x" in a), false, "delete");
/* the following are not tested by test262 */
assert(delete "abc"[100], true);
err = false;
try {
delete null.a;
} catch(e) {
err = (e instanceof TypeError);
}
assert(err, true, "delete");
err = false;
try {
a = { f() { delete super.a; } };
a.f();
} catch(e) {
err = (e instanceof ReferenceError);
}
assert(err, true, "delete");
}
function test_prototype()
{
function f() { }
assert(f.prototype.constructor, f, "prototype");
}
function test_arguments()
{
function f2() {
assert(arguments.length, 2, "arguments");
assert(arguments[0], 1, "arguments");
assert(arguments[1], 3, "arguments");
}
f2(1, 3);
}
function test_class()
{
var o;
class C {
constructor() {
this.x = 10;
}
f() {
return 1;
}
static F() {
return -1;
}
get y() {
return 12;
}
};
class D extends C {
constructor() {
super();
this.z = 20;
}
g() {
return 2;
}
static G() {
return -2;
}
h() {
return super.f();
}
static H() {
return super["F"]();
}
}
assert(C.F() === -1);
assert(Object.getOwnPropertyDescriptor(C.prototype, "y").get.name === "get y");
o = new C();
assert(o.f() === 1);
assert(o.x === 10);
assert(D.F() === -1);
assert(D.G() === -2);
assert(D.H() === -1);
o = new D();
assert(o.f() === 1);
assert(o.g() === 2);
assert(o.x === 10);
assert(o.z === 20);
assert(o.h() === 1);
/* test class name scope */
var E1 = class E { static F() { return E; } };
assert(E1 === E1.F());
};
function test_template()
{
var a, b;
b = 123;
a = `abc${b}d`;
assert(a === "abc123d");
a = String.raw `abc${b}d`;
assert(a === "abc123d");
}
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 = { x, get, set, async };
assert(JSON.stringify(a), '{"x":0,"get":1,"set":2,"async":3}');
}
function test_regexp_skip()
{
var a, b;
[a, b = /abc\(/] = [1];
assert(a === 1);
[a, b =/abc\(/] = [2];
assert(a === 2);
}
function test_labels()
{
do x: { break x; } while(0);
if (1)
x: { break x; }
else
x: { break x; }
with ({}) x: { break x; };
while (0) x: { break x; };
}
test_op1();
test_cvt();
test_eq();
test_inc_dec();
test_op2();
test_delete();
test_prototype();
test_arguments();
test_class();
test_template();
test_object_literal();
test_regexp_skip();
test_labels();

244
tests/test_qjscalc.js Normal file
View File

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

247
tests/test_std.js Normal file
View File

@ -0,0 +1,247 @@
import * as std from "std";
import * as os from "os";
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 + ")" : ""));
}
// load more elaborate version of assert if available
try { std.loadScript("test_assert.js"); } catch(e) {}
/*----------------*/
function test_printf()
{
assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc");
}
function test_file1()
{
var f, len, str, size, buf, ret, i, str1;
f = std.tmpfile();
str = "hello world\n";
f.puts(str);
f.seek(0, std.SEEK_SET);
str1 = f.readAsString();
assert(str1 === str);
f.seek(0, std.SEEK_END);
size = f.tell();
assert(size === str.length);
f.seek(0, std.SEEK_SET);
buf = new Uint8Array(size);
ret = f.read(buf.buffer, 0, size);
assert(ret === size);
for(i = 0; i < size; i++)
assert(buf[i] === str.charCodeAt(i));
f.close();
}
function test_file2()
{
var f, str, i, size;
f = std.tmpfile();
str = "hello world\n";
size = str.length;
for(i = 0; i < size; i++)
f.putByte(str.charCodeAt(i));
f.seek(0, std.SEEK_SET);
for(i = 0; i < size; i++) {
assert(str.charCodeAt(i) === f.getByte());
}
assert(f.getByte() === -1);
f.close();
}
function test_getline()
{
var f, line, line_count, lines, i;
lines = ["hello world", "line 1", "line 2" ];
f = std.tmpfile();
for(i = 0; i < lines.length; i++) {
f.puts(lines[i], "\n");
}
f.seek(0, std.SEEK_SET);
assert(!f.eof());
line_count = 0;
for(;;) {
line = f.getline();
if (line === null)
break;
assert(line == lines[line_count]);
line_count++;
}
assert(f.eof());
assert(line_count === lines.length);
f.close();
}
function test_popen()
{
var str, f, fname = "tmp_file.txt";
var content = "hello world";
f = std.open(fname, "w");
f.puts(content);
f.close();
/* execute the 'cat' shell command */
f = std.popen("cat " + fname, "r");
str = f.readAsString();
f.close();
assert(str, content);
os.remove(fname);
}
function test_os()
{
var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path;
assert(os.isatty(0));
fdir = "test_tmp_dir";
fname = "tmp_file.txt";
fpath = fdir + "/" + fname;
link_path = fdir + "/test_link";
os.remove(link_path);
os.remove(fpath);
os.remove(fdir);
err = os.mkdir(fdir, 0o755);
assert(err === 0);
fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC);
assert(fd >= 0);
buf = new Uint8Array(10);
for(i = 0; i < buf.length; i++)
buf[i] = i;
assert(os.write(fd, buf.buffer, 0, buf.length) === buf.length);
assert(os.seek(fd, 0, os.SEEK_SET) === 0);
buf2 = new Uint8Array(buf.length);
assert(os.read(fd, buf2.buffer, 0, buf2.length) === buf2.length);
for(i = 0; i < buf.length; i++)
assert(buf[i] == buf2[i]);
assert(os.close(fd) === 0);
[files, err] = os.readdir(fdir);
assert(err, 0);
assert(files.indexOf(fname) >= 0);
fdate = 10000;
err = os.utimes(fpath, fdate, fdate);
assert(err, 0);
[st, err] = os.stat(fpath);
assert(err, 0);
assert(st.mode & os.S_IFMT, os.S_IFREG);
assert(st.mtime, fdate);
err = os.symlink(fname, link_path);
assert(err === 0);
[st, err] = os.lstat(link_path);
assert(err, 0);
assert(st.mode & os.S_IFMT, os.S_IFLNK);
[buf, err] = os.readlink(link_path);
assert(err, 0);
assert(buf, fname);
assert(os.remove(link_path) === 0);
[buf, err] = os.getcwd();
assert(err, 0);
[buf2, err] = os.realpath(".");
assert(err, 0);
assert(buf, buf2);
assert(os.remove(fpath) === 0);
fd = os.open(fpath, os.O_RDONLY);
assert(fd < 0);
assert(os.remove(fdir) === 0);
}
function test_os_exec()
{
var ret, fds, pid, f, status;
ret = os.exec(["true"]);
assert(ret, 0);
ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false });
assert(ret, 1);
fds = os.pipe();
pid = os.exec(["echo", "hello"], { stdout: fds[1], block: false } );
assert(pid >= 0);
os.close(fds[1]); /* close the write end (as it is only in the child) */
f = std.fdopen(fds[0], "r");
assert(f.getline(), "hello");
assert(f.getline(), null);
f.close();
[ret, status] = os.waitpid(pid, 0);
assert(ret, pid);
assert(status & 0x7f, 0); /* exited */
assert(status >> 8, 0); /* exit code */
pid = os.exec(["cat"], { block: false } );
assert(pid >= 0);
os.kill(pid, os.SIGQUIT);
[ret, status] = os.waitpid(pid, 0);
assert(ret, pid);
assert(status & 0x7f, os.SIGQUIT);
}
function test_timer()
{
var th, i;
/* just test that a timer can be inserted and removed */
th = [];
for(i = 0; i < 3; i++)
th[i] = os.setTimeout(function () { }, 1000);
for(i = 0; i < 3; i++)
os.clearTimeout(th[i]);
}
test_printf();
test_file1();
test_file2();
test_getline();
test_popen();
test_os();
test_os_exec();
test_timer();

19
unicode_download.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
set -e
url="ftp://ftp.unicode.org/Public/12.1.0/ucd"
emoji_url="ftp://ftp.unicode.org/Public/emoji/12.0/emoji-data.txt"
files="CaseFolding.txt DerivedNormalizationProps.txt PropList.txt \
SpecialCasing.txt CompositionExclusions.txt ScriptExtensions.txt \
UnicodeData.txt DerivedCoreProperties.txt NormalizationTest.txt Scripts.txt \
PropertyValueAliases.txt"
mkdir -p unicode
for f in $files; do
g="${url}/${f}"
wget $g -O unicode/$f
done
wget $emoji_url -O unicode/emoji-data.txt

3057
unicode_gen.c Normal file

File diff suppressed because it is too large Load Diff

280
unicode_gen_def.h Normal file
View File

@ -0,0 +1,280 @@
#ifdef UNICODE_GENERAL_CATEGORY
DEF(Cn, "Unassigned") /* must be zero */
DEF(Lu, "Uppercase_Letter")
DEF(Ll, "Lowercase_Letter")
DEF(Lt, "Titlecase_Letter")
DEF(Lm, "Modifier_Letter")
DEF(Lo, "Other_Letter")
DEF(Mn, "Nonspacing_Mark")
DEF(Mc, "Spacing_Mark")
DEF(Me, "Enclosing_Mark")
DEF(Nd, "Decimal_Number,digit")
DEF(Nl, "Letter_Number")
DEF(No, "Other_Number")
DEF(Sm, "Math_Symbol")
DEF(Sc, "Currency_Symbol")
DEF(Sk, "Modifier_Symbol")
DEF(So, "Other_Symbol")
DEF(Pc, "Connector_Punctuation")
DEF(Pd, "Dash_Punctuation")
DEF(Ps, "Open_Punctuation")
DEF(Pe, "Close_Punctuation")
DEF(Pi, "Initial_Punctuation")
DEF(Pf, "Final_Punctuation")
DEF(Po, "Other_Punctuation")
DEF(Zs, "Space_Separator")
DEF(Zl, "Line_Separator")
DEF(Zp, "Paragraph_Separator")
DEF(Cc, "Control,cntrl")
DEF(Cf, "Format")
DEF(Cs, "Surrogate")
DEF(Co, "Private_Use")
/* synthetic properties */
DEF(LC, "Cased_Letter")
DEF(L, "Letter")
DEF(M, "Mark,Combining_Mark")
DEF(N, "Number")
DEF(S, "Symbol")
DEF(P, "Punctuation,punct")
DEF(Z, "Separator")
DEF(C, "Other")
#endif
#ifdef UNICODE_SCRIPT
/* scripts aliases names in PropertyValueAliases.txt */
DEF(Unknown, "Zzzz")
DEF(Adlam, "Adlm")
DEF(Ahom, "Ahom")
DEF(Anatolian_Hieroglyphs, "Hluw")
DEF(Arabic, "Arab")
DEF(Armenian, "Armn")
DEF(Avestan, "Avst")
DEF(Balinese, "Bali")
DEF(Bamum, "Bamu")
DEF(Bassa_Vah, "Bass")
DEF(Batak, "Batk")
DEF(Bengali, "Beng")
DEF(Bhaiksuki, "Bhks")
DEF(Bopomofo, "Bopo")
DEF(Brahmi, "Brah")
DEF(Braille, "Brai")
DEF(Buginese, "Bugi")
DEF(Buhid, "Buhd")
DEF(Canadian_Aboriginal, "Cans")
DEF(Carian, "Cari")
DEF(Caucasian_Albanian, "Aghb")
DEF(Chakma, "Cakm")
DEF(Cham, "Cham")
DEF(Cherokee, "Cher")
DEF(Common, "Zyyy")
DEF(Coptic, "Copt,Qaac")
DEF(Cuneiform, "Xsux")
DEF(Cypriot, "Cprt")
DEF(Cyrillic, "Cyrl")
DEF(Deseret, "Dsrt")
DEF(Devanagari, "Deva")
DEF(Dogra, "Dogr")
DEF(Duployan, "Dupl")
DEF(Egyptian_Hieroglyphs, "Egyp")
DEF(Elbasan, "Elba")
DEF(Elymaic, "Elym")
DEF(Ethiopic, "Ethi")
DEF(Georgian, "Geor")
DEF(Glagolitic, "Glag")
DEF(Gothic, "Goth")
DEF(Grantha, "Gran")
DEF(Greek, "Grek")
DEF(Gujarati, "Gujr")
DEF(Gunjala_Gondi, "Gong")
DEF(Gurmukhi, "Guru")
DEF(Han, "Hani")
DEF(Hangul, "Hang")
DEF(Hanifi_Rohingya, "Rohg")
DEF(Hanunoo, "Hano")
DEF(Hatran, "Hatr")
DEF(Hebrew, "Hebr")
DEF(Hiragana, "Hira")
DEF(Imperial_Aramaic, "Armi")
DEF(Inherited, "Zinh,Qaai")
DEF(Inscriptional_Pahlavi, "Phli")
DEF(Inscriptional_Parthian, "Prti")
DEF(Javanese, "Java")
DEF(Kaithi, "Kthi")
DEF(Kannada, "Knda")
DEF(Katakana, "Kana")
DEF(Kayah_Li, "Kali")
DEF(Kharoshthi, "Khar")
DEF(Khmer, "Khmr")
DEF(Khojki, "Khoj")
DEF(Khudawadi, "Sind")
DEF(Lao, "Laoo")
DEF(Latin, "Latn")
DEF(Lepcha, "Lepc")
DEF(Limbu, "Limb")
DEF(Linear_A, "Lina")
DEF(Linear_B, "Linb")
DEF(Lisu, "Lisu")
DEF(Lycian, "Lyci")
DEF(Lydian, "Lydi")
DEF(Makasar, "Maka")
DEF(Mahajani, "Mahj")
DEF(Malayalam, "Mlym")
DEF(Mandaic, "Mand")
DEF(Manichaean, "Mani")
DEF(Marchen, "Marc")
DEF(Masaram_Gondi, "Gonm")
DEF(Medefaidrin, "Medf")
DEF(Meetei_Mayek, "Mtei")
DEF(Mende_Kikakui, "Mend")
DEF(Meroitic_Cursive, "Merc")
DEF(Meroitic_Hieroglyphs, "Mero")
DEF(Miao, "Plrd")
DEF(Modi, "Modi")
DEF(Mongolian, "Mong")
DEF(Mro, "Mroo")
DEF(Multani, "Mult")
DEF(Myanmar, "Mymr")
DEF(Nabataean, "Nbat")
DEF(Nandinagari, "Nand")
DEF(New_Tai_Lue, "Talu")
DEF(Newa, "Newa")
DEF(Nko, "Nkoo")
DEF(Nushu, "Nshu")
DEF(Nyiakeng_Puachue_Hmong, "Hmnp")
DEF(Ogham, "Ogam")
DEF(Ol_Chiki, "Olck")
DEF(Old_Hungarian, "Hung")
DEF(Old_Italic, "Ital")
DEF(Old_North_Arabian, "Narb")
DEF(Old_Permic, "Perm")
DEF(Old_Persian, "Xpeo")
DEF(Old_Sogdian, "Sogo")
DEF(Old_South_Arabian, "Sarb")
DEF(Old_Turkic, "Orkh")
DEF(Oriya, "Orya")
DEF(Osage, "Osge")
DEF(Osmanya, "Osma")
DEF(Pahawh_Hmong, "Hmng")
DEF(Palmyrene, "Palm")
DEF(Pau_Cin_Hau, "Pauc")
DEF(Phags_Pa, "Phag")
DEF(Phoenician, "Phnx")
DEF(Psalter_Pahlavi, "Phlp")
DEF(Rejang, "Rjng")
DEF(Runic, "Runr")
DEF(Samaritan, "Samr")
DEF(Saurashtra, "Saur")
DEF(Sharada, "Shrd")
DEF(Shavian, "Shaw")
DEF(Siddham, "Sidd")
DEF(SignWriting, "Sgnw")
DEF(Sinhala, "Sinh")
DEF(Sogdian, "Sogd")
DEF(Sora_Sompeng, "Sora")
DEF(Soyombo, "Soyo")
DEF(Sundanese, "Sund")
DEF(Syloti_Nagri, "Sylo")
DEF(Syriac, "Syrc")
DEF(Tagalog, "Tglg")
DEF(Tagbanwa, "Tagb")
DEF(Tai_Le, "Tale")
DEF(Tai_Tham, "Lana")
DEF(Tai_Viet, "Tavt")
DEF(Takri, "Takr")
DEF(Tamil, "Taml")
DEF(Tangut, "Tang")
DEF(Telugu, "Telu")
DEF(Thaana, "Thaa")
DEF(Thai, "Thai")
DEF(Tibetan, "Tibt")
DEF(Tifinagh, "Tfng")
DEF(Tirhuta, "Tirh")
DEF(Ugaritic, "Ugar")
DEF(Vai, "Vaii")
DEF(Wancho, "Wcho")
DEF(Warang_Citi, "Wara")
DEF(Yi, "Yiii")
DEF(Zanabazar_Square, "Zanb")
#endif
#ifdef UNICODE_PROP_LIST
/* Prop list not exported to regexp */
DEF(Hyphen, "")
DEF(Other_Math, "")
DEF(Other_Alphabetic, "")
DEF(Other_Lowercase, "")
DEF(Other_Uppercase, "")
DEF(Other_Grapheme_Extend, "")
DEF(Other_Default_Ignorable_Code_Point, "")
DEF(Other_ID_Start, "")
DEF(Other_ID_Continue, "")
DEF(Prepended_Concatenation_Mark, "")
/* additional computed properties for smaller tables */
DEF(ID_Continue1, "")
DEF(XID_Start1, "")
DEF(XID_Continue1, "")
DEF(Changes_When_Titlecased1, "")
DEF(Changes_When_Casefolded1, "")
DEF(Changes_When_NFKC_Casefolded1, "")
/* Prop list exported to JS */
DEF(ASCII_Hex_Digit, "AHex")
DEF(Bidi_Control, "Bidi_C")
DEF(Dash, "")
DEF(Deprecated, "Dep")
DEF(Diacritic, "Dia")
DEF(Extender, "Ext")
DEF(Hex_Digit, "Hex")
DEF(IDS_Binary_Operator, "IDSB")
DEF(IDS_Trinary_Operator, "IDST")
DEF(Ideographic, "Ideo")
DEF(Join_Control, "Join_C")
DEF(Logical_Order_Exception, "LOE")
DEF(Noncharacter_Code_Point, "NChar")
DEF(Pattern_Syntax, "Pat_Syn")
DEF(Pattern_White_Space, "Pat_WS")
DEF(Quotation_Mark, "QMark")
DEF(Radical, "")
DEF(Regional_Indicator, "RI")
DEF(Sentence_Terminal, "STerm")
DEF(Soft_Dotted, "SD")
DEF(Terminal_Punctuation, "Term")
DEF(Unified_Ideograph, "UIdeo")
DEF(Variation_Selector, "VS")
DEF(White_Space, "space")
DEF(Bidi_Mirrored, "Bidi_M")
DEF(Emoji, "")
DEF(Emoji_Component, "")
DEF(Emoji_Modifier, "")
DEF(Emoji_Modifier_Base, "")
DEF(Emoji_Presentation, "")
DEF(Extended_Pictographic, "")
DEF(Default_Ignorable_Code_Point, "DI")
DEF(ID_Start, "IDS")
DEF(Case_Ignorable, "CI")
/* other binary properties */
DEF(ASCII,"")
DEF(Alphabetic, "Alpha")
DEF(Any, "")
DEF(Assigned,"")
DEF(Cased, "")
DEF(Changes_When_Casefolded, "CWCF")
DEF(Changes_When_Casemapped, "CWCM")
DEF(Changes_When_Lowercased, "CWL")
DEF(Changes_When_NFKC_Casefolded, "CWKCF")
DEF(Changes_When_Titlecased, "CWT")
DEF(Changes_When_Uppercased, "CWU")
DEF(Grapheme_Base, "Gr_Base")
DEF(Grapheme_Extend, "Gr_Ext")
DEF(ID_Continue, "IDC")
DEF(Lowercase, "Lower")
DEF(Math, "")
DEF(Uppercase, "Upper")
DEF(XID_Continue, "XIDC")
DEF(XID_Start, "XIDS")
/* internal tables with index */
DEF(Cased1, "")
#endif