2020-07-05 release

This commit is contained in:
bellard 2020-09-06 19:07:30 +02:00
parent 1722758717
commit 8900766099
30 changed files with 3775 additions and 1729 deletions

View file

@ -1,3 +1,12 @@
2020-07-05:
- modified JS_GetPrototype() to return a live value
- REPL: support unicode characters larger than 16 bits
- added os.Worker
- improved object serialization
- added std.parseExtJSON
- misc bug fixes
2020-04-12:
- added cross realm support

View file

@ -99,6 +99,10 @@ DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
ifdef CONFIG_BIGNUM
DEFINES+=-DCONFIG_BIGNUM
endif
ifdef CONFIG_WIN32
DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior
endif
CFLAGS+=$(DEFINES)
CFLAGS_DEBUG=$(CFLAGS) -O0
CFLAGS_SMALL=$(CFLAGS) -Os
@ -115,8 +119,8 @@ CFLAGS+=-p
LDFLAGS+=-p
endif
ifdef CONFIG_ASAN
CFLAGS+=-fsanitize=address
LDFLAGS+=-fsanitize=address
CFLAGS+=-fsanitize=address -fno-omit-frame-pointer
LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer
endif
ifdef CONFIG_WIN32
LDEXPORT=
@ -166,9 +170,10 @@ QJS_LIB_OBJS+=$(OBJDIR)/libbf.o
QJS_OBJS+=$(OBJDIR)/qjscalc.o
endif
HOST_LIBS=-lm -ldl -lpthread
LIBS=-lm
ifndef CONFIG_WIN32
LIBS+=-ldl
LIBS+=-ldl -lpthread
endif
$(OBJDIR):
@ -187,7 +192,7 @@ ifneq ($(CROSS_PREFIX),)
$(QJSC): $(OBJDIR)/qjsc.host.o \
$(patsubst %.o, %.host.o, $(QJS_LIB_OBJS))
$(HOST_CC) $(LDFLAGS) -o $@ $^ $(LIBS)
$(HOST_CC) $(LDFLAGS) -o $@ $^ $(HOST_LIBS)
endif #CROSS_PREFIX
@ -239,13 +244,13 @@ libunicode-table.h: unicode_gen
endif
run-test262: $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
run-test262-debug: $(patsubst %.o, %.debug.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
run-test262-32: $(patsubst %.o, %.m32.o, $(OBJDIR)/run-test262.o $(QJS_LIB_OBJS))
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS) -lpthread
$(CC) -m32 $(LDFLAGS) -o $@ $^ $(LIBS)
# object suffix order: nolto, [m32|m32s]
@ -285,7 +290,7 @@ unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c u
clean:
rm -f repl.c qjscalc.c out.c
rm -f *.a *.o *.d *~ jscompress unicode_gen regexp_test $(PROGS)
rm -f hello.c hello_module.c test_fib.c
rm -f hello.c test_fib.c
rm -f examples/*.so tests/*.so
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug
rm -rf run-test262-debug run-test262-32
@ -379,10 +384,11 @@ endif
test: qjs
./qjs tests/test_closure.js
./qjs tests/test_op.js
./qjs tests/test_language.js
./qjs tests/test_builtin.js
./qjs tests/test_loop.js
./qjs tests/test_std.js
./qjs tests/test_worker.js
ifndef CONFIG_DARWIN
ifdef CONFIG_BIGNUM
./qjs --bignum tests/test_bjson.js
@ -398,10 +404,11 @@ ifdef CONFIG_BIGNUM
endif
ifdef CONFIG_M32
./qjs32 tests/test_closure.js
./qjs32 tests/test_op.js
./qjs32 tests/test_language.js
./qjs32 tests/test_builtin.js
./qjs32 tests/test_loop.js
./qjs32 tests/test_std.js
./qjs32 tests/test_worker.js
ifdef CONFIG_BIGNUM
./qjs32 --bignum tests/test_op_overloading.js
./qjs32 --bignum tests/test_bignum.js

8
TODO
View file

@ -1,4 +1,5 @@
Misc:
- use realpath in module name normalizer and put it in quickjs-libc
- 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
@ -56,12 +57,12 @@ Extensions:
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
- readline: handle double width unicode characters
- multiline editing
- runtime object and function inspectors
- interactive object browser
@ -73,6 +74,5 @@ REPL:
Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Test262: 28/70829 errors, 877 excluded, 425 skipped
Test262 commit: 4a8e49b3ca7f9f74a4cafe6621ff9ba548ccc353
Test262: 30/71095 errors, 870 excluded, 549 skipped
Test262 commit: 281eb10b2844929a7c0ac04527f5b42ce56509fd

View file

@ -1 +1 @@
2020-04-12
2020-07-05

View file

@ -268,6 +268,10 @@ void dbuf_free(DynBuf *s);
static inline BOOL dbuf_error(DynBuf *s) {
return s->error;
}
static inline void dbuf_set_error(DynBuf *s)
{
s->error = TRUE;
}
#define UTF8_CHAR_LEN_MAX 6

View file

@ -1,7 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<!-- Created by GNU Texinfo 6.1, http://www.gnu.org/software/texinfo/ -->
<!-- Created by GNU Texinfo 6.5, http://www.gnu.org/software/texinfo/ -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Javascript Bignum Extensions</title>
<meta name="description" content="Javascript Bignum Extensions">
@ -9,7 +10,6 @@
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta name="Generator" content="makeinfo">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href="#SEC_Contents" rel="contents" title="Table of Contents">
<style type="text/css">
<!--

Binary file not shown.

View file

@ -1,7 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<!-- Created by GNU Texinfo 6.1, http://www.gnu.org/software/texinfo/ -->
<!-- Created by GNU Texinfo 6.5, http://www.gnu.org/software/texinfo/ -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>QuickJS Javascript Engine</title>
<meta name="description" content="QuickJS Javascript Engine">
@ -9,7 +10,6 @@
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta name="Generator" content="makeinfo">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link href="#SEC_Contents" rel="contents" title="Table of Contents">
<style type="text/css">
<!--
@ -73,10 +73,9 @@ ul.no-bullet {list-style: none}
<li><a name="toc-Language-support" href="#Language-support">3.1 Language support</a>
<ul class="no-bullet">
<li><a name="toc-ES2020-support" href="#ES2020-support">3.1.1 ES2020 support</a></li>
<li><a name="toc-JSON" href="#JSON">3.1.2 JSON</a></li>
<li><a name="toc-ECMA402" href="#ECMA402">3.1.3 ECMA402</a></li>
<li><a name="toc-Extensions" href="#Extensions">3.1.4 Extensions</a></li>
<li><a name="toc-Mathematical-extensions" href="#Mathematical-extensions">3.1.5 Mathematical extensions</a></li>
<li><a name="toc-ECMA402" href="#ECMA402">3.1.2 ECMA402</a></li>
<li><a name="toc-Extensions" href="#Extensions">3.1.3 Extensions</a></li>
<li><a name="toc-Mathematical-extensions" href="#Mathematical-extensions">3.1.4 Mathematical extensions</a></li>
</ul></li>
<li><a name="toc-Modules" href="#Modules">3.2 Modules</a></li>
<li><a name="toc-Standard-library" href="#Standard-library">3.3 Standard library</a>
@ -407,18 +406,13 @@ B (legacy web compatibility) and the Unicode related features.
</li></ul>
<a name="JSON"></a>
<h4 class="subsection">3.1.2 JSON</h4>
<p>The JSON parser is currently more tolerant than the specification.
</p>
<a name="ECMA402"></a>
<h4 class="subsection">3.1.3 ECMA402</h4>
<h4 class="subsection">3.1.2 ECMA402</h4>
<p>ECMA402 (Internationalization API) is not supported.
</p>
<a name="Extensions"></a>
<h4 class="subsection">3.1.4 Extensions</h4>
<h4 class="subsection">3.1.3 Extensions</h4>
<ul>
<li> The directive <code>&quot;use strip&quot;</code> indicates that the debug information (including the source code of the functions) should not be retained to save memory. As <code>&quot;use strict&quot;</code>, the directive can be global to a script or local to a function.
@ -428,7 +422,7 @@ B (legacy web compatibility) and the Unicode related features.
</li></ul>
<a name="Mathematical-extensions"></a>
<h4 class="subsection">3.1.5 Mathematical extensions</h4>
<h4 class="subsection">3.1.4 Mathematical extensions</h4>
<p>The mathematical extensions are fully backward compatible with
standard Javascript. See <code>jsbignum.pdf</code> for more information.
@ -557,7 +551,7 @@ no error occured.
</p>
</dd>
<dt><code>printf(fmt, ...args)</code></dt>
<dd><p>Equivalent to <code>std.out.printf(fmt, ...args)</code>
<dd><p>Equivalent to <code>std.out.printf(fmt, ...args)</code>.
</p>
</dd>
<dt><code>sprintf(fmt, ...args)</code></dt>
@ -636,6 +630,21 @@ optional properties:
</dd>
</dl>
</dd>
<dt><code>parseExtJSON(str)</code></dt>
<dd>
<p>Parse <code>str</code> using a superset of <code>JSON.parse</code>. The
following extensions are accepted:
</p>
<ul>
<li> Single line and multiline comments
</li><li> unquoted properties (ASCII-only Javascript identifiers)
</li><li> trailing comma in array and object definitions
</li><li> single quoted strings
</li><li> <code>\f</code> and <code>\v</code> are accepted as space characters
</li><li> leading plus in numbers
</li><li> octal (<code>0o</code> prefix) and hexadecimal (<code>0x</code> prefix) numbers
</li></ul>
</dd>
</dl>
@ -649,8 +658,14 @@ optional properties:
<dd><p>Outputs the string with the UTF-8 encoding.
</p></dd>
<dt><code>printf(fmt, ...args)</code></dt>
<dd><p>Formatted printf, same formats as the libc printf.
</p></dd>
<dd><p>Formatted printf.
</p>
<p>The same formats as the standard C library <code>printf</code> are
supported. Integer format types (e.g. <code>%d</code>) truncate the Numbers
or BigInts to 32 bits. Use the <code>l</code> modifier (e.g. <code>%ld</code>) to
truncate to 64 bits.
</p>
</dd>
<dt><code>flush()</code></dt>
<dd><p>Flush the buffered file.
</p></dd>
@ -718,6 +733,7 @@ is read up its end.
</li><li> signals
</li><li> timers
</li><li> asynchronous I/O
</li><li> workers (threads)
</li></ul>
<p>The OS functions usually return 0 if OK or an OS specific negative
@ -869,7 +885,7 @@ the handler.
<dd><p>Call the function <code>func</code> when the signal <code>signal</code>
happens. Only a single handler per signal number is supported. Use
<code>null</code> to set the default handler or <code>undefined</code> to ignore
the signal.
the signal. Signal handlers can only be defined in the main thread.
</p>
</dd>
<dt><code>SIGINT</code></dt>
@ -972,6 +988,49 @@ to the timer.
<dd><p>Return a string representing the platform: <code>&quot;linux&quot;</code>, <code>&quot;darwin&quot;</code>,
<code>&quot;win32&quot;</code> or <code>&quot;js&quot;</code>.
</p>
</dd>
<dt><code>Worker(source)</code></dt>
<dd><p>Constructor to create a new thread (worker) with an API close to the
<code>WebWorkers</code>. <code>source</code> is a string containing the module
source which is executed in the newly created thread. Threads normally
don&rsquo;t share any data and communicate between each other with
messages. Nested workers are not supported. An example is available in
<samp>tests/test_worker.js</samp>.
</p>
<p>The worker class has the following static properties:
</p>
<dl compact="compact">
<dt><code>parent</code></dt>
<dd><p>In the created worker, <code>Worker.parent</code> represents the parent
worker and is used to send or receive messages.
</p></dd>
</dl>
<p>The worker instances have the following properties:
</p>
<dl compact="compact">
<dt><code>postMessage(msg)</code></dt>
<dd>
<p>Send a message to the corresponding worker. <code>msg</code> is cloned in
the destination worker using an algorithm similar to the <code>HTML</code>
structured clone algorithm. <code>SharedArrayBuffer</code> are shared
between workers.
</p>
<p>Current limitations: <code>Map</code> and <code>Set</code> are not supported
yet.
</p>
</dd>
<dt><code>onmessage</code></dt>
<dd>
<p>Getter and setter. Set a function which is called each time a
message is received. The function is called with a single
argument. It is an object with a <code>data</code> property containing the
received message. The thread is not terminated if there is at least
one non <code>null</code> <code>onmessage</code> handler.
</p>
</dd>
</dl>
</dd>
</dl>

Binary file not shown.

View file

@ -272,10 +272,6 @@ The following features are not supported yet:
@end itemize
@subsection JSON
The JSON parser is currently more tolerant than the specification.
@subsection ECMA402
ECMA402 (Internationalization API) is not supported.
@ -405,7 +401,7 @@ no error occured.
Equivalent to @code{std.out.puts(str)}.
@item printf(fmt, ...args)
Equivalent to @code{std.out.printf(fmt, ...args)}
Equivalent to @code{std.out.printf(fmt, ...args)}.
@item sprintf(fmt, ...args)
Equivalent to the libc sprintf().
@ -474,6 +470,20 @@ optional properties:
@end table
@item parseExtJSON(str)
Parse @code{str} using a superset of @code{JSON.parse}. The
following extensions are accepted:
@itemize
@item Single line and multiline comments
@item unquoted properties (ASCII-only Javascript identifiers)
@item trailing comma in array and object definitions
@item single quoted strings
@item @code{\f} and @code{\v} are accepted as space characters
@item leading plus in numbers
@item octal (@code{0o} prefix) and hexadecimal (@code{0x} prefix) numbers
@end itemize
@end table
FILE prototype:
@ -484,7 +494,13 @@ Close the file. Return 0 if OK or @code{-errno} in case of I/O error.
@item puts(str)
Outputs the string with the UTF-8 encoding.
@item printf(fmt, ...args)
Formatted printf, same formats as the libc printf.
Formatted printf.
The same formats as the standard C library @code{printf} are
supported. Integer format types (e.g. @code{%d}) truncate the Numbers
or BigInts to 32 bits. Use the @code{l} modifier (e.g. @code{%ld}) to
truncate to 64 bits.
@item flush()
Flush the buffered file.
@item seek(offset, whence)
@ -537,6 +553,7 @@ The @code{os} module provides Operating System specific functions:
@item signals
@item timers
@item asynchronous I/O
@item workers (threads)
@end itemize
The OS functions usually return 0 if OK or an OS specific negative
@ -664,7 +681,7 @@ the handler.
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.
the signal. Signal handlers can only be defined in the main thread.
@item SIGINT
@item SIGABRT
@ -747,6 +764,45 @@ Cancel a timer.
Return a string representing the platform: @code{"linux"}, @code{"darwin"},
@code{"win32"} or @code{"js"}.
@item Worker(source)
Constructor to create a new thread (worker) with an API close to the
@code{WebWorkers}. @code{source} is a string containing the module
source which is executed in the newly created thread. Threads normally
don't share any data and communicate between each other with
messages. Nested workers are not supported. An example is available in
@file{tests/test_worker.js}.
The worker class has the following static properties:
@table @code
@item parent
In the created worker, @code{Worker.parent} represents the parent
worker and is used to send or receive messages.
@end table
The worker instances have the following properties:
@table @code
@item postMessage(msg)
Send a message to the corresponding worker. @code{msg} is cloned in
the destination worker using an algorithm similar to the @code{HTML}
structured clone algorithm. @code{SharedArrayBuffer} are shared
between workers.
Current limitations: @code{Map} and @code{Set} are not supported
yet.
@item onmessage
Getter and setter. Set a function which is called each time a
message is received. The function is called with a single
argument. It is an object with a @code{data} property containing the
received message. The thread is not terminated if there is at least
one non @code{null} @code{onmessage} handler.
@end table
@end table
@section QuickJS C API

View file

@ -130,11 +130,11 @@ static int js_point_init(JSContext *ctx, JSModuleDef *m)
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_SetClassProto(ctx, js_point_class_id, point_proto);
JS_SetModuleExport(ctx, m, "Point", point_class);
return 0;

75
libbf.c
View file

@ -3370,12 +3370,14 @@ slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv,
}
/* 'n' is the number of output limbs */
static void bf_integer_to_radix_rec(bf_t *pow_tab,
limb_t *out, const bf_t *a, limb_t n,
int level, limb_t n0, limb_t radixl,
unsigned int radixl_bits)
static int bf_integer_to_radix_rec(bf_t *pow_tab,
limb_t *out, const bf_t *a, limb_t n,
int level, limb_t n0, limb_t radixl,
unsigned int radixl_bits)
{
limb_t n1, n2, q_prec;
int ret;
assert(n >= 1);
if (n == 1) {
out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn);
@ -3402,63 +3404,81 @@ static void bf_integer_to_radix_rec(bf_t *pow_tab,
n1 = n - n2;
B = &pow_tab[2 * level];
B_inv = &pow_tab[2 * level + 1];
ret = 0;
if (B->len == 0) {
/* compute BASE^n2 */
bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ);
ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ);
/* we use enough bits for the maximum possible 'n1' value,
i.e. n2 + 1 */
bf_set_ui(&R, 1);
bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN);
ret |= bf_set_ui(&R, 1);
ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN);
}
// printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2);
q_prec = n1 * radixl_bits;
bf_mul(&Q, a, B_inv, q_prec, BF_RNDN);
bf_rint(&Q, BF_RNDZ);
ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN);
ret |= bf_rint(&Q, BF_RNDZ);
bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ);
bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ);
ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ);
ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ);
if (ret & BF_ST_MEM_ERROR)
goto fail;
/* adjust if necessary */
q_add = 0;
while (R.sign && R.len != 0) {
bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ);
if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ))
goto fail;
q_add--;
}
while (bf_cmpu(&R, B) >= 0) {
bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ);
if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ))
goto fail;
q_add++;
}
if (q_add != 0) {
bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ);
if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ))
goto fail;
}
if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0,
radixl, radixl_bits))
goto fail;
if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0,
radixl, radixl_bits)) {
fail:
bf_delete(&Q);
bf_delete(&R);
return -1;
}
bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0,
radixl, radixl_bits);
bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0,
radixl, radixl_bits);
bf_delete(&Q);
bf_delete(&R);
}
return 0;
}
static void bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl)
/* return 0 if OK != 0 if memory error */
static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl)
{
bf_context_t *s = r->ctx;
limb_t r_len;
bf_t *pow_tab;
int i, pow_tab_len;
int i, pow_tab_len, ret;
r_len = r->len;
pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */
pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len);
if (!pow_tab)
return -1;
for(i = 0; i < pow_tab_len; i++)
bf_init(r->ctx, &pow_tab[i]);
bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl,
ceil_log2(radixl));
ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl,
ceil_log2(radixl));
for(i = 0; i < pow_tab_len; i++) {
bf_delete(&pow_tab[i]);
}
bf_free(s, pow_tab);
return ret;
}
/* a must be >= 0. 'P' is the wanted number of digits in radix
@ -3625,8 +3645,14 @@ static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits,
a = &a_s;
bf_init(a1->ctx, a);
n = (n_digits + digits_per_limb - 1) / digits_per_limb;
bf_resize(a, n);
bf_integer_to_radix(a, a1, radixl);
if (bf_resize(a, n)) {
dbuf_set_error(s);
goto done;
}
if (bf_integer_to_radix(a, a1, radixl)) {
dbuf_set_error(s);
goto done;
}
radix_bits = 0;
pos = n;
pos_incr = 1;
@ -3659,6 +3685,7 @@ static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits,
buf_pos += l;
i += l;
}
done:
if (a != a1)
bf_delete(a);
}

View file

@ -110,12 +110,14 @@ static inline int is_digit(int c) {
return c >= '0' && c <= '9';
}
/* insert 'len' bytes at position 'pos' */
static void dbuf_insert(DynBuf *s, int pos, int len)
/* insert 'len' bytes at position 'pos'. Return < 0 if error. */
static int dbuf_insert(DynBuf *s, int pos, int len)
{
dbuf_realloc(s, s->size + len);
if (dbuf_realloc(s, s->size + len))
return -1;
memmove(s->buf + pos + len, s->buf + pos, s->size - pos);
s->size += len;
return 0;
}
/* canonicalize with the specific JS regexp rules */
@ -434,6 +436,11 @@ static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s,
return -1;
}
static int re_parse_out_of_memory(REParseState *s)
{
return re_parse_error(s, "out of memory");
}
/* If allow_overflow is false, return -1 in case of
overflow. Otherwise return INT32_MAX. */
static int parse_digits(const uint8_t **pp, BOOL allow_overflow)
@ -693,7 +700,7 @@ static int parse_unicode_property(REParseState *s, CharRange *cr,
*pp = p;
return 0;
out_of_memory:
return re_parse_error(s, "out of memory");
return re_parse_out_of_memory(s);
}
#endif /* CONFIG_ALL_UNICODE */
@ -928,7 +935,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp)
*pp = p;
return 0;
memory_error:
re_parse_error(s, "out of memory");
re_parse_out_of_memory(s);
fail:
cr_free(cr);
return -1;
@ -1295,6 +1302,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
return -1;
re_emit_op(s, REOP_match);
/* jump after the 'match' after the lookahead is successful */
if (dbuf_error(&s->byte_code))
return -1;
put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4));
} else if (p[2] == '<') {
p += 3;
@ -1548,12 +1557,15 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
if (quant_max > 0) {
/* specific optimization for simple quantifiers */
if (dbuf_error(&s->byte_code))
goto out_of_memory;
len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start,
s->byte_code.size - last_atom_start);
if (len > 0) {
re_emit_op(s, REOP_match);
dbuf_insert(&s->byte_code, last_atom_start, 17);
if (dbuf_insert(&s->byte_code, last_atom_start, 17))
goto out_of_memory;
pos = last_atom_start;
s->byte_code.buf[pos++] = REOP_simple_greedy_quant;
put_u32(&s->byte_code.buf[pos],
@ -1569,6 +1581,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
}
}
if (dbuf_error(&s->byte_code))
goto out_of_memory;
add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start,
s->byte_code.size - last_atom_start) == 0);
} else {
@ -1582,7 +1596,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
/* need to reset the capture in case the atom is
not executed */
if (last_capture_count != s->capture_count) {
dbuf_insert(&s->byte_code, last_atom_start, 3);
if (dbuf_insert(&s->byte_code, last_atom_start, 3))
goto out_of_memory;
s->byte_code.buf[last_atom_start++] = REOP_save_reset;
s->byte_code.buf[last_atom_start++] = last_capture_count;
s->byte_code.buf[last_atom_start++] = s->capture_count - 1;
@ -1590,12 +1605,14 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
if (quant_max == 0) {
s->byte_code.size = last_atom_start;
} else if (quant_max == 1) {
dbuf_insert(&s->byte_code, last_atom_start, 5);
if (dbuf_insert(&s->byte_code, last_atom_start, 5))
goto out_of_memory;
s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
greedy;
put_u32(s->byte_code.buf + last_atom_start + 1, len);
} else if (quant_max == INT32_MAX) {
dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check);
if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check))
goto out_of_memory;
s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
greedy;
put_u32(s->byte_code.buf + last_atom_start + 1,
@ -1611,7 +1628,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
re_emit_goto(s, REOP_goto, last_atom_start);
}
} else {
dbuf_insert(&s->byte_code, last_atom_start, 10);
if (dbuf_insert(&s->byte_code, last_atom_start, 10))
goto out_of_memory;
pos = last_atom_start;
s->byte_code.buf[pos++] = REOP_push_i32;
put_u32(s->byte_code.buf + pos, quant_max);
@ -1629,7 +1647,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
if (quant_min == 1) {
/* nothing to add */
} else {
dbuf_insert(&s->byte_code, last_atom_start, 5);
if (dbuf_insert(&s->byte_code, last_atom_start, 5))
goto out_of_memory;
s->byte_code.buf[last_atom_start] = REOP_push_i32;
put_u32(s->byte_code.buf + last_atom_start + 1,
quant_min);
@ -1670,6 +1689,8 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
done:
s->buf_ptr = p;
return 0;
out_of_memory:
return re_parse_out_of_memory(s);
}
static int re_parse_alternative(REParseState *s, BOOL is_backward_dir)
@ -1719,7 +1740,9 @@ static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)
len = s->byte_code.size - start;
/* insert a split before the first alternative */
dbuf_insert(&s->byte_code, start, 5);
if (dbuf_insert(&s->byte_code, start, 5)) {
return re_parse_out_of_memory(s);
}
s->byte_code.buf[start] = REOP_split_next_first;
put_u32(s->byte_code.buf + start + 1, len + 5);
@ -1844,7 +1867,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
}
if (dbuf_error(&s->byte_code)) {
re_parse_error(s, "out of memory");
re_parse_out_of_memory(s);
goto error;
}

15
qjs.c
View file

@ -270,6 +270,7 @@ void help(void)
"-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"
" --stack-size n limit the stack size to 'n' bytes\n"
" --unhandled-rejection dump unhandled promise rejections\n"
"-q --quit just instantiate the interpreter and quit\n");
exit(1);
@ -295,7 +296,8 @@ int main(int argc, char **argv)
#ifdef CONFIG_BIGNUM
int load_jscalc, bignum_ext = 0;
#endif
size_t stack_size = 0;
#ifdef CONFIG_BIGNUM
/* load jscalc runtime if invoked as 'qjscalc' */
{
@ -407,6 +409,14 @@ int main(int argc, char **argv)
memory_limit = (size_t)strtod(argv[optind++], NULL);
continue;
}
if (!strcmp(longopt, "stack-size")) {
if (optind >= argc) {
fprintf(stderr, "expecting stack size");
exit(1);
}
stack_size = (size_t)strtod(argv[optind++], NULL);
continue;
}
if (opt) {
fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
} else {
@ -428,6 +438,9 @@ int main(int argc, char **argv)
}
if (memory_limit != 0)
JS_SetMemoryLimit(rt, memory_limit);
if (stack_size != 0)
JS_SetMaxStackSize(rt, stack_size);
js_std_init_handlers(rt);
ctx = JS_NewContext(rt);
if (!ctx) {
fprintf(stderr, "qjs: cannot allocate JS context\n");

17
qjsc.c
View file

@ -326,6 +326,7 @@ static const char main_c_template1[] =
" JSRuntime *rt;\n"
" JSContext *ctx;\n"
" rt = JS_NewRuntime();\n"
" js_std_init_handlers(rt);\n"
;
static const char main_c_template2[] =
@ -351,7 +352,8 @@ void help(void)
"-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"
);
"-S n set the maximum stack size to 'n' bytes (default=%d)\n",
JS_DEFAULT_STACK_SIZE);
#ifdef CONFIG_LTO
{
int i;
@ -447,6 +449,7 @@ static int output_executable(const char *out_filename, const char *cfilename,
*arg++ = libjsname;
*arg++ = "-lm";
*arg++ = "-ldl";
*arg++ = "-lpthread";
*arg = NULL;
if (verbose) {
@ -487,6 +490,7 @@ int main(int argc, char **argv)
BOOL use_lto;
int module;
OutputTypeEnum output_type;
size_t stack_size;
#ifdef CONFIG_BIGNUM
BOOL bignum_ext = FALSE;
#endif
@ -499,13 +503,14 @@ int main(int argc, char **argv)
byte_swap = FALSE;
verbose = 0;
use_lto = FALSE;
stack_size = 0;
/* 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:");
c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:");
if (c == -1)
break;
switch(c) {
@ -580,6 +585,9 @@ int main(int argc, char **argv)
case 'p':
c_ident_prefix = optarg;
break;
case 'S':
stack_size = (size_t)strtod(optarg, NULL);
break;
default:
break;
}
@ -652,6 +660,11 @@ int main(int argc, char **argv)
fputs(main_c_template1, fo);
fprintf(fo, " ctx = JS_NewContextRaw(rt);\n");
if (stack_size != 0) {
fprintf(fo, " JS_SetMaxStackSize(rt, %u);\n",
(unsigned int)stack_size);
}
/* add the module loader if necessary */
if (feature_bitmap & (1 << FE_MODULE_LOADER)) {
fprintf(fo, " JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);\n");

View file

@ -82,6 +82,7 @@ DEF(length, "length")
DEF(fileName, "fileName")
DEF(lineNumber, "lineNumber")
DEF(message, "message")
DEF(errors, "errors")
DEF(stack, "stack")
DEF(name, "name")
DEF(toString, "toString")

File diff suppressed because it is too large Load diff

View file

@ -29,10 +29,15 @@
#include "quickjs.h"
#ifdef __cplusplus
extern "C" {
#endif
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_init_handlers(JSRuntime *rt);
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);
@ -46,4 +51,8 @@ void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
JSValueConst reason,
JS_BOOL is_handled, void *opaque);
#ifdef __cplusplus
} /* extern "C" { */
#endif
#endif /* QUICKJS_LIBC_H */

3327
quickjs.c

File diff suppressed because it is too large Load diff

View file

@ -498,7 +498,7 @@ int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)
{
return JS_MKVAL(JS_TAG_BOOL, val);
return JS_MKVAL(JS_TAG_BOOL, (val != 0));
}
static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val)
@ -746,7 +746,7 @@ 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);
JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val);
#define JS_GPN_STRING_MASK (1 << 0)
#define JS_GPN_SYMBOL_MASK (1 << 1)
@ -796,6 +796,9 @@ 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);
#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */
JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
const char *filename, int flags);
JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
JSValueConst replacer, JSValueConst space0);
@ -810,6 +813,14 @@ JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
size_t *pbyte_offset,
size_t *pbyte_length,
size_t *pbytes_per_element);
typedef struct {
void *(*sab_alloc)(void *opaque, size_t size);
void (*sab_free)(void *opaque, void *ptr);
void (*sab_dup)(void *opaque, void *ptr);
void *sab_opaque;
} JSSharedArrayBufferFunctions;
void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
const JSSharedArrayBufferFunctions *sf);
JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
@ -853,14 +864,24 @@ 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 */
#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */
#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to
encode arbitrary object
graph */
uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
int flags);
uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
int flags, uint8_t ***psab_tab, size_t *psab_tab_len);
#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */
#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */
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);

72
repl.js
View file

@ -209,6 +209,29 @@ import * as os from "os";
(is_alpha(c) || is_digit(c) || c == '_' || c == '$');
}
function ucs_length(str) {
var len, c, i, str_len = str.length;
len = 0;
/* we never count the trailing surrogate to have the
following property: ucs_length(str) =
ucs_length(str.substring(0, a)) + ucs_length(str.substring(a,
str.length)) for 0 <= a <= str.length */
for(i = 0; i < str_len; i++) {
c = str.charCodeAt(i);
if (c < 0xdc00 || c >= 0xe000)
len++;
}
return len;
}
function is_trailing_surrogate(c) {
var d;
if (typeof c !== "string")
return false;
d = c.codePointAt(0); /* can be NaN if empty string */
return d >= 0xdc00 && d < 0xe000;
}
function is_balanced(a, b) {
switch (a + b) {
case "()":
@ -235,6 +258,7 @@ import * as os from "os";
std.puts("\x1b[" + ((n != 1) ? n : "") + code);
}
/* XXX: handle double-width characters */
function move_cursor(delta) {
var i, l;
if (delta > 0) {
@ -269,15 +293,16 @@ import * as os from "os";
}
function update() {
var i;
var i, cmd_len;
/* cursor_pos is the position in 16 bit characters inside the
UTF-16 string 'cmd' */
if (cmd != last_cmd) {
if (!show_colors && last_cmd.substring(0, last_cursor_pos) == cmd.substring(0, last_cursor_pos)) {
/* optimize common case */
std.puts(cmd.substring(last_cursor_pos));
} else {
/* goto the start of the line */
move_cursor(-last_cursor_pos);
move_cursor(-ucs_length(last_cmd.substring(0, last_cursor_pos)));
if (show_colors) {
var str = mexpr ? mexpr + '\n' + cmd : cmd;
var start = str.length - cmd.length;
@ -287,8 +312,7 @@ import * as os from "os";
std.puts(cmd);
}
}
/* Note: assuming no surrogate pairs */
term_cursor_x = (term_cursor_x + cmd.length) % term_width;
term_cursor_x = (term_cursor_x + ucs_length(cmd)) % term_width;
if (term_cursor_x == 0) {
/* show the cursor on the next line */
std.puts(" \x08");
@ -298,7 +322,11 @@ import * as os from "os";
last_cmd = cmd;
last_cursor_pos = cmd.length;
}
move_cursor(cursor_pos - last_cursor_pos);
if (cursor_pos > last_cursor_pos) {
move_cursor(ucs_length(cmd.substring(last_cursor_pos, cursor_pos)));
} else if (cursor_pos < last_cursor_pos) {
move_cursor(-ucs_length(cmd.substring(cursor_pos, last_cursor_pos)));
}
last_cursor_pos = cursor_pos;
std.out.flush();
}
@ -333,13 +361,19 @@ import * as os from "os";
}
function forward_char() {
if (cursor_pos < cmd.length)
if (cursor_pos < cmd.length) {
cursor_pos++;
while (is_trailing_surrogate(cmd.charAt(cursor_pos)))
cursor_pos++;
}
}
function backward_char() {
if (cursor_pos > 0)
if (cursor_pos > 0) {
cursor_pos--;
while (is_trailing_surrogate(cmd.charAt(cursor_pos)))
cursor_pos--;
}
}
function skip_word_forward(pos) {
@ -419,8 +453,18 @@ import * as os from "os";
}
function delete_char_dir(dir) {
var start = cursor_pos - (dir < 0);
var end = start + 1;
var start, end;
start = cursor_pos;
if (dir < 0) {
start--;
while (is_trailing_surrogate(cmd.charAt(start)))
start--;
}
end = start + 1;
while (is_trailing_surrogate(cmd.charAt(end)))
end++;
if (start >= 0 && start < cmd.length) {
if (last_fun === kill_region) {
kill_region(start, end, dir);
@ -752,7 +796,7 @@ import * as os from "os";
function readline_print_prompt()
{
std.puts(prompt);
term_cursor_x = prompt.length % term_width;
term_cursor_x = ucs_length(prompt) % term_width;
last_cmd = "";
last_cursor_pos = 0;
}
@ -785,7 +829,7 @@ import * as os from "os";
function handle_char(c1) {
var c;
c = String.fromCharCode(c1);
c = String.fromCodePoint(c1);
switch(readline_state) {
case 0:
if (c == '\x1b') { /* '^[' - ESC */
@ -825,7 +869,7 @@ import * as os from "os";
var fun;
if (quote_flag) {
if (keys.length === 1)
if (ucs_length(keys) === 1)
insert(keys);
quote_flag = false;
} else if (fun = commands[keys]) {
@ -845,7 +889,7 @@ import * as os from "os";
return;
}
last_fun = this_fun;
} else if (keys.length === 1 && keys >= ' ') {
} else if (ucs_length(keys) === 1 && keys >= ' ') {
insert(keys);
last_fun = insert;
} else {

View file

@ -58,6 +58,7 @@ arrow-function
async-functions
async-iteration
Atomics
Atomics.waitAsync=skip
BigInt
caller
class

View file

@ -2,6 +2,8 @@ test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-r
test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js:20: strict mode: Test262Error: Expected a ReferenceError but got a ReferenceError
test262/test/built-ins/Proxy/ownKeys/trap-is-undefined-target-is-proxy.js:29: Test262Error: Expected [0, length, foo, Symbol()] and [Symbol(), length, foo, 0] to have the same contents.
test262/test/built-ins/Proxy/ownKeys/trap-is-undefined-target-is-proxy.js:29: strict mode: Test262Error: Expected [0, length, foo, Symbol()] and [Symbol(), length, foo, 0] to have the same contents.
test262/test/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js:46: SyntaxError: invalid group name
test262/test/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js:46: strict mode: SyntaxError: invalid group name
test262/test/language/expressions/arrow-function/eval-var-scope-syntax-err.js:47: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/language/expressions/async-arrow-function/eval-var-scope-syntax-err.js:49: TypeError: $DONE() not called
test262/test/language/expressions/async-function/named-eval-var-scope-syntax-err.js:33: TypeError: $DONE() not called
@ -18,8 +20,8 @@ test262/test/language/expressions/object/method-definition/async-gen-meth-eval-v
test262/test/language/expressions/object/method-definition/async-meth-eval-var-scope-syntax-err.js:36: TypeError: $DONE() not called
test262/test/language/expressions/object/method-definition/gen-meth-eval-var-scope-syntax-err.js:54: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/language/expressions/object/method-definition/meth-eval-var-scope-syntax-err.js:50: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
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
test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: cannot read property 'c' of undefined
test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: cannot read property '_b' of undefined
test262/test/language/statements/async-function/eval-var-scope-syntax-err.js:33: TypeError: $DONE() not called
test262/test/language/statements/async-generator/eval-var-scope-syntax-err.js:28: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/language/statements/class/elements/grammar-private-field-optional-chaining.js:26: SyntaxError: expecting field name

View file

@ -31,6 +31,7 @@ static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val,
uint64_t pos, len;
JSValue obj;
size_t size;
int flags;
if (JS_ToIndex(ctx, &pos, argv[1]))
return JS_EXCEPTION;
@ -41,7 +42,10 @@ static JSValue js_bjson_read(JSContext *ctx, JSValueConst this_val,
return JS_EXCEPTION;
if (pos + len > size)
return JS_ThrowRangeError(ctx, "array buffer overflow");
obj = JS_ReadObject(ctx, buf + pos, len, 0);
flags = 0;
if (JS_ToBool(ctx, argv[3]))
flags |= JS_READ_OBJ_REFERENCE;
obj = JS_ReadObject(ctx, buf + pos, len, flags);
return obj;
}
@ -51,8 +55,12 @@ static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val,
size_t len;
uint8_t *buf;
JSValue array;
int flags;
buf = JS_WriteObject(ctx, &len, argv[0], 0);
flags = 0;
if (JS_ToBool(ctx, argv[1]))
flags |= JS_WRITE_OBJ_REFERENCE;
buf = JS_WriteObject(ctx, &len, argv[0], flags);
if (!buf)
return JS_EXCEPTION;
array = JS_NewArrayBufferCopy(ctx, buf, len);
@ -61,8 +69,8 @@ static JSValue js_bjson_write(JSContext *ctx, JSValueConst this_val,
}
static const JSCFunctionListEntry js_bjson_funcs[] = {
JS_CFUNC_DEF("read", 3, js_bjson_read ),
JS_CFUNC_DEF("write", 1, js_bjson_write ),
JS_CFUNC_DEF("read", 4, js_bjson_read ),
JS_CFUNC_DEF("write", 2, js_bjson_write ),
};
static int js_bjson_init(JSContext *ctx, JSModuleDef *m)

View file

@ -228,6 +228,19 @@ function prop_create(n)
return n * 4;
}
function prop_delete(n)
{
var obj, j;
obj = {};
for(j = 0; j < n; j++) {
obj[j] = 1;
}
for(j = 0; j < n; j++) {
delete obj[j];
}
return n;
}
function array_read(n)
{
var tab, len, sum, i, j;
@ -945,6 +958,7 @@ function main(argc, argv, g)
prop_read,
prop_write,
prop_create,
prop_delete,
array_read,
array_write,
array_prop_create,

View file

@ -1,12 +1,20 @@
import * as bjson from "./bjson.so";
function assert(b, str)
{
if (b) {
function assert(actual, expected, message) {
if (arguments.length == 1)
expected = true;
if (actual === expected)
return;
} else {
throw Error("assertion failed: " + str);
}
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 toHex(a)
@ -24,6 +32,20 @@ function toHex(a)
return s;
}
function isArrayLike(a)
{
return Array.isArray(a) ||
(a instanceof Uint8ClampedArray) ||
(a instanceof Uint8Array) ||
(a instanceof Uint16Array) ||
(a instanceof Uint32Array) ||
(a instanceof Int8Array) ||
(a instanceof Int16Array) ||
(a instanceof Int32Array) ||
(a instanceof Float32Array) ||
(a instanceof Float64Array);
}
function toStr(a)
{
var s, i, props, prop;
@ -32,7 +54,15 @@ function toStr(a)
case "object":
if (a === null)
return "null";
if (Array.isArray(a)) {
if (a instanceof Date) {
s = "Date(" + toStr(a.valueOf()) + ")";
} else if (a instanceof Number) {
s = "Number(" + toStr(a.valueOf()) + ")";
} else if (a instanceof String) {
s = "String(" + toStr(a.valueOf()) + ")";
} else if (a instanceof Boolean) {
s = "Boolean(" + toStr(a.valueOf()) + ")";
} else if (isArrayLike(a)) {
s = "[";
for(i = 0; i < a.length; i++) {
if (i != 0)
@ -85,6 +115,35 @@ function bjson_test(a)
}
}
/* test multiple references to an object including circular
references */
function bjson_test_reference()
{
var array, buf, i, n, array_buffer;
n = 16;
array = [];
for(i = 0; i < n; i++)
array[i] = {};
array_buffer = new ArrayBuffer(n);
for(i = 0; i < n; i++) {
array[i].next = array[(i + 1) % n];
array[i].idx = i;
array[i].typed_array = new Uint8Array(array_buffer, i, 1);
}
buf = bjson.write(array, true);
array = bjson.read(buf, 0, buf.byteLength, true);
/* check the result */
for(i = 0; i < n; i++) {
assert(array[i].next, array[(i + 1) % n]);
assert(array[i].idx, i);
assert(array[i].typed_array.buffer, array_buffer);
assert(array[i].typed_array.length, 1);
assert(array[i].typed_array.byteOffset, i);
}
}
function bjson_test_all()
{
var obj;
@ -111,6 +170,11 @@ function bjson_test_all()
BigDecimal("1.233e-1000")]);
}
bjson_test([new Date(1234), new String("abc"), new Number(-12.1), new Boolean(true)]);
bjson_test(new Int32Array([123123, 222111, -32222]));
bjson_test(new Float64Array([123123, 222111.5]));
/* tested with a circular reference */
obj = {};
obj.x = obj;
@ -120,6 +184,8 @@ function bjson_test_all()
} catch(e) {
assert(e instanceof TypeError);
}
bjson_test_reference();
}
bjson_test_all();

View file

@ -287,6 +287,10 @@ function test_math()
assert(Math.ceil(a), 2);
assert(Math.imul(0x12345678, 123), -1088058456);
assert(Math.fround(0.1), 0.10000000149011612);
assert(Math.hypot() == 0);
assert(Math.hypot(-2) == 2);
assert(Math.hypot(3, 4) == 5);
assert(Math.abs(Math.hypot(3, 4, 5) - 7.0710678118654755) <= 1e-15);
}
function test_number()

View file

@ -311,10 +311,21 @@ function test_template()
var a, b;
b = 123;
a = `abc${b}d`;
assert(a === "abc123d");
assert(a, "abc123d");
a = String.raw `abc${b}d`;
assert(a === "abc123d");
assert(a, "abc123d");
a = "aaa";
b = "bbb";
assert(`aaa${a, b}ccc`, "aaabbbccc");
}
function test_template_skip()
{
var a = "Bar";
var { b = `${a + `a${a}` }baz` } = {};
assert(b, "BaraBarbaz");
}
function test_object_literal()
@ -358,6 +369,7 @@ test_prototype();
test_arguments();
test_class();
test_template();
test_template_skip();
test_object_literal();
test_regexp_skip();
test_labels();

View file

@ -26,6 +26,12 @@ try { std.loadScript("test_assert.js"); } catch(e) {}
function test_printf()
{
assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc");
assert(std.sprintf("%010d", 123), "0000000123");
assert(std.sprintf("%x", -2), "fffffffe");
assert(std.sprintf("%lx", -2), "fffffffffffffffe");
assert(std.sprintf("%10.1f", 2.1), " 2.1");
assert(std.sprintf("%*.*f", 10, 2, -2.13), " -2.13");
assert(std.sprintf("%#lx", 0x7fffffffffffffffn), "0x7fffffffffffffff");
}
function test_file1()
@ -119,6 +125,20 @@ function test_popen()
os.remove(fname);
}
function test_ext_json()
{
var expected, input, obj;
expected = '{"x":false,"y":true,"z2":null,"a":[1,8,160],"s":"str"}';
input = `{ "x":false, /*comments are allowed */
"y":true, // also a comment
z2:null, // unquoted property names
"a":[+1,0o10,0xa0,], // plus prefix, octal, hexadecimal
"s":"str",} // trailing comma in objects and arrays
`;
obj = std.parseExtJSON(input);
assert(JSON.stringify(obj), expected);
}
function test_os()
{
var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path;
@ -258,3 +278,4 @@ test_popen();
test_os();
test_os_exec();
test_timer();
test_ext_json();

93
tests/test_worker.js Normal file
View file

@ -0,0 +1,93 @@
/* os.Worker API test */
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 + ")" : ""));
}
var worker;
function test_worker()
{
var counter;
/* Note: can use std.loadFile() to read from a file */
worker = new os.Worker(`
import * as std from "std";
import * as os from "os";
var parent = os.Worker.parent;
function handle_msg(e) {
var ev = e.data;
// print("child_recv", JSON.stringify(ev));
switch(ev.type) {
case "abort":
parent.postMessage({ type: "done" });
break;
case "sab":
/* modify the SharedArrayBuffer */
ev.buf[2] = 10;
parent.postMessage({ type: "sab_done", buf: ev.buf });
break;
}
}
function worker_main() {
var i;
parent.onmessage = handle_msg;
for(i = 0; i < 10; i++) {
parent.postMessage({ type: "num", num: i });
}
}
worker_main();
`);
counter = 0;
worker.onmessage = function (e) {
var ev = e.data;
// print("recv", JSON.stringify(ev));
switch(ev.type) {
case "num":
assert(ev.num, counter);
counter++;
if (counter == 10) {
/* test SharedArrayBuffer modification */
let sab = new SharedArrayBuffer(10);
let buf = new Uint8Array(sab);
worker.postMessage({ type: "sab", buf: buf });
}
break;
case "sab_done":
{
let buf = ev.buf;
/* check that the SharedArrayBuffer was modified */
assert(buf[2], 10);
worker.postMessage({ type: "abort" });
}
break;
case "done":
/* terminate */
worker.onmessage = null;
break;
}
};
}
test_worker();